diff --git a/cogl/.gitignore b/cogl/.gitignore new file mode 100644 index 000000000..25c354b0d --- /dev/null +++ b/cogl/.gitignore @@ -0,0 +1,107 @@ +ABOUT-NLS +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +compile +*.pc +.deps +.libs +.dirstamp +*.o +*.lo +*.la +*.gcov +*.exe +/README +stamp-enum-types +stamp-marshal +/build/autotools/*.m4 +/build/win32/*.bat +!/build/autotools/acglib.m4 +!/build/autotools/introspection.m4 +!/build/autotools/as-glibconfig.m4 +!/build/autotools/as-linguas.m4 +!/build/autotools/as-compiler-flag.m4 +/build/config.guess +/build/config.rpath +/build/config.sub +*.gir +*.typelib +/cogl-pango/cogl-pango.rc +/cogl/cogl.rc +/cogl/cogl-defines.h +/cogl/cogl-defines.h.win32 +/cogl/cogl-defines.h.win32_SDL +/cogl/cogl-egl-defines.h +/cogl/cogl-enum-types.c +/cogl/cogl-enum-types.h +/cogl/cogl-gl-header.h +/cogl-path/cogl-path-enum-types.c +/cogl-path/cogl-path-enum-types.h +config.h +config.h.in +config.h.win32 +config.log +config.lt +config.status +configure +depcomp +/deps/glib/glibconfig.h +/deps/gmodule/gmoduleconf.h +/doc/reference/cogl/cogl-docs.xml +/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml +/doc/reference/cogl-gst/cogl-gst-docs.xml +/examples/cogl-basic-video-player +/examples/cogl-crate +/examples/cogl-gles2-context +/examples/cogl-gles2-gears +/examples/cogl-hello +/examples/cogl-info +/examples/cogl-msaa +/examples/cogl-point-sprites +/examples/cogl-sdl-hello +/examples/cogl-sdl2-hello +/examples/cogl-stereo +/examples/cogl-x11-foreign +/examples/cogl-x11-tfp +/examples/cogland +gtk-doc.make +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +stamp-h1 +TAGS +/tests/tools/disable-npots.sh +/tests/conform/test-launcher.sh +/tests/interactive/wrapper.sh +/tests/conform/*.bat +/tests/conform/config.env +/tests/conform/.log +/tests/unit/.log +/tests/micro-perf/test-journal +/tests/config.env +/po/POTFILES +/po/*.gmo +/po/Makefile.in.in +/po/Makevars.template +/po/Rules-quot +/po/boldquot.sed +/po/en@boldquot.header +/po/en@quot.header +/po/insert-header.sin +/po/quot.sed +/po/remove-potcdate.sin +/po/remove-potcdate.sed +/po/stamp-po +*.swn +*.swo +*.swp +*~ +*.orig +*.rej +.DS_Store +.testlogs-* diff --git a/cogl/Makefile.am b/cogl/Makefile.am new file mode 100644 index 000000000..25f12fffc --- /dev/null +++ b/cogl/Makefile.am @@ -0,0 +1,37 @@ +SUBDIRS = test-fixtures + +SUBDIRS += cogl + +if BUILD_COGL_PATH +SUBDIRS += cogl-path +endif + +if BUILD_COGL_PANGO +SUBDIRS += cogl-pango +endif + +if BUILD_COGL_GLES2 +SUBDIRS += cogl-gles2 +endif + +SUBDIRS += tests + +ACLOCAL_AMFLAGS = -I build/autotools ${ACLOCAL_FLAGS} + +EXTRA_DIST = \ + config-custom.h + +# .changelog expects these to be initializes +CLEANFILES= +DISTCLEANFILES= + +DISTCHECK_CONFIGURE_FLAGS = \ + --enable-maintainer-flags \ + --enable-profile \ + --enable-gles2 \ + --enable-gl \ + --enable-xlib-egl-platform \ + --enable-wayland-egl-platform \ + --enable-glx \ + --enable-wayland-egl-server \ + --enable-cogl-gst diff --git a/cogl/build/autotools/Makefile.am.enums b/cogl/build/autotools/Makefile.am.enums new file mode 100644 index 000000000..2fd69d5bd --- /dev/null +++ b/cogl/build/autotools/Makefile.am.enums @@ -0,0 +1,52 @@ +# Rules for generating enumeration types using glib-mkenums +# +# Define: +# glib_enum_h = header template file +# glib_enum_c = source template file +# glib_enum_headers = list of headers to parse +# +# before including Makefile.am.enums. You will also need to have +# the following targets already defined: +# +# CLEANFILES +# DISTCLEANFILES +# BUILT_SOURCES +# EXTRA_DIST +# +# Author: Emmanuele Bassi + +# Basic sanity checks +$(if $(GLIB_MKENUMS),,$(error Need to define GLIB_MKENUMS)) + +$(if $(or $(glib_enum_h), \ + $(glib_enum_c)),, \ + $(error Need to define glib_enum_h and glib_enum_c)) + +$(if $(glib_enum_headers),,$(error Need to define glib_enum_headers)) + +enum_tmpl_h=$(addprefix $(srcdir)/, $(glib_enum_h:.h=.h.in)) +enum_tmpl_c=$(addprefix $(srcdir)/, $(glib_enum_c:.c=.c.in)) +enum_headers=$(addprefix $(srcdir)/, $(glib_enum_headers)) + +CLEANFILES += stamp-enum-types +DISTCLEANFILES += $(glib_enum_h) $(glib_enum_c) +BUILT_SOURCES += $(glib_enum_h) $(glib_enum_c) +EXTRA_DIST += $(enum_tmpl_h) $(enum_tmpl_c) + +stamp-enum-types: $(enum_headers) $(enum_tmpl_h) + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --template $(enum_tmpl_h) \ + $(enum_headers) > xgen-eh \ + && (cmp -s xgen-eh $(glib_enum_h) || cp -f xgen-eh $(glib_enum_h)) \ + && rm -f xgen-eh \ + && echo timestamp > $(@F) + +$(glib_enum_h): stamp-enum-types + @true + +$(glib_enum_c): $(enum_headers) $(enum_tmpl_h) $(enum_tmpl_c) + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --template $(enum_tmpl_c) \ + $(enum_headers) > xgen-ec \ + && cp -f xgen-ec $(glib_enum_c) \ + && rm -f xgen-ec diff --git a/cogl/build/autotools/as-compiler-flag.m4 b/cogl/build/autotools/as-compiler-flag.m4 new file mode 100644 index 000000000..0f660cf07 --- /dev/null +++ b/cogl/build/autotools/as-compiler-flag.m4 @@ -0,0 +1,62 @@ +dnl as-compiler-flag.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flags + +dnl David Schleef + +dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $ + +dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + m4_ifvaln([$2],[$2]) + true + else + m4_ifvaln([$3],[$3]) + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_COMPILER_FLAGS(VAR, FLAGS) +dnl Tries to compile with the given CFLAGS. + +AC_DEFUN([AS_COMPILER_FLAGS], +[ + list=$2 + flags_supported="" + flags_unsupported="" + AC_MSG_CHECKING([for supported compiler flags]) + for each in $list + do + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $each" + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + flags_supported="$flags_supported $each" + else + flags_unsupported="$flags_unsupported $each" + fi + done + AC_MSG_RESULT([$flags_supported]) + if test "X$flags_unsupported" != X ; then + AC_MSG_WARN([unsupported compiler flags: $flags_unsupported]) + fi + $1="$$1 $flags_supported" +]) + diff --git a/cogl/build/autotools/introspection.m4 b/cogl/build/autotools/introspection.m4 new file mode 100644 index 000000000..589721c5a --- /dev/null +++ b/cogl/build/autotools/introspection.m4 @@ -0,0 +1,94 @@ +dnl -*- mode: autoconf -*- +dnl Copyright 2009 Johan Dahlin +dnl +dnl This file is free software; the author(s) gives unlimited +dnl permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl + +# serial 1 + +m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], +[ + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([LT_INIT],[$0])dnl setup libtool first + + dnl enable/disable introspection + m4_if([$2], [require], + [dnl + enable_introspection=yes + ],[dnl + AC_ARG_ENABLE(introspection, + AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], + [Enable introspection for this build]),, + [enable_introspection=auto]) + ])dnl + + AC_MSG_CHECKING([for gobject-introspection]) + + dnl presence/version checking + AS_CASE([$enable_introspection], + [no], [dnl + found_introspection="no (disabled, use --enable-introspection to enable)" + ],dnl + [yes],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0],, + AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], + found_introspection=yes, + AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) + ],dnl + [auto],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) + ],dnl + [dnl + AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) + ])dnl + + AC_MSG_RESULT([$found_introspection]) + + INTROSPECTION_SCANNER= + INTROSPECTION_COMPILER= + INTROSPECTION_GENERATE= + INTROSPECTION_GIRDIR= + INTROSPECTION_TYPELIBDIR= + if test "x$found_introspection" = "xyes"; then + INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` + INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` + INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` + INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" + INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` + INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` + INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection + fi + AC_SUBST(INTROSPECTION_SCANNER) + AC_SUBST(INTROSPECTION_COMPILER) + AC_SUBST(INTROSPECTION_GENERATE) + AC_SUBST(INTROSPECTION_GIRDIR) + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_CFLAGS) + AC_SUBST(INTROSPECTION_LIBS) + AC_SUBST(INTROSPECTION_MAKEFILE) + + AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") +]) + + +dnl Usage: +dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) + +AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) +]) + +dnl Usage: +dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) + + +AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) +]) diff --git a/cogl/cogl-gles2/GLES2/gl2.h b/cogl/cogl-gles2/GLES2/gl2.h new file mode 100644 index 000000000..718fb3b9b --- /dev/null +++ b/cogl/cogl-gles2/GLES2/gl2.h @@ -0,0 +1,169 @@ +#ifndef __gl2_h_ +#define __gl2_h_ + +/* $Revision: 16803 $ on $Date:: 2012-02-02 09:49:18 -0800 #$ */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/*------------------------------------------------------------------------- + * GL core functions. + *-----------------------------------------------------------------------*/ + +GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture); +GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar* name); +GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); +GL_APICALL void GL_APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_APICALL void GL_APIENTRY glBlendEquation ( GLenum mode ); +GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); +GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data); +GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target); +GL_APICALL void GL_APIENTRY glClear (GLbitfield mask); +GL_APICALL void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_APICALL void GL_APIENTRY glClearDepthf (GLclampf depth); +GL_APICALL void GL_APIENTRY glClearStencil (GLint s); +GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader); +GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL GLuint GL_APIENTRY glCreateProgram (void); +GL_APICALL GLuint GL_APIENTRY glCreateShader (GLenum type); +GL_APICALL void GL_APIENTRY glCullFace (GLenum mode); +GL_APICALL void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers); +GL_APICALL void GL_APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers); +GL_APICALL void GL_APIENTRY glDeleteProgram (GLuint program); +GL_APICALL void GL_APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers); +GL_APICALL void GL_APIENTRY glDeleteShader (GLuint shader); +GL_APICALL void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint* textures); +GL_APICALL void GL_APIENTRY glDepthFunc (GLenum func); +GL_APICALL void GL_APIENTRY glDepthMask (GLboolean flag); +GL_APICALL void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY glDisable (GLenum cap); +GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices); +GL_APICALL void GL_APIENTRY glEnable (GLenum cap); +GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY glFinish (void); +GL_APICALL void GL_APIENTRY glFlush (void); +GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_APICALL void GL_APIENTRY glFrontFace (GLenum mode); +GL_APICALL void GL_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers); +GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target); +GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers); +GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers); +GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint* textures); +GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); +GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const GLchar* name); +GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params); +GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL GLenum GL_APIENTRY glGetError (void); +GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog); +GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog); +GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); +GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source); +GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenum name); +GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint* params); +GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const GLchar* name); +GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid** pointer); +GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode); +GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); +GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap); +GL_APICALL GLboolean GL_APIENTRY glIsFramebuffer (GLuint framebuffer); +GL_APICALL GLboolean GL_APIENTRY glIsProgram (GLuint program); +GL_APICALL GLboolean GL_APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GL_APICALL GLboolean GL_APIENTRY glIsShader (GLuint shader); +GL_APICALL GLboolean GL_APIENTRY glIsTexture (GLuint texture); +GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width); +GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program); +GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels); +GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void); +GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length); +GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); +GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); +GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass); +GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat* params); +GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint* params); +GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat x); +GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint x); +GL_APICALL void GL_APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform2f (GLint location, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform2i (GLint location, GLint x, GLint y); +GL_APICALL void GL_APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform3i (GLint location, GLint x, GLint y, GLint z); +GL_APICALL void GL_APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w); +GL_APICALL void GL_APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUseProgram (GLuint program); +GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program); +GL_APICALL void GL_APIENTRY glVertexAttrib1f (GLuint indx, GLfloat x); +GL_APICALL void GL_APIENTRY glVertexAttrib1fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY glVertexAttrib2fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr); +GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +#ifdef __cplusplus +} +#endif + +#endif /* __gl2_h_ */ diff --git a/cogl/cogl-gles2/GLES2/gl2ext.h b/cogl/cogl-gles2/GLES2/gl2ext.h new file mode 100644 index 000000000..e4016a5ad --- /dev/null +++ b/cogl/cogl-gles2/GLES2/gl2ext.h @@ -0,0 +1,1498 @@ +#ifndef __gl2ext_h_ +#define __gl2ext_h_ + +/* $Revision: 16994 $ on $Date:: 2012-02-29 18:29:34 -0800 #$ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +#ifndef GL_APIENTRYP +# define GL_APIENTRYP GL_APIENTRY* +#endif + +/*------------------------------------------------------------------------* + * OES extension tokens + *------------------------------------------------------------------------*/ + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#endif + +/* GL_OES_depth_texture */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +typedef void* GLeglImageOES; +#endif + +/* GL_OES_EGL_image_external */ +#ifndef GL_OES_EGL_image_external +/* GLeglImageOES defined in GL_OES_EGL_image already. */ +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_SAMPLER_EXTERNAL_OES 0x8D66 +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_UNSIGNED_INT 0x1405 +#endif + +/* GL_OES_get_program_binary */ +#ifndef GL_OES_get_program_binary +#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE +#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA8_OES 0x8058 +#endif + +/* GL_OES_standard_derivatives */ +#ifndef GL_OES_standard_derivatives +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#endif + +/* GL_OES_texture_3D */ +#ifndef GL_OES_texture_3D +#define GL_TEXTURE_WRAP_R_OES 0x8072 +#define GL_TEXTURE_3D_OES 0x806F +#define GL_TEXTURE_BINDING_3D_OES 0x806A +#define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073 +#define GL_SAMPLER_3D_OES 0x8B5F +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4 +#endif + +/* GL_OES_texture_float */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_texture_float_linear */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_texture_half_float */ +#ifndef GL_OES_texture_half_float +#define GL_HALF_FLOAT_OES 0x8D61 +#endif + +/* GL_OES_texture_half_float_linear */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_texture_npot */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#endif + +/* GL_OES_vertex_half_float */ +/* GL_HALF_FLOAT_OES defined in GL_OES_texture_half_float already. */ + +/* GL_OES_vertex_type_10_10_10_2 */ +#ifndef GL_OES_vertex_type_10_10_10_2 +#define GL_UNSIGNED_INT_10_10_10_2_OES 0x8DF6 +#define GL_INT_10_10_10_2_OES 0x8DF7 +#endif + +/*------------------------------------------------------------------------* + * AMD extension tokens + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_3DC_X_AMD 0x87F9 +#define GL_3DC_XY_AMD 0x87FA +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +/* GL_AMD_performance_monitor */ +#ifndef GL_AMD_performance_monitor +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +#endif + +/* GL_AMD_program_binary_Z400 */ +#ifndef GL_AMD_program_binary_Z400 +#define GL_Z400_BINARY_AMD 0x8740 +#endif + +/*------------------------------------------------------------------------* + * ANGLE extension tokens + *------------------------------------------------------------------------*/ + +/* GL_ANGLE_framebuffer_blit */ +#ifndef GL_ANGLE_framebuffer_blit +#define GL_READ_FRAMEBUFFER_ANGLE 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_ANGLE 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_ANGLE 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_ANGLE 0x8CAA +#endif + +/* GL_ANGLE_framebuffer_multisample */ +#ifndef GL_ANGLE_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_ANGLE 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE 0x8D56 +#define GL_MAX_SAMPLES_ANGLE 0x8D57 +#endif + +/* GL_ANGLE_instanced_arrays */ +#ifndef GL_ANGLE_instanced_arrays +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE 0x88FE +#endif + +/* GL_ANGLE_pack_reverse_row_order */ +#ifndef GL_ANGLE_pack_reverse_row_order +#define GL_PACK_REVERSE_ROW_ORDER_ANGLE 0x93A4 +#endif + +/* GL_ANGLE_texture_compression_dxt3 */ +#ifndef GL_ANGLE_texture_compression_dxt3 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE 0x83F2 +#endif + +/* GL_ANGLE_texture_compression_dxt5 */ +#ifndef GL_ANGLE_texture_compression_dxt5 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE 0x83F3 +#endif + +/* GL_ANGLE_texture_usage */ +#ifndef GL_ANGLE_texture_usage +#define GL_TEXTURE_USAGE_ANGLE 0x93A2 +#define GL_FRAMEBUFFER_ATTACHMENT_ANGLE 0x93A3 +#endif + +/* GL_ANGLE_translated_shader_source */ +#ifndef GL_ANGLE_translated_shader_source +#define GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE 0x93A0 +#endif + +/*------------------------------------------------------------------------* + * APPLE extension tokens + *------------------------------------------------------------------------*/ + +/* GL_APPLE_rgb_422 */ +#ifndef GL_APPLE_rgb_422 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#endif + +/* GL_APPLE_framebuffer_multisample */ +#ifndef GL_APPLE_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_APPLE 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_APPLE 0x8D56 +#define GL_MAX_SAMPLES_APPLE 0x8D57 +#define GL_READ_FRAMEBUFFER_APPLE 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_APPLE 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_APPLE 0x8CAA +#endif + +/* GL_APPLE_texture_format_BGRA8888 */ +#ifndef GL_APPLE_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + +/* GL_APPLE_texture_max_level */ +#ifndef GL_APPLE_texture_max_level +#define GL_TEXTURE_MAX_LEVEL_APPLE 0x813D +#endif + +/*------------------------------------------------------------------------* + * ARM extension tokens + *------------------------------------------------------------------------*/ + +/* GL_ARM_mali_shader_binary */ +#ifndef GL_ARM_mali_shader_binary +#define GL_MALI_SHADER_BINARY_ARM 0x8F60 +#endif + +/* GL_ARM_rgba8 */ +/* No new tokens introduced by this extension. */ + +/*------------------------------------------------------------------------* + * EXT extension tokens + *------------------------------------------------------------------------*/ + +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#endif + +/* GL_EXT_color_buffer_half_float */ +#ifndef GL_EXT_color_buffer_half_float +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_RG16F_EXT 0x822F +#define GL_R16F_EXT 0x822D +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211 +#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17 +#endif + +/* GL_EXT_debug_label */ +#ifndef GL_EXT_debug_label +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +#endif + +/* GL_EXT_debug_marker */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_COLOR_EXT 0x1800 +#define GL_DEPTH_EXT 0x1801 +#define GL_STENCIL_EXT 0x1802 +#endif + +/* GL_EXT_multisampled_render_to_texture */ +#ifndef GL_EXT_multisampled_render_to_texture +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT 0x8D6C +#define GL_RENDERBUFFER_SAMPLES_EXT 0x9133 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x9134 +#define GL_MAX_SAMPLES_EXT 0x9135 +#endif + +/* GL_EXT_multi_draw_arrays */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_occlusion_query_boolean */ +#ifndef GL_EXT_occlusion_query_boolean +#define GL_ANY_SAMPLES_PASSED_EXT 0x8C2F +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A +#define GL_CURRENT_QUERY_EXT 0x8865 +#define GL_QUERY_RESULT_EXT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867 +#endif + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_BGRA_EXT 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#endif + +/* GL_EXT_robustness */ +#ifndef GL_EXT_robustness +/* reuse GL_NO_ERROR */ +#define GL_GUILTY_CONTEXT_RESET_EXT 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_EXT 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_EXT 0x8255 +#define GL_CONTEXT_ROBUST_ACCESS_EXT 0x90F3 +#define GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252 +#define GL_NO_RESET_NOTIFICATION_EXT 0x8261 +#endif + +/* GL_EXT_separate_shader_objects */ +#ifndef GL_EXT_separate_shader_objects +#define GL_VERTEX_SHADER_BIT_EXT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT_EXT 0x00000002 +#define GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE_EXT 0x8258 +#define GL_ACTIVE_PROGRAM_EXT 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A +#endif + +/* GL_EXT_shader_texture_lod */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_shadow_samplers */ +#ifndef GL_EXT_shadow_samplers +#define GL_TEXTURE_COMPARE_MODE_EXT 0x884C +#define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D +#define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E +#define GL_SAMPLER_2D_SHADOW_EXT 0x8B62 +#endif + +/* GL_EXT_sRGB */ +#ifndef GL_EXT_sRGB +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT 0x8210 +#endif + +/* GL_EXT_texture_compression_dxt1 */ +#ifndef GL_EXT_texture_compression_dxt1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + +/* GL_EXT_texture_rg */ +#ifndef GL_EXT_texture_rg +#define GL_RED_EXT 0x1903 +#define GL_RG_EXT 0x8227 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#endif + +/* GL_EXT_texture_storage */ +#ifndef GL_EXT_texture_storage +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_ALPHA8_EXT 0x803C +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGB32F_EXT 0x8815 +#define GL_ALPHA32F_EXT 0x8816 +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +/* reuse GL_RGBA16F_EXT */ +/* reuse GL_RGB16F_EXT */ +#define GL_ALPHA16F_EXT 0x881C +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGB10_EXT 0x8052 +#define GL_BGRA8_EXT 0x93A1 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#define GL_R32F_EXT 0x822E +#define GL_RG32F_EXT 0x8230 +#define GL_R16F_EXT 0x822D +#define GL_RG16F_EXT 0x822F +#endif + +/* GL_EXT_texture_type_2_10_10_10_REV */ +#ifndef GL_EXT_texture_type_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368 +#endif + +/* GL_EXT_unpack_subimage */ +#ifndef GL_EXT_unpack_subimage +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#endif + +/*------------------------------------------------------------------------* + * DMP extension tokens + *------------------------------------------------------------------------*/ + +/* GL_DMP_shader_binary */ +#ifndef GL_DMP_shader_binary +#define GL_SHADER_BINARY_DMP 0x9250 +#endif + +/*------------------------------------------------------------------------* + * IMG extension tokens + *------------------------------------------------------------------------*/ + +/* GL_IMG_program_binary */ +#ifndef GL_IMG_program_binary +#define GL_SGX_PROGRAM_BINARY_IMG 0x9130 +#endif + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_BGRA_IMG 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365 +#endif + +/* GL_IMG_shader_binary */ +#ifndef GL_IMG_shader_binary +#define GL_SGX_BINARY_IMG 0x8C0A +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134 +#define GL_MAX_SAMPLES_IMG 0x9135 +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif + +/*------------------------------------------------------------------------* + * NV extension tokens + *------------------------------------------------------------------------*/ + +/* GL_NV_coverage_sample */ +#ifndef GL_NV_coverage_sample +#define GL_COVERAGE_COMPONENT_NV 0x8ED0 +#define GL_COVERAGE_COMPONENT4_NV 0x8ED1 +#define GL_COVERAGE_ATTACHMENT_NV 0x8ED2 +#define GL_COVERAGE_BUFFERS_NV 0x8ED3 +#define GL_COVERAGE_SAMPLES_NV 0x8ED4 +#define GL_COVERAGE_ALL_FRAGMENTS_NV 0x8ED5 +#define GL_COVERAGE_EDGE_FRAGMENTS_NV 0x8ED6 +#define GL_COVERAGE_AUTOMATIC_NV 0x8ED7 +#define GL_COVERAGE_BUFFER_BIT_NV 0x8000 +#endif + +/* GL_NV_depth_nonlinear */ +#ifndef GL_NV_depth_nonlinear +#define GL_DEPTH_COMPONENT16_NONLINEAR_NV 0x8E2C +#endif + +/* GL_NV_draw_buffers */ +#ifndef GL_NV_draw_buffers +#define GL_MAX_DRAW_BUFFERS_NV 0x8824 +#define GL_DRAW_BUFFER0_NV 0x8825 +#define GL_DRAW_BUFFER1_NV 0x8826 +#define GL_DRAW_BUFFER2_NV 0x8827 +#define GL_DRAW_BUFFER3_NV 0x8828 +#define GL_DRAW_BUFFER4_NV 0x8829 +#define GL_DRAW_BUFFER5_NV 0x882A +#define GL_DRAW_BUFFER6_NV 0x882B +#define GL_DRAW_BUFFER7_NV 0x882C +#define GL_DRAW_BUFFER8_NV 0x882D +#define GL_DRAW_BUFFER9_NV 0x882E +#define GL_DRAW_BUFFER10_NV 0x882F +#define GL_DRAW_BUFFER11_NV 0x8830 +#define GL_DRAW_BUFFER12_NV 0x8831 +#define GL_DRAW_BUFFER13_NV 0x8832 +#define GL_DRAW_BUFFER14_NV 0x8833 +#define GL_DRAW_BUFFER15_NV 0x8834 +#define GL_COLOR_ATTACHMENT0_NV 0x8CE0 +#define GL_COLOR_ATTACHMENT1_NV 0x8CE1 +#define GL_COLOR_ATTACHMENT2_NV 0x8CE2 +#define GL_COLOR_ATTACHMENT3_NV 0x8CE3 +#define GL_COLOR_ATTACHMENT4_NV 0x8CE4 +#define GL_COLOR_ATTACHMENT5_NV 0x8CE5 +#define GL_COLOR_ATTACHMENT6_NV 0x8CE6 +#define GL_COLOR_ATTACHMENT7_NV 0x8CE7 +#define GL_COLOR_ATTACHMENT8_NV 0x8CE8 +#define GL_COLOR_ATTACHMENT9_NV 0x8CE9 +#define GL_COLOR_ATTACHMENT10_NV 0x8CEA +#define GL_COLOR_ATTACHMENT11_NV 0x8CEB +#define GL_COLOR_ATTACHMENT12_NV 0x8CEC +#define GL_COLOR_ATTACHMENT13_NV 0x8CED +#define GL_COLOR_ATTACHMENT14_NV 0x8CEE +#define GL_COLOR_ATTACHMENT15_NV 0x8CEF +#endif + +/* GL_NV_fbo_color_attachments */ +#ifndef GL_NV_fbo_color_attachments +#define GL_MAX_COLOR_ATTACHMENTS_NV 0x8CDF +/* GL_COLOR_ATTACHMENT{0-15}_NV defined in GL_NV_draw_buffers already. */ +#endif + +/* GL_NV_fence */ +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#endif + +/* GL_NV_read_buffer */ +#ifndef GL_NV_read_buffer +#define GL_READ_BUFFER_NV 0x0C02 +#endif + +/* GL_NV_read_buffer_front */ +/* No new tokens introduced by this extension. */ + +/* GL_NV_read_depth */ +/* No new tokens introduced by this extension. */ + +/* GL_NV_read_depth_stencil */ +/* No new tokens introduced by this extension. */ + +/* GL_NV_read_stencil */ +/* No new tokens introduced by this extension. */ + +/* GL_NV_texture_compression_s3tc_update */ +/* No new tokens introduced by this extension. */ + +/* GL_NV_texture_npot_2D_mipmap */ +/* No new tokens introduced by this extension. */ + +/*------------------------------------------------------------------------* + * QCOM extension tokens + *------------------------------------------------------------------------*/ + +/* GL_QCOM_alpha_test */ +#ifndef GL_QCOM_alpha_test +#define GL_ALPHA_TEST_QCOM 0x0BC0 +#define GL_ALPHA_TEST_FUNC_QCOM 0x0BC1 +#define GL_ALPHA_TEST_REF_QCOM 0x0BC2 +#endif + +/* GL_QCOM_driver_control */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_TEXTURE_WIDTH_QCOM 0x8BD2 +#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3 +#define GL_TEXTURE_DEPTH_QCOM 0x8BD4 +#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5 +#define GL_TEXTURE_FORMAT_QCOM 0x8BD6 +#define GL_TEXTURE_TYPE_QCOM 0x8BD7 +#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8 +#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9 +#define GL_TEXTURE_TARGET_QCOM 0x8BDA +#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB +#define GL_STATE_RESTORE 0x8BDC +#endif + +/* GL_QCOM_extended_get2 */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_WRITEONLY_RENDERING_QCOM 0x8823 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001 +#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002 +#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004 +#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008 +#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010 +#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020 +#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040 +#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080 +#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100 +#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200 +#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400 +#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800 +#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000 +#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000 +#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000 +#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000 +#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000 +#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000 +#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000 +#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000 +#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000 +#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000 +#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000 +#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000 +#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000 +#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000 +#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000 +#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000 +#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000 +#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000 +#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000 +#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000 +#endif + +/*------------------------------------------------------------------------* + * VIV extension tokens + *------------------------------------------------------------------------*/ + +/* GL_VIV_shader_binary */ +#ifndef GL_VIV_shader_binary +#define GL_SHADER_BINARY_VIV 0x8FC4 +#endif + +/*------------------------------------------------------------------------* + * End of extension tokens, start of corresponding extension functions + *------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------* + * OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_OES_compressed_ETC1_RGB8_texture 1 +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_OES_depth24 1 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_OES_depth32 1 +#endif + +/* GL_OES_depth_texture */ +#ifndef GL_OES_depth_texture +#define GL_OES_depth_texture 1 +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +#define GL_OES_EGL_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); +#endif +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); +#endif + +/* GL_OES_EGL_image_external */ +#ifndef GL_OES_EGL_image_external +#define GL_OES_EGL_image_external 1 +/* glEGLImageTargetTexture2DOES defined in GL_OES_EGL_image already. */ +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_OES_element_index_uint 1 +#endif + +/* GL_OES_fbo_render_mipmap */ +#ifndef GL_OES_fbo_render_mipmap +#define GL_OES_fbo_render_mipmap 1 +#endif + +/* GL_OES_fragment_precision_high */ +#ifndef GL_OES_fragment_precision_high +#define GL_OES_fragment_precision_high 1 +#endif + +/* GL_OES_get_program_binary */ +#ifndef GL_OES_get_program_binary +#define GL_OES_get_program_binary 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length); +#endif +typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length); +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_OES_mapbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); +GL_APICALL GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); +GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid** params); +#endif +typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); +typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid** params); +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_OES_packed_depth_stencil 1 +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_OES_rgb8_rgba8 1 +#endif + +/* GL_OES_standard_derivatives */ +#ifndef GL_OES_standard_derivatives +#define GL_OES_standard_derivatives 1 +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_OES_stencil1 1 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_OES_stencil4 1 +#endif + +/* GL_OES_texture_3D */ +#ifndef GL_OES_texture_3D +#define GL_OES_texture_3D 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glCopyTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glFramebufferTexture3DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +#endif +typedef void (GL_APIENTRYP PFNGLTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels); +typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels); +typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data); +typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +#endif + +/* GL_OES_texture_float */ +#ifndef GL_OES_texture_float +#define GL_OES_texture_float 1 +#endif + +/* GL_OES_texture_float_linear */ +#ifndef GL_OES_texture_float_linear +#define GL_OES_texture_float_linear 1 +#endif + +/* GL_OES_texture_half_float */ +#ifndef GL_OES_texture_half_float +#define GL_OES_texture_half_float 1 +#endif + +/* GL_OES_texture_half_float_linear */ +#ifndef GL_OES_texture_half_float_linear +#define GL_OES_texture_half_float_linear 1 +#endif + +/* GL_OES_texture_npot */ +#ifndef GL_OES_texture_npot +#define GL_OES_texture_npot 1 +#endif + +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_OES_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glBindVertexArrayOES (GLuint array); +GL_APICALL void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays); +GL_APICALL void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays); +GL_APICALL GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array); +#endif +typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array); +typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays); +typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array); +#endif + +/* GL_OES_vertex_half_float */ +#ifndef GL_OES_vertex_half_float +#define GL_OES_vertex_half_float 1 +#endif + +/* GL_OES_vertex_type_10_10_10_2 */ +#ifndef GL_OES_vertex_type_10_10_10_2 +#define GL_OES_vertex_type_10_10_10_2 1 +#endif + +/*------------------------------------------------------------------------* + * AMD extension functions + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_AMD_compressed_3DC_texture 1 +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_AMD_compressed_ATC_texture 1 +#endif + +/* AMD_performance_monitor */ +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, GLvoid *data); +GL_APICALL void GL_APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GL_APICALL void GL_APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GL_APICALL void GL_APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList); +GL_APICALL void GL_APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GL_APICALL void GL_APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, GLvoid *data); +typedef void (GL_APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (GL_APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (GL_APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList); +typedef void (GL_APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (GL_APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif + +/* GL_AMD_program_binary_Z400 */ +#ifndef GL_AMD_program_binary_Z400 +#define GL_AMD_program_binary_Z400 1 +#endif + +/*------------------------------------------------------------------------* + * ANGLE extension functions + *------------------------------------------------------------------------*/ + +/* GL_ANGLE_framebuffer_blit */ +#ifndef GL_ANGLE_framebuffer_blit +#define GL_ANGLE_framebuffer_blit 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glBlitFramebufferANGLE (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +typedef void (GL_APIENTRYP PFNGLBLITFRAMEBUFFERANGLEPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif + +/* GL_ANGLE_framebuffer_multisample */ +#ifndef GL_ANGLE_framebuffer_multisample +#define GL_ANGLE_framebuffer_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleANGLE (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEANGLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +#ifndef GL_ANGLE_instanced_arrays +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glDrawArraysInstancedANGLE (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GL_APICALL void GL_APIENTRY glDrawElementsInstancedANGLE (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +GL_APICALL void GL_APIENTRY glVertexAttribDivisorANGLE (GLuint index, GLuint divisor); +#endif +typedef void (GL_APIENTRYP PFLGLDRAWARRAYSINSTANCEDANGLEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (GL_APIENTRYP PFLGLDRAWELEMENTSINSTANCEDANGLEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +typedef void (GL_APIENTRYP PFLGLVERTEXATTRIBDIVISORANGLEPROC) (GLuint index, GLuint divisor); +#endif + +/* GL_ANGLE_pack_reverse_row_order */ +#ifndef GL_ANGLE_pack_reverse_row_order +#define GL_ANGLE_pack_reverse_row_order 1 +#endif + +/* GL_ANGLE_texture_compression_dxt3 */ +#ifndef GL_ANGLE_texture_compression_dxt3 +#define GL_ANGLE_texture_compression_dxt3 1 +#endif + +/* GL_ANGLE_texture_compression_dxt5 */ +#ifndef GL_ANGLE_texture_compression_dxt5 +#define GL_ANGLE_texture_compression_dxt5 1 +#endif + +/* GL_ANGLE_texture_usage */ +#ifndef GL_ANGLE_texture_usage +#define GL_ANGLE_texture_usage 1 +#endif + +#ifndef GL_ANGLE_translated_shader_source +#define GL_ANGLE_translated_shader_source 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glGetTranslatedShaderSourceANGLE (GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source); +#endif +typedef void (GL_APIENTRYP PFLGLGETTRANSLATEDSHADERSOURCEANGLEPROC) (GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source); +#endif + +/*------------------------------------------------------------------------* + * APPLE extension functions + *------------------------------------------------------------------------*/ + +/* GL_APPLE_rgb_422 */ +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#endif + +/* GL_APPLE_framebuffer_multisample */ +#ifndef GL_APPLE_framebuffer_multisample +#define GL_APPLE_framebuffer_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleAPPLE (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +GL_APICALL void GL_APIENTRY glResolveMultisampleFramebufferAPPLE (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEAPPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLRESOLVEMULTISAMPLEFRAMEBUFFERAPPLEPROC) (void); +#endif + +/* GL_APPLE_texture_format_BGRA8888 */ +#ifndef GL_APPLE_texture_format_BGRA8888 +#define GL_APPLE_texture_format_BGRA8888 1 +#endif + +/* GL_APPLE_texture_max_level */ +#ifndef GL_APPLE_texture_max_level +#define GL_APPLE_texture_max_level 1 +#endif + +/*------------------------------------------------------------------------* + * ARM extension functions + *------------------------------------------------------------------------*/ + +/* GL_ARM_mali_shader_binary */ +#ifndef GL_ARM_mali_shader_binary +#define GL_ARM_mali_shader_binary 1 +#endif + +/* GL_ARM_rgba8 */ +#ifndef GL_ARM_rgba8 +#define GL_ARM_rgba8 1 +#endif + +/*------------------------------------------------------------------------* + * EXT extension functions + *------------------------------------------------------------------------*/ + +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#endif + +/* GL_EXT_color_buffer_half_float */ +#ifndef GL_EXT_color_buffer_half_float +#define GL_EXT_color_buffer_half_float 1 +#endif + +/* GL_EXT_debug_label */ +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GL_APICALL void GL_APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +typedef void (GL_APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (GL_APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif + +/* GL_EXT_debug_marker */ +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GL_APICALL void GL_APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GL_APICALL void GL_APIENTRY glPopGroupMarkerEXT (void); +#endif +typedef void (GL_APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (GL_APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (GL_APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_EXT_discard_framebuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif +typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif + +/* GL_EXT_multisampled_render_to_texture */ +#ifndef GL_EXT_multisampled_render_to_texture +#define GL_EXT_multisampled_render_to_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleEXT (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleEXT (GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glMultiDrawArraysEXT (GLenum, GLint *, GLsizei *, GLsizei); +GL_APICALL void GL_APIENTRY glMultiDrawElementsEXT (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +/* GL_EXT_occlusion_query_boolean */ +#ifndef GL_EXT_occlusion_query_boolean +#define GL_EXT_occlusion_query_boolean 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glGenQueriesEXT (GLsizei n, GLuint *ids); +GL_APICALL void GL_APIENTRY glDeleteQueriesEXT (GLsizei n, const GLuint *ids); +GL_APICALL GLboolean GL_APIENTRY glIsQueryEXT (GLuint id); +GL_APICALL void GL_APIENTRY glBeginQueryEXT (GLenum target, GLuint id); +GL_APICALL void GL_APIENTRY glEndQueryEXT (GLenum target); +GL_APICALL void GL_APIENTRY glGetQueryivEXT (GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glGetQueryObjectuivEXT (GLuint id, GLenum pname, GLuint *params); +#endif +typedef void (GL_APIENTRYP PFNGLGENQUERIESEXTPROC) (GLsizei n, GLuint *ids); +typedef void (GL_APIENTRYP PFNGLDELETEQUERIESEXTPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (GL_APIENTRYP PFNGLISQUERYEXTPROC) (GLuint id); +typedef void (GL_APIENTRYP PFNGLBEGINQUERYEXTPROC) (GLenum target, GLuint id); +typedef void (GL_APIENTRYP PFNGLENDQUERYEXTPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLGETQUERYIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLGETQUERYOBJECTUIVEXTPROC) (GLuint id, GLenum pname, GLuint *params); +#endif + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_EXT_read_format_bgra 1 +#endif + +/* GL_EXT_robustness */ +#ifndef GL_EXT_robustness +#define GL_EXT_robustness 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL GLenum GL_APIENTRY glGetGraphicsResetStatusEXT (void); +GL_APICALL void GL_APIENTRY glReadnPixelsEXT (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GL_APICALL void GL_APIENTRY glGetnUniformfvEXT (GLuint program, GLint location, GLsizei bufSize, float *params); +GL_APICALL void GL_APIENTRY glGetnUniformivEXT (GLuint program, GLint location, GLsizei bufSize, GLint *params); +#endif +typedef GLenum (GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSEXTPROC) (void); +typedef void (GL_APIENTRYP PFNGLREADNPIXELSEXTPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (GL_APIENTRYP PFNGLGETNUNIFORMFVEXTPROC) (GLuint program, GLint location, GLsizei bufSize, float *params); +typedef void (GL_APIENTRYP PFNGLGETNUNIFORMIVEXTPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +#endif + +/* GL_EXT_separate_shader_objects */ +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glUseProgramStagesEXT (GLuint pipeline, GLbitfield stages, GLuint program); +GL_APICALL void GL_APIENTRY glActiveShaderProgramEXT (GLuint pipeline, GLuint program); +GL_APICALL GLuint GL_APIENTRY glCreateShaderProgramvEXT (GLenum type, GLsizei count, const GLchar **strings); +GL_APICALL void GL_APIENTRY glBindProgramPipelineEXT (GLuint pipeline); +GL_APICALL void GL_APIENTRY glDeleteProgramPipelinesEXT (GLsizei n, const GLuint *pipelines); +GL_APICALL void GL_APIENTRY glGenProgramPipelinesEXT (GLsizei n, GLuint *pipelines); +GL_APICALL GLboolean GL_APIENTRY glIsProgramPipelineEXT (GLuint pipeline); +GL_APICALL void GL_APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +GL_APICALL void GL_APIENTRY glGetProgramPipelineivEXT (GLuint pipeline, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint x); +GL_APICALL void GL_APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint x, GLint y); +GL_APICALL void GL_APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint x, GLint y, GLint z); +GL_APICALL void GL_APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint x, GLint y, GLint z, GLint w); +GL_APICALL void GL_APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat x); +GL_APICALL void GL_APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY glValidateProgramPipelineEXT (GLuint pipeline); +GL_APICALL void GL_APIENTRY glGetProgramPipelineInfoLogEXT (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif +typedef void (GL_APIENTRYP PFNGLUSEPROGRAMSTAGESEXTPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (GL_APIENTRYP PFNGLACTIVESHADERPROGRAMEXTPROC) (GLuint pipeline, GLuint program); +typedef GLuint (GL_APIENTRYP PFNGLCREATESHADERPROGRAMVEXTPROC) (GLenum type, GLsizei count, const GLchar **strings); +typedef void (GL_APIENTRYP PFNGLBINDPROGRAMPIPELINEEXTPROC) (GLuint pipeline); +typedef void (GL_APIENTRYP PFNGLDELETEPROGRAMPIPELINESEXTPROC) (GLsizei n, const GLuint *pipelines); +typedef void (GL_APIENTRYP PFNGLGENPROGRAMPIPELINESEXTPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (GL_APIENTRYP PFNGLISPROGRAMPIPELINEEXTPROC) (GLuint pipeline); +typedef void (GL_APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +typedef void (GL_APIENTRYP PFNGLGETPROGRAMPIPELINEIVEXTPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint x); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint x, GLint y); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint x, GLint y, GLint z); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint x, GLint y, GLint z, GLint w); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat x); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat x, GLfloat y); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (GL_APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEEXTPROC) (GLuint pipeline); +typedef void (GL_APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif + +/* GL_EXT_shader_texture_lod */ +#ifndef GL_EXT_shader_texture_lod +#define GL_EXT_shader_texture_lod 1 +#endif + +/* GL_EXT_shadow_samplers */ +#ifndef GL_EXT_shadow_samplers +#define GL_EXT_shadow_samplers 1 +#endif + +/* GL_EXT_sRGB */ +#ifndef GL_EXT_sRGB +#define GL_EXT_sRGB 1 +#endif + +/* GL_EXT_texture_compression_dxt1 */ +#ifndef GL_EXT_texture_compression_dxt1 +#define GL_EXT_texture_compression_dxt1 1 +#endif + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_EXT_texture_format_BGRA8888 1 +#endif + +/* GL_EXT_texture_rg */ +#ifndef GL_EXT_texture_rg +#define GL_EXT_texture_rg 1 +#endif + +/* GL_EXT_texture_storage */ +#ifndef GL_EXT_texture_storage +#define GL_EXT_texture_storage 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GL_APICALL void GL_APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GL_APICALL void GL_APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GL_APICALL void GL_APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif +typedef void (GL_APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GL_APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif + +/* GL_EXT_texture_type_2_10_10_10_REV */ +#ifndef GL_EXT_texture_type_2_10_10_10_REV +#define GL_EXT_texture_type_2_10_10_10_REV 1 +#endif + +/* GL_EXT_unpack_subimage */ +#ifndef GL_EXT_unpack_subimage +#define GL_EXT_unpack_subimage 1 +#endif + +/*------------------------------------------------------------------------* + * DMP extension functions + *------------------------------------------------------------------------*/ + +/* GL_DMP_shader_binary */ +#ifndef GL_DMP_shader_binary +#define GL_DMP_shader_binary 1 +#endif + +/*------------------------------------------------------------------------* + * IMG extension functions + *------------------------------------------------------------------------*/ + +/* GL_IMG_program_binary */ +#ifndef GL_IMG_program_binary +#define GL_IMG_program_binary 1 +#endif + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_IMG_read_format 1 +#endif + +/* GL_IMG_shader_binary */ +#ifndef GL_IMG_shader_binary +#define GL_IMG_shader_binary 1 +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_IMG_texture_compression_pvrtc 1 +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_IMG_multisampled_render_to_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif + +/*------------------------------------------------------------------------* + * NV extension functions + *------------------------------------------------------------------------*/ + +/* GL_NV_coverage_sample */ +#ifndef GL_NV_coverage_sample +#define GL_NV_coverage_sample 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glCoverageMaskNV (GLboolean mask); +GL_APICALL void GL_APIENTRY glCoverageOperationNV (GLenum operation); +#endif +typedef void (GL_APIENTRYP PFNGLCOVERAGEMASKNVPROC) (GLboolean mask); +typedef void (GL_APIENTRYP PFNGLCOVERAGEOPERATIONNVPROC) (GLenum operation); +#endif + +/* GL_NV_depth_nonlinear */ +#ifndef GL_NV_depth_nonlinear +#define GL_NV_depth_nonlinear 1 +#endif + +/* GL_NV_draw_buffers */ +#ifndef GL_NV_draw_buffers +#define GL_NV_draw_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glDrawBuffersNV (GLsizei n, const GLenum *bufs); +#endif +typedef void (GL_APIENTRYP PFNGLDRAWBUFFERSNVPROC) (GLsizei n, const GLenum *bufs); +#endif + +/* GL_NV_fbo_color_attachments */ +#ifndef GL_NV_fbo_color_attachments +#define GL_NV_fbo_color_attachments 1 +#endif + +/* GL_NV_fence */ +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glDeleteFencesNV (GLsizei, const GLuint *); +GL_APICALL void GL_APIENTRY glGenFencesNV (GLsizei, GLuint *); +GL_APICALL GLboolean GL_APIENTRY glIsFenceNV (GLuint); +GL_APICALL GLboolean GL_APIENTRY glTestFenceNV (GLuint); +GL_APICALL void GL_APIENTRY glGetFenceivNV (GLuint, GLenum, GLint *); +GL_APICALL void GL_APIENTRY glFinishFenceNV (GLuint); +GL_APICALL void GL_APIENTRY glSetFenceNV (GLuint, GLenum); +#endif +typedef void (GL_APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (GL_APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (GL_APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (GL_APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#endif + +/* GL_NV_read_buffer */ +#ifndef GL_NV_read_buffer +#define GL_NV_read_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glReadBufferNV (GLenum mode); +#endif +typedef void (GL_APIENTRYP PFNGLREADBUFFERNVPROC) (GLenum mode); +#endif + +/* GL_NV_read_buffer_front */ +#ifndef GL_NV_read_buffer_front +#define GL_NV_read_buffer_front 1 +#endif + +/* GL_NV_read_depth */ +#ifndef GL_NV_read_depth +#define GL_NV_read_depth 1 +#endif + +/* GL_NV_read_depth_stencil */ +#ifndef GL_NV_read_depth_stencil +#define GL_NV_read_depth_stencil 1 +#endif + +/* GL_NV_read_stencil */ +#ifndef GL_NV_read_stencil +#define GL_NV_read_stencil 1 +#endif + +/* GL_NV_texture_compression_s3tc_update */ +#ifndef GL_NV_texture_compression_s3tc_update +#define GL_NV_texture_compression_s3tc_update 1 +#endif + +/* GL_NV_texture_npot_2D_mipmap */ +#ifndef GL_NV_texture_npot_2D_mipmap +#define GL_NV_texture_npot_2D_mipmap 1 +#endif + +/*------------------------------------------------------------------------* + * QCOM extension functions + *------------------------------------------------------------------------*/ + +/* GL_QCOM_alpha_test */ +#ifndef GL_QCOM_alpha_test +#define GL_QCOM_alpha_test 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glAlphaFuncQCOM (GLenum func, GLclampf ref); +#endif +typedef void (GL_APIENTRYP PFNGLALPHAFUNCQCOMPROC) (GLenum func, GLclampf ref); +#endif + +/* GL_QCOM_driver_control */ +#ifndef GL_QCOM_driver_control +#define GL_QCOM_driver_control 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls); +GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +GL_APICALL void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl); +GL_APICALL void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl); +#endif +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls); +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +#endif + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_QCOM_extended_get 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures); +GL_APICALL void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +GL_APICALL void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +GL_APICALL void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +GL_APICALL void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +GL_APICALL void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params); +#endif + +/* GL_QCOM_extended_get2 */ +#ifndef GL_QCOM_extended_get2 +#define GL_QCOM_extended_get2 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders); +GL_APICALL void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +GL_APICALL GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program); +GL_APICALL void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_QCOM_perfmon_global_mode 1 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_QCOM_writeonly_rendering 1 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_QCOM_tiled_rendering 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); +#endif +typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask); +#endif + +/*------------------------------------------------------------------------* + * VIV extension tokens + *------------------------------------------------------------------------*/ + +/* GL_VIV_shader_binary */ +#ifndef GL_VIV_shader_binary +#define GL_VIV_shader_binary 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gl2ext_h_ */ diff --git a/cogl/cogl-gles2/GLES2/gl2platform.h b/cogl/cogl-gles2/GLES2/gl2platform.h new file mode 100644 index 000000000..8bc44b07e --- /dev/null +++ b/cogl/cogl-gles2/GLES2/gl2platform.h @@ -0,0 +1,28 @@ +#ifndef __gl2platform_h_ +#define __gl2platform_h_ + +/* $Revision: 10602 $ on $Date:: 2010-03-04 22:35:34 -0800 #$ */ + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "OpenGL-ES" component "Registry". + */ + +#ifndef GL_APICALL +#define GL_APICALL +#endif + +#ifndef GL_APIENTRY +#define GL_APIENTRY +#endif + +#endif /* __gl2platform_h_ */ diff --git a/cogl/cogl-gles2/Makefile.am b/cogl/cogl-gles2/Makefile.am new file mode 100644 index 000000000..47baafcc0 --- /dev/null +++ b/cogl/cogl-gles2/Makefile.am @@ -0,0 +1,31 @@ +# preamble + +NULL = + +mutterlibdir = $(libdir)/mutter +mutterlib_LTLIBRARIES = libmutter-cogl-gles2.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir) + +AM_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS) + +libmutter_cogl_gles2_la_SOURCES = cogl-gles2-api.c +libmutter_cogl_gles2_la_LDFLAGS = \ + -no-undefined \ + -rpath $(mutterlibdir) \ + -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \ + -export-dynamic \ + -export-symbols-regex "^gl*" + +coglgles2includedir = $(includedir)/mutter/cogl/cogl-gles2/GLES2 +coglgles2include_HEADERS = \ + GLES2/gl2.h \ + GLES2/gl2ext.h \ + GLES2/gl2platform.h + +pc_files = mutter-cogl-gles2-1.0.pc + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pc_files) diff --git a/cogl/cogl-gles2/cogl-gles2-api.c b/cogl/cogl-gles2/cogl-gles2-api.c new file mode 100644 index 000000000..22ab2b716 --- /dev/null +++ b/cogl/cogl-gles2/cogl-gles2-api.c @@ -0,0 +1,1048 @@ + +#include + +#include + +void +glBindTexture (GLenum target, GLuint texture) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBindTexture (target, texture); +} + +void +glBlendFunc (GLenum sfactor, GLenum dfactor) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBlendFunc (sfactor, dfactor); +} + +void +glClear (GLbitfield mask) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glClear (mask); +} + +void +glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glClearColor (red, green, blue, alpha); +} + +void +glClearStencil (GLint s) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glClearStencil (s); +} + +void +glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glColorMask (red, green, blue, alpha); +} + +void +glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCopyTexSubImage2D (target, level, xoffset, yoffset, x, y, width, + height); +} + +void +glDeleteTextures (GLsizei n, const GLuint * textures) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteTextures (n, textures); +} + +void +glDepthFunc (GLenum func) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDepthFunc (func); +} + +void +glDepthMask (GLboolean flag) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDepthMask (flag); +} + +void +glDisable (GLenum cap) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDisable (cap); +} + +void +glDrawArrays (GLenum mode, GLint first, GLsizei count) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDrawArrays (mode, first, count); +} + +void +glDrawElements (GLenum mode, GLsizei count, GLenum type, + const GLvoid * indices) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDrawElements (mode, count, type, indices); +} + +void +glEnable (GLenum cap) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glEnable (cap); +} + +void +glFinish (void) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glFinish (); +} + +void +glFlush (void) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glFlush (); +} + +void +glFrontFace (GLenum mode) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glFrontFace (mode); +} + +void +glCullFace (GLenum mode) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCullFace (mode); +} + +void +glGenTextures (GLsizei n, GLuint * textures) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGenTextures (n, textures); +} + +GLenum +glGetError (void) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glGetError (); +} + +void +glGetIntegerv (GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetIntegerv (pname, params); +} + +void +glGetBooleanv (GLenum pname, GLboolean * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetBooleanv (pname, params); +} + +void +glGetFloatv (GLenum pname, GLfloat * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetFloatv (pname, params); +} + +const GLubyte * +glGetString (GLenum name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glGetString (name); +} + +void +glHint (GLenum target, GLenum mode) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glHint (target, mode); +} + +GLboolean +glIsTexture (GLuint texture) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsTexture (texture); +} + +void +glPixelStorei (GLenum pname, GLint param) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glPixelStorei (pname, param); +} + +void +glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, GLvoid * pixels) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glReadPixels (x, y, width, height, format, type, pixels); +} + +void +glScissor (GLint x, GLint y, GLsizei width, GLsizei height) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glScissor (x, y, width, height); +} + +void +glStencilFunc (GLenum func, GLint ref, GLuint mask) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilFunc (func, ref, mask); +} + +void +glStencilMask (GLuint mask) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilMask (mask); +} + +void +glStencilOp (GLenum fail, GLenum zfail, GLenum zpass) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilOp (fail, zfail, zpass); +} + +void +glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, + GLsizei height, GLint border, GLenum format, GLenum type, + const GLvoid * pixels) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexImage2D (target, level, internalformat, width, height, border, + format, type, pixels); +} + +void +glTexParameterf (GLenum target, GLenum pname, GLfloat param) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexParameterf (target, pname, param); +} + +void +glTexParameterfv (GLenum target, GLenum pname, const GLfloat * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexParameterfv (target, pname, params); +} + +void +glTexParameteri (GLenum target, GLenum pname, GLint param) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexParameteri (target, pname, param); +} + +void +glTexParameteriv (GLenum target, GLenum pname, const GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexParameteriv (target, pname, params); +} + +void +glGetTexParameterfv (GLenum target, GLenum pname, GLfloat * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetTexParameterfv (target, pname, params); +} + +void +glGetTexParameteriv (GLenum target, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetTexParameteriv (target, pname, params); +} + +void +glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, + const GLvoid * pixels) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glTexSubImage2D (target, level, xoffset, yoffset, width, height, + format, type, pixels); +} + +void +glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, + GLint y, GLsizei width, GLsizei height, GLint border) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCopyTexImage2D (target, level, internalformat, + x, y, width, height, border); +} + +void +glViewport (GLint x, GLint y, GLsizei width, GLsizei height) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glViewport (x, y, width, height); +} + +GLboolean +glIsEnabled (GLenum cap) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsEnabled (cap); +} + +void +glLineWidth (GLfloat width) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glLineWidth (width); +} + +void +glPolygonOffset (GLfloat factor, GLfloat units) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glPolygonOffset (factor, units); +} + +void +glDepthRangef (GLfloat near_val, GLfloat far_val) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDepthRangef (near_val, far_val); +} + +void +glClearDepthf (GLclampf depth) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glClearDepthf (depth); +} + +void +glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid * data) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCompressedTexImage2D (target, level, internalformat, width, + height, border, imageSize, data); +} + +void +glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const GLvoid * data) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCompressedTexSubImage2D (target, level, + xoffset, yoffset, width, height, + format, imageSize, data); +} + +void +glSampleCoverage (GLclampf value, GLboolean invert) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glSampleCoverage (value, invert); +} + +void +glGetBufferParameteriv (GLenum target, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetBufferParameteriv (target, pname, params); +} + +void +glGenBuffers (GLsizei n, GLuint * buffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGenBuffers (n, buffers); +} + +void +glBindBuffer (GLenum target, GLuint buffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBindBuffer (target, buffer); +} + +void +glBufferData (GLenum target, GLsizeiptr size, const GLvoid * data, + GLenum usage) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBufferData (target, size, data, usage); +} + +void +glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, + const GLvoid * data) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBufferSubData (target, offset, size, data); +} + +void +glDeleteBuffers (GLsizei n, const GLuint * buffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteBuffers (n, buffers); +} + +GLboolean +glIsBuffer (GLuint buffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsBuffer (buffer); +} + +void +glActiveTexture (GLenum texture) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glActiveTexture (texture); +} + +void +glGenRenderbuffers (GLsizei n, GLuint * renderbuffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGenRenderbuffers (n, renderbuffers); +} + +void +glDeleteRenderbuffers (GLsizei n, const GLuint * renderbuffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteRenderbuffers (n, renderbuffers); +} + +void +glBindRenderbuffer (GLenum target, GLuint renderbuffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBindRenderbuffer (target, renderbuffer); +} + +void +glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, + GLsizei height) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glRenderbufferStorage (target, internalformat, width, height); +} + +void +glGenFramebuffers (GLsizei n, GLuint * framebuffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGenFramebuffers (n, framebuffers); +} + +void +glBindFramebuffer (GLenum target, GLuint framebuffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBindFramebuffer (target, framebuffer); +} + +void +glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glFramebufferTexture2D (target, attachment, + textarget, texture, level); +} + +void +glFramebufferRenderbuffer (GLenum target, GLenum attachment, + GLenum renderbuffertarget, GLuint renderbuffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glFramebufferRenderbuffer (target, attachment, + renderbuffertarget, renderbuffer); +} + +GLboolean +glIsRenderbuffer (GLuint renderbuffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsRenderbuffer (renderbuffer); +} + +GLenum +glCheckFramebufferStatus (GLenum target) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glCheckFramebufferStatus (target); +} + +void +glDeleteFramebuffers (GLsizei n, const GLuint * framebuffers) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteFramebuffers (n, framebuffers); +} + +void +glGenerateMipmap (GLenum target) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGenerateMipmap (target); +} + +void +glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, + GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetFramebufferAttachmentParameteriv (target, + attachment, pname, params); +} + +void +glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetRenderbufferParameteriv (target, pname, params); +} + +GLboolean +glIsFramebuffer (GLuint framebuffer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsFramebuffer (framebuffer); +} + +void +glBlendEquation (GLenum mode) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBlendEquation (mode); +} + +void +glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBlendColor (red, green, blue, alpha); +} + +void +glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, + GLenum dstAlpha) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBlendFuncSeparate (srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +void +glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBlendEquationSeparate (modeRGB, modeAlpha); +} + +void +glReleaseShaderCompiler (void) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glReleaseShaderCompiler (); +} + +void +glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, + GLint * range, GLint * precision) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetShaderPrecisionFormat (shadertype, precisiontype, + range, precision); +} + +void +glShaderBinary (GLsizei n, const GLuint * shaders, GLenum binaryformat, + const GLvoid * binary, GLsizei length) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glShaderBinary (n, shaders, binaryformat, binary, length); +} + +void +glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilFuncSeparate (face, func, ref, mask); +} + +void +glStencilMaskSeparate (GLenum face, GLuint mask) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilMaskSeparate (face, mask); +} + +void +glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glStencilOpSeparate (face, fail, zfail, zpass); +} + +GLuint +glCreateProgram (void) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glCreateProgram (); +} + +GLuint +glCreateShader (GLenum shaderType) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glCreateShader (shaderType); +} + +void +glShaderSource (GLuint shader, GLsizei count, + const GLchar * const *string, const GLint * length) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glShaderSource (shader, count, string, length); +} + +void +glCompileShader (GLuint shader) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glCompileShader (shader); +} + +void +glDeleteShader (GLuint shader) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteShader (shader); +} + +void +glAttachShader (GLuint program, GLuint shader) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glAttachShader (program, shader); +} + +void +glLinkProgram (GLuint program) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glLinkProgram (program); +} + +void +glUseProgram (GLuint program) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUseProgram (program); +} + +GLint +glGetUniformLocation (GLuint program, const char *name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glGetUniformLocation (program, name); +} + +void +glDeleteProgram (GLuint program) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDeleteProgram (program); +} + +void +glGetShaderInfoLog (GLuint shader, GLsizei maxLength, GLsizei * length, + char *infoLog) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetShaderInfoLog (shader, maxLength, length, infoLog); +} + +void +glGetShaderiv (GLuint shader, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetShaderiv (shader, pname, params); +} + +void +glVertexAttribPointer (GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + const GLvoid * pointer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttribPointer (index, size, type, normalized, stride, + pointer); +} + +void +glEnableVertexAttribArray (GLuint index) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glEnableVertexAttribArray (index); +} + +void +glDisableVertexAttribArray (GLuint index) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDisableVertexAttribArray (index); +} + +void +glUniform1f (GLint location, GLfloat v0) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform1f (location, v0); +} + +void +glUniform2f (GLint location, GLfloat v0, GLfloat v1) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform2f (location, v0, v1); +} + +void +glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform3f (location, v0, v1, v2); +} + +void +glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform4f (location, v0, v1, v2, v3); +} + +void +glUniform1fv (GLint location, GLsizei count, const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform1fv (location, count, value); +} + +void +glUniform2fv (GLint location, GLsizei count, const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform2fv (location, count, value); +} + +void +glUniform3fv (GLint location, GLsizei count, const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform3fv (location, count, value); +} + +void +glUniform4fv (GLint location, GLsizei count, const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform4fv (location, count, value); +} + +void +glUniform1i (GLint location, GLint v0) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform1i (location, v0); +} + +void +glUniform2i (GLint location, GLint v0, GLint v1) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform2i (location, v0, v1); +} + +void +glUniform3i (GLint location, GLint v0, GLint v1, GLint v2) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform3i (location, v0, v1, v2); +} + +void +glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform4i (location, v0, v1, v2, v3); +} + +void +glUniform1iv (GLint location, GLsizei count, const GLint * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform1iv (location, count, value); +} + +void +glUniform2iv (GLint location, GLsizei count, const GLint * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform2iv (location, count, value); +} + +void +glUniform3iv (GLint location, GLsizei count, const GLint * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform3iv (location, count, value); +} + +void +glUniform4iv (GLint location, GLsizei count, const GLint * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniform4iv (location, count, value); +} + +void +glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, + const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniformMatrix2fv (location, count, transpose, value); +} + +void +glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, + const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniformMatrix3fv (location, count, transpose, value); +} + +void +glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, + const GLfloat * value) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glUniformMatrix4fv (location, count, transpose, value); +} + +void +glGetUniformfv (GLuint program, GLint location, GLfloat * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetUniformfv (program, location, params); +} + +void +glGetUniformiv (GLuint program, GLint location, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetUniformiv (program, location, params); +} + +void +glGetProgramiv (GLuint program, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetProgramiv (program, pname, params); +} + +void +glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei * length, + char *infoLog) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetProgramInfoLog (program, bufSize, length, infoLog); +} + +void +glVertexAttrib1f (GLuint indx, GLfloat x) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib1f (indx, x); +} + +void +glVertexAttrib1fv (GLuint indx, const GLfloat * values) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib1fv (indx, values); +} + +void +glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib2f (indx, x, y); +} + +void +glVertexAttrib2fv (GLuint indx, const GLfloat * values) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib2fv (indx, values); +} + +void +glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib3f (indx, x, y, z); +} + +void +glVertexAttrib3fv (GLuint indx, const GLfloat * values) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib3fv (indx, values); +} + +void +glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib4f (index, x, y, z, w); +} + +void +glVertexAttrib4fv (GLuint indx, const GLfloat * values) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glVertexAttrib4fv (indx, values); +} + +void +glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetVertexAttribfv (index, pname, params); +} + +void +glGetVertexAttribiv (GLuint index, GLenum pname, GLint * params) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetVertexAttribiv (index, pname, params); +} + +void +glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid ** pointer) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetVertexAttribPointerv (index, pname, pointer); +} + +GLint +glGetAttribLocation (GLuint program, const char *name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glGetAttribLocation (program, name); +} + +void +glBindAttribLocation (GLuint program, GLuint index, const GLchar * name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glBindAttribLocation (program, index, name); +} + +void +glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, + GLsizei * length, GLint * size, GLenum * type, + GLchar * name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetActiveAttrib (program, index, bufsize, length, size, type, + name); +} + +void +glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, + GLsizei * length, GLint * size, GLenum * type, + GLchar * name) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetActiveUniform (program, index, bufsize, length, size, type, + name); +} + +void +glDetachShader (GLuint program, GLuint shader) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glDetachShader (program, shader); +} + +void +glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei * count, + GLuint * shaders) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetAttachedShaders (program, maxcount, count, shaders); +} + +void +glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei * length, + GLchar * source) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glGetShaderSource (shader, bufsize, length, source); +} + +GLboolean +glIsShader (GLuint shader) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsShader (shader); +} + +GLboolean +glIsProgram (GLuint program) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + return vtable->glIsProgram (program); +} + +void +glValidateProgram (GLuint program) +{ + CoglGLES2Vtable *vtable = cogl_gles2_get_current_vtable (); + vtable->glValidateProgram (program); +} diff --git a/cogl/cogl-gles2/mutter-cogl-gles2-1.0.pc.in b/cogl/cogl-gles2/mutter-cogl-gles2-1.0.pc.in new file mode 100644 index 000000000..a66935318 --- /dev/null +++ b/cogl/cogl-gles2/mutter-cogl-gles2-1.0.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/mutter +includedir=@includedir@/mutter +apiversion=1.0 +requires=@COGL_PKG_REQUIRES@ mutter-cogl-1.0 + +Name: Cogl +Description: An object oriented GL/GLES Abstraction/Utility Layer +Version: @COGL_1_VERSION@ +Libs: -L${libdir} -lmutter-cogl-gles2 +Cflags: -I${includedir}/cogl +Requires: ${requires} diff --git a/cogl/cogl-pango/Makefile.am b/cogl/cogl-pango/Makefile.am new file mode 100644 index 000000000..e32ad26a5 --- /dev/null +++ b/cogl/cogl-pango/Makefile.am @@ -0,0 +1,109 @@ +NULL = + +CLEANFILES = +DISTCLEANFILES = + +EXTRA_DIST = + +source_c = \ + cogl-pango-display-list.c \ + cogl-pango-fontmap.c \ + cogl-pango-render.c \ + cogl-pango-glyph-cache.c \ + cogl-pango-pipeline-cache.c \ + $(NULL) + +source_h = cogl-pango.h + +source_h_priv = \ + cogl-pango-display-list.h \ + cogl-pango-private.h \ + cogl-pango-glyph-cache.h \ + cogl-pango-pipeline-cache.h \ + $(NULL) + +mutterlibdir = $(libdir)/mutter +mutterlib_LTLIBRARIES = libmutter-cogl-pango.la + +libmutter_cogl_pango_la_SOURCES = $(source_c) $(source_h) $(source_h_priv) +libmutter_cogl_pango_la_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_PANGO_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS) +libmutter_cogl_pango_la_LIBADD = $(top_builddir)/cogl/libmutter-cogl.la +libmutter_cogl_pango_la_LIBADD += $(COGL_DEP_LIBS) $(COGL_PANGO_DEP_LIBS) $(COGL_EXTRA_LDFLAGS) +libmutter_cogl_pango_la_LDFLAGS = \ + -export-dynamic \ + -rpath $(mutterlibdir) \ + -export-symbols-regex "^cogl_pango_.*" \ + -no-undefined \ + -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ + +AM_CPPFLAGS = \ + -DCOGL_COMPILATION \ + -DG_LOG_DOMAIN=\"CoglPango\" \ + -I$(top_srcdir)/cogl \ + -I$(top_builddir)/cogl \ + -I$(top_srcdir)/cogl/winsys \ + -I$(top_srcdir) \ + -I$(top_builddir) + +cogl_base_includedir = $(includedir)/mutter +cogl_pangoheadersdir = $(cogl_base_includedir)/cogl/cogl-pango +cogl_pangoheaders_HEADERS = $(source_h) + +pc_files = mutter-cogl-pango-1.0.pc + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pc_files) + +DISTCLEANFILES += $(pc_files) + +EXTRA_DIST += cogl-pango.symbols + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_GIRS = + +if HAVE_INTROSPECTION +INTROSPECTION_COMPILER_ARGS=--includedir=$(top_builddir)/cogl + +CoglPango-1.0.gir: libmutter-cogl-pango.la Makefile + +CoglPango_1_0_gir_NAMESPACE = CoglPango +CoglPango_1_0_gir_VERSION = 1.0 +CoglPango_1_0_gir_LIBS = $(top_builddir)/cogl/libmutter-cogl.la libmutter-cogl-pango.la +CoglPango_1_0_gir_FILES = $(source_h) $(source_c) +CoglPango_1_0_gir_CFLAGS = $(AM_CPPFLAGS) $(COGL_DEP_CFLAGS) $(COGL_PANGO_DEP_CFLAGS) +CoglPango_1_0_gir_INCLUDES = Pango-1.0 PangoCairo-1.0 +CoglPango_1_0_gir_EXPORT_PACKAGES = cogl-pango-1.0 +CoglPango_1_0_gir_SCANNERFLAGS = \ + --warn-all \ + --identifier-prefix=CoglPango \ + --symbol-prefix=cogl_pango \ + --c-include='cogl-pango/cogl-pango.h' \ + --include-uninstalled=$(top_builddir)/cogl/Cogl-1.0.gir + +CoglPango-2.0.gir: libmutter-cogl-pango.la Makefile + +CoglPango_2_0_gir_NAMESPACE = CoglPango +CoglPango_2_0_gir_VERSION = 2.0 +CoglPango_2_0_gir_LIBS = $(top_builddir)/cogl/libmutter-cogl.la libmutter-cogl-pango.la +CoglPango_2_0_gir_FILES = $(source_h) $(source_c) +CoglPango_2_0_gir_CFLAGS = $(AM_CPPFLAGS) $(COGL_DEP_CFLAGS) $(COGL_PANGO_DEP_CFLAGS) +CoglPango_2_0_gir_INCLUDES = Pango-1.0 PangoCairo-1.0 +CoglPango_2_0_gir_EXPORT_PACKAGES = cogl-pango-2.0-experimental +CoglPango_2_0_gir_SCANNERFLAGS = \ + --warn-all \ + --identifier-prefix=CoglPango \ + --symbol-prefix=cogl_pango \ + --c-include='cogl-pango/cogl-pango.h' \ + --include-uninstalled=$(top_builddir)/cogl/Cogl-2.0.gir + +INTROSPECTION_GIRS += CoglPango-1.0.gir CoglPango-2.0.gir + +girdir = $(mutterlibdir) +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(mutterlibdir) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) +endif diff --git a/cogl/cogl-pango/cogl-pango-display-list.c b/cogl/cogl-pango/cogl-pango-display-list.c new file mode 100644 index 000000000..6967ab024 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-display-list.c @@ -0,0 +1,499 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cogl-pango-display-list.h" +#include "cogl-pango-pipeline-cache.h" +#include "cogl/cogl-context-private.h" + +typedef enum +{ + COGL_PANGO_DISPLAY_LIST_TEXTURE, + COGL_PANGO_DISPLAY_LIST_RECTANGLE, + COGL_PANGO_DISPLAY_LIST_TRAPEZOID +} CoglPangoDisplayListNodeType; + +typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode; +typedef struct _CoglPangoDisplayListRectangle CoglPangoDisplayListRectangle; + +struct _CoglPangoDisplayList +{ + CoglBool color_override; + CoglColor color; + GSList *nodes; + GSList *last_node; + CoglPangoPipelineCache *pipeline_cache; +}; + +/* This matches the format expected by cogl_rectangles_with_texture_coords */ +struct _CoglPangoDisplayListRectangle +{ + float x_1, y_1, x_2, y_2; + float s_1, t_1, s_2, t_2; +}; + +struct _CoglPangoDisplayListNode +{ + CoglPangoDisplayListNodeType type; + + CoglBool color_override; + CoglColor color; + + CoglPipeline *pipeline; + + union + { + struct + { + /* The texture to render these coords from */ + CoglTexture *texture; + /* Array of rectangles in the format expected by + cogl_rectangles_with_texture_coords */ + GArray *rectangles; + /* A primitive representing those vertices */ + CoglPrimitive *primitive; + } texture; + + struct + { + float x_1, y_1; + float x_2, y_2; + } rectangle; + + struct + { + CoglPrimitive *primitive; + } trapezoid; + } d; +}; + +CoglPangoDisplayList * +_cogl_pango_display_list_new (CoglPangoPipelineCache *pipeline_cache) +{ + CoglPangoDisplayList *dl = g_slice_new0 (CoglPangoDisplayList); + + dl->pipeline_cache = pipeline_cache; + + return dl; +} + +static void +_cogl_pango_display_list_append_node (CoglPangoDisplayList *dl, + CoglPangoDisplayListNode *node) +{ + if (dl->last_node) + dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node); + else + dl->last_node = dl->nodes = g_slist_prepend (NULL, node); +} + +void +_cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl, + const CoglColor *color) +{ + dl->color_override = TRUE; + dl->color = *color; +} + +void +_cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl) +{ + dl->color_override = FALSE; +} + +void +_cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, + CoglTexture *texture, + float x_1, float y_1, + float x_2, float y_2, + float tx_1, float ty_1, + float tx_2, float ty_2) +{ + CoglPangoDisplayListNode *node; + CoglPangoDisplayListRectangle *rectangle; + + /* Add to the last node if it is a texture node with the same + target texture */ + if (dl->last_node + && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE + && node->d.texture.texture == texture + && (dl->color_override + ? (node->color_override && cogl_color_equal (&dl->color, &node->color)) + : !node->color_override)) + { + /* Get rid of the vertex buffer so that it will be recreated */ + if (node->d.texture.primitive != NULL) + { + cogl_object_unref (node->d.texture.primitive); + node->d.texture.primitive = NULL; + } + } + else + { + /* Otherwise create a new node */ + node = g_slice_new (CoglPangoDisplayListNode); + + node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE; + node->color_override = dl->color_override; + node->color = dl->color; + node->pipeline = NULL; + node->d.texture.texture = cogl_object_ref (texture); + node->d.texture.rectangles + = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListRectangle)); + node->d.texture.primitive = NULL; + + _cogl_pango_display_list_append_node (dl, node); + } + + g_array_set_size (node->d.texture.rectangles, + node->d.texture.rectangles->len + 1); + rectangle = &g_array_index (node->d.texture.rectangles, + CoglPangoDisplayListRectangle, + node->d.texture.rectangles->len - 1); + rectangle->x_1 = x_1; + rectangle->y_1 = y_1; + rectangle->x_2 = x_2; + rectangle->y_2 = y_2; + rectangle->s_1 = tx_1; + rectangle->t_1 = ty_1; + rectangle->s_2 = tx_2; + rectangle->t_2 = ty_2; +} + +void +_cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, + float x_1, float y_1, + float x_2, float y_2) +{ + CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); + + node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE; + node->color_override = dl->color_override; + node->color = dl->color; + node->d.rectangle.x_1 = x_1; + node->d.rectangle.y_1 = y_1; + node->d.rectangle.x_2 = x_2; + node->d.rectangle.y_2 = y_2; + node->pipeline = NULL; + + _cogl_pango_display_list_append_node (dl, node); +} + +void +_cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, + float y_1, + float x_11, + float x_21, + float y_2, + float x_12, + float x_22) +{ + CoglContext *ctx = dl->pipeline_cache->ctx; + CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); + CoglVertexP2 vertices[4] = { + { x_11, y_1 }, + { x_12, y_2 }, + { x_22, y_2 }, + { x_21, y_1 } + }; + + node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID; + node->color_override = dl->color_override; + node->color = dl->color; + node->pipeline = NULL; + + node->d.trapezoid.primitive = + cogl_primitive_new_p2 (ctx, + COGL_VERTICES_MODE_TRIANGLE_FAN, + 4, + vertices); + + _cogl_pango_display_list_append_node (dl, node); +} + +static void +emit_rectangles_through_journal (CoglFramebuffer *fb, + CoglPipeline *pipeline, + CoglPangoDisplayListNode *node) +{ + const float *rectangles = (const float *)node->d.texture.rectangles->data; + + cogl_framebuffer_draw_textured_rectangles (fb, + pipeline, + rectangles, + node->d.texture.rectangles->len); +} + +static void +emit_vertex_buffer_geometry (CoglFramebuffer *fb, + CoglPipeline *pipeline, + CoglPangoDisplayListNode *node) +{ + CoglContext *ctx = fb->context; + + /* It's expensive to go through the Cogl journal for large runs + * of text in part because the journal transforms the quads in software + * to avoid changing the modelview matrix. So for larger runs of text + * we load the vertices into a VBO, and this has the added advantage + * that if the text doesn't change from frame to frame the VBO can + * be re-used avoiding the repeated cost of validating the data and + * mapping it into the GPU... */ + + if (node->d.texture.primitive == NULL) + { + CoglAttributeBuffer *buffer; + CoglVertexP2T2 *verts, *v; + int n_verts; + CoglBool allocated = FALSE; + CoglAttribute *attributes[2]; + CoglPrimitive *prim; + int i; + + n_verts = node->d.texture.rectangles->len * 4; + + buffer + = cogl_attribute_buffer_new_with_size (ctx, + n_verts * + sizeof (CoglVertexP2T2)); + + if ((verts = cogl_buffer_map (COGL_BUFFER (buffer), + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD)) == NULL) + { + verts = g_new (CoglVertexP2T2, n_verts); + allocated = TRUE; + } + + v = verts; + + /* Copy the rectangles into the buffer and expand into four + vertices instead of just two */ + for (i = 0; i < node->d.texture.rectangles->len; i++) + { + const CoglPangoDisplayListRectangle *rectangle + = &g_array_index (node->d.texture.rectangles, + CoglPangoDisplayListRectangle, i); + + v->x = rectangle->x_1; + v->y = rectangle->y_1; + v->s = rectangle->s_1; + v->t = rectangle->t_1; + v++; + v->x = rectangle->x_1; + v->y = rectangle->y_2; + v->s = rectangle->s_1; + v->t = rectangle->t_2; + v++; + v->x = rectangle->x_2; + v->y = rectangle->y_2; + v->s = rectangle->s_2; + v->t = rectangle->t_2; + v++; + v->x = rectangle->x_2; + v->y = rectangle->y_1; + v->s = rectangle->s_2; + v->t = rectangle->t_1; + v++; + } + + if (allocated) + { + cogl_buffer_set_data (COGL_BUFFER (buffer), + 0, /* offset */ + verts, + sizeof (CoglVertexP2T2) * n_verts); + g_free (verts); + } + else + cogl_buffer_unmap (COGL_BUFFER (buffer)); + + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (CoglVertexP2T2), + G_STRUCT_OFFSET (CoglVertexP2T2, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP2T2), + G_STRUCT_OFFSET (CoglVertexP2T2, s), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + n_verts, + attributes, + 2 /* n_attributes */); + +#ifdef CLUTTER_COGL_HAS_GL + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS)) + cogl_primitive_set_mode (prim, GL_QUADS); + else +#endif + { + /* GLES doesn't support GL_QUADS so instead we use a VBO + with indexed vertices to generate GL_TRIANGLES from the + quads */ + + CoglIndices *indices = + cogl_get_rectangle_indices (ctx, node->d.texture.rectangles->len); + + cogl_primitive_set_indices (prim, indices, + node->d.texture.rectangles->len * 6); + } + + node->d.texture.primitive = prim; + + cogl_object_unref (buffer); + cogl_object_unref (attributes[0]); + cogl_object_unref (attributes[1]); + } + + cogl_primitive_draw (node->d.texture.primitive, + fb, + pipeline); +} + +static void +_cogl_framebuffer_draw_display_list_texture (CoglFramebuffer *fb, + CoglPipeline *pipeline, + CoglPangoDisplayListNode *node) +{ + /* For small runs of text like icon labels, we can get better performance + * going through the Cogl journal since text may then be batched together + * with other geometry. */ + /* FIXME: 25 is a number I plucked out of thin air; it would be good + * to determine this empirically! */ + if (node->d.texture.rectangles->len < 25) + emit_rectangles_through_journal (fb, pipeline, node); + else + emit_vertex_buffer_geometry (fb, pipeline, node); +} + +void +_cogl_pango_display_list_render (CoglFramebuffer *fb, + CoglPangoDisplayList *dl, + const CoglColor *color) +{ + GSList *l; + + for (l = dl->nodes; l; l = l->next) + { + CoglPangoDisplayListNode *node = l->data; + CoglColor draw_color; + + if (node->pipeline == NULL) + { + if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) + node->pipeline = + _cogl_pango_pipeline_cache_get (dl->pipeline_cache, + node->d.texture.texture); + else + node->pipeline = + _cogl_pango_pipeline_cache_get (dl->pipeline_cache, + NULL); + } + + if (node->color_override) + /* Use the override color but preserve the alpha from the + draw color */ + cogl_color_init_from_4ub (&draw_color, + cogl_color_get_red_byte (&node->color), + cogl_color_get_green_byte (&node->color), + cogl_color_get_blue_byte (&node->color), + cogl_color_get_alpha_byte (color)); + else + draw_color = *color; + cogl_color_premultiply (&draw_color); + + cogl_pipeline_set_color (node->pipeline, &draw_color); + + switch (node->type) + { + case COGL_PANGO_DISPLAY_LIST_TEXTURE: + _cogl_framebuffer_draw_display_list_texture (fb, node->pipeline, node); + break; + + case COGL_PANGO_DISPLAY_LIST_RECTANGLE: + cogl_framebuffer_draw_rectangle (fb, + node->pipeline, + node->d.rectangle.x_1, + node->d.rectangle.y_1, + node->d.rectangle.x_2, + node->d.rectangle.y_2); + break; + + case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: + cogl_framebuffer_draw_primitive (fb, node->pipeline, + node->d.trapezoid.primitive); + break; + } + } +} + +static void +_cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node) +{ + if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) + { + g_array_free (node->d.texture.rectangles, TRUE); + if (node->d.texture.texture != NULL) + cogl_object_unref (node->d.texture.texture); + if (node->d.texture.primitive != NULL) + cogl_object_unref (node->d.texture.primitive); + } + else if (node->type == COGL_PANGO_DISPLAY_LIST_TRAPEZOID) + cogl_object_unref (node->d.trapezoid.primitive); + + if (node->pipeline) + cogl_object_unref (node->pipeline); + + g_slice_free (CoglPangoDisplayListNode, node); +} + +void +_cogl_pango_display_list_clear (CoglPangoDisplayList *dl) +{ + g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL); + g_slist_free (dl->nodes); + dl->nodes = NULL; + dl->last_node = NULL; +} + +void +_cogl_pango_display_list_free (CoglPangoDisplayList *dl) +{ + _cogl_pango_display_list_clear (dl); + g_slice_free (CoglPangoDisplayList, dl); +} diff --git a/cogl/cogl-pango/cogl-pango-display-list.h b/cogl/cogl-pango/cogl-pango-display-list.h new file mode 100644 index 000000000..5dbc074e0 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-display-list.h @@ -0,0 +1,84 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_PANGO_DISPLAY_LIST_H__ +#define __COGL_PANGO_DISPLAY_LIST_H__ + +#include +#include "cogl-pango-pipeline-cache.h" + +COGL_BEGIN_DECLS + +typedef struct _CoglPangoDisplayList CoglPangoDisplayList; + +CoglPangoDisplayList * +_cogl_pango_display_list_new (CoglPangoPipelineCache *); + +void +_cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl, + const CoglColor *color); + +void +_cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl); + +void +_cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, + CoglTexture *texture, + float x_1, float y_1, + float x_2, float y_2, + float tx_1, float ty_1, + float tx_2, float ty_2); + +void +_cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, + float x_1, float y_1, + float x_2, float y_2); + +void +_cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, + float y_1, + float x_11, + float x_21, + float y_2, + float x_12, + float x_22); + +void +_cogl_pango_display_list_render (CoglFramebuffer *framebuffer, + CoglPangoDisplayList *dl, + const CoglColor *color); + +void +_cogl_pango_display_list_clear (CoglPangoDisplayList *dl); + +void +_cogl_pango_display_list_free (CoglPangoDisplayList *dl); + +COGL_END_DECLS + +#endif /* __COGL_PANGO_DISPLAY_LIST_H__ */ diff --git a/cogl/cogl-pango/cogl-pango-fontmap.c b/cogl/cogl-pango/cogl-pango-fontmap.c new file mode 100644 index 000000000..a9c7df065 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-fontmap.c @@ -0,0 +1,184 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * SECTION:cogl-pango + * @short_description: COGL-based text rendering using Pango + * + * FIXME + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* This is needed to get the Pango headers to export stuff needed to + subclass */ +#ifndef PANGO_ENABLE_BACKEND +#define PANGO_ENABLE_BACKEND 1 +#endif + +#include +#include +#include + +#include "cogl-pango.h" +#include "cogl-pango-private.h" +#include "cogl-util.h" +#include "cogl/cogl-context-private.h" + +static GQuark cogl_pango_font_map_get_priv_key (void) G_GNUC_CONST; + +typedef struct _CoglPangoFontMapPriv +{ + CoglContext *ctx; + PangoRenderer *renderer; +} CoglPangoFontMapPriv; + +static void +free_priv (gpointer data) +{ + CoglPangoFontMapPriv *priv = data; + + cogl_object_unref (priv->ctx); + cogl_object_unref (priv->renderer); + + g_free (priv); +} + +PangoFontMap * +cogl_pango_font_map_new (void) +{ + PangoFontMap *fm = pango_cairo_font_map_new (); + CoglPangoFontMapPriv *priv = g_new0 (CoglPangoFontMapPriv, 1); + + _COGL_GET_CONTEXT (context, NULL); + + priv->ctx = cogl_object_ref (context); + + /* XXX: The public pango api doesn't let us sub-class + * PangoCairoFontMap so we attach our own private data using qdata + * for now. */ + g_object_set_qdata_full (G_OBJECT (fm), + cogl_pango_font_map_get_priv_key (), + priv, + free_priv); + + return fm; +} + +PangoContext * +cogl_pango_font_map_create_context (CoglPangoFontMap *fm) +{ + _COGL_RETURN_VAL_IF_FAIL (COGL_PANGO_IS_FONT_MAP (fm), NULL); + +#if PANGO_VERSION_CHECK (1, 22, 0) + /* We can just directly use the pango context from the Cairo font + map */ + return pango_font_map_create_context (PANGO_FONT_MAP (fm)); +#else + return pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fm)); +#endif +} + +static CoglPangoFontMapPriv * +_cogl_pango_font_map_get_priv (CoglPangoFontMap *fm) +{ + return g_object_get_qdata (G_OBJECT (fm), + cogl_pango_font_map_get_priv_key ()); +} + +PangoRenderer * +_cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm) +{ + CoglPangoFontMapPriv *priv = _cogl_pango_font_map_get_priv (fm); + if (G_UNLIKELY (!priv->renderer)) + priv->renderer = _cogl_pango_renderer_new (priv->ctx); + return priv->renderer; +} + +PangoRenderer * +cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm) +{ + return _cogl_pango_font_map_get_renderer (fm); +} + +CoglContext * +_cogl_pango_font_map_get_cogl_context (CoglPangoFontMap *fm) +{ + CoglPangoFontMapPriv *priv = _cogl_pango_font_map_get_priv (fm); + return priv->ctx; +} + +void +cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map, + double dpi) +{ + _COGL_RETURN_IF_FAIL (COGL_PANGO_IS_FONT_MAP (font_map)); + + pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (font_map), dpi); +} + +void +cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *fm) +{ + PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); + + _cogl_pango_renderer_clear_glyph_cache (COGL_PANGO_RENDERER (renderer)); +} + +void +cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *fm, + CoglBool value) +{ + PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); + + _cogl_pango_renderer_set_use_mipmapping (COGL_PANGO_RENDERER (renderer), + value); +} + +CoglBool +cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *fm) +{ + PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); + + return + _cogl_pango_renderer_get_use_mipmapping (COGL_PANGO_RENDERER (renderer)); +} + +static GQuark +cogl_pango_font_map_get_priv_key (void) +{ + static GQuark priv_key = 0; + + if (G_UNLIKELY (priv_key == 0)) + priv_key = g_quark_from_static_string ("CoglPangoFontMap"); + + return priv_key; +} diff --git a/cogl/cogl-pango/cogl-pango-glyph-cache.c b/cogl/cogl-pango/cogl-pango-glyph-cache.c new file mode 100644 index 000000000..093404f28 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-glyph-cache.c @@ -0,0 +1,433 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-pango-glyph-cache.h" +#include "cogl-pango-private.h" +#include "cogl/cogl-atlas.h" +#include "cogl/cogl-atlas-texture-private.h" + +typedef struct _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey; + +struct _CoglPangoGlyphCache +{ + CoglContext *ctx; + + /* Hash table to quickly check whether a particular glyph in a + particular font is already cached */ + GHashTable *hash_table; + + /* List of CoglAtlases */ + GSList *atlases; + + /* List of callbacks to invoke when an atlas is reorganized */ + GHookList reorganize_callbacks; + + /* TRUE if we've ever stored a texture in the global atlas. This is + used to make sure we only register one callback to listen for + global atlas reorganizations */ + CoglBool using_global_atlas; + + /* True if some of the glyphs are dirty. This is used as an + optimization in _cogl_pango_glyph_cache_set_dirty_glyphs to avoid + iterating the hash table if we know none of them are dirty */ + CoglBool has_dirty_glyphs; + + /* Whether mipmapping is being used for this cache. This only + affects whether we decide to put the glyph in the global atlas */ + CoglBool use_mipmapping; +}; + +struct _CoglPangoGlyphCacheKey +{ + PangoFont *font; + PangoGlyph glyph; +}; + +static void +cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value) +{ + if (value->texture) + cogl_object_unref (value->texture); + g_slice_free (CoglPangoGlyphCacheValue, value); +} + +static void +cogl_pango_glyph_cache_key_free (CoglPangoGlyphCacheKey *key) +{ + g_object_unref (key->font); + g_slice_free (CoglPangoGlyphCacheKey, key); +} + +static unsigned int +cogl_pango_glyph_cache_hash_func (const void *key) +{ + const CoglPangoGlyphCacheKey *cache_key + = (const CoglPangoGlyphCacheKey *) key; + + /* Generate a number affected by both the font and the glyph + number. We can safely directly compare the pointers because the + key holds a reference to the font so it is not possible that a + different font will have the same memory address */ + return GPOINTER_TO_UINT (cache_key->font) ^ cache_key->glyph; +} + +static CoglBool +cogl_pango_glyph_cache_equal_func (const void *a, const void *b) +{ + const CoglPangoGlyphCacheKey *key_a + = (const CoglPangoGlyphCacheKey *) a; + const CoglPangoGlyphCacheKey *key_b + = (const CoglPangoGlyphCacheKey *) b; + + /* We can safely directly compare the pointers for the fonts because + the key holds a reference to the font so it is not possible that + a different font will have the same memory address */ + return key_a->font == key_b->font + && key_a->glyph == key_b->glyph; +} + +CoglPangoGlyphCache * +cogl_pango_glyph_cache_new (CoglContext *ctx, + CoglBool use_mipmapping) +{ + CoglPangoGlyphCache *cache; + + cache = g_malloc (sizeof (CoglPangoGlyphCache)); + + /* Note: as a rule we don't take references to a CoglContext + * internally since */ + cache->ctx = ctx; + + cache->hash_table = g_hash_table_new_full + (cogl_pango_glyph_cache_hash_func, + cogl_pango_glyph_cache_equal_func, + (GDestroyNotify) cogl_pango_glyph_cache_key_free, + (GDestroyNotify) cogl_pango_glyph_cache_value_free); + + cache->atlases = NULL; + g_hook_list_init (&cache->reorganize_callbacks, sizeof (GHook)); + + cache->has_dirty_glyphs = FALSE; + + cache->using_global_atlas = FALSE; + + cache->use_mipmapping = use_mipmapping; + + return cache; +} + +static void +cogl_pango_glyph_cache_reorganize_cb (void *user_data) +{ + CoglPangoGlyphCache *cache = user_data; + + g_hook_list_invoke (&cache->reorganize_callbacks, FALSE); +} + +void +cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache) +{ + g_slist_foreach (cache->atlases, (GFunc) cogl_object_unref, NULL); + g_slist_free (cache->atlases); + cache->atlases = NULL; + cache->has_dirty_glyphs = FALSE; + + g_hash_table_remove_all (cache->hash_table); +} + +void +cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache) +{ + if (cache->using_global_atlas) + { + _cogl_atlas_texture_remove_reorganize_callback ( + cache->ctx, + cogl_pango_glyph_cache_reorganize_cb, cache); + } + + cogl_pango_glyph_cache_clear (cache); + + g_hash_table_unref (cache->hash_table); + + g_hook_list_clear (&cache->reorganize_callbacks); + + g_free (cache); +} + +static void +cogl_pango_glyph_cache_update_position_cb (void *user_data, + CoglTexture *new_texture, + const CoglRectangleMapEntry *rect) +{ + CoglPangoGlyphCacheValue *value = user_data; + float tex_width, tex_height; + + if (value->texture) + cogl_object_unref (value->texture); + value->texture = cogl_object_ref (new_texture); + + tex_width = cogl_texture_get_width (new_texture); + tex_height = cogl_texture_get_height (new_texture); + + value->tx1 = rect->x / tex_width; + value->ty1 = rect->y / tex_height; + value->tx2 = (rect->x + value->draw_width) / tex_width; + value->ty2 = (rect->y + value->draw_height) / tex_height; + + value->tx_pixel = rect->x; + value->ty_pixel = rect->y; + + /* The glyph has changed position so it will need to be redrawn */ + value->dirty = TRUE; +} + +static CoglBool +cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph, + CoglPangoGlyphCacheValue *value) +{ + CoglAtlasTexture *texture; + CoglError *ignore_error = NULL; + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SHARED_ATLAS)) + return FALSE; + + /* If the cache is using mipmapping then we can't use the global + atlas because it would just get migrated back out */ + if (cache->use_mipmapping) + return FALSE; + + texture = cogl_atlas_texture_new_with_size (cache->ctx, + value->draw_width, + value->draw_height); + if (!cogl_texture_allocate (COGL_TEXTURE (texture), &ignore_error)) + { + cogl_error_free (ignore_error); + return FALSE; + } + + value->texture = COGL_TEXTURE (texture); + value->tx1 = 0; + value->ty1 = 0; + value->tx2 = 1; + value->ty2 = 1; + value->tx_pixel = 0; + value->ty_pixel = 0; + + /* The first time we store a texture in the global atlas we'll + register for notifications when the global atlas is reorganized + so we can forward the notification on as a glyph + reorganization */ + if (!cache->using_global_atlas) + { + _cogl_atlas_texture_add_reorganize_callback + (cache->ctx, + cogl_pango_glyph_cache_reorganize_cb, cache); + cache->using_global_atlas = TRUE; + } + + return TRUE; +} + +static CoglBool +cogl_pango_glyph_cache_add_to_local_atlas (CoglPangoGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph, + CoglPangoGlyphCacheValue *value) +{ + CoglAtlas *atlas = NULL; + GSList *l; + + /* Look for an atlas that can reserve the space */ + for (l = cache->atlases; l; l = l->next) + if (_cogl_atlas_reserve_space (l->data, + value->draw_width + 1, + value->draw_height + 1, + value)) + { + atlas = l->data; + break; + } + + /* If we couldn't find one then start a new atlas */ + if (atlas == NULL) + { + atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8, + COGL_ATLAS_CLEAR_TEXTURE | + COGL_ATLAS_DISABLE_MIGRATION, + cogl_pango_glyph_cache_update_position_cb); + COGL_NOTE (ATLAS, "Created new atlas for glyphs: %p", atlas); + /* If we still can't reserve space then something has gone + seriously wrong so we'll just give up */ + if (!_cogl_atlas_reserve_space (atlas, + value->draw_width + 1, + value->draw_height + 1, + value)) + { + cogl_object_unref (atlas); + return FALSE; + } + + _cogl_atlas_add_reorganize_callback + (atlas, cogl_pango_glyph_cache_reorganize_cb, NULL, cache); + + cache->atlases = g_slist_prepend (cache->atlases, atlas); + } + + return TRUE; +} + +CoglPangoGlyphCacheValue * +cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, + CoglBool create, + PangoFont *font, + PangoGlyph glyph) +{ + CoglPangoGlyphCacheKey lookup_key; + CoglPangoGlyphCacheValue *value; + + lookup_key.font = font; + lookup_key.glyph = glyph; + + value = g_hash_table_lookup (cache->hash_table, &lookup_key); + + if (create && value == NULL) + { + CoglPangoGlyphCacheKey *key; + PangoRectangle ink_rect; + + value = g_slice_new (CoglPangoGlyphCacheValue); + value->texture = NULL; + + pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL); + pango_extents_to_pixels (&ink_rect, NULL); + + value->draw_x = ink_rect.x; + value->draw_y = ink_rect.y; + value->draw_width = ink_rect.width; + value->draw_height = ink_rect.height; + + /* If the glyph is zero-sized then we don't need to reserve any + space for it and we can just avoid painting anything */ + if (ink_rect.width < 1 || ink_rect.height < 1) + value->dirty = FALSE; + else + { + /* Try adding the glyph to the global atlas... */ + if (!cogl_pango_glyph_cache_add_to_global_atlas (cache, + font, + glyph, + value) && + /* If it fails try the local atlas */ + !cogl_pango_glyph_cache_add_to_local_atlas (cache, + font, + glyph, + value)) + { + cogl_pango_glyph_cache_value_free (value); + return NULL; + } + + value->dirty = TRUE; + cache->has_dirty_glyphs = TRUE; + } + + key = g_slice_new (CoglPangoGlyphCacheKey); + key->font = g_object_ref (font); + key->glyph = glyph; + + g_hash_table_insert (cache->hash_table, key, value); + } + + return value; +} + +static void +_cogl_pango_glyph_cache_set_dirty_glyphs_cb (void *key_ptr, + void *value_ptr, + void *user_data) +{ + CoglPangoGlyphCacheKey *key = key_ptr; + CoglPangoGlyphCacheValue *value = value_ptr; + CoglPangoGlyphCacheDirtyFunc func = user_data; + + if (value->dirty) + { + func (key->font, key->glyph, value); + + value->dirty = FALSE; + } +} + +void +_cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache, + CoglPangoGlyphCacheDirtyFunc func) +{ + /* If we know that there are no dirty glyphs then we can shortcut + out early */ + if (!cache->has_dirty_glyphs) + return; + + g_hash_table_foreach (cache->hash_table, + _cogl_pango_glyph_cache_set_dirty_glyphs_cb, + func); + + cache->has_dirty_glyphs = FALSE; +} + +void +_cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache, + GHookFunc func, + void *user_data) +{ + GHook *hook = g_hook_alloc (&cache->reorganize_callbacks); + hook->func = func; + hook->data = user_data; + g_hook_prepend (&cache->reorganize_callbacks, hook); +} + +void +_cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache, + GHookFunc func, + void *user_data) +{ + GHook *hook = g_hook_find_func_data (&cache->reorganize_callbacks, + FALSE, + func, + user_data); + + if (hook) + g_hook_destroy_link (&cache->reorganize_callbacks, hook); +} diff --git a/cogl/cogl-pango/cogl-pango-glyph-cache.h b/cogl/cogl-pango/cogl-pango-glyph-cache.h new file mode 100644 index 000000000..ca33203bc --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-glyph-cache.h @@ -0,0 +1,100 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_PANGO_GLYPH_CACHE_H__ +#define __COGL_PANGO_GLYPH_CACHE_H__ + +#include +#include + +#include "cogl/cogl-texture.h" + +COGL_BEGIN_DECLS + +typedef struct _CoglPangoGlyphCache CoglPangoGlyphCache; +typedef struct _CoglPangoGlyphCacheValue CoglPangoGlyphCacheValue; + +struct _CoglPangoGlyphCacheValue +{ + CoglTexture *texture; + + float tx1; + float ty1; + float tx2; + float ty2; + + int tx_pixel; + int ty_pixel; + + int draw_x; + int draw_y; + int draw_width; + int draw_height; + + /* This will be set to TRUE when the glyph atlas is reorganized + which means the glyph will need to be redrawn */ + CoglBool dirty; +}; + +typedef void (* CoglPangoGlyphCacheDirtyFunc) (PangoFont *font, + PangoGlyph glyph, + CoglPangoGlyphCacheValue *value); + +CoglPangoGlyphCache * +cogl_pango_glyph_cache_new (CoglContext *ctx, + CoglBool use_mipmapping); + +void +cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache); + +CoglPangoGlyphCacheValue * +cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, + CoglBool create, + PangoFont *font, + PangoGlyph glyph); + +void +cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache); + +void +_cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache, + GHookFunc func, + void *user_data); + +void +_cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache, + GHookFunc func, + void *user_data); + +void +_cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache, + CoglPangoGlyphCacheDirtyFunc func); + +COGL_END_DECLS + +#endif /* __COGL_PANGO_GLYPH_CACHE_H__ */ diff --git a/cogl/cogl-pango/cogl-pango-pipeline-cache.c b/cogl/cogl-pango/cogl-pango-pipeline-cache.c new file mode 100644 index 000000000..625a8fa41 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-pipeline-cache.c @@ -0,0 +1,242 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "cogl-pango-pipeline-cache.h" + +#include "cogl/cogl-context-private.h" +#include "cogl/cogl-texture-private.h" + +typedef struct _CoglPangoPipelineCacheEntry CoglPangoPipelineCacheEntry; + +struct _CoglPangoPipelineCacheEntry +{ + /* This will take a reference or it can be NULL to represent the + pipeline used to render colors */ + CoglTexture *texture; + + /* This will only take a weak reference */ + CoglPipeline *pipeline; +}; + +static void +_cogl_pango_pipeline_cache_key_destroy (void *data) +{ + if (data) + cogl_object_unref (data); +} + +static void +_cogl_pango_pipeline_cache_value_destroy (void *data) +{ + CoglPangoPipelineCacheEntry *cache_entry = data; + + if (cache_entry->texture) + cogl_object_unref (cache_entry->texture); + + /* We don't need to unref the pipeline because it only takes a weak + reference */ + + g_slice_free (CoglPangoPipelineCacheEntry, cache_entry); +} + +CoglPangoPipelineCache * +_cogl_pango_pipeline_cache_new (CoglContext *ctx, + CoglBool use_mipmapping) +{ + CoglPangoPipelineCache *cache = g_new (CoglPangoPipelineCache, 1); + + cache->ctx = cogl_object_ref (ctx); + + /* The key is the pipeline pointer. A reference is taken when the + pipeline is used as a key so we should unref it again in the + destroy function */ + cache->hash_table = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + _cogl_pango_pipeline_cache_key_destroy, + _cogl_pango_pipeline_cache_value_destroy); + + cache->base_texture_rgba_pipeline = NULL; + cache->base_texture_alpha_pipeline = NULL; + + cache->use_mipmapping = use_mipmapping; + + return cache; +} + +static CoglPipeline * +get_base_texture_rgba_pipeline (CoglPangoPipelineCache *cache) +{ + if (cache->base_texture_rgba_pipeline == NULL) + { + CoglPipeline *pipeline; + + pipeline = cache->base_texture_rgba_pipeline = + cogl_pipeline_new (cache->ctx); + + cogl_pipeline_set_layer_wrap_mode (pipeline, 0, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); + + if (cache->use_mipmapping) + cogl_pipeline_set_layer_filters + (pipeline, 0, + COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, + COGL_PIPELINE_FILTER_LINEAR); + } + + return cache->base_texture_rgba_pipeline; +} + +static CoglPipeline * +get_base_texture_alpha_pipeline (CoglPangoPipelineCache *cache) +{ + if (cache->base_texture_alpha_pipeline == NULL) + { + CoglPipeline *pipeline; + + pipeline = cogl_pipeline_copy (get_base_texture_rgba_pipeline (cache)); + cache->base_texture_alpha_pipeline = pipeline; + + /* The default combine mode of materials is to modulate (A x B) + * the texture RGBA channels with the RGBA channels of the + * previous layer (which in our case is just the font color) + * + * Since the RGB for an alpha texture is defined as 0, this gives us: + * + * result.rgb = color.rgb * 0 + * result.a = color.a * texture.a + * + * What we want is premultiplied rgba values: + * + * result.rgba = color.rgb * texture.a + * result.a = color.a * texture.a + */ + cogl_pipeline_set_layer_combine (pipeline, 0, /* layer */ + "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", + NULL); + } + + return cache->base_texture_alpha_pipeline; +} + +typedef struct +{ + CoglPangoPipelineCache *cache; + CoglTexture *texture; +} PipelineDestroyNotifyData; + +static void +pipeline_destroy_notify_cb (void *user_data) +{ + PipelineDestroyNotifyData *data = user_data; + + g_hash_table_remove (data->cache->hash_table, data->texture); + g_slice_free (PipelineDestroyNotifyData, data); +} + +CoglPipeline * +_cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, + CoglTexture *texture) +{ + CoglPangoPipelineCacheEntry *entry; + PipelineDestroyNotifyData *destroy_data; + static CoglUserDataKey pipeline_destroy_notify_key; + + /* Look for an existing entry */ + entry = g_hash_table_lookup (cache->hash_table, texture); + + if (entry) + return cogl_object_ref (entry->pipeline); + + /* No existing pipeline was found so let's create another */ + entry = g_slice_new (CoglPangoPipelineCacheEntry); + + if (texture) + { + CoglPipeline *base; + + entry->texture = cogl_object_ref (texture); + + if (_cogl_texture_get_format (entry->texture) == COGL_PIXEL_FORMAT_A_8) + base = get_base_texture_alpha_pipeline (cache); + else + base = get_base_texture_rgba_pipeline (cache); + + entry->pipeline = cogl_pipeline_copy (base); + + cogl_pipeline_set_layer_texture (entry->pipeline, 0 /* layer */, texture); + } + else + { + entry->texture = NULL; + entry->pipeline = cogl_pipeline_new (cache->ctx); + } + + /* Add a weak reference to the pipeline so we can remove it from the + hash table when it is destroyed */ + destroy_data = g_slice_new (PipelineDestroyNotifyData); + destroy_data->cache = cache; + destroy_data->texture = texture; + cogl_object_set_user_data (COGL_OBJECT (entry->pipeline), + &pipeline_destroy_notify_key, + destroy_data, + pipeline_destroy_notify_cb); + + g_hash_table_insert (cache->hash_table, + texture ? cogl_object_ref (texture) : NULL, + entry); + + /* This doesn't take a reference on the pipeline so that it will use + the newly created reference */ + return entry->pipeline; +} + +void +_cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache) +{ + if (cache->base_texture_rgba_pipeline) + cogl_object_unref (cache->base_texture_rgba_pipeline); + if (cache->base_texture_alpha_pipeline) + cogl_object_unref (cache->base_texture_alpha_pipeline); + + g_hash_table_destroy (cache->hash_table); + + cogl_object_unref (cache->ctx); + + g_free (cache); +} diff --git a/cogl/cogl-pango/cogl-pango-pipeline-cache.h b/cogl/cogl-pango/cogl-pango-pipeline-cache.h new file mode 100644 index 000000000..c8abe2c34 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-pipeline-cache.h @@ -0,0 +1,72 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PANGO_PIPELINE_CACHE_H__ +#define __COGL_PANGO_PIPELINE_CACHE_H__ + +#include + +#include "cogl/cogl-context-private.h" + +COGL_BEGIN_DECLS + +typedef struct _CoglPangoPipelineCache +{ + CoglContext *ctx; + + GHashTable *hash_table; + + CoglPipeline *base_texture_alpha_pipeline; + CoglPipeline *base_texture_rgba_pipeline; + + CoglBool use_mipmapping; +} CoglPangoPipelineCache; + + +CoglPangoPipelineCache * +_cogl_pango_pipeline_cache_new (CoglContext *ctx, + CoglBool use_mipmapping); + +/* Returns a pipeline that can be used to render glyphs in the given + texture. The pipeline has a new reference so it is up to the caller + to unref it */ +CoglPipeline * +_cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, + CoglTexture *texture); + +void +_cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache); + +COGL_END_DECLS + +#endif /* __COGL_PANGO_PIPELINE_CACHE_H__ */ diff --git a/cogl/cogl-pango/cogl-pango-private.h b/cogl/cogl-pango/cogl-pango-private.h new file mode 100644 index 000000000..e0d1f51fa --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-private.h @@ -0,0 +1,65 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + * Robert Bragg + * Matthew Allum + */ + +#ifndef __COGL_PANGO_PRIVATE_H__ +#define __COGL_PANGO_PRIVATE_H__ + +#include "cogl-pango.h" + +COGL_BEGIN_DECLS + +PangoRenderer * +_cogl_pango_renderer_new (CoglContext *context); + +void +_cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer); + +void +_cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer, + CoglBool value); +CoglBool +_cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer); + + + +CoglContext * +_cogl_pango_font_map_get_cogl_context (CoglPangoFontMap *fm); + +PangoRenderer * +_cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm); + +COGL_END_DECLS + +#endif /* __COGL_PANGO_PRIVATE_H__ */ diff --git a/cogl/cogl-pango/cogl-pango-render.c b/cogl/cogl-pango/cogl-pango-render.c new file mode 100644 index 000000000..a8855eac0 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango-render.c @@ -0,0 +1,929 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + * Robert Bragg + * Matthew Allum + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef PANGO_ENABLE_BACKEND +#define PANGO_ENABLE_BACKEND 1 +#endif + +#include +#include +#include +#include + +#include "cogl/cogl-debug.h" +#include "cogl/cogl-context-private.h" +#include "cogl/cogl-texture-private.h" +#include "cogl-pango-private.h" +#include "cogl-pango-glyph-cache.h" +#include "cogl-pango-display-list.h" + +enum +{ + PROP_0, + + PROP_COGL_CONTEXT, + PROP_LAST +}; + +typedef struct +{ + CoglPangoGlyphCache *glyph_cache; + CoglPangoPipelineCache *pipeline_cache; +} CoglPangoRendererCaches; + +struct _CoglPangoRenderer +{ + PangoRenderer parent_instance; + + CoglContext *ctx; + + /* Two caches of glyphs as textures and their corresponding pipeline + caches, one with mipmapped textures and one without */ + CoglPangoRendererCaches no_mipmap_caches; + CoglPangoRendererCaches mipmap_caches; + + CoglBool use_mipmapping; + + /* The current display list that is being built */ + CoglPangoDisplayList *display_list; +}; + +struct _CoglPangoRendererClass +{ + PangoRendererClass class_instance; +}; + +typedef struct _CoglPangoLayoutQdata CoglPangoLayoutQdata; + +/* An instance of this struct gets attached to each PangoLayout to + cache the VBO and to detect changes to the layout */ +struct _CoglPangoLayoutQdata +{ + CoglPangoRenderer *renderer; + /* The cache of the geometry for the layout */ + CoglPangoDisplayList *display_list; + /* A reference to the first line of the layout. This is just used to + detect changes */ + PangoLayoutLine *first_line; + /* Whether mipmapping was previously used to render this layout. We + need to regenerate the display list if the mipmapping value is + changed because it will be using a different set of textures */ + CoglBool mipmapping_used; +}; + +static void +_cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line); + +typedef struct +{ + CoglPangoDisplayList *display_list; + float x1, y1, x2, y2; +} CoglPangoRendererSliceCbData; + +PangoRenderer * +_cogl_pango_renderer_new (CoglContext *context) +{ + return PANGO_RENDERER (g_object_new (COGL_PANGO_TYPE_RENDERER, + "context", context, NULL)); +} + +static void +cogl_pango_renderer_slice_cb (CoglTexture *texture, + const float *slice_coords, + const float *virtual_coords, + void *user_data) +{ + CoglPangoRendererSliceCbData *data = user_data; + + /* Note: this assumes that there is only one slice containing the + whole texture and it doesn't attempt to split up the vertex + coordinates based on the virtual_coords */ + + _cogl_pango_display_list_add_texture (data->display_list, + texture, + data->x1, + data->y1, + data->x2, + data->y2, + slice_coords[0], + slice_coords[1], + slice_coords[2], + slice_coords[3]); +} + +static void +cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, + CoglPangoGlyphCacheValue *cache_value, + float x1, + float y1) +{ + CoglPangoRendererSliceCbData data; + + _COGL_RETURN_IF_FAIL (priv->display_list != NULL); + + data.display_list = priv->display_list; + data.x1 = x1; + data.y1 = y1; + data.x2 = x1 + (float) cache_value->draw_width; + data.y2 = y1 + (float) cache_value->draw_height; + + /* We iterate the internal sub textures of the texture so that we + can get a pointer to the base texture even if the texture is in + the global atlas. That way the display list can recognise that + the neighbouring glyphs are coming from the same atlas and bundle + them together into a single VBO */ + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (cache_value->texture), + cache_value->tx1, + cache_value->ty1, + cache_value->tx2, + cache_value->ty2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + cogl_pango_renderer_slice_cb, + &data); +} + +static void cogl_pango_renderer_dispose (GObject *object); +static void cogl_pango_renderer_finalize (GObject *object); +static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y); +static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height); +static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, + PangoRenderPart part, + double y1, + double x11, + double x21, + double y2, + double x12, + double x22); + +G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER); + +static void +cogl_pango_renderer_init (CoglPangoRenderer *priv) +{ +} + +static void +_cogl_pango_renderer_constructed (GObject *gobject) +{ + CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (gobject); + CoglContext *ctx = renderer->ctx; + + renderer->no_mipmap_caches.pipeline_cache = + _cogl_pango_pipeline_cache_new (ctx, FALSE); + renderer->mipmap_caches.pipeline_cache = + _cogl_pango_pipeline_cache_new (ctx, TRUE); + + renderer->no_mipmap_caches.glyph_cache = + cogl_pango_glyph_cache_new (ctx, FALSE); + renderer->mipmap_caches.glyph_cache = + cogl_pango_glyph_cache_new (ctx, TRUE); + + _cogl_pango_renderer_set_use_mipmapping (renderer, FALSE); + + if (G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed) + G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed (gobject); +} + +static void +cogl_pango_renderer_set_property (GObject *object, + unsigned int prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (object); + + switch (prop_id) + { + case PROP_COGL_CONTEXT: + renderer->ctx = g_value_get_pointer (value); + cogl_object_ref (renderer->ctx); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cogl_pango_renderer_class_init (CoglPangoRendererClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); + GParamSpec *pspec; + + object_class->set_property = cogl_pango_renderer_set_property; + object_class->constructed = _cogl_pango_renderer_constructed; + object_class->dispose = cogl_pango_renderer_dispose; + object_class->finalize = cogl_pango_renderer_finalize; + + pspec = g_param_spec_pointer ("context", + "Context", + "The Cogl Context", + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_property (object_class, PROP_COGL_CONTEXT, pspec); + + renderer_class->draw_glyphs = cogl_pango_renderer_draw_glyphs; + renderer_class->draw_rectangle = cogl_pango_renderer_draw_rectangle; + renderer_class->draw_trapezoid = cogl_pango_renderer_draw_trapezoid; +} + +static void +cogl_pango_renderer_dispose (GObject *object) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object); + + if (priv->ctx) + { + cogl_object_unref (priv->ctx); + priv->ctx = NULL; + } +} + +static void +cogl_pango_renderer_finalize (GObject *object) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object); + + cogl_pango_glyph_cache_free (priv->no_mipmap_caches.glyph_cache); + cogl_pango_glyph_cache_free (priv->mipmap_caches.glyph_cache); + + _cogl_pango_pipeline_cache_free (priv->no_mipmap_caches.pipeline_cache); + _cogl_pango_pipeline_cache_free (priv->mipmap_caches.pipeline_cache); + + G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); +} + +static CoglPangoRenderer * +cogl_pango_get_renderer_from_context (PangoContext *context) +{ + PangoFontMap *font_map; + CoglPangoFontMap *cogl_font_map; + PangoRenderer *renderer; + + font_map = pango_context_get_font_map (context); + g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (font_map), NULL); + + cogl_font_map = COGL_PANGO_FONT_MAP (font_map); + + renderer = _cogl_pango_font_map_get_renderer (cogl_font_map); + + g_return_val_if_fail (COGL_PANGO_IS_RENDERER (renderer), NULL); + + return COGL_PANGO_RENDERER (renderer); +} + +static GQuark +cogl_pango_layout_get_qdata_key (void) +{ + static GQuark key = 0; + + if (G_UNLIKELY (key == 0)) + key = g_quark_from_static_string ("CoglPangoDisplayList"); + + return key; +} + +static void +cogl_pango_layout_qdata_forget_display_list (CoglPangoLayoutQdata *qdata) +{ + if (qdata->display_list) + { + CoglPangoRendererCaches *caches = qdata->mipmapping_used ? + &qdata->renderer->mipmap_caches : + &qdata->renderer->no_mipmap_caches; + + _cogl_pango_glyph_cache_remove_reorganize_callback + (caches->glyph_cache, + (GHookFunc) cogl_pango_layout_qdata_forget_display_list, + qdata); + + _cogl_pango_display_list_free (qdata->display_list); + + qdata->display_list = NULL; + } +} + +static void +cogl_pango_render_qdata_destroy (CoglPangoLayoutQdata *qdata) +{ + cogl_pango_layout_qdata_forget_display_list (qdata); + if (qdata->first_line) + pango_layout_line_unref (qdata->first_line); + g_slice_free (CoglPangoLayoutQdata, qdata); +} + +void +cogl_pango_show_layout (CoglFramebuffer *fb, + PangoLayout *layout, + float x, + float y, + const CoglColor *color) +{ + PangoContext *context; + CoglPangoRenderer *priv; + CoglPangoLayoutQdata *qdata; + + context = pango_layout_get_context (layout); + priv = cogl_pango_get_renderer_from_context (context); + if (G_UNLIKELY (!priv)) + return; + + qdata = g_object_get_qdata (G_OBJECT (layout), + cogl_pango_layout_get_qdata_key ()); + + if (qdata == NULL) + { + qdata = g_slice_new0 (CoglPangoLayoutQdata); + qdata->renderer = priv; + g_object_set_qdata_full (G_OBJECT (layout), + cogl_pango_layout_get_qdata_key (), + qdata, + (GDestroyNotify) + cogl_pango_render_qdata_destroy); + } + + /* Check if the layout has changed since the last build of the + display list. This trick was suggested by Behdad Esfahbod here: + http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */ + if (qdata->display_list && + ((qdata->first_line && + qdata->first_line->layout != layout) || + qdata->mipmapping_used != priv->use_mipmapping)) + cogl_pango_layout_qdata_forget_display_list (qdata); + + if (qdata->display_list == NULL) + { + CoglPangoRendererCaches *caches = priv->use_mipmapping ? + &priv->mipmap_caches : + &priv->no_mipmap_caches; + + cogl_pango_ensure_glyph_cache_for_layout (layout); + + qdata->display_list = + _cogl_pango_display_list_new (caches->pipeline_cache); + + /* Register for notification of when the glyph cache changes so + we can rebuild the display list */ + _cogl_pango_glyph_cache_add_reorganize_callback + (caches->glyph_cache, + (GHookFunc) cogl_pango_layout_qdata_forget_display_list, + qdata); + + priv->display_list = qdata->display_list; + pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); + priv->display_list = NULL; + + qdata->mipmapping_used = priv->use_mipmapping; + } + + cogl_framebuffer_push_matrix (fb); + cogl_framebuffer_translate (fb, x, y, 0); + + _cogl_pango_display_list_render (fb, + qdata->display_list, + color); + + cogl_framebuffer_pop_matrix (fb); + + /* Keep a reference to the first line of the layout so we can detect + changes */ + if (qdata->first_line) + { + pango_layout_line_unref (qdata->first_line); + qdata->first_line = NULL; + } + if (pango_layout_get_line_count (layout) > 0) + { + qdata->first_line = pango_layout_get_line (layout, 0); + pango_layout_line_ref (qdata->first_line); + } +} + +void +cogl_pango_render_layout_subpixel (PangoLayout *layout, + int x, + int y, + const CoglColor *color, + int flags) +{ + cogl_pango_show_layout (cogl_get_draw_framebuffer (), + layout, + x / (float) PANGO_SCALE, + y / (float) PANGO_SCALE, + color); +} + +void +cogl_pango_render_layout (PangoLayout *layout, + int x, + int y, + const CoglColor *color, + int flags) +{ + cogl_pango_render_layout_subpixel (layout, + x * PANGO_SCALE, + y * PANGO_SCALE, + color, + flags); +} + +void +cogl_pango_show_layout_line (CoglFramebuffer *fb, + PangoLayoutLine *line, + float x, + float y, + const CoglColor *color) +{ + PangoContext *context; + CoglPangoRenderer *priv; + CoglPangoRendererCaches *caches; + int pango_x = x * PANGO_SCALE; + int pango_y = y * PANGO_SCALE; + + context = pango_layout_get_context (line->layout); + priv = cogl_pango_get_renderer_from_context (context); + if (G_UNLIKELY (!priv)) + return; + + caches = (priv->use_mipmapping ? + &priv->mipmap_caches : + &priv->no_mipmap_caches); + + priv->display_list = _cogl_pango_display_list_new (caches->pipeline_cache); + + _cogl_pango_ensure_glyph_cache_for_layout_line (line); + + pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, + pango_x, pango_y); + + _cogl_pango_display_list_render (fb, + priv->display_list, + color); + + _cogl_pango_display_list_free (priv->display_list); + priv->display_list = NULL; +} + +void +cogl_pango_render_layout_line (PangoLayoutLine *line, + int x, + int y, + const CoglColor *color) +{ + cogl_pango_show_layout_line (cogl_get_draw_framebuffer (), + line, + x / (float) PANGO_SCALE, + y / (float) PANGO_SCALE, + color); +} + +void +_cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer) +{ + cogl_pango_glyph_cache_clear (renderer->mipmap_caches.glyph_cache); + cogl_pango_glyph_cache_clear (renderer->no_mipmap_caches.glyph_cache); +} + +void +_cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer, + CoglBool value) +{ + renderer->use_mipmapping = value; +} + +CoglBool +_cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer) +{ + return renderer->use_mipmapping; +} + +static CoglPangoGlyphCacheValue * +cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer, + CoglBool create, + PangoFont *font, + PangoGlyph glyph) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); + CoglPangoRendererCaches *caches = (priv->use_mipmapping ? + &priv->mipmap_caches : + &priv->no_mipmap_caches); + + return cogl_pango_glyph_cache_lookup (caches->glyph_cache, + create, font, glyph); +} + +static void +cogl_pango_renderer_set_dirty_glyph (PangoFont *font, + PangoGlyph glyph, + CoglPangoGlyphCacheValue *value) +{ + cairo_surface_t *surface; + cairo_t *cr; + cairo_scaled_font_t *scaled_font; + cairo_glyph_t cairo_glyph; + cairo_format_t format_cairo; + CoglPixelFormat format_cogl; + + COGL_NOTE (PANGO, "redrawing glyph %i", glyph); + + /* Glyphs that don't take up any space will end up without a + texture. These should never become dirty so they shouldn't end up + here */ + _COGL_RETURN_IF_FAIL (value->texture != NULL); + + if (_cogl_texture_get_format (value->texture) == COGL_PIXEL_FORMAT_A_8) + { + format_cairo = CAIRO_FORMAT_A8; + format_cogl = COGL_PIXEL_FORMAT_A_8; + } + else + { + format_cairo = CAIRO_FORMAT_ARGB32; + + /* Cairo stores the data in native byte order as ARGB but Cogl's + pixel formats specify the actual byte order. Therefore we + need to use a different format depending on the + architecture */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + format_cogl = COGL_PIXEL_FORMAT_BGRA_8888_PRE; +#else + format_cogl = COGL_PIXEL_FORMAT_ARGB_8888_PRE; +#endif + } + + surface = cairo_image_surface_create (format_cairo, + value->draw_width, + value->draw_height); + cr = cairo_create (surface); + + scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font)); + cairo_set_scaled_font (cr, scaled_font); + + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + + cairo_glyph.x = -value->draw_x; + cairo_glyph.y = -value->draw_y; + /* The PangoCairo glyph numbers directly map to Cairo glyph + numbers */ + cairo_glyph.index = glyph; + cairo_show_glyphs (cr, &cairo_glyph, 1); + + cairo_destroy (cr); + cairo_surface_flush (surface); + + /* Copy the glyph to the texture */ + cogl_texture_set_region (value->texture, + 0, /* src_x */ + 0, /* src_y */ + value->tx_pixel, /* dst_x */ + value->ty_pixel, /* dst_y */ + value->draw_width, /* dst_width */ + value->draw_height, /* dst_height */ + value->draw_width, /* width */ + value->draw_height, /* height */ + format_cogl, + cairo_image_surface_get_stride (surface), + cairo_image_surface_get_data (surface)); + + cairo_surface_destroy (surface); +} + +static void +_cogl_pango_ensure_glyph_cache_for_layout_line_internal (PangoLayoutLine *line) +{ + PangoContext *context; + PangoRenderer *renderer; + GSList *l; + + context = pango_layout_get_context (line->layout); + renderer = + PANGO_RENDERER (cogl_pango_get_renderer_from_context (context)); + + for (l = line->runs; l; l = l->next) + { + PangoLayoutRun *run = l->data; + PangoGlyphString *glyphs = run->glyphs; + int i; + + for (i = 0; i < glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = &glyphs->glyphs[i]; + + /* If the glyph isn't cached then this will reserve + space for it now. We won't actually draw the glyph + yet because reserving space could cause all of the + other glyphs to be moved so we might as well redraw + them all later once we know that the position is + settled */ + cogl_pango_renderer_get_cached_glyph (renderer, TRUE, + run->item->analysis.font, + gi->glyph); + } + } +} + +static void +_cogl_pango_set_dirty_glyphs (CoglPangoRenderer *priv) +{ + _cogl_pango_glyph_cache_set_dirty_glyphs + (priv->mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph); + _cogl_pango_glyph_cache_set_dirty_glyphs + (priv->no_mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph); +} + +static void +_cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line) +{ + PangoContext *context; + CoglPangoRenderer *priv; + + context = pango_layout_get_context (line->layout); + priv = cogl_pango_get_renderer_from_context (context); + + _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line); + + /* Now that we know all of the positions are settled we'll fill in + any dirty glyphs */ + _cogl_pango_set_dirty_glyphs (priv); +} + +void +cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout) +{ + PangoContext *context; + CoglPangoRenderer *priv; + PangoLayoutIter *iter; + + context = pango_layout_get_context (layout); + priv = cogl_pango_get_renderer_from_context (context); + + _COGL_RETURN_IF_FAIL (PANGO_IS_LAYOUT (layout)); + + if ((iter = pango_layout_get_iter (layout)) == NULL) + return; + + do + { + PangoLayoutLine *line; + + line = pango_layout_iter_get_line_readonly (iter); + + _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line); + } + while (pango_layout_iter_next_line (iter)); + + pango_layout_iter_free (iter); + + /* Now that we know all of the positions are settled we'll fill in + any dirty glyphs */ + _cogl_pango_set_dirty_glyphs (priv); +} + +static void +cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer, + PangoRenderPart part) +{ + PangoColor *pango_color = pango_renderer_get_color (renderer, part); + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); + + if (pango_color) + { + CoglColor color; + + cogl_color_init_from_4ub (&color, + pango_color->red >> 8, + pango_color->green >> 8, + pango_color->blue >> 8, + 0xff); + + _cogl_pango_display_list_set_color_override (priv->display_list, &color); + } + else + _cogl_pango_display_list_remove_color_override (priv->display_list); +} + +static void +cogl_pango_renderer_draw_box (PangoRenderer *renderer, + int x, + int y, + int width, + int height) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); + + _COGL_RETURN_IF_FAIL (priv->display_list != NULL); + + _cogl_pango_display_list_add_rectangle (priv->display_list, + x, + y - height, + x + width, + y); +} + +static void +cogl_pango_renderer_get_device_units (PangoRenderer *renderer, + int xin, + int yin, + float *xout, + float *yout) +{ + const PangoMatrix *matrix; + + if ((matrix = pango_renderer_get_matrix (renderer))) + { + /* Convert user-space coords to device coords */ + *xout = ((xin * matrix->xx + yin * matrix->xy) + / PANGO_SCALE + matrix->x0); + *yout = ((yin * matrix->yy + xin * matrix->yx) + / PANGO_SCALE + matrix->y0); + } + else + { + *xout = PANGO_PIXELS (xin); + *yout = PANGO_PIXELS (yin); + } +} + +static void +cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); + float x1, x2, y1, y2; + + _COGL_RETURN_IF_FAIL (priv->display_list != NULL); + + cogl_pango_renderer_set_color_for_part (renderer, part); + + cogl_pango_renderer_get_device_units (renderer, + x, y, + &x1, &y1); + cogl_pango_renderer_get_device_units (renderer, + x + width, y + height, + &x2, &y2); + + _cogl_pango_display_list_add_rectangle (priv->display_list, + x1, y1, x2, y2); +} + +static void +cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, + PangoRenderPart part, + double y1, + double x11, + double x21, + double y2, + double x12, + double x22) +{ + CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); + + _COGL_RETURN_IF_FAIL (priv->display_list != NULL); + + cogl_pango_renderer_set_color_for_part (renderer, part); + + _cogl_pango_display_list_add_trapezoid (priv->display_list, + y1, + x11, + x21, + y2, + x12, + x22); +} + +static void +cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int xi, + int yi) +{ + CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer; + CoglPangoGlyphCacheValue *cache_value; + int i; + + cogl_pango_renderer_set_color_for_part (renderer, + PANGO_RENDER_PART_FOREGROUND); + + for (i = 0; i < glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = glyphs->glyphs + i; + float x, y; + + cogl_pango_renderer_get_device_units (renderer, + xi + gi->geometry.x_offset, + yi + gi->geometry.y_offset, + &x, &y); + + if ((gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)) + { + if (font == NULL) + { + cogl_pango_renderer_draw_box (renderer, + x, + y, + PANGO_UNKNOWN_GLYPH_WIDTH, + PANGO_UNKNOWN_GLYPH_HEIGHT); + } + else + { + PangoRectangle ink_rect; + + pango_font_get_glyph_extents (font, gi->glyph, &ink_rect, NULL); + pango_extents_to_pixels (&ink_rect, NULL); + + cogl_pango_renderer_draw_box (renderer, + x + ink_rect.x, + y + ink_rect.y + ink_rect.height, + ink_rect.width, + ink_rect.height); + } + } + else + { + /* Get the texture containing the glyph */ + cache_value = + cogl_pango_renderer_get_cached_glyph (renderer, + FALSE, + font, + gi->glyph); + + /* cogl_pango_ensure_glyph_cache_for_layout should always be + called before rendering a layout so we should never have + a dirty glyph here */ + g_assert (cache_value == NULL || !cache_value->dirty); + + if (cache_value == NULL) + { + cogl_pango_renderer_draw_box (renderer, + x, + y, + PANGO_UNKNOWN_GLYPH_WIDTH, + PANGO_UNKNOWN_GLYPH_HEIGHT); + } + else if (cache_value->texture) + { + x += (float)(cache_value->draw_x); + y += (float)(cache_value->draw_y); + + cogl_pango_renderer_draw_glyph (priv, cache_value, x, y); + } + } + + xi += gi->geometry.width; + } +} diff --git a/cogl/cogl-pango/cogl-pango.h b/cogl/cogl-pango/cogl-pango.h new file mode 100644 index 000000000..871284973 --- /dev/null +++ b/cogl/cogl-pango/cogl-pango.h @@ -0,0 +1,298 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008 OpenedHand + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + * Robert Bragg + * Matthew Allum + */ + +#ifndef __COGL_PANGO_H__ +#define __COGL_PANGO_H__ + +#include +#include +#include + +/* XXX: Currently this header may be included both as an internal + * header (within the cogl-pango implementation) and as a public + * header. + * + * Since should not be included for internal use we + * determine the current context and switch between including cogl.h + * or specific internal cogl headers here... + */ +#ifndef COGL_COMPILATION +#include +#else +#include "cogl/cogl-context.h" +#endif + +COGL_BEGIN_DECLS + +/* It's too difficult to actually subclass the pango cairo font + * map. Instead we just make a fake set of macros that actually just + * directly use the original type + */ +#define COGL_PANGO_TYPE_FONT_MAP PANGO_TYPE_CAIRO_FONT_MAP +#define COGL_PANGO_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_FONT_MAP, CoglPangoFontMap)) +#define COGL_PANGO_IS_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_FONT_MAP)) + +typedef PangoCairoFontMap CoglPangoFontMap; + +/** + * cogl_pango_font_map_new: + * + * Creates a new font map. + * + * Return value: (transfer full): the newly created #PangoFontMap + * + * Since: 1.14 + */ +PangoFontMap * +cogl_pango_font_map_new (void); + +/** + * cogl_pango_font_map_create_context: + * @font_map: a #CoglPangoFontMap + * + * Create a #PangoContext for the given @font_map. + * + * Returns: (transfer full): the newly created context: free with g_object_unref(). + */ +PangoContext * +cogl_pango_font_map_create_context (CoglPangoFontMap *font_map); + +/** + * cogl_pango_font_map_set_resolution: + * @font_map: a #CoglPangoFontMap + * @dpi: The resolution in "dots per inch". (Physical inches aren't + * actually involved; the terminology is conventional.) + * + * Sets the resolution for the @font_map. This is a scale factor + * between points specified in a #PangoFontDescription and Cogl units. + * The default value is %96, meaning that a 10 point font will be 13 + * units high. (10 * 96. / 72. = 13.3). + * + * Since: 1.14 + */ +void +cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map, + double dpi); + +/** + * cogl_pango_font_map_clear_glyph_cache: + * @font_map: a #CoglPangoFontMap + * + * Clears the glyph cache for @font_map. + * + * Since: 1.0 + */ +void +cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map); + +/** + * cogl_pango_ensure_glyph_cache_for_layout: + * @layout: A #PangoLayout + * + * This updates any internal glyph cache textures as necessary to be + * able to render the given @layout. + * + * This api should be used to avoid mid-scene modifications of + * glyph-cache textures which can lead to undefined rendering results. + * + * Since: 1.0 + */ +void +cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout); + +/** + * cogl_pango_font_map_set_use_mipmapping: + * @font_map: a #CoglPangoFontMap + * @value: %TRUE to enable the use of mipmapping + * + * Sets whether the renderer for the passed font map should use + * mipmapping when rendering a #PangoLayout. + * + * Since: 1.0 + */ +void +cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map, + CoglBool value); + +/** + * cogl_pango_font_map_get_use_mipmapping: + * @font_map: a #CoglPangoFontMap + * + * Retrieves whether the #CoglPangoRenderer used by @font_map will use + * mipmapping when rendering the glyphs. + * + * Return value: %TRUE if mipmapping is used, %FALSE otherwise. + * + * Since: 1.0 + */ +CoglBool +cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map); + +/** + * cogl_pango_font_map_get_renderer: + * @font_map: a #CoglPangoFontMap + * + * Retrieves the #CoglPangoRenderer for the passed @font_map. + * + * Return value: (transfer none): a #PangoRenderer + * + * Since: 1.0 + */ +PangoRenderer * +cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map); + +/** + * cogl_pango_show_layout: + * @framebuffer: A #CoglFramebuffer to draw too. + * @layout: a #PangoLayout + * @x: X coordinate to render the layout at + * @y: Y coordinate to render the layout at + * @color: color to use when rendering the layout + * + * Draws a solidly coloured @layout on the given @framebuffer at (@x, + * @y) within the @framebuffer's current model-view coordinate + * space. + * + * Since: 1.14 + */ +void +cogl_pango_show_layout (CoglFramebuffer *framebuffer, + PangoLayout *layout, + float x, + float y, + const CoglColor *color); + +/** + * cogl_pango_show_layout_line: + * @framebuffer: A #CoglFramebuffer to draw too. + * @line: a #PangoLayoutLine + * @x: X coordinate to render the line at + * @y: Y coordinate to render the line at + * @color: color to use when rendering the line + * + * Draws a solidly coloured @line on the given @framebuffer at (@x, + * @y) within the @framebuffer's current model-view coordinate + * space. + * + * Since: 1.14 + */ +void +cogl_pango_show_layout_line (CoglFramebuffer *framebuffer, + PangoLayoutLine *line, + float x, + float y, + const CoglColor *color); + + +#define COGL_PANGO_TYPE_RENDERER (cogl_pango_renderer_get_type ()) +#define COGL_PANGO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRenderer)) +#define COGL_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass)) +#define COGL_PANGO_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_RENDERER)) +#define COGL_PANGO_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COGL_PANGO_TYPE_RENDERER)) +#define COGL_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass)) + +/* opaque types */ +typedef struct _CoglPangoRenderer CoglPangoRenderer; +typedef struct _CoglPangoRendererClass CoglPangoRendererClass; + +GType cogl_pango_renderer_get_type (void) G_GNUC_CONST; + +/** + * cogl_pango_render_layout_subpixel: + * @layout: a #PangoLayout + * @x: X coordinate (in Pango units) to render the layout at + * @y: Y coordinate (in Pango units) to render the layout at + * @color: color to use when rendering the layout + * @flags: + * + * Draws a solidly coloured @layout on the given @framebuffer at (@x, + * @y) within the @framebuffer's current model-view coordinate + * space. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pango_show_layout() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pango_show_layout) +void +cogl_pango_render_layout_subpixel (PangoLayout *layout, + int x, + int y, + const CoglColor *color, + int flags); + +/** + * cogl_pango_render_layout: + * @layout: a #PangoLayout + * @x: X coordinate to render the layout at + * @y: Y coordinate to render the layout at + * @color: color to use when rendering the layout + * @flags: + * + * Draws a solidly coloured @layout on the given @framebuffer at (@x, + * @y) within the @framebuffer's current model-view coordinate + * space. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pango_show_layout() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pango_show_layout) +void +cogl_pango_render_layout (PangoLayout *layout, + int x, + int y, + const CoglColor *color, + int flags); + +/** + * cogl_pango_render_layout_line: + * @line: a #PangoLayoutLine + * @x: X coordinate to render the line at + * @y: Y coordinate to render the line at + * @color: color to use when rendering the line + * + * Renders @line at the given coordinates using the given color. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pango_show_layout() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pango_show_layout_line) +void +cogl_pango_render_layout_line (PangoLayoutLine *line, + int x, + int y, + const CoglColor *color); + +COGL_END_DECLS + +#endif /* __COGL_PANGO_H__ */ diff --git a/cogl/cogl-pango/cogl-pango.symbols b/cogl/cogl-pango/cogl-pango.symbols new file mode 100644 index 000000000..b86c9560e --- /dev/null +++ b/cogl/cogl-pango/cogl-pango.symbols @@ -0,0 +1,12 @@ +cogl_pango_ensure_glyph_cache_for_layout +cogl_pango_font_map_clear_glyph_cache +cogl_pango_font_map_create_context +cogl_pango_font_map_get_renderer +cogl_pango_font_map_get_use_mipmapping +cogl_pango_font_map_new +cogl_pango_font_map_set_resolution +cogl_pango_font_map_set_use_mipmapping +cogl_pango_renderer_get_type +cogl_pango_render_layout +cogl_pango_render_layout_line +cogl_pango_render_layout_subpixel diff --git a/cogl/cogl-pango/mutter-cogl-pango-1.0.pc.in b/cogl/cogl-pango/mutter-cogl-pango-1.0.pc.in new file mode 100644 index 000000000..64ab7b40b --- /dev/null +++ b/cogl/cogl-pango/mutter-cogl-pango-1.0.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/mutter +includedir=@includedir@/mutter +apiversion=1.0 +requires=@COGL_PKG_REQUIRES@ mutter-cogl-1.0 + +Name: Cogl +Description: An object oriented GL/GLES Abstraction/Utility Layer +Version: @COGL_1_VERSION@ +Libs: -L${libdir} -lmutter-cogl-pango +Cflags: -I${includedir}/cogl +Requires: ${requires} diff --git a/cogl/cogl-path/Makefile.am b/cogl/cogl-path/Makefile.am new file mode 100644 index 000000000..3408c7b88 --- /dev/null +++ b/cogl/cogl-path/Makefile.am @@ -0,0 +1,104 @@ +NULL = + +BUILT_SOURCES = + +CLEANFILES = +DISTCLEANFILES = + +EXTRA_DIST = + +# tesselator sources +cogl_tesselator_sources = \ + tesselator/dict-list.h \ + tesselator/dict.c \ + tesselator/dict.h \ + tesselator/geom.c \ + tesselator/geom.h \ + tesselator/gluos.h \ + tesselator/memalloc.h \ + tesselator/mesh.c \ + tesselator/mesh.h \ + tesselator/normal.c \ + tesselator/normal.h \ + tesselator/priorityq-heap.h \ + tesselator/priorityq-sort.h \ + tesselator/priorityq.c \ + tesselator/priorityq.h \ + tesselator/render.c \ + tesselator/render.h \ + tesselator/sweep.c \ + tesselator/sweep.h \ + tesselator/tess.c \ + tesselator/tess.h \ + tesselator/tesselator.h \ + tesselator/tessmono.c \ + tesselator/tessmono.h \ + tesselator/GL/glu.h \ + $(NULL) + +source_c = \ + $(cogl_tesselator_sources) \ + cogl-path-private.h \ + cogl1-path.c \ + cogl-path.c \ + $(NULL) + +EXTRA_DIST += \ + tesselator/README \ + tesselator/priorityq-heap.c \ + cogl-path.symbols \ + $(NULL) + +source_1_x_h = \ + cogl-path-types.h \ + cogl1-path-functions.h \ + $(NULL) + +source_h = \ + cogl-path.h \ + $(source_1_x_h) \ + cogl2-path-functions.h \ + $(NULL) + +# glib-mkenums rules +glib_enum_h = cogl-path-enum-types.h +glib_enum_c = cogl-path-enum-types.c +glib_enum_headers = $(source_1_x_h) +include $(top_srcdir)/build/autotools/Makefile.am.enums + +mutterlibdir = $(libdir)/mutter +mutterlib_LTLIBRARIES = libmutter-cogl-path.la + +libmutter_cogl_path_la_SOURCES = $(source_c) $(source_h) +nodist_libmutter_cogl_path_la_SOURCES = $(BUILT_SOURCES) +libmutter_cogl_path_la_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS) +libmutter_cogl_path_la_LIBADD = $(top_builddir)/cogl/libmutter-cogl.la +libmutter_cogl_path_la_LIBADD += $(COGL_DEP_LIBS) $(COGL_EXTRA_LDFLAGS) +libmutter_cogl_path_la_LDFLAGS = \ + -export-dynamic \ + -export-symbols-regex "^(cogl|cogl2)_(framebuffer|path|is|clip|[sg]et)_.*" \ + -no-undefined \ + -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \ + -rpath $(mutterlibdir) + +AM_CPPFLAGS = \ + -DCOGL_COMPILATION \ + -DG_LOG_DOMAIN=\"CoglPath\" \ + -I$(srcdir)/tesselator \ + -I$(top_srcdir)/cogl \ + -I$(top_builddir)/cogl \ + -I$(top_srcdir)/cogl/winsys \ + -I$(top_srcdir) \ + -I$(top_builddir) + +cogl_base_includedir = $(includedir)/mutter +cogl_pathheadersdir = $(cogl_base_includedir)/cogl/cogl-path +cogl_pathheaders_HEADERS = $(source_h) +nodist_cogl_pathheaders_HEADERS = cogl-path-enum-types.h + +pc_files = mutter-cogl-path-1.0.pc + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pc_files) + +DISTCLEANFILES += $(pc_files) diff --git a/cogl/cogl-path/cogl-path-enum-types.c.in b/cogl/cogl-path/cogl-path-enum-types.c.in new file mode 100644 index 000000000..54746076e --- /dev/null +++ b/cogl/cogl-path/cogl-path-enum-types.c.in @@ -0,0 +1,50 @@ +/*** BEGIN file-header ***/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* We need to undefine this so that we will be sure to include + * cogl-path.h instead of cogl2-path.h when we include the framebuffer + * header. Otherwise it will include both headers and it won't + * compile. */ +#undef COGL_ENABLE_EXPERIMENTAL_2_0_API + +#include "cogl-path-enum-types.h" +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} +/*** END value-tail ***/ diff --git a/cogl/cogl-path/cogl-path-enum-types.h.in b/cogl/cogl-path/cogl-path-enum-types.h.in new file mode 100644 index 000000000..071686acd --- /dev/null +++ b/cogl/cogl-path/cogl-path-enum-types.h.in @@ -0,0 +1,25 @@ +/*** BEGIN file-header ***/ +#ifndef __COGL_PATH_ENUM_TYPES_H__ +#define __COGL_PATH_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __COGL_PATH_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define COGL_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) + +/*** END value-header ***/ diff --git a/cogl/cogl-path/cogl-path-private.h b/cogl/cogl-path/cogl-path-private.h new file mode 100644 index 000000000..078e58ffc --- /dev/null +++ b/cogl/cogl-path/cogl-path-private.h @@ -0,0 +1,126 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PATH_PRIVATE_H +#define __COGL_PATH_PRIVATE_H + +#include "cogl-object.h" +#include "cogl-attribute-private.h" + +typedef struct _floatVec2 +{ + float x; + float y; +} floatVec2; + +typedef struct _CoglPathNode +{ + float x; + float y; + unsigned int path_size; +} CoglPathNode; + +typedef struct _CoglBezQuad +{ + floatVec2 p1; + floatVec2 p2; + floatVec2 p3; +} CoglBezQuad; + +typedef struct _CoglBezCubic +{ + floatVec2 p1; + floatVec2 p2; + floatVec2 p3; + floatVec2 p4; +} CoglBezCubic; + +typedef struct _CoglPathData CoglPathData; + +struct _CoglPath +{ + CoglObject _parent; + + CoglPathData *data; +}; + +#define COGL_PATH_N_ATTRIBUTES 2 + +struct _CoglPathData +{ + unsigned int ref_count; + + CoglContext *context; + + CoglPathFillRule fill_rule; + + GArray *path_nodes; + + floatVec2 path_start; + floatVec2 path_pen; + unsigned int last_path; + floatVec2 path_nodes_min; + floatVec2 path_nodes_max; + + CoglAttributeBuffer *fill_attribute_buffer; + CoglIndices *fill_vbo_indices; + unsigned int fill_vbo_n_indices; + CoglAttribute *fill_attributes[COGL_PATH_N_ATTRIBUTES + 1]; + CoglPrimitive *fill_primitive; + + CoglAttributeBuffer *stroke_attribute_buffer; + CoglAttribute **stroke_attributes; + unsigned int stroke_n_attributes; + + /* This is used as an optimisation for when the path contains a + single contour specified using cogl2_path_rectangle. Cogl is more + optimised to handle rectangles than paths so we can detect this + case and divert to the journal or a rectangle clip. If it is TRUE + then the entire path can be described by calling + _cogl_path_get_bounds */ + CoglBool is_rectangle; +}; + +void +_cogl_add_path_to_stencil_buffer (CoglPath *path, + CoglBool merge, + CoglBool need_clear); + +void +_cogl_path_get_bounds (CoglPath *path, + float *min_x, + float *min_y, + float *max_x, + float *max_y); + +CoglBool +_cogl_path_is_rectangle (CoglPath *path); + +#endif /* __COGL_PATH_PRIVATE_H */ diff --git a/cogl/cogl-path/cogl-path-types.h b/cogl/cogl-path/cogl-path-types.h new file mode 100644 index 000000000..53c06ee67 --- /dev/null +++ b/cogl/cogl-path/cogl-path-types.h @@ -0,0 +1,85 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PATH_TYPES_H__ +#define __COGL_PATH_TYPES_H__ + +#include + +COGL_BEGIN_DECLS + +typedef struct _CoglPath CoglPath; + +/** + * CoglPathFillRule: + * @COGL_PATH_FILL_RULE_NON_ZERO: Each time the line crosses an edge of + * the path from left to right one is added to a counter and each time + * it crosses from right to left the counter is decremented. If the + * counter is non-zero then the point will be filled. See . + * @COGL_PATH_FILL_RULE_EVEN_ODD: If the line crosses an edge of the + * path an odd number of times then the point will filled, otherwise + * it won't. See . + * + * #CoglPathFillRule is used to determine how a path is filled. There + * are two options - 'non-zero' and 'even-odd'. To work out whether any + * point will be filled imagine drawing an infinetely long line in any + * direction from that point. The number of times and the direction + * that the edges of the path crosses this line determines whether the + * line is filled as described below. Any open sub paths are treated + * as if there was an extra line joining the first point and the last + * point. + * + * The default fill rule when creating a path is %COGL_PATH_FILL_RULE_EVEN_ODD. + * + *
+ * Example of filling various paths using the non-zero rule + * + *
+ * + *
+ * Example of filling various paths using the even-odd rule + * + *
+ * + * Since: 1.4 + */ +typedef enum { + COGL_PATH_FILL_RULE_NON_ZERO, + COGL_PATH_FILL_RULE_EVEN_ODD +} CoglPathFillRule; + +COGL_END_DECLS + +#endif /* __COGL_PATH_TYPES_H__ */ diff --git a/cogl/cogl-path/cogl-path.c b/cogl/cogl-path/cogl-path.c new file mode 100644 index 000000000..2b4b3c683 --- /dev/null +++ b/cogl/cogl-path/cogl-path.c @@ -0,0 +1,1602 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Ivan Leben + * Øyvind Kolås + * Neil Roberts + * Robert Bragg + */ + +#include "config.h" + +#include "cogl-util.h" +#include "cogl-object.h" +#include "cogl-context-private.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-primitive-private.h" +#include "cogl-texture-private.h" +#include "cogl-primitives-private.h" +#include "cogl-private.h" +#include "cogl-attribute-private.h" +#include "cogl1-context.h" +#include "tesselator/tesselator.h" + +#include "cogl-path/cogl-path.h" +#include "cogl-path-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +static void _cogl_path_free (CoglPath *path); + +static void _cogl_path_build_fill_attribute_buffer (CoglPath *path); +static CoglPrimitive *_cogl_path_get_fill_primitive (CoglPath *path); +static void _cogl_path_build_stroke_attribute_buffer (CoglPath *path); + +COGL_OBJECT_DEFINE (Path, path); +COGL_GTYPE_DEFINE_CLASS (Path, path); + +static void +_cogl_path_data_clear_vbos (CoglPathData *data) +{ + int i; + + if (data->fill_attribute_buffer) + { + cogl_object_unref (data->fill_attribute_buffer); + cogl_object_unref (data->fill_vbo_indices); + + for (i = 0; i < COGL_PATH_N_ATTRIBUTES; i++) + cogl_object_unref (data->fill_attributes[i]); + + data->fill_attribute_buffer = NULL; + } + + if (data->fill_primitive) + { + cogl_object_unref (data->fill_primitive); + data->fill_primitive = NULL; + } + + if (data->stroke_attribute_buffer) + { + cogl_object_unref (data->stroke_attribute_buffer); + + for (i = 0; i < data->stroke_n_attributes; i++) + cogl_object_unref (data->stroke_attributes[i]); + + g_free (data->stroke_attributes); + + data->stroke_attribute_buffer = NULL; + } +} + +static void +_cogl_path_data_unref (CoglPathData *data) +{ + if (--data->ref_count <= 0) + { + _cogl_path_data_clear_vbos (data); + + g_array_free (data->path_nodes, TRUE); + + g_slice_free (CoglPathData, data); + } +} + +static void +_cogl_path_modify (CoglPath *path) +{ + /* This needs to be called whenever the path is about to be modified + to implement copy-on-write semantics */ + + /* If there is more than one path using the data then we need to + copy the data instead */ + if (path->data->ref_count != 1) + { + CoglPathData *old_data = path->data; + + path->data = g_slice_dup (CoglPathData, old_data); + path->data->path_nodes = g_array_new (FALSE, FALSE, + sizeof (CoglPathNode)); + g_array_append_vals (path->data->path_nodes, + old_data->path_nodes->data, + old_data->path_nodes->len); + + path->data->fill_attribute_buffer = NULL; + path->data->fill_primitive = NULL; + path->data->stroke_attribute_buffer = NULL; + path->data->ref_count = 1; + + _cogl_path_data_unref (old_data); + } + else + /* The path is altered so the vbos will now be invalid */ + _cogl_path_data_clear_vbos (path->data); +} + +void +cogl2_path_set_fill_rule (CoglPath *path, + CoglPathFillRule fill_rule) +{ + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + if (path->data->fill_rule != fill_rule) + { + _cogl_path_modify (path); + + path->data->fill_rule = fill_rule; + } +} + +CoglPathFillRule +cogl2_path_get_fill_rule (CoglPath *path) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (path), COGL_PATH_FILL_RULE_NON_ZERO); + + return path->data->fill_rule; +} + +static void +_cogl_path_add_node (CoglPath *path, + CoglBool new_sub_path, + float x, + float y) +{ + CoglPathNode new_node; + CoglPathData *data; + + _cogl_path_modify (path); + + data = path->data; + + new_node.x = x; + new_node.y = y; + new_node.path_size = 0; + + if (new_sub_path || data->path_nodes->len == 0) + data->last_path = data->path_nodes->len; + + g_array_append_val (data->path_nodes, new_node); + + g_array_index (data->path_nodes, CoglPathNode, data->last_path).path_size++; + + if (data->path_nodes->len == 1) + { + data->path_nodes_min.x = data->path_nodes_max.x = x; + data->path_nodes_min.y = data->path_nodes_max.y = y; + } + else + { + if (x < data->path_nodes_min.x) + data->path_nodes_min.x = x; + if (x > data->path_nodes_max.x) + data->path_nodes_max.x = x; + if (y < data->path_nodes_min.y) + data->path_nodes_min.y = y; + if (y > data->path_nodes_max.y) + data->path_nodes_max.y = y; + } + + /* Once the path nodes have been modified then we'll assume it's no + longer a rectangle. cogl2_path_rectangle will set this back to + TRUE if this has been called from there */ + data->is_rectangle = FALSE; +} + +static void +_cogl_path_stroke_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline) +{ + CoglPathData *data; + CoglPipeline *copy = NULL; + unsigned int path_start; + int path_num = 0; + CoglPathNode *node; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + data = path->data; + + if (data->path_nodes->len == 0) + return; + + if (cogl_pipeline_get_n_layers (pipeline) != 0) + { + copy = cogl_pipeline_copy (pipeline); + _cogl_pipeline_prune_to_n_layers (copy, 0); + pipeline = copy; + } + + _cogl_path_build_stroke_attribute_buffer (path); + + for (path_start = 0; + path_start < data->path_nodes->len; + path_start += node->path_size) + { + CoglPrimitive *primitive; + + node = &g_array_index (data->path_nodes, CoglPathNode, path_start); + + primitive = + cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP, + node->path_size, + &data->stroke_attributes[path_num], + 1); + cogl_primitive_draw (primitive, framebuffer, pipeline); + cogl_object_unref (primitive); + + path_num++; + } + + if (copy) + cogl_object_unref (copy); +} + +void +_cogl_path_get_bounds (CoglPath *path, + float *min_x, + float *min_y, + float *max_x, + float *max_y) +{ + CoglPathData *data = path->data; + + if (data->path_nodes->len == 0) + { + *min_x = 0.0f; + *min_y = 0.0f; + *max_x = 0.0f; + *max_y = 0.0f; + } + else + { + *min_x = data->path_nodes_min.x; + *min_y = data->path_nodes_min.y; + *max_x = data->path_nodes_max.x; + *max_y = data->path_nodes_max.y; + } +} + +static void +_cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline) +{ + /* We need at least three stencil bits to combine clips */ + if (_cogl_framebuffer_get_stencil_bits (framebuffer) >= 3) + { + static CoglBool seen_warning = FALSE; + + if (!seen_warning) + { + g_warning ("Paths can not be filled using materials with " + "sliced textures unless there is a stencil " + "buffer"); + seen_warning = TRUE; + } + } + + cogl_framebuffer_push_path_clip (framebuffer, path); + cogl_framebuffer_draw_rectangle (framebuffer, + pipeline, + path->data->path_nodes_min.x, + path->data->path_nodes_min.y, + path->data->path_nodes_max.x, + path->data->path_nodes_max.y); + cogl_framebuffer_pop_clip (framebuffer); +} + +static CoglBool +validate_layer_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglBool *needs_fallback = user_data; + CoglTexture *texture = _cogl_pipeline_layer_get_texture (layer); + + /* If any of the layers of the current pipeline contain sliced + * textures or textures with waste then it won't work to draw the + * path directly. Instead we fallback to pushing the path as a clip + * on the clip-stack and drawing the path's bounding rectangle + * instead. + */ + + if (texture != NULL && (cogl_texture_is_sliced (texture) || + !_cogl_texture_can_hardware_repeat (texture))) + *needs_fallback = TRUE; + + return !*needs_fallback; +} + +static void +_cogl_path_fill_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags) +{ + if (path->data->path_nodes->len == 0) + return; + + /* If the path is a simple rectangle then we can divert to using + cogl_framebuffer_draw_rectangle which should be faster because it + can go through the journal instead of uploading the geometry just + for two triangles */ + if (path->data->is_rectangle && flags == 0) + { + float x_1, y_1, x_2, y_2; + + _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2); + cogl_framebuffer_draw_rectangle (framebuffer, + pipeline, + x_1, y_1, + x_2, y_2); + } + else + { + CoglBool needs_fallback = FALSE; + CoglPrimitive *primitive; + + _cogl_pipeline_foreach_layer_internal (pipeline, + validate_layer_cb, + &needs_fallback); + if (needs_fallback) + { + _cogl_path_fill_nodes_with_clipped_rectangle (path, + framebuffer, + pipeline); + return; + } + + primitive = _cogl_path_get_fill_primitive (path); + + _cogl_primitive_draw (primitive, framebuffer, pipeline, flags); + } +} + +/* TODO: Update to the protoype used in the Cogl master branch. + * This is experimental API but not in sync with the cogl_path_fill() + * api in Cogl master which takes explicit framebuffer and pipeline + * arguments */ +void +cogl2_path_fill (CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_fill_nodes (path, + cogl_get_draw_framebuffer (), + cogl_get_source (), + 0 /* flags */); +} + +/* TODO: Update to the protoype used in the Cogl master branch. + * This is experimental API but not in sync with the cogl_path_fill() + * api in Cogl master which takes explicit framebuffer and pipeline + * arguments */ +void +cogl2_path_stroke (CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + if (path->data->path_nodes->len == 0) + return; + + _cogl_path_stroke_nodes (path, + cogl_get_draw_framebuffer (), + cogl_get_source ()); +} + +void +cogl2_path_move_to (CoglPath *path, + float x, + float y) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_add_node (path, TRUE, x, y); + + data = path->data; + + data->path_start.x = x; + data->path_start.y = y; + + data->path_pen = data->path_start; +} + +void +cogl2_path_rel_move_to (CoglPath *path, + float x, + float y) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + data = path->data; + + cogl2_path_move_to (path, + data->path_pen.x + x, + data->path_pen.y + y); +} + +void +cogl2_path_line_to (CoglPath *path, + float x, + float y) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_add_node (path, FALSE, x, y); + + data = path->data; + + data->path_pen.x = x; + data->path_pen.y = y; +} + +void +cogl2_path_rel_line_to (CoglPath *path, + float x, + float y) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + data = path->data; + + cogl2_path_line_to (path, + data->path_pen.x + x, + data->path_pen.y + y); +} + +void +cogl2_path_close (CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_add_node (path, FALSE, path->data->path_start.x, + path->data->path_start.y); + + path->data->path_pen = path->data->path_start; +} + +void +cogl2_path_line (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2) +{ + cogl2_path_move_to (path, x_1, y_1); + cogl2_path_line_to (path, x_2, y_2); +} + +void +cogl2_path_polyline (CoglPath *path, + const float *coords, + int num_points) +{ + int c = 0; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + cogl2_path_move_to (path, coords[0], coords[1]); + + for (c = 1; c < num_points; ++c) + cogl2_path_line_to (path, coords[2*c], coords[2*c+1]); +} + +void +cogl2_path_polygon (CoglPath *path, + const float *coords, + int num_points) +{ + cogl2_path_polyline (path, coords, num_points); + cogl2_path_close (path); +} + +void +cogl2_path_rectangle (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2) +{ + CoglBool is_rectangle; + + /* If the path was previously empty and the rectangle isn't mirrored + then we'll record that this is a simple rectangle path so that we + can optimise it */ + is_rectangle = (path->data->path_nodes->len == 0 && + x_2 >= x_1 && + y_2 >= y_1); + + cogl2_path_move_to (path, x_1, y_1); + cogl2_path_line_to (path, x_2, y_1); + cogl2_path_line_to (path, x_2, y_2); + cogl2_path_line_to (path, x_1, y_2); + cogl2_path_close (path); + + path->data->is_rectangle = is_rectangle; +} + +CoglBool +_cogl_path_is_rectangle (CoglPath *path) +{ + return path->data->is_rectangle; +} + +static void +_cogl_path_arc (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2, + float angle_step, + unsigned int move_first) +{ + float a = 0x0; + float cosa = 0x0; + float sina = 0x0; + float px = 0x0; + float py = 0x0; + + /* Fix invalid angles */ + + if (angle_1 == angle_2 || angle_step == 0x0) + return; + + if (angle_step < 0x0) + angle_step = -angle_step; + + /* Walk the arc by given step */ + + a = angle_1; + while (a != angle_2) + { + cosa = cosf (a * (G_PI/180.0)); + sina = sinf (a * (G_PI/180.0)); + + px = center_x + (cosa * radius_x); + py = center_y + (sina * radius_y); + + if (a == angle_1 && move_first) + cogl2_path_move_to (path, px, py); + else + cogl2_path_line_to (path, px, py); + + if (G_LIKELY (angle_2 > angle_1)) + { + a += angle_step; + if (a > angle_2) + a = angle_2; + } + else + { + a -= angle_step; + if (a < angle_2) + a = angle_2; + } + } + + /* Make sure the final point is drawn */ + + cosa = cosf (angle_2 * (G_PI/180.0)); + sina = sinf (angle_2 * (G_PI/180.0)); + + px = center_x + (cosa * radius_x); + py = center_y + (sina * radius_y); + + cogl2_path_line_to (path, px, py); +} + +void +cogl2_path_arc (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2) +{ + float angle_step = 10; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + /* it is documented that a move to is needed to create a freestanding + * arc + */ + _cogl_path_arc (path, + center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + + +static void +_cogl_path_rel_arc (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2, + float angle_step) +{ + CoglPathData *data; + + data = path->data; + + _cogl_path_arc (path, + data->path_pen.x + center_x, + data->path_pen.y + center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +void +cogl2_path_ellipse (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y) +{ + float angle_step = 10; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + /* FIXME: if shows to be slow might be optimized + * by mirroring just a quarter of it */ + + _cogl_path_arc (path, + center_x, center_y, + radius_x, radius_y, + 0, 360, + angle_step, 1 /* move first */); + + cogl2_path_close (path); +} + +void +cogl2_path_round_rectangle (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float radius, + float arc_step) +{ + float inner_width = x_2 - x_1 - radius * 2; + float inner_height = y_2 - y_1 - radius * 2; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + cogl2_path_move_to (path, x_1, y_1 + radius); + _cogl_path_rel_arc (path, + radius, 0, + radius, radius, + 180, + 270, + arc_step); + + cogl2_path_line_to (path, + path->data->path_pen.x + inner_width, + path->data->path_pen.y); + _cogl_path_rel_arc (path, + 0, radius, + radius, radius, + -90, + 0, + arc_step); + + cogl2_path_line_to (path, + path->data->path_pen.x, + path->data->path_pen.y + inner_height); + + _cogl_path_rel_arc (path, + -radius, 0, + radius, radius, + 0, + 90, + arc_step); + + cogl2_path_line_to (path, + path->data->path_pen.x - inner_width, + path->data->path_pen.y); + _cogl_path_rel_arc (path, + 0, -radius, + radius, radius, + 90, + 180, + arc_step); + + cogl2_path_close (path); +} + +static void +_cogl_path_bezier3_sub (CoglPath *path, + CoglBezCubic *cubic) +{ + CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezCubic *cleft; + CoglBezCubic *cright; + CoglBezCubic *c; + floatVec2 dif1; + floatVec2 dif2; + floatVec2 mm; + floatVec2 c1; + floatVec2 c2; + floatVec2 c3; + floatVec2 c4; + floatVec2 c5; + int cindex; + + /* Put first curve on stack */ + cubics[0] = *cubic; + cindex = 0; + + while (cindex >= 0) + { + c = &cubics[cindex]; + + + /* Calculate distance of control points from their + * counterparts on the line between end points */ + dif1.x = (c->p2.x * 3) - (c->p1.x * 2) - c->p4.x; + dif1.y = (c->p2.y * 3) - (c->p1.y * 2) - c->p4.y; + dif2.x = (c->p3.x * 3) - (c->p4.x * 2) - c->p1.x; + dif2.y = (c->p3.y * 3) - (c->p4.y * 2) - c->p1.y; + + if (dif1.x < 0) + dif1.x = -dif1.x; + if (dif1.y < 0) + dif1.y = -dif1.y; + if (dif2.x < 0) + dif2.x = -dif2.x; + if (dif2.y < 0) + dif2.y = -dif2.y; + + + /* Pick the greatest of two distances */ + if (dif1.x < dif2.x) dif1.x = dif2.x; + if (dif1.y < dif2.y) dif1.y = dif2.y; + + /* Cancel if the curve is flat enough */ + if (dif1.x + dif1.y <= 1.0 || + cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) + { + /* Add subdivision point (skip last) */ + if (cindex == 0) + return; + + _cogl_path_add_node (path, FALSE, c->p4.x, c->p4.y); + + --cindex; + + continue; + } + + /* Left recursion goes on top of stack! */ + cright = c; cleft = &cubics[++cindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((c->p1.x + c->p2.x) / 2); + c1.y = ((c->p1.y + c->p2.y) / 2); + mm.x = ((c->p2.x + c->p3.x) / 2); + mm.y = ((c->p2.y + c->p3.y) / 2); + c5.x = ((c->p3.x + c->p4.x) / 2); + c5.y = ((c->p3.y + c->p4.y) / 2); + + c2.x = ((c1.x + mm.x) / 2); + c2.y = ((c1.y + mm.y) / 2); + c4.x = ((mm.x + c5.x) / 2); + c4.y = ((mm.y + c5.y) / 2); + + c3.x = ((c2.x + c4.x) / 2); + c3.y = ((c2.y + c4.y) / 2); + + /* Add left recursion to stack */ + cleft->p1 = c->p1; + cleft->p2 = c1; + cleft->p3 = c2; + cleft->p4 = c3; + + /* Add right recursion to stack */ + cright->p1 = c3; + cright->p2 = c4; + cright->p3 = c5; + cright->p4 = c->p4; + } +} + +void +cogl2_path_curve_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3) +{ + CoglBezCubic cubic; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + /* Prepare cubic curve */ + cubic.p1 = path->data->path_pen; + cubic.p2.x = x_1; + cubic.p2.y = y_1; + cubic.p3.x = x_2; + cubic.p3.y = y_2; + cubic.p4.x = x_3; + cubic.p4.y = y_3; + + /* Run subdivision */ + _cogl_path_bezier3_sub (path, &cubic); + + /* Add last point */ + _cogl_path_add_node (path, FALSE, cubic.p4.x, cubic.p4.y); + path->data->path_pen = cubic.p4; +} + +void +cogl2_path_rel_curve_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + data = path->data; + + cogl2_path_curve_to (path, + data->path_pen.x + x_1, + data->path_pen.y + y_1, + data->path_pen.x + x_2, + data->path_pen.y + y_2, + data->path_pen.x + x_3, + data->path_pen.y + y_3); +} + +CoglPath * +cogl2_path_new (void) +{ + CoglPath *path; + CoglPathData *data; + + _COGL_GET_CONTEXT (ctx, NULL); + + path = g_slice_new (CoglPath); + data = path->data = g_slice_new (CoglPathData); + + data->ref_count = 1; + data->context = ctx; + data->fill_rule = COGL_PATH_FILL_RULE_EVEN_ODD; + data->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); + data->last_path = 0; + data->fill_attribute_buffer = NULL; + data->stroke_attribute_buffer = NULL; + data->fill_primitive = NULL; + data->is_rectangle = FALSE; + + return _cogl_path_object_new (path); +} + +CoglPath * +cogl_path_copy (CoglPath *old_path) +{ + CoglPath *new_path; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (old_path), NULL); + + new_path = g_slice_new (CoglPath); + new_path->data = old_path->data; + new_path->data->ref_count++; + + return _cogl_path_object_new (new_path); +} + +static void +_cogl_path_free (CoglPath *path) +{ + _cogl_path_data_unref (path->data); + g_slice_free (CoglPath, path); +} + +/* If second order beziers were needed the following code could + * be re-enabled: + */ +#if 0 + +static void +_cogl_path_bezier2_sub (CoglPath *path, + CoglBezQuad *quad) +{ + CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezQuad *qleft; + CoglBezQuad *qright; + CoglBezQuad *q; + floatVec2 mid; + floatVec2 dif; + floatVec2 c1; + floatVec2 c2; + floatVec2 c3; + int qindex; + + /* Put first curve on stack */ + quads[0] = *quad; + qindex = 0; + + /* While stack is not empty */ + while (qindex >= 0) + { + + q = &quads[qindex]; + + /* Calculate distance of control point from its + * counterpart on the line between end points */ + mid.x = ((q->p1.x + q->p3.x) / 2); + mid.y = ((q->p1.y + q->p3.y) / 2); + dif.x = (q->p2.x - mid.x); + dif.y = (q->p2.y - mid.y); + if (dif.x < 0) dif.x = -dif.x; + if (dif.y < 0) dif.y = -dif.y; + + /* Cancel if the curve is flat enough */ + if (dif.x + dif.y <= 1.0 || + qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) + { + /* Add subdivision point (skip last) */ + if (qindex == 0) return; + _cogl_path_add_node (path, FALSE, q->p3.x, q->p3.y); + --qindex; continue; + } + + /* Left recursion goes on top of stack! */ + qright = q; qleft = &quads[++qindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((q->p1.x + q->p2.x) / 2); + c1.y = ((q->p1.y + q->p2.y) / 2); + c3.x = ((q->p2.x + q->p3.x) / 2); + c3.y = ((q->p2.y + q->p3.y) / 2); + c2.x = ((c1.x + c3.x) / 2); + c2.y = ((c1.y + c3.y) / 2); + + /* Add left recursion onto stack */ + qleft->p1 = q->p1; + qleft->p2 = c1; + qleft->p3 = c2; + + /* Add right recursion onto stack */ + qright->p1 = c2; + qright->p2 = c3; + qright->p3 = q->p3; + } +} + +void +cogl_path_curve2_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2) +{ + CoglBezQuad quad; + + /* Prepare quadratic curve */ + quad.p1 = path->data->path_pen; + quad.p2.x = x_1; + quad.p2.y = y_1; + quad.p3.x = x_2; + quad.p3.y = y_2; + + /* Run subdivision */ + _cogl_path_bezier2_sub (&quad); + + /* Add last point */ + _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y); + path->data->path_pen = quad.p3; +} + +void +cogl_rel_curve2_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2) +{ + CoglPathData *data; + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + data = path->data; + + cogl_path_curve2_to (data->path_pen.x + x_1, + data->path_pen.y + y_1, + data->path_pen.x + x_2, + data->path_pen.y + y_2); +} + +#endif + +typedef struct _CoglPathTesselator CoglPathTesselator; +typedef struct _CoglPathTesselatorVertex CoglPathTesselatorVertex; + +struct _CoglPathTesselator +{ + GLUtesselator *glu_tess; + GLenum primitive_type; + int vertex_number; + /* Array of CoglPathTesselatorVertex. This needs to grow when the + combine callback is called */ + GArray *vertices; + /* Array of integers for the indices into the vertices array. Each + element will either be uint8_t, uint16_t or uint32_t depending on + the number of vertices */ + GArray *indices; + CoglIndicesType indices_type; + /* Indices used to split fans and strips */ + int index_a, index_b; +}; + +struct _CoglPathTesselatorVertex +{ + float x, y, s, t; +}; + +static void +_cogl_path_tesselator_begin (GLenum type, + CoglPathTesselator *tess) +{ + g_assert (type == GL_TRIANGLES || + type == GL_TRIANGLE_FAN || + type == GL_TRIANGLE_STRIP); + + tess->primitive_type = type; + tess->vertex_number = 0; +} + +static CoglIndicesType +_cogl_path_tesselator_get_indices_type_for_size (int n_vertices) +{ + if (n_vertices <= 256) + return COGL_INDICES_TYPE_UNSIGNED_BYTE; + else if (n_vertices <= 65536) + return COGL_INDICES_TYPE_UNSIGNED_SHORT; + else + return COGL_INDICES_TYPE_UNSIGNED_INT; +} + +static void +_cogl_path_tesselator_allocate_indices_array (CoglPathTesselator *tess) +{ + switch (tess->indices_type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + tess->indices = g_array_new (FALSE, FALSE, sizeof (uint8_t)); + break; + + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + tess->indices = g_array_new (FALSE, FALSE, sizeof (uint16_t)); + break; + + case COGL_INDICES_TYPE_UNSIGNED_INT: + tess->indices = g_array_new (FALSE, FALSE, sizeof (uint32_t)); + break; + } +} + +static void +_cogl_path_tesselator_add_index (CoglPathTesselator *tess, int vertex_index) +{ + switch (tess->indices_type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + { + uint8_t val = vertex_index; + g_array_append_val (tess->indices, val); + } + break; + + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + { + uint16_t val = vertex_index; + g_array_append_val (tess->indices, val); + } + break; + + case COGL_INDICES_TYPE_UNSIGNED_INT: + { + uint32_t val = vertex_index; + g_array_append_val (tess->indices, val); + } + break; + } +} + +static void +_cogl_path_tesselator_vertex (void *vertex_data, + CoglPathTesselator *tess) +{ + int vertex_index; + + vertex_index = GPOINTER_TO_INT (vertex_data); + + /* This tries to convert all of the primitives into GL_TRIANGLES + with indices to share vertices */ + switch (tess->primitive_type) + { + case GL_TRIANGLES: + /* Directly use the vertex */ + _cogl_path_tesselator_add_index (tess, vertex_index); + break; + + case GL_TRIANGLE_FAN: + if (tess->vertex_number == 0) + tess->index_a = vertex_index; + else if (tess->vertex_number == 1) + tess->index_b = vertex_index; + else + { + /* Create a triangle with the first vertex, the previous + vertex and this vertex */ + _cogl_path_tesselator_add_index (tess, tess->index_a); + _cogl_path_tesselator_add_index (tess, tess->index_b); + _cogl_path_tesselator_add_index (tess, vertex_index); + /* Next time we will use this vertex as the previous + vertex */ + tess->index_b = vertex_index; + } + break; + + case GL_TRIANGLE_STRIP: + if (tess->vertex_number == 0) + tess->index_a = vertex_index; + else if (tess->vertex_number == 1) + tess->index_b = vertex_index; + else + { + _cogl_path_tesselator_add_index (tess, tess->index_a); + _cogl_path_tesselator_add_index (tess, tess->index_b); + _cogl_path_tesselator_add_index (tess, vertex_index); + if (tess->vertex_number & 1) + tess->index_b = vertex_index; + else + tess->index_a = vertex_index; + } + break; + + default: + g_assert_not_reached (); + } + + tess->vertex_number++; +} + +static void +_cogl_path_tesselator_end (CoglPathTesselator *tess) +{ + tess->primitive_type = GL_FALSE; +} + +static void +_cogl_path_tesselator_combine (double coords[3], + void *vertex_data[4], + float weight[4], + void **out_data, + CoglPathTesselator *tess) +{ + CoglPathTesselatorVertex *vertex; + CoglIndicesType new_indices_type; + int i; + + /* Add a new vertex to the array */ + g_array_set_size (tess->vertices, tess->vertices->len + 1); + vertex = &g_array_index (tess->vertices, + CoglPathTesselatorVertex, + tess->vertices->len - 1); + /* The data is just the index to the vertex */ + *out_data = GINT_TO_POINTER (tess->vertices->len - 1); + /* Set the coordinates of the new vertex */ + vertex->x = coords[0]; + vertex->y = coords[1]; + /* Generate the texture coordinates as the weighted average of the + four incoming coordinates */ + vertex->s = 0.0f; + vertex->t = 0.0f; + for (i = 0; i < 4; i++) + { + CoglPathTesselatorVertex *old_vertex = + &g_array_index (tess->vertices, CoglPathTesselatorVertex, + GPOINTER_TO_INT (vertex_data[i])); + vertex->s += old_vertex->s * weight[i]; + vertex->t += old_vertex->t * weight[i]; + } + + /* Check if we've reached the limit for the data type of our indices */ + new_indices_type = + _cogl_path_tesselator_get_indices_type_for_size (tess->vertices->len); + if (new_indices_type != tess->indices_type) + { + CoglIndicesType old_indices_type = new_indices_type; + GArray *old_vertices = tess->indices; + + /* Copy the indices to an array of the new type */ + tess->indices_type = new_indices_type; + _cogl_path_tesselator_allocate_indices_array (tess); + + switch (old_indices_type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + for (i = 0; i < old_vertices->len; i++) + _cogl_path_tesselator_add_index (tess, + g_array_index (old_vertices, + uint8_t, i)); + break; + + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + for (i = 0; i < old_vertices->len; i++) + _cogl_path_tesselator_add_index (tess, + g_array_index (old_vertices, + uint16_t, i)); + break; + + case COGL_INDICES_TYPE_UNSIGNED_INT: + for (i = 0; i < old_vertices->len; i++) + _cogl_path_tesselator_add_index (tess, + g_array_index (old_vertices, + uint32_t, i)); + break; + } + + g_array_free (old_vertices, TRUE); + } +} + +static void +_cogl_path_build_fill_attribute_buffer (CoglPath *path) +{ + CoglPathTesselator tess; + unsigned int path_start = 0; + CoglPathData *data = path->data; + int i; + + /* If we've already got a vbo then we don't need to do anything */ + if (data->fill_attribute_buffer) + return; + + tess.primitive_type = FALSE; + + /* Generate a vertex for each point on the path */ + tess.vertices = g_array_new (FALSE, FALSE, sizeof (CoglPathTesselatorVertex)); + g_array_set_size (tess.vertices, data->path_nodes->len); + for (i = 0; i < data->path_nodes->len; i++) + { + CoglPathNode *node = + &g_array_index (data->path_nodes, CoglPathNode, i); + CoglPathTesselatorVertex *vertex = + &g_array_index (tess.vertices, CoglPathTesselatorVertex, i); + + vertex->x = node->x; + vertex->y = node->y; + + /* Add texture coordinates so that a texture would be drawn to + fit the bounding box of the path and then cropped by the + path */ + if (data->path_nodes_min.x == data->path_nodes_max.x) + vertex->s = 0.0f; + else + vertex->s = ((node->x - data->path_nodes_min.x) + / (data->path_nodes_max.x - data->path_nodes_min.x)); + if (data->path_nodes_min.y == data->path_nodes_max.y) + vertex->t = 0.0f; + else + vertex->t = ((node->y - data->path_nodes_min.y) + / (data->path_nodes_max.y - data->path_nodes_min.y)); + } + + tess.indices_type = + _cogl_path_tesselator_get_indices_type_for_size (data->path_nodes->len); + _cogl_path_tesselator_allocate_indices_array (&tess); + + tess.glu_tess = gluNewTess (); + + if (data->fill_rule == COGL_PATH_FILL_RULE_EVEN_ODD) + gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE, + GLU_TESS_WINDING_ODD); + else + gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE, + GLU_TESS_WINDING_NONZERO); + + /* All vertices are on the xy-plane */ + gluTessNormal (tess.glu_tess, 0.0, 0.0, 1.0); + + gluTessCallback (tess.glu_tess, GLU_TESS_BEGIN_DATA, + _cogl_path_tesselator_begin); + gluTessCallback (tess.glu_tess, GLU_TESS_VERTEX_DATA, + _cogl_path_tesselator_vertex); + gluTessCallback (tess.glu_tess, GLU_TESS_END_DATA, + _cogl_path_tesselator_end); + gluTessCallback (tess.glu_tess, GLU_TESS_COMBINE_DATA, + _cogl_path_tesselator_combine); + + gluTessBeginPolygon (tess.glu_tess, &tess); + + while (path_start < data->path_nodes->len) + { + CoglPathNode *node = + &g_array_index (data->path_nodes, CoglPathNode, path_start); + + gluTessBeginContour (tess.glu_tess); + + for (i = 0; i < node->path_size; i++) + { + double vertex[3] = { node[i].x, node[i].y, 0.0 }; + gluTessVertex (tess.glu_tess, vertex, + GINT_TO_POINTER (i + path_start)); + } + + gluTessEndContour (tess.glu_tess); + + path_start += node->path_size; + } + + gluTessEndPolygon (tess.glu_tess); + + gluDeleteTess (tess.glu_tess); + + data->fill_attribute_buffer = + cogl_attribute_buffer_new (data->context, + sizeof (CoglPathTesselatorVertex) * + tess.vertices->len, + tess.vertices->data); + g_array_free (tess.vertices, TRUE); + + data->fill_attributes[0] = + cogl_attribute_new (data->fill_attribute_buffer, + "cogl_position_in", + sizeof (CoglPathTesselatorVertex), + G_STRUCT_OFFSET (CoglPathTesselatorVertex, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + data->fill_attributes[1] = + cogl_attribute_new (data->fill_attribute_buffer, + "cogl_tex_coord0_in", + sizeof (CoglPathTesselatorVertex), + G_STRUCT_OFFSET (CoglPathTesselatorVertex, s), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + data->fill_vbo_indices = cogl_indices_new (data->context, + tess.indices_type, + tess.indices->data, + tess.indices->len); + data->fill_vbo_n_indices = tess.indices->len; + g_array_free (tess.indices, TRUE); +} + +static CoglPrimitive * +_cogl_path_get_fill_primitive (CoglPath *path) +{ + if (path->data->fill_primitive) + return path->data->fill_primitive; + + _cogl_path_build_fill_attribute_buffer (path); + + path->data->fill_primitive = + cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + path->data->fill_vbo_n_indices, + path->data->fill_attributes, + COGL_PATH_N_ATTRIBUTES); + cogl_primitive_set_indices (path->data->fill_primitive, + path->data->fill_vbo_indices, + path->data->fill_vbo_n_indices); + + return path->data->fill_primitive; +} + +static CoglClipStack * +_cogl_clip_stack_push_from_path (CoglClipStack *stack, + CoglPath *path, + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) +{ + float x_1, y_1, x_2, y_2; + + _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2); + + /* If the path is a simple rectangle then we can divert to pushing a + rectangle clip instead which usually won't involve the stencil + buffer */ + if (_cogl_path_is_rectangle (path)) + return _cogl_clip_stack_push_rectangle (stack, + x_1, y_1, + x_2, y_2, + modelview_entry, + projection_entry, + viewport); + else + { + CoglPrimitive *primitive = _cogl_path_get_fill_primitive (path); + + return _cogl_clip_stack_push_primitive (stack, + primitive, + x_1, y_1, x_2, y_2, + modelview_entry, + projection_entry, + viewport); + } +} + +void +cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, + CoglPath *path) +{ + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; + + framebuffer->clip_stack = + _cogl_clip_stack_push_from_path (framebuffer->clip_stack, + path, + modelview_entry, + projection_entry, + viewport); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + +/* XXX: deprecated */ +void +cogl_clip_push_from_path (CoglPath *path) +{ + cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (), path); +} + +static void +_cogl_path_build_stroke_attribute_buffer (CoglPath *path) +{ + CoglPathData *data = path->data; + CoglBuffer *buffer; + unsigned int n_attributes = 0; + unsigned int path_start; + CoglPathNode *node; + floatVec2 *buffer_p; + unsigned int i; + + /* If we've already got a cached vbo then we don't need to do anything */ + if (data->stroke_attribute_buffer) + return; + + data->stroke_attribute_buffer = + cogl_attribute_buffer_new_with_size (data->context, + data->path_nodes->len * + sizeof (floatVec2)); + + buffer = COGL_BUFFER (data->stroke_attribute_buffer); + buffer_p = _cogl_buffer_map_for_fill_or_fallback (buffer); + + /* Copy the vertices in and count the number of sub paths. Each sub + path will form a separate attribute so we can paint the disjoint + line strips */ + for (path_start = 0; + path_start < data->path_nodes->len; + path_start += node->path_size) + { + node = &g_array_index (data->path_nodes, CoglPathNode, path_start); + + for (i = 0; i < node->path_size; i++) + { + buffer_p[path_start + i].x = node[i].x; + buffer_p[path_start + i].y = node[i].y; + } + + n_attributes++; + } + + _cogl_buffer_unmap_for_fill_or_fallback (buffer); + + data->stroke_attributes = g_new (CoglAttribute *, n_attributes); + + /* Now we can loop the sub paths again to create the attributes */ + for (i = 0, path_start = 0; + path_start < data->path_nodes->len; + i++, path_start += node->path_size) + { + node = &g_array_index (data->path_nodes, CoglPathNode, path_start); + + data->stroke_attributes[i] = + cogl_attribute_new (data->stroke_attribute_buffer, + "cogl_position_in", + sizeof (floatVec2), + path_start * sizeof (floatVec2), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + } + + data->stroke_n_attributes = n_attributes; +} + +/* XXX: deprecated */ +void +cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */); +} + +/* XXX: deprecated */ +void +cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_stroke_nodes (path, framebuffer, pipeline); +} diff --git a/cogl/cogl-path/cogl-path.h b/cogl/cogl-path/cogl-path.h new file mode 100644 index 000000000..4991bbe67 --- /dev/null +++ b/cogl/cogl-path/cogl-path.h @@ -0,0 +1,68 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PATH_H__ +#define __COGL_PATH_H__ + +/** + * SECTION:cogl-paths + * @short_description: Functions for constructing and drawing 2D paths. + * + * There are two levels on which drawing with cogl-paths can be used. + * The highest level functions construct various simple primitive + * shapes to be either filled or stroked. Using a lower-level set of + * functions more complex and arbitrary paths can be constructed by + * concatenating straight line, bezier curve and arc segments. + * + * When constructing arbitrary paths, the current pen location is + * initialized using the move_to command. The subsequent path segments + * implicitly use the last pen location as their first vertex and move + * the pen location to the last vertex they produce at the end. Also + * there are special versions of functions that allow specifying the + * vertices of the path segments relative to the last pen location + * rather then in the absolute coordinates. + */ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +#include + +#ifdef COGL_ENABLE_EXPERIMENTAL_2_0_API +#include +#else +#include +#endif + +#endif /* __COGL_PATH_H__ */ + diff --git a/cogl/cogl-path/cogl-path.symbols b/cogl/cogl-path/cogl-path.symbols new file mode 100644 index 000000000..b643ec0cd --- /dev/null +++ b/cogl/cogl-path/cogl-path.symbols @@ -0,0 +1,59 @@ +/* cogl1-path-functions.h */ +cogl_clip_push_from_path +cogl_clip_push_from_path_preserve +cogl_get_path +cogl_is_path +cogl_path_arc +cogl_path_close +cogl_path_copy +cogl_path_curve_to +cogl_path_ellipse +cogl_path_fill +cogl_path_fill_preserve +cogl_path_get_fill_rule +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_path_get_gtype +#endif +cogl_path_line +cogl_path_line_to +cogl_path_move_to +cogl_path_new +cogl_path_polygon +cogl_path_polyline +cogl_path_rectangle +cogl_path_rel_curve_to +cogl_path_rel_line_to +cogl_path_rel_move_to +cogl_path_round_rectangle +cogl_path_set_fill_rule +cogl_path_stroke +cogl_path_stroke_preserve +cogl_set_path + +/* cogl2-path-functions.h */ +cogl_framebuffer_fill_path +cogl_framebuffer_push_path_clip +cogl_framebuffer_stroke_path +cogl2_clip_push_from_path +cogl2_path_arc +cogl2_path_close +cogl2_path_curve_to +cogl2_path_ellipse +cogl2_path_fill +cogl2_path_get_fill_rule +cogl2_path_line +cogl2_path_line_to +cogl2_path_move_to +cogl2_path_new +cogl2_path_polygon +cogl2_path_polyline +cogl2_path_rectangle +cogl2_path_rel_curve_to +cogl2_path_rel_line_to +cogl2_path_rel_move_to +cogl2_path_round_rectangle +cogl2_path_set_fill_rule +cogl2_path_stroke + +/* cogl-path-enums.h-contents may change as header is generated */ +cogl_path_fill_rule_get_type diff --git a/cogl/cogl-path/cogl1-path-functions.h b/cogl/cogl-path/cogl1-path-functions.h new file mode 100644 index 000000000..18315ab8a --- /dev/null +++ b/cogl/cogl-path/cogl1-path-functions.h @@ -0,0 +1,467 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PATH_FUNCTIONS_H__ +#define __COGL_PATH_FUNCTIONS_H__ + +/* The functions are declared separately because cogl-path.c needs to + get the function declarations from the old 1.0 API without + colliding with the enum declarations from the 2.0 API */ + +#include + +COGL_BEGIN_DECLS + +/** + * cogl_is_path: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing path object. + * + * Return value: %TRUE if the handle references a #CoglPath, + * %FALSE otherwise + */ +CoglBool +cogl_is_path (CoglHandle handle); + +/** + * cogl_path_set_fill_rule: + * @fill_rule: The new fill rule. + * + * Sets the fill rule of the current path to @fill_rule. This will + * affect how the path is filled when cogl_path_fill() is later + * called. Note that the fill rule state is attached to the path so + * calling cogl_get_path() will preserve the fill rule and calling + * cogl_path_new() will reset the fill rule back to the default. + * + * Since: 1.4 + */ +void +cogl_path_set_fill_rule (CoglPathFillRule fill_rule); + +/** + * cogl_path_get_fill_rule: + * + * Retrieves the fill rule set using cogl_path_set_fill_rule(). + * + * Return value: the fill rule that is used for the current path. + * + * Since: 1.4 + */ +CoglPathFillRule +cogl_path_get_fill_rule (void); + +/** + * cogl_path_fill: + * + * Fills the interior of the constructed shape using the current + * drawing color. The current path is then cleared. To use the path + * again, call cogl_path_fill_preserve() instead. + * + * The interior of the shape is determined using the fill rule of the + * path. See %CoglPathFillRule for details. + **/ +void +cogl_path_fill (void); + +/** + * cogl_path_fill_preserve: + * + * Fills the interior of the constructed shape using the current + * drawing color and preserves the path to be used again. See + * cogl_path_fill() for a description what is considered the interior + * of the shape. + * + * Since: 1.0 + **/ +void +cogl_path_fill_preserve (void); + +/** + * cogl_path_stroke: + * + * Strokes the constructed shape using the current drawing color and a + * width of 1 pixel (regardless of the current transformation + * matrix). To current path is then cleared. To use the path again, + * call cogl_path_stroke_preserve() instead. + **/ +void +cogl_path_stroke (void); + +/** + * cogl_path_stroke_preserve: + * + * Strokes the constructed shape using the current drawing color and + * preserves the path to be used again. + * + * Since: 1.0 + **/ +void +cogl_path_stroke_preserve (void); + +/** + * cogl_path_new: + * + * Clears the current path and starts a new one. Creating a new path + * also resets the fill rule to the default which is + * %COGL_PATH_FILL_RULE_EVEN_ODD. + * + * Since: 1.0 + */ +void +cogl_path_new (void); + +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Moves the pen to the given location. If there is an existing path + * this will start a new disjoint subpath. + **/ +void +cogl_path_move_to (float x, + float y); + + +/** + * cogl_path_rel_move_to: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Moves the pen to the given offset relative to the current pen + * location. If there is an existing path this will start a new + * disjoint subpath. + **/ +void +cogl_path_rel_move_to (float x, + float y); + +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + **/ +void +cogl_path_line_to (float x, + float y); + +/** + * cogl_path_rel_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + **/ +void +cogl_path_rel_line_to (float x, + float y); + + +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radius of the elliptical arc + * @angle_1: Angle in degrees at which the arc begin + * @angle_2: Angle in degrees at which the arc ends + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. If you perform a move_to to the arcs start just before + * drawing it you create a free standing arc. + * + * The angles are measured in degrees where 0° is in the direction of + * the positive X axis and 90° is in the direction of the positive Y + * axis. The angle of the arc begins at @angle_1 and heads towards + * @angle_2 (so if @angle_2 is less than @angle_1 it will decrease, + * otherwise it will increase). + **/ +void +cogl_path_arc (float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2); + +/** + * cogl_path_curve_to: + * @x_1: X coordinate of the second bezier control point + * @y_1: Y coordinate of the second bezier control point + * @x_2: X coordinate of the third bezier control point + * @y_2: Y coordinate of the third bezier control point + * @x_3: X coordinate of the fourth bezier control point + * @y_3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + **/ +void +cogl_path_curve_to (float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3); + +/** + * cogl_path_rel_curve_to: + * @x_1: X coordinate of the second bezier control point + * @y_1: Y coordinate of the second bezier control point + * @x_2: X coordinate of the third bezier control point + * @y_2: Y coordinate of the third bezier control point + * @x_3: X coordinate of the fourth bezier control point + * @y_3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + */ +void +cogl_path_rel_curve_to (float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3); + +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + **/ +void +cogl_path_close (void); + +/** + * cogl_path_line: + * @x_1: X coordinate of the start line vertex + * @y_1: Y coordinate of the start line vertex + * @x_2: X coordinate of the end line vertex + * @y_2: Y coordinate of the end line vertex + * + * Constructs a straight line shape starting and ending at the given + * coordinates. If there is an existing path this will start a new + * disjoint sub-path. + **/ +void +cogl_path_line (float x_1, + float y_1, + float x_2, + float y_2); + +/** + * cogl_path_polyline: + * @coords: (in) (array) (transfer none): A pointer to the first element of an + * array of fixed-point values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Constructs a series of straight line segments, starting from the + * first given vertex coordinate. If there is an existing path this + * will start a new disjoint sub-path. Each subsequent segment starts + * where the previous one ended and ends at the next given vertex + * coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + **/ +void +cogl_path_polyline (const float *coords, + int num_points); + + +/** + * cogl_path_polygon: + * @coords: (in) (array) (transfer none): A pointer to the first element of + * an array of fixed-point values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Constructs a polygonal shape of the given number of vertices. If + * there is an existing path this will start a new disjoint sub-path. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + **/ +void +cogl_path_polygon (const float *coords, + int num_points); + + +/** + * cogl_path_rectangle: + * @x_1: X coordinate of the top-left corner. + * @y_1: Y coordinate of the top-left corner. + * @x_2: X coordinate of the bottom-right corner. + * @y_2: Y coordinate of the bottom-right corner. + * + * Constructs a rectangular shape at the given coordinates. If there + * is an existing path this will start a new disjoint sub-path. + **/ +void +cogl_path_rectangle (float x_1, + float y_1, + float x_2, + float y_2); + +/** + * cogl_path_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * + * Constructs an ellipse shape. If there is an existing path this will + * start a new disjoint sub-path. + **/ +void +cogl_path_ellipse (float center_x, + float center_y, + float radius_x, + float radius_y); + +/** + * cogl_path_round_rectangle: + * @x_1: X coordinate of the top-left corner. + * @y_1: Y coordinate of the top-left corner. + * @x_2: X coordinate of the bottom-right corner. + * @y_2: Y coordinate of the bottom-right corner. + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Constructs a rectangular shape with rounded corners. If there is an + * existing path this will start a new disjoint sub-path. + **/ +void +cogl_path_round_rectangle (float x_1, + float y_1, + float x_2, + float y_2, + float radius, + float arc_step); + +/** + * cogl_get_path: (skip) + * + * Gets a pointer to the current path. The path can later be used + * again by calling cogl_path_set(). Note that the path isn't copied + * so if you later call any functions to add to the path it will + * affect the returned object too. No reference is taken on the path + * so if you want to retain it you should take your own reference with + * cogl_object_ref(). + * + * Return value: a pointer to the current path. + * + * Since: 1.4 + */ +CoglPath * +cogl_get_path (void); + +/** + * cogl_set_path: (skip) + * @path: A #CoglPath object + * + * Replaces the current path with @path. A reference is taken on the + * object so if you no longer need the path you should unref with + * cogl_object_unref(). + * + * Since: 1.4 + */ +void +cogl_set_path (CoglPath *path); + +/** + * cogl_path_copy: (skip) + * @path: A #CoglPath object + * + * Returns a new copy of the path in @path. The new path has a + * reference count of 1 so you should unref it with + * cogl_object_unref() if you no longer need it. + * + * Internally the path will share the data until one of the paths is + * modified so copying paths should be relatively cheap. + * + * Return value: (transfer full): a copy of the path in @path. + */ +CoglPath * +cogl_path_copy (CoglPath *path); + +/** + * cogl_clip_push_from_path_preserve: + * + * Sets a new clipping area using the current path. The current path + * is then cleared. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * cogl_clip_pop(). + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_framebuffer_push_path_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_path_clip) +void +cogl_clip_push_from_path_preserve (void); + +/** + * cogl_clip_push_from_path: + * + * Sets a new clipping area using the current path. The current path + * is then cleared. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * cogl_clip_pop(). + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_framebuffer_push_path_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_path_clip) +void +cogl_clip_push_from_path (void); + +COGL_END_DECLS + +#endif /* __COGL_PATH_FUNCTIONS_H__ */ + diff --git a/cogl/cogl-path/cogl1-path.c b/cogl/cogl-path/cogl1-path.c new file mode 100644 index 000000000..b2c59239e --- /dev/null +++ b/cogl/cogl-path/cogl1-path.c @@ -0,0 +1,353 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-object.h" +#include "cogl-context-private.h" + +#include "cogl-path-types.h" + +#include "cogl2-path-functions.h" + +#undef cogl_path_set_fill_rule +#undef cogl_path_get_fill_rule +#undef cogl_path_fill +#undef cogl_path_fill_preserve +#undef cogl_path_stroke +#undef cogl_path_stroke_preserve +#undef cogl_path_move_to +#undef cogl_path_rel_move_to +#undef cogl_path_line_to +#undef cogl_path_rel_line_to +#undef cogl_path_close +#undef cogl_path_new +#undef cogl_path_line +#undef cogl_path_polyline +#undef cogl_path_polygon +#undef cogl_path_rectangle +#undef cogl_path_arc +#undef cogl_path_ellipse +#undef cogl_path_round_rectangle +#undef cogl_path_curve_to +#undef cogl_path_rel_curve_to +#undef cogl_clip_push_from_path + +#include "cogl1-path-functions.h" + +#include +#include + +static void +ensure_current_path (CoglContext *ctx) +{ + if (ctx->current_path == NULL) + ctx->current_path = cogl2_path_new (); +} + +static CoglPath * +get_current_path (CoglContext *ctx) +{ + ensure_current_path (ctx); + return ctx->current_path; +} + +void +cogl_path_set_fill_rule (CoglPathFillRule fill_rule) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_set_fill_rule (get_current_path (ctx), fill_rule); +} + +CoglPathFillRule +cogl_path_get_fill_rule (void) +{ + _COGL_GET_CONTEXT (ctx, COGL_PATH_FILL_RULE_EVEN_ODD); + + return cogl2_path_get_fill_rule (get_current_path (ctx)); +} + +void +cogl_path_fill (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_fill (get_current_path (ctx)); + + if (ctx->current_path) + cogl_object_unref (ctx->current_path); + ctx->current_path = cogl2_path_new (); +} + +void +cogl_path_fill_preserve (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_fill (get_current_path (ctx)); +} + +void +cogl_path_stroke (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_stroke (get_current_path (ctx)); + + if (ctx->current_path) + cogl_object_unref (ctx->current_path); + ctx->current_path = cogl2_path_new (); +} + +void +cogl_path_stroke_preserve (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_stroke (get_current_path (ctx)); +} + +void +cogl_path_move_to (float x, + float y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_move_to (get_current_path (ctx), x, y); +} + +void +cogl_path_rel_move_to (float x, + float y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_rel_move_to (get_current_path (ctx), x, y); +} + +void +cogl_path_line_to (float x, + float y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_line_to (get_current_path (ctx), x, y); +} + +void +cogl_path_rel_line_to (float x, + float y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_rel_line_to (get_current_path (ctx), x, y); +} + +void +cogl_path_close (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_close (get_current_path (ctx)); +} + +void +cogl_path_new (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_path) + cogl_object_unref (ctx->current_path); + ctx->current_path = cogl2_path_new (); +} + +void +cogl_path_line (float x_1, + float y_1, + float x_2, + float y_2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_line (get_current_path (ctx), x_1, y_1, x_2, y_2); +} + +void +cogl_path_polyline (const float *coords, + int num_points) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_polyline (get_current_path (ctx), coords, num_points); +} + +void +cogl_path_polygon (const float *coords, + int num_points) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_polygon (get_current_path (ctx), coords, num_points); +} + +void +cogl_path_rectangle (float x_1, + float y_1, + float x_2, + float y_2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_rectangle (get_current_path (ctx), x_1, y_1, x_2, y_2); +} + +void +cogl_path_arc (float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_arc (get_current_path (ctx), + center_x, + center_y, + radius_x, + radius_y, + angle_1, + angle_2); +} + +void +cogl_path_ellipse (float center_x, + float center_y, + float radius_x, + float radius_y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_ellipse (get_current_path (ctx), + center_x, + center_y, + radius_x, + radius_y); +} + +void +cogl_path_round_rectangle (float x_1, + float y_1, + float x_2, + float y_2, + float radius, + float arc_step) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_round_rectangle (get_current_path (ctx), + x_1, y_1, x_2, y_2, radius, arc_step); +} + +void +cogl_path_curve_to (float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_curve_to (get_current_path (ctx), + x_1, y_1, x_2, y_2, x_3, y_3); +} + +void +cogl_path_rel_curve_to (float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl2_path_rel_curve_to (get_current_path (ctx), + x_1, y_1, x_2, y_2, x_3, y_3); +} + +CoglPath * +cogl_get_path (void) +{ + _COGL_GET_CONTEXT (ctx, NULL); + + return get_current_path (ctx); +} + +void +cogl_set_path (CoglPath *path) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + /* Reference the new object first in case it is the same as the old + object */ + cogl_object_ref (path); + if (ctx->current_path) + cogl_object_unref (ctx->current_path); + ctx->current_path = path; +} + +void +cogl_clip_push_from_path_preserve (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (), + get_current_path (ctx)); +} + +void +cogl_clip_push_from_path (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_clip_push_from_path_preserve (); + + if (ctx->current_path) + cogl_object_unref (ctx->current_path); + ctx->current_path = cogl2_path_new (); +} diff --git a/cogl/cogl-path/cogl2-path-functions.h b/cogl/cogl-path/cogl2-path-functions.h new file mode 100644 index 000000000..72c1fc84f --- /dev/null +++ b/cogl/cogl-path/cogl2-path-functions.h @@ -0,0 +1,545 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL2_PATH_FUNCTIONS_H__ +#define __COGL2_PATH_FUNCTIONS_H__ + +#include +#ifdef COGL_COMPILATION +#include "cogl-context.h" +#else +#include +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_path_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_path_get_gtype (void); +#endif + +#define cogl_path_new cogl2_path_new +/** + * cogl_path_new: + * + * Creates a new, empty path object. The default fill rule is + * %COGL_PATH_FILL_RULE_EVEN_ODD. + * + * Return value: A pointer to a newly allocated #CoglPath, which can + * be freed using cogl_object_unref(). + * + * Since: 2.0 + */ +CoglPath * +cogl_path_new (void); + +/** + * cogl_path_copy: + * @path: A #CoglPath object + * + * Returns a new copy of the path in @path. The new path has a + * reference count of 1 so you should unref it with + * cogl_object_unref() if you no longer need it. + * + * Internally the path will share the data until one of the paths is + * modified so copying paths should be relatively cheap. + * + * Return value: (transfer full): a copy of the path in @path. + * + * Since: 2.0 + */ +CoglPath * +cogl_path_copy (CoglPath *path); + +/** + * cogl_is_path: + * @object: A #CoglObject + * + * Gets whether the given object references an existing path object. + * + * Return value: %TRUE if the object references a #CoglPath, + * %FALSE otherwise. + * + * Since: 2.0 + */ +CoglBool +cogl_is_path (void *object); + +#define cogl_path_move_to cogl2_path_move_to +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Moves the pen to the given location. If there is an existing path + * this will start a new disjoint subpath. + * + * Since: 2.0 + */ +void +cogl_path_move_to (CoglPath *path, + float x, + float y); + +#define cogl_path_rel_move_to cogl2_path_rel_move_to +/** + * cogl_path_rel_move_to: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Moves the pen to the given offset relative to the current pen + * location. If there is an existing path this will start a new + * disjoint subpath. + * + * Since: 2.0 + */ +void +cogl_path_rel_move_to (CoglPath *path, + float x, + float y); + +#define cogl_path_line_to cogl2_path_line_to +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + * + * Since: 2.0 + */ +void +cogl_path_line_to (CoglPath *path, + float x, + float y); + +#define cogl_path_rel_line_to cogl2_path_rel_line_to +/** + * cogl_path_rel_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + * + * Since: 2.0 + */ +void +cogl_path_rel_line_to (CoglPath *path, + float x, + float y); + +#define cogl_path_arc cogl2_path_arc +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radius of the elliptical arc + * @angle_1: Angle in degrees at which the arc begin + * @angle_2: Angle in degrees at which the arc ends + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. If you perform a move_to to the arcs start just before + * drawing it you create a free standing arc. + * + * The angles are measured in degrees where 0° is in the direction of + * the positive X axis and 90° is in the direction of the positive Y + * axis. The angle of the arc begins at @angle_1 and heads towards + * @angle_2 (so if @angle_2 is less than @angle_1 it will decrease, + * otherwise it will increase). + * + * Since: 2.0 + */ +void +cogl_path_arc (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y, + float angle_1, + float angle_2); + +#define cogl_path_curve_to cogl2_path_curve_to +/** + * cogl_path_curve_to: + * @x_1: X coordinate of the second bezier control point + * @y_1: Y coordinate of the second bezier control point + * @x_2: X coordinate of the third bezier control point + * @y_2: Y coordinate of the third bezier control point + * @x_3: X coordinate of the fourth bezier control point + * @y_3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + * + * Since: 2.0 + */ +void +cogl_path_curve_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3); + +#define cogl_path_rel_curve_to cogl2_path_rel_curve_to +/** + * cogl_path_rel_curve_to: + * @x_1: X coordinate of the second bezier control point + * @y_1: Y coordinate of the second bezier control point + * @x_2: X coordinate of the third bezier control point + * @y_2: Y coordinate of the third bezier control point + * @x_3: X coordinate of the fourth bezier control point + * @y_3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + * + * Since: 2.0 + */ +void +cogl_path_rel_curve_to (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float x_3, + float y_3); + +#define cogl_path_close cogl2_path_close +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + * + * Since: 2.0 + */ +void +cogl_path_close (CoglPath *path); + +#define cogl_path_line cogl2_path_line +/** + * cogl_path_line: + * @x_1: X coordinate of the start line vertex + * @y_1: Y coordinate of the start line vertex + * @x_2: X coordinate of the end line vertex + * @y_2: Y coordinate of the end line vertex + * + * Constructs a straight line shape starting and ending at the given + * coordinates. If there is an existing path this will start a new + * disjoint sub-path. + * + * Since: 2.0 + */ +void +cogl_path_line (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2); + +#define cogl_path_polyline cogl2_path_polyline +/** + * cogl_path_polyline: + * @coords: (in) (array) (transfer none): A pointer to the first element of an + * array of fixed-point values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Constructs a series of straight line segments, starting from the + * first given vertex coordinate. If there is an existing path this + * will start a new disjoint sub-path. Each subsequent segment starts + * where the previous one ended and ends at the next given vertex + * coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + * + * Since: 2.0 + */ +void +cogl_path_polyline (CoglPath *path, + const float *coords, + int num_points); + +#define cogl_path_polygon cogl2_path_polygon +/** + * cogl_path_polygon: + * @coords: (in) (array) (transfer none): A pointer to the first element of + * an array of fixed-point values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Constructs a polygonal shape of the given number of vertices. If + * there is an existing path this will start a new disjoint sub-path. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + * + * Since: 2.0 + */ +void +cogl_path_polygon (CoglPath *path, + const float *coords, + int num_points); + +#define cogl_path_rectangle cogl2_path_rectangle +/** + * cogl_path_rectangle: + * @x_1: X coordinate of the top-left corner. + * @y_1: Y coordinate of the top-left corner. + * @x_2: X coordinate of the bottom-right corner. + * @y_2: Y coordinate of the bottom-right corner. + * + * Constructs a rectangular shape at the given coordinates. If there + * is an existing path this will start a new disjoint sub-path. + * + * Since: 2.0 + */ +void +cogl_path_rectangle (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2); + +#define cogl_path_ellipse cogl2_path_ellipse +/** + * cogl_path_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * + * Constructs an ellipse shape. If there is an existing path this will + * start a new disjoint sub-path. + * + * Since: 2.0 + */ +void +cogl_path_ellipse (CoglPath *path, + float center_x, + float center_y, + float radius_x, + float radius_y); + +#define cogl_path_round_rectangle cogl2_path_round_rectangle +/** + * cogl_path_round_rectangle: + * @x_1: X coordinate of the top-left corner. + * @y_1: Y coordinate of the top-left corner. + * @x_2: X coordinate of the bottom-right corner. + * @y_2: Y coordinate of the bottom-right corner. + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Constructs a rectangular shape with rounded corners. If there is an + * existing path this will start a new disjoint sub-path. + * + * Since: 2.0 + */ +void +cogl_path_round_rectangle (CoglPath *path, + float x_1, + float y_1, + float x_2, + float y_2, + float radius, + float arc_step); + +#define cogl_path_set_fill_rule cogl2_path_set_fill_rule +/** + * cogl_path_set_fill_rule: + * @fill_rule: The new fill rule. + * + * Sets the fill rule of the current path to @fill_rule. This will + * affect how the path is filled when cogl_path_fill() is later + * called. Note that the fill rule state is attached to the path so + * calling cogl_get_path() will preserve the fill rule and calling + * cogl_path_new() will reset the fill rule back to the default. + * + * Since: 2.0 + */ +void +cogl_path_set_fill_rule (CoglPath *path, CoglPathFillRule fill_rule); + +#define cogl_path_get_fill_rule cogl2_path_get_fill_rule +/** + * cogl_path_get_fill_rule: + * + * Retrieves the fill rule set using cogl_path_set_fill_rule(). + * + * Return value: the fill rule that is used for the current path. + * + * Since: 2.0 + */ +CoglPathFillRule +cogl_path_get_fill_rule (CoglPath *path); + +#define cogl_path_fill cogl2_path_fill +/** + * cogl_path_fill: + * + * Fills the interior of the constructed shape using the current + * drawing color. + * + * The interior of the shape is determined using the fill rule of the + * path. See %CoglPathFillRule for details. + * + * The result of referencing sliced textures in your current + * pipeline when filling a path are undefined. You should pass + * the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will + * use while filling a path. + * + * Since: 2.0 + */ +void +cogl_path_fill (CoglPath *path); + +/** + * cogl_framebuffer_fill_path: + * @framebuffer: A #CoglFramebuffer + * @pipeline: A #CoglPipeline to render with + * @path: The #CoglPath to fill + * + * Fills the interior of the path using the fragment operations + * defined by the pipeline. + * + * The interior of the shape is determined using the fill rule of the + * path. See %CoglPathFillRule for details. + * + * The result of referencing sliced textures in your current + * pipeline when filling a path are undefined. You should pass + * the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will + * use while filling a path. + * + * Stability: unstable + * Deprecated: 1.16: Use cogl_path_fill() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_path_fill) +void +cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path); + +#define cogl_path_stroke cogl2_path_stroke +/** + * cogl_path_stroke: + * + * Strokes the constructed shape using the current drawing color and a + * width of 1 pixel (regardless of the current transformation + * matrix). + * + * Since: 2.0 + */ +void +cogl_path_stroke (CoglPath *path); + +/** + * cogl_framebuffer_stroke_path: + * @framebuffer: A #CoglFramebuffer + * @pipeline: A #CoglPipeline to render with + * @path: The #CoglPath to stroke + * + * Strokes the edge of the path using the fragment operations defined + * by the pipeline. The stroke line will have a width of 1 pixel + * regardless of the current transformation matrix. + * + * Stability: unstable + * Deprecated: 1.16: Use cogl_path_stroke() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_path_stroke) +void +cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path); + +/** + * cogl_framebuffer_push_path_clip: + * @framebuffer: A #CoglFramebuffer pointer + * @path: The path to clip with. + * + * Sets a new clipping area using the silhouette of the specified, + * filled @path. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * cogl_framebuffer_pop_clip(). + * + * Since: 1.0 + * Stability: unstable + */ +void +cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, + CoglPath *path); + +#define cogl_clip_push_from_path cogl2_clip_push_from_path +/** + * cogl_clip_push_from_path: + * @path: The path to clip with. + * + * Sets a new clipping area using the silhouette of the specified, + * filled @path. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * call cogl_clip_pop(). + * + * Since: 1.8 + * Stability: Unstable + * Deprecated: 1.16: Use cogl_framebuffer_push_path_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_path_clip) +void +cogl_clip_push_from_path (CoglPath *path); + +COGL_END_DECLS + +#endif /* __COGL2_PATH_FUNCTIONS_H__ */ + diff --git a/cogl/cogl-path/mutter-cogl-path-1.0.pc.in b/cogl/cogl-path/mutter-cogl-path-1.0.pc.in new file mode 100644 index 000000000..959b77251 --- /dev/null +++ b/cogl/cogl-path/mutter-cogl-path-1.0.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/mutter +includedir=@includedir@/mutter +apiversion=1.0 +requires=@COGL_PKG_REQUIRES@ mutter-cogl-1.0 + +Name: Cogl +Description: A 2D path drawing library for Cogl +Version: @COGL_1_VERSION@ +Libs: -L${libdir} -lmutter-cogl-path +Cflags: -I${includedir}/cogl +Requires: ${requires} diff --git a/cogl/cogl-path/tesselator/GL/glu.h b/cogl/cogl-path/tesselator/GL/glu.h new file mode 100644 index 000000000..18c4024b7 --- /dev/null +++ b/cogl/cogl-path/tesselator/GL/glu.h @@ -0,0 +1,47 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* This is just a wrapper to use our simplified version of glu.h so + that the tesselator code can still #include */ + +#include "../tesselator.h" + +/* These aren't defined on GLES and we don't really want the + tesselator code to use them but we're also trying to avoid + modifying the C files so we just force them to be empty here */ + +#undef GLAPI +#define GLAPI + +#undef GLAPIENTRY +#define GLAPIENTRY + +/* GLES doesn't define a GLdouble type so lets just force it to a + regular double */ +#define GLdouble double diff --git a/cogl/cogl-path/tesselator/README b/cogl/cogl-path/tesselator/README new file mode 100644 index 000000000..66a6011e2 --- /dev/null +++ b/cogl/cogl-path/tesselator/README @@ -0,0 +1,446 @@ +/* +*/ + +General Polygon Tesselation +--------------------------- + + This note describes a tesselator for polygons consisting of one or + more closed contours. It is backward-compatible with the current + OpenGL Utilities tesselator, and is intended to replace it. Here is + a summary of the major differences: + + - input contours can be intersecting, self-intersecting, or degenerate. + + - supports a choice of several winding rules for determining which parts + of the polygon are on the "interior". This makes it possible to do + CSG operations on polygons. + + - boundary extraction: instead of tesselating the polygon, returns a + set of closed contours which separate the interior from the exterior. + + - returns the output as a small number of triangle fans and strips, + rather than a list of independent triangles (when possible). + + - output is available as an explicit mesh (a quad-edge structure), + in addition to the normal callback interface. + + - the algorithm used is extremely robust. + + +The interface +------------- + + The tesselator state is maintained in a "tesselator object". + These are allocated and destroyed using + + GLUtesselator *gluNewTess( void ); + void gluDeleteTess( GLUtesselator *tess ); + + Several tesselator objects may be used simultaneously. + + Inputs + ------ + + The input contours are specified with the following routines: + + void gluTessBeginPolygon( GLUtesselator *tess ); + void gluTessBeginContour( GLUtesselator *tess ); + void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data ); + void gluTessEndContour( GLUtesselator *tess ); + void gluTessEndPolygon( GLUtesselator *tess ); + + Within each BeginPolygon/EndPolygon pair, there can be zero or more + calls to BeginContour/EndContour. Within each contour, there are zero + or more calls to gluTessVertex(). The vertices specify a closed + contour (the last vertex of each contour is automatically linked to + the first). + + "coords" give the coordinates of the vertex in 3-space. For useful + results, all vertices should lie in some plane, since the vertices + are projected onto a plane before tesselation. "data" is a pointer + to a user-defined vertex structure, which typically contains other + information such as color, texture coordinates, normal, etc. It is + used to refer to the vertex during rendering. + + The library can be compiled in single- or double-precision; the type + GLUcoord represents either "float" or "double" accordingly. The GLU + version will be available in double-precision only. Compile with + GLU_TESS_API_FLOAT defined to get the single-precision version. + + When EndPolygon is called, the tesselation algorithm determines + which regions are interior to the given contours, according to one + of several "winding rules" described below. The interior regions + are then tesselated, and the output is provided as callbacks. + + + Rendering Callbacks + ------------------- + + Callbacks are specified by the client using + + void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)()); + + If "fn" is NULL, any previously defined callback is discarded. + + The callbacks used to provide output are: /* which == */ + + void begin( GLenum type ); /* GLU_TESS_BEGIN */ + void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */ + void vertex( void *data ); /* GLU_TESS_VERTEX */ + void end( void ); /* GLU_TESS_END */ + + Any of the callbacks may be left undefined; if so, the corresponding + information will not be supplied during rendering. + + The "begin" callback indicates the start of a primitive; type is one + of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the + notes on "boundary extraction" below). + + It is followed by any number of "vertex" callbacks, which supply the + vertices in the same order as expected by the corresponding glBegin() + call. After the last vertex of a given primitive, there is a callback + to "end". + + If the "edgeFlag" callback is provided, no triangle fans or strips + will be used. When edgeFlag is called, if "flag" is GL_TRUE then each + vertex which follows begins an edge which lies on the polygon boundary + (ie. an edge which separates an interior region from an exterior one). + If "flag" is GL_FALSE, each vertex which follows begins an edge which lies + in the polygon interior. "edgeFlag" will be called before the first + call to "vertex". + + Other Callbacks + --------------- + + void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */ + + - Returns an explicit mesh, represented using the quad-edge structure + (Guibas/Stolfi '85). Other implementations of this interface might + use a different mesh structure, so this is available only only as an + SGI extension. When the mesh is no longer needed, it should be freed + using + + void gluDeleteMesh( GLUmesh *mesh ); + + There is a brief description of this data structure in the include + file "mesh.h". For the full details, see L. Guibas and J. Stolfi, + Primitives for the manipulation of general subdivisions and the + computation of Voronoi diagrams, ACM Transactions on Graphics, + 4(2):74-123, April 1985. For an introduction, see the course notes + for CS348a, "Mathematical Foundations of Computer Graphics", + available at the Stanford bookstore (and taught during the fall + quarter). + + void error( GLenum errno ); /* GLU_TESS_ERROR */ + + - errno is one of GLU_TESS_MISSING_BEGIN_POLYGON, + GLU_TESS_MISSING_END_POLYGON, + GLU_TESS_MISSING_BEGIN_CONTOUR, + GLU_TESS_MISSING_END_CONTOUR, + GLU_TESS_COORD_TOO_LARGE, + GLU_TESS_NEED_COMBINE_CALLBACK + + The first four are obvious. The interface recovers from these + errors by inserting the missing call(s). + + GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded + the predefined constant GLU_TESS_MAX_COORD in absolute value, and + that the value has been clamped. (Coordinate values must be small + enough so that two can be multiplied together without overflow.) + + GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an + intersection between two edges in the input data, and the "combine" + callback (below) was not provided. No output will be generated. + + + void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */ + GLUcoord weight[4], void **outData ); + + - When the algorithm detects an intersection, or wishes to merge + features, it needs to create a new vertex. The vertex is defined + as a linear combination of up to 4 existing vertices, referenced + by data[0..3]. The coefficients of the linear combination are + given by weight[0..3]; these weights always sum to 1.0. All vertex + pointers are valid even when some of the weights are zero. + "coords" gives the location of the new vertex. + + The user must allocate another vertex, interpolate parameters + using "data" and "weights", and return the new vertex pointer in + "outData". This handle is supplied during rendering callbacks. + For example, if the polygon lies in an arbitrary plane in 3-space, + and we associate a color with each vertex, the combine callback might + look like this: + + void myCombine( GLUcoord coords[3], VERTEX *d[4], + GLUcoord w[4], VERTEX **dataOut ) + { + VERTEX *new = new_vertex(); + + new->x = coords[0]; + new->y = coords[1]; + new->z = coords[2]; + new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r; + new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g; + new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b; + new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a; + *dataOut = new; + } + + If the algorithm detects an intersection, then the "combine" callback + must be defined, and must write a non-NULL pointer into "dataOut". + Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no + output is generated. This is the only error that can occur during + tesselation and rendering. + + + Control over Tesselation + ------------------------ + + void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value ); + + Properties defined: + + - GLU_TESS_WINDING_RULE. Possible values: + + GLU_TESS_WINDING_ODD + GLU_TESS_WINDING_NONZERO + GLU_TESS_WINDING_POSITIVE + GLU_TESS_WINDING_NEGATIVE + GLU_TESS_WINDING_ABS_GEQ_TWO + + The input contours parition the plane into regions. A winding + rule determines which of these regions are inside the polygon. + + For a single contour C, the winding number of a point x is simply + the signed number of revolutions we make around x as we travel + once around C (where CCW is positive). When there are several + contours, the individual winding numbers are summed. This + procedure associates a signed integer value with each point x in + the plane. Note that the winding number is the same for all + points in a single region. + + The winding rule classifies a region as "inside" if its winding + number belongs to the chosen category (odd, nonzero, positive, + negative, or absolute value of at least two). The current GLU + tesselator implements the "odd" rule. The "nonzero" rule is another + common way to define the interior. The other three rules are + useful for polygon CSG operations (see below). + + - GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero). + + If TRUE, returns a set of closed contours which separate the + polygon interior and exterior (rather than a tesselation). + Exterior contours are oriented CCW with respect to the normal, + interior contours are oriented CW. The GLU_TESS_BEGIN callback + uses the type GL_LINE_LOOP for each contour. + + - GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0. + + This specifies a tolerance for merging features to reduce the size + of the output. For example, two vertices which are very close to + each other might be replaced by a single vertex. The tolerance + is multiplied by the largest coordinate magnitude of any input vertex; + this specifies the maximum distance that any feature can move as the + result of a single merge operation. If a single feature takes part + in several merge operations, the total distance moved could be larger. + + Feature merging is completely optional; the tolerance is only a hint. + The implementation is free to merge in some cases and not in others, + or to never merge features at all. The default tolerance is zero. + + The current implementation merges vertices only if they are exactly + coincident, regardless of the current tolerance. A vertex is + spliced into an edge only if the implementation is unable to + distinguish which side of the edge the vertex lies on. + Two edges are merged only when both endpoints are identical. + + + void gluTessNormal( GLUtesselator *tess, + GLUcoord x, GLUcoord y, GLUcoord z ) + + - Lets the user supply the polygon normal, if known. All input data + is projected into a plane perpendicular to the normal before + tesselation. All output triangles are oriented CCW with + respect to the normal (CW orientation can be obtained by + reversing the sign of the supplied normal). For example, if + you know that all polygons lie in the x-y plane, call + "gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons. + + - If the supplied normal is (0,0,0) (the default value), the + normal is determined as follows. The direction of the normal, + up to its sign, is found by fitting a plane to the vertices, + without regard to how the vertices are connected. It is + expected that the input data lies approximately in plane; + otherwise projection perpendicular to the computed normal may + substantially change the geometry. The sign of the normal is + chosen so that the sum of the signed areas of all input contours + is non-negative (where a CCW contour has positive area). + + - The supplied normal persists until it is changed by another + call to gluTessNormal. + + + Backward compatibility with the GLU tesselator + ---------------------------------------------- + + The preferred interface is the one described above. The following + routines are obsolete, and are provided only for backward compatibility: + + typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */ + + void gluBeginPolygon( GLUtesselator *tess ); + void gluNextContour( GLUtesselator *tess, GLenum type ); + void gluEndPolygon( GLUtesselator *tess ); + + "type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or + GLU_UNKNOWN. It is ignored by the current GLU tesselator. + + GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined + as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END, + GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG. + + +Polygon CSG operations +---------------------- + + The features of the tesselator make it easy to find the union, difference, + or intersection of several polygons. + + First, assume that each polygon is defined so that the winding number + is 0 for each exterior region, and 1 for each interior region. Under + this model, CCW contours define the outer boundary of the polygon, and + CW contours define holes. Contours may be nested, but a nested + contour must be oriented oppositely from the contour that contains it. + + If the original polygons do not satisfy this description, they can be + converted to this form by first running the tesselator with the + GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of + contours satisfying the restriction above. By allocating two + tesselator objects, the callbacks from one tesselator can be fed + directly to the input of another. + + Given two or more polygons of the form above, CSG operations can be + implemented as follows: + + Union + Draw all the input contours as a single polygon. The winding number + of each resulting region is the number of original polygons + which cover it. The union can be extracted using the + GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules. + Note that with the nonzero rule, we would get the same result if + all contour orientations were reversed. + + Intersection (two polygons at a time only) + Draw a single polygon using the contours from both input polygons. + Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this + winding rule looks at the absolute value, reversing all contour + orientations does not change the result.) + + Difference + + Suppose we want to compute A \ (B union C union D). Draw a single + polygon consisting of the unmodified contours from A, followed by + the contours of B,C,D with the vertex order reversed (this changes + the winding number of the interior regions to -1). To extract the + result, use the GLU_TESS_WINDING_POSITIVE rule. + + If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an + alternative to reversing the vertex order is to reverse the sign of + the supplied normal. For example in the x-y plane, call + gluTessNormal( tess, 0.0, 0.0, -1.0 ). + + +Performance +----------- + + The tesselator is not intended for immediate-mode rendering; when + possible the output should be cached in a user structure or display + list. General polygon tesselation is an inherently difficult problem, + especially given the goal of extreme robustness. + + The implementation makes an effort to output a small number of fans + and strips; this should improve the rendering performance when the + output is used in a display list. + + Single-contour input polygons are first tested to see whether they can + be rendered as a triangle fan with respect to the first vertex (to + avoid running the full decomposition algorithm on convex polygons). + Non-convex polygons may be rendered by this "fast path" as well, if + the algorithm gets lucky in its choice of a starting vertex. + + For best performance follow these guidelines: + + - supply the polygon normal, if available, using gluTessNormal(). + This represents about 10% of the computation time. For example, + if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1). + + - render many polygons using the same tesselator object, rather than + allocating a new tesselator for each one. (In a multi-threaded, + multi-processor environment you may get better performance using + several tesselators.) + + +Comparison with the GLU tesselator +---------------------------------- + + On polygons which make it through the "fast path", the tesselator is + 3 to 5 times faster than the GLU tesselator. + + On polygons which don't make it through the fast path (but which don't + have self-intersections or degeneracies), it is about 2 times slower. + + On polygons with self-intersections or degeneraces, there is nothing + to compare against. + + The new tesselator generates many more fans and strips, reducing the + number of vertices that need to be sent to the hardware. + + Key to the statistics: + + vert number of input vertices on all contours + cntr number of input contours + tri number of triangles in all output primitives + strip number of triangle strips + fan number of triangle fans + ind number of independent triangles + ms number of milliseconds for tesselation + (on a 150MHz R4400 Indy) + + Convex polygon examples: + +New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms +Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms +New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms +Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms +New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms +Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms + + Concave single-contour polygons: + +New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms +Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms +New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms +Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms +New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms +Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms +New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms +Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms + + Multiple contours, but no intersections: + +New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms +Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms +New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms +Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms +New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms +Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms + + Self-intersecting and degenerate examples: + +Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms +Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms +Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms +Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms +: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms +: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms +: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms diff --git a/cogl/cogl-path/tesselator/dict-list.h b/cogl/cogl-path/tesselator/dict-list.h new file mode 100644 index 000000000..11331a76e --- /dev/null +++ b/cogl/cogl-path/tesselator/dict-list.h @@ -0,0 +1,100 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/cogl/cogl-path/tesselator/dict.c b/cogl/cogl-path/tesselator/dict.c new file mode 100644 index 000000000..49d4f759e --- /dev/null +++ b/cogl/cogl-path/tesselator/dict.c @@ -0,0 +1,111 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include +#include "dict-list.h" +#include "memalloc.h" + +/* really __gl_dictListNewDict */ +Dict *dictNewDict( void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ) +{ + Dict *dict = (Dict *) memAlloc( sizeof( Dict )); + DictNode *head; + + if (dict == NULL) return NULL; + + head = &dict->head; + + head->key = NULL; + head->next = head; + head->prev = head; + + dict->frame = frame; + dict->leq = leq; + + return dict; +} + +/* really __gl_dictListDeleteDict */ +void dictDeleteDict( Dict *dict ) +{ + DictNode *node, *next; + + for( node = dict->head.next; node != &dict->head; node = next ) { + next = node->next; + memFree( node ); + } + memFree( dict ); +} + +/* really __gl_dictListInsertBefore */ +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ) +{ + DictNode *newNode; + + do { + node = node->prev; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key)); + + newNode = (DictNode *) memAlloc( sizeof( DictNode )); + if (newNode == NULL) return NULL; + + newNode->key = key; + newNode->next = node->next; + node->next->prev = newNode; + newNode->prev = node; + node->next = newNode; + + return newNode; +} + +/* really __gl_dictListDelete */ +void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/ +{ + node->next->prev = node->prev; + node->prev->next = node->next; + memFree( node ); +} + +/* really __gl_dictListSearch */ +DictNode *dictSearch( Dict *dict, DictKey key ) +{ + DictNode *node = &dict->head; + + do { + node = node->next; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key)); + + return node; +} diff --git a/cogl/cogl-path/tesselator/dict.h b/cogl/cogl-path/tesselator/dict.h new file mode 100644 index 000000000..11331a76e --- /dev/null +++ b/cogl/cogl-path/tesselator/dict.h @@ -0,0 +1,100 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/cogl/cogl-path/tesselator/geom.c b/cogl/cogl-path/tesselator/geom.c new file mode 100644 index 000000000..35b36a394 --- /dev/null +++ b/cogl/cogl-path/tesselator/geom.c @@ -0,0 +1,264 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include "mesh.h" +#include "geom.h" + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ) +{ + /* Returns TRUE if u is lexicographically <= v. */ + + return VertLeq( u, v ); +} + +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->t = 0 and + * let r be the negated result (this evaluates (uw)(v->s)), then + * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR)); + } else { + return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches EdgeEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + return (v->t - w->t) * gapL + (v->t - u->t) * gapR; + } + /* vertical line */ + return 0; +} + + +/*********************************************************************** + * Define versions of EdgeSign, EdgeEval with s and t transposed. + */ + +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->s = 0 and + * let r be the negated result (this evaluates (uw)(v->t)), then + * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s). + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR)); + } else { + return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches TransEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + return (v->s - w->s) * gapL + (v->s - u->s) * gapR; + } + /* vertical line */ + return 0; +} + + +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* For almost-degenerate situations, the results are not reliable. + * Unless the floating-point arithmetic can be performed without + * rounding errors, *any* implementation will give incorrect results + * on some degenerate inputs, so the client must have some way to + * handle this situation. + */ + return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0; +} + +/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b), + * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces + * this in the rare case that one argument is slightly negative. + * The implementation is extremely stable numerically. + * In particular it guarantees that the result r satisfies + * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate + * even when a and b differ greatly in magnitude. + */ +#define RealInterpolate(a,x,b,y) \ + (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \ + ((a <= b) ? ((b == 0) ? ((x+y) / 2) \ + : (x + (y-x) * (a/(a+b)))) \ + : (y + (x-y) * (b/(a+b))))) + +#ifndef FOR_TRITE_TEST_PROGRAM +#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y) +#else + +/* Claim: the ONLY property the sweep algorithm relies on is that + * MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that. + */ +#include +extern int RandomInterpolate; + +GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y) +{ +printf("*********************%d\n",RandomInterpolate); + if( RandomInterpolate ) { + a = 1.2 * drand48() - 0.1; + a = (a < 0) ? 0 : ((a > 1) ? 1 : a); + b = 1.0 - a; + } + return RealInterpolate(a,x,b,y); +} + +#endif + +#define Swap(a,b) do { GLUvertex *t = a; a = b; b = t; } while (0) + +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ) +/* Given edges (o1,d1) and (o2,d2), compute their point of intersection. + * The computed point is guaranteed to lie in the intersection of the + * bounding rectangles defined by each edge. + */ +{ + GLdouble z1, z2; + + /* This is certainly not the most efficient way to find the intersection + * of two line segments, but it is very numerically stable. + * + * Strategy: find the two middle vertices in the VertLeq ordering, + * and interpolate the intersection s-value from these. Then repeat + * using the TransLeq ordering to find the intersection t-value. + */ + + if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! VertLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->s = (o2->s + d1->s) / 2; + } else if( VertLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = EdgeEval( o1, o2, d1 ); + z2 = EdgeEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d1->s ); + } else { + /* Interpolate between o2 and d2 */ + z1 = EdgeSign( o1, o2, d1 ); + z2 = -EdgeSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d2->s ); + } + + /* Now repeat the process for t */ + + if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! TransLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->t = (o2->t + d1->t) / 2; + } else if( TransLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = TransEval( o1, o2, d1 ); + z2 = TransEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d1->t ); + } else { + /* Interpolate between o2 and d2 */ + z1 = TransSign( o1, o2, d1 ); + z2 = -TransSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d2->t ); + } +} diff --git a/cogl/cogl-path/tesselator/geom.h b/cogl/cogl-path/tesselator/geom.h new file mode 100644 index 000000000..5cb76c7d1 --- /dev/null +++ b/cogl/cogl-path/tesselator/geom.h @@ -0,0 +1,84 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __geom_h_ +#define __geom_h_ + +#include "mesh.h" + +#ifdef NO_BRANCH_CONDITIONS +/* MIPS architecture has special instructions to evaluate boolean + * conditions -- more efficient than branching, IF you can get the + * compiler to generate the right instructions (SGI compiler doesn't) + */ +#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t)) +#define VertLeq(u,v) (((u)->s < (v)->s) | \ + ((u)->s == (v)->s & (u)->t <= (v)->t)) +#else +#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t) +#define VertLeq(u,v) (((u)->s < (v)->s) || \ + ((u)->s == (v)->s && (u)->t <= (v)->t)) +#endif + +#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w) +#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w) + +/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */ + +#define TransLeq(u,v) (((u)->t < (v)->t) || \ + ((u)->t == (v)->t && (u)->s <= (v)->s)) +#define TransEval(u,v,w) __gl_transEval(u,v,w) +#define TransSign(u,v,w) __gl_transSign(u,v,w) + + +#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org ) +#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst ) + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t)) + +#define VertCCW(u,v,w) __gl_vertCCW(u,v,w) + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ); +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ); + +#endif diff --git a/cogl/cogl-path/tesselator/gluos.h b/cogl/cogl-path/tesselator/gluos.h new file mode 100644 index 000000000..d6c3ae998 --- /dev/null +++ b/cogl/cogl-path/tesselator/gluos.h @@ -0,0 +1 @@ +/* This is a stub header to avoid having to change tess.c */ diff --git a/cogl/cogl-path/tesselator/memalloc.h b/cogl/cogl-path/tesselator/memalloc.h new file mode 100644 index 000000000..a094b6cdc --- /dev/null +++ b/cogl/cogl-path/tesselator/memalloc.h @@ -0,0 +1,49 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is a simple replacement for memalloc from the SGI tesselator + code to force it to use glib's allocation instead */ + +#ifndef __MEMALLOC_H__ +#define __MEMALLOC_H__ + +#include + +#define memRealloc g_realloc +#define memAlloc g_malloc +#define memFree g_free +#define memInit(x) 1 + +/* tess.c defines TRUE and FALSE itself unconditionally so we need to + undefine it from the glib headers */ +#undef TRUE +#undef FALSE + +#endif /* __MEMALLOC_H__ */ diff --git a/cogl/cogl-path/tesselator/mesh.c b/cogl/cogl-path/tesselator/mesh.c new file mode 100644 index 000000000..36cb3a7be --- /dev/null +++ b/cogl/cogl-path/tesselator/mesh.c @@ -0,0 +1,798 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "memalloc.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +static GLUvertex *allocVertex() +{ + return (GLUvertex *)memAlloc( sizeof( GLUvertex )); +} + +static GLUface *allocFace() +{ + return (GLUface *)memAlloc( sizeof( GLUface )); +} + +/************************ Utility Routines ************************/ + +/* Allocate and free half-edges in pairs for efficiency. + * The *only* place that should use this fact is allocation/free. + */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +/* MakeEdge creates a new pair of half-edges which form their own loop. + * No vertex or face structures are allocated, but these must be assigned + * before the current edge operation is completed. + */ +static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext ) +{ + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUhalfEdge *ePrev; + EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair )); + if (pair == NULL) return NULL; + + e = &pair->e; + eSym = &pair->eSym; + + /* Make sure eNext points to the first edge of the edge pair */ + if( eNext->Sym < eNext ) { eNext = eNext->Sym; } + + /* Insert in circular doubly-linked list before eNext. + * Note that the prev pointer is stored in Sym->next. + */ + ePrev = eNext->Sym->next; + eSym->next = ePrev; + ePrev->Sym->next = e; + e->next = eNext; + eNext->Sym->next = eSym; + + e->Sym = eSym; + e->Onext = e; + e->Lnext = eSym; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->Sym = e; + eSym->Onext = eSym; + eSym->Lnext = e; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return e; +} + +/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the + * CS348a notes (see mesh.h). Basically it modifies the mesh so that + * a->Onext and b->Onext are exchanged. This can have various effects + * depending on whether a and b belong to different face or vertex rings. + * For more explanation see __gl_meshSplice() below. + */ +static void Splice( GLUhalfEdge *a, GLUhalfEdge *b ) +{ + GLUhalfEdge *aOnext = a->Onext; + GLUhalfEdge *bOnext = b->Onext; + + aOnext->Sym->Lnext = b; + bOnext->Sym->Lnext = a; + a->Onext = bOnext; + b->Onext = aOnext; +} + +/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the + * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives + * a place to insert the new vertex in the global vertex list. We insert + * the new vertex *before* vNext so that algorithms which walk the vertex + * list will not see the newly created vertices. + */ +static void MakeVertex( GLUvertex *newVertex, + GLUhalfEdge *eOrig, GLUvertex *vNext ) +{ + GLUhalfEdge *e; + GLUvertex *vPrev; + GLUvertex *vNew = newVertex; + + assert(vNew != NULL); + + /* insert in circular doubly-linked list before vNext */ + vPrev = vNext->prev; + vNew->prev = vPrev; + vPrev->next = vNew; + vNew->next = vNext; + vNext->prev = vNew; + + vNew->anEdge = eOrig; + vNew->data = NULL; + /* leave coords, s, t undefined */ + + /* fix other edges on this vertex loop */ + e = eOrig; + do { + e->Org = vNew; + e = e->Onext; + } while( e != eOrig ); +} + +/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left + * face of all edges in the face loop to which eOrig belongs. "fNext" gives + * a place to insert the new face in the global face list. We insert + * the new face *before* fNext so that algorithms which walk the face + * list will not see the newly created faces. + */ +static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext ) +{ + GLUhalfEdge *e; + GLUface *fPrev; + GLUface *fNew = newFace; + + assert(fNew != NULL); + + /* insert in circular doubly-linked list before fNext */ + fPrev = fNext->prev; + fNew->prev = fPrev; + fPrev->next = fNew; + fNew->next = fNext; + fNext->prev = fNew; + + fNew->anEdge = eOrig; + fNew->data = NULL; + fNew->trail = NULL; + fNew->marked = FALSE; + + /* The new face is marked "inside" if the old one was. This is a + * convenience for the common case where a face has been split in two. + */ + fNew->inside = fNext->inside; + + /* fix other edges on this face loop */ + e = eOrig; + do { + e->Lface = fNew; + e = e->Lnext; + } while( e != eOrig ); +} + +/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym), + * and removes from the global edge list. + */ +static void KillEdge( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *ePrev, *eNext; + + /* Half-edges are allocated in pairs, see EdgePair above */ + if( eDel->Sym < eDel ) { eDel = eDel->Sym; } + + /* delete from circular doubly-linked list */ + eNext = eDel->next; + ePrev = eDel->Sym->next; + eNext->Sym->next = ePrev; + ePrev->Sym->next = eNext; + + memFree( eDel ); +} + + +/* KillVertex( vDel ) destroys a vertex and removes it from the global + * vertex list. It updates the vertex loop to point to a given new vertex. + */ +static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg ) +{ + GLUhalfEdge *e, *eStart = vDel->anEdge; + GLUvertex *vPrev, *vNext; + + /* change the origin of all affected edges */ + e = eStart; + do { + e->Org = newOrg; + e = e->Onext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + vPrev = vDel->prev; + vNext = vDel->next; + vNext->prev = vPrev; + vPrev->next = vNext; + + memFree( vDel ); +} + +/* KillFace( fDel ) destroys a face and removes it from the global face + * list. It updates the face loop to point to a given new face. + */ +static void KillFace( GLUface *fDel, GLUface *newLface ) +{ + GLUhalfEdge *e, *eStart = fDel->anEdge; + GLUface *fPrev, *fNext; + + /* change the left face of all affected edges */ + e = eStart; + do { + e->Lface = newLface; + e = e->Lnext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fDel->prev; + fNext = fDel->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fDel ); +} + + +/****************** Basic Edge Operations **********************/ + +/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). + * The loop consists of the two new half-edges. + */ +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ) +{ + GLUvertex *newVertex1= allocVertex(); + GLUvertex *newVertex2= allocVertex(); + GLUface *newFace= allocFace(); + GLUhalfEdge *e; + + /* if any one is null then all get freed */ + if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) { + if (newVertex1 != NULL) memFree(newVertex1); + if (newVertex2 != NULL) memFree(newVertex2); + if (newFace != NULL) memFree(newFace); + return NULL; + } + + e = MakeEdge( &mesh->eHead ); + if (e == NULL) { + memFree(newVertex1); + memFree(newVertex2); + memFree(newFace); + return NULL; + } + + MakeVertex( newVertex1, e, &mesh->vHead ); + MakeVertex( newVertex2, e->Sym, &mesh->vHead ); + MakeFace( newFace, e, &mesh->fHead ); + return e; +} + + +/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * Some special cases: + * If eDst == eOrg, the operation has no effect. + * If eDst == eOrg->Lnext, the new face will have a single edge. + * If eDst == eOrg->Lprev, the old face will have a single edge. + * If eDst == eOrg->Onext, the new vertex will have a single edge. + * If eDst == eOrg->Oprev, the old vertex will have a single edge. + */ +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + int joiningLoops = FALSE; + int joiningVertices = FALSE; + + if( eOrg == eDst ) return 1; + + if( eDst->Org != eOrg->Org ) { + /* We are merging two disjoint vertices -- destroy eDst->Org */ + joiningVertices = TRUE; + KillVertex( eDst->Org, eOrg->Org ); + } + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Change the edge structure */ + Splice( eDst, eOrg ); + + if( ! joiningVertices ) { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return 0; + + /* We split one vertex into two -- the new vertex is eDst->Org. + * Make sure the old vertex points to a valid half-edge. + */ + MakeVertex( newVertex, eDst, eOrg->Org ); + eOrg->Org->anEdge = eOrg; + } + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We split one loop into two -- the new loop is eDst->Lface. + * Make sure the old face points to a valid half-edge. + */ + MakeFace( newFace, eDst, eOrg->Lface ); + eOrg->Lface->anEdge = eOrg; + } + + return 1; +} + + +/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * This function could be implemented as two calls to __gl_meshSplice + * plus a few calls to memFree, but this would allocate and delete + * unnecessary vertices and faces. + */ +int __gl_meshDelete( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *eDelSym = eDel->Sym; + int joiningLoops = FALSE; + + /* First step: disconnect the origin vertex eDel->Org. We make all + * changes to get a consistent mesh in this "intermediate" state. + */ + if( eDel->Lface != eDel->Rface ) { + /* We are joining two loops into one -- remove the left face */ + joiningLoops = TRUE; + KillFace( eDel->Lface, eDel->Rface ); + } + + if( eDel->Onext == eDel ) { + KillVertex( eDel->Org, NULL ); + } else { + /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */ + eDel->Rface->anEdge = eDel->Oprev; + eDel->Org->anEdge = eDel->Onext; + + Splice( eDel, eDel->Oprev ); + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We are splitting one loop into two -- create a new loop for eDel. */ + MakeFace( newFace, eDel, eDel->Lface ); + } + } + + /* Claim: the mesh is now in a consistent state, except that eDel->Org + * may have been deleted. Now we disconnect eDel->Dst. + */ + if( eDelSym->Onext == eDelSym ) { + KillVertex( eDelSym->Org, NULL ); + KillFace( eDelSym->Lface, NULL ); + } else { + /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */ + eDel->Lface->anEdge = eDelSym->Oprev; + eDelSym->Org->anEdge = eDelSym->Onext; + Splice( eDelSym, eDelSym->Oprev ); + } + + /* Any isolated vertices or faces have already been freed. */ + KillEdge( eDel ); + + return 1; +} + + +/******************** Other Edge Operations **********************/ + +/* All these routines can be implemented with the basic edge + * operations above. They are provided for convenience and efficiency. + */ + + +/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNewSym; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return NULL; + + MakeVertex( newVertex, eNewSym, eNew->Org ); + } + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + return eNew; +} + + +/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNew; + GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg ); + if (tempHalfEdge == NULL) return NULL; + + eNew = tempHalfEdge->Sym; + + /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */ + Splice( eOrg->Sym, eOrg->Sym->Oprev ); + Splice( eOrg->Sym, eNew ); + + /* Set the vertex and face information */ + eOrg->Dst = eNew->Org; + eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */ + eNew->Rface = eOrg->Rface; + eNew->winding = eOrg->winding; /* copy old winding information */ + eNew->Sym->winding = eOrg->Sym->winding; + + return eNew; +} + + +/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * If (eOrg == eDst), the new face will have only two edges. + * If (eOrg->Lnext == eDst), the old face is reduced to a single edge. + * If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. + */ +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + GLUhalfEdge *eNewSym; + int joiningLoops = FALSE; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + Splice( eNewSym, eDst ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + eNewSym->Org = eDst->Org; + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + /* Make sure the old face points to a valid half-edge */ + eOrg->Lface->anEdge = eNewSym; + + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return NULL; + + /* We split one loop into two -- the new loop is eNew->Lface */ + MakeFace( newFace, eNew, eOrg->Lface ); + } + return eNew; +} + + +/******************** Other Operations **********************/ + +/* __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + */ +void __gl_meshZapFace( GLUface *fZap ) +{ + GLUhalfEdge *eStart = fZap->anEdge; + GLUhalfEdge *e, *eNext, *eSym; + GLUface *fPrev, *fNext; + + /* walk around face, deleting edges whose right face is also NULL */ + eNext = eStart->Lnext; + do { + e = eNext; + eNext = e->Lnext; + + e->Lface = NULL; + if( e->Rface == NULL ) { + /* delete the edge -- see __gl_MeshDelete above */ + + if( e->Onext == e ) { + KillVertex( e->Org, NULL ); + } else { + /* Make sure that e->Org points to a valid half-edge */ + e->Org->anEdge = e->Onext; + Splice( e, e->Oprev ); + } + eSym = e->Sym; + if( eSym->Onext == eSym ) { + KillVertex( eSym->Org, NULL ); + } else { + /* Make sure that eSym->Org points to a valid half-edge */ + eSym->Org->anEdge = eSym->Onext; + Splice( eSym, eSym->Oprev ); + } + KillEdge( e ); + } + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fZap->prev; + fNext = fZap->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fZap ); +} + + +/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + */ +GLUmesh *__gl_meshNewMesh( void ) +{ + GLUvertex *v; + GLUface *f; + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh )); + if (mesh == NULL) { + return NULL; + } + + v = &mesh->vHead; + f = &mesh->fHead; + e = &mesh->eHead; + eSym = &mesh->eHeadSym; + + v->next = v->prev = v; + v->anEdge = NULL; + v->data = NULL; + + f->next = f->prev = f; + f->anEdge = NULL; + f->data = NULL; + f->trail = NULL; + f->marked = FALSE; + f->inside = FALSE; + + e->next = e; + e->Sym = eSym; + e->Onext = NULL; + e->Lnext = NULL; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->next = eSym; + eSym->Sym = e; + eSym->Onext = NULL; + eSym->Lnext = NULL; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return mesh; +} + + +/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + */ +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ) +{ + GLUface *f1 = &mesh1->fHead; + GLUvertex *v1 = &mesh1->vHead; + GLUhalfEdge *e1 = &mesh1->eHead; + GLUface *f2 = &mesh2->fHead; + GLUvertex *v2 = &mesh2->vHead; + GLUhalfEdge *e2 = &mesh2->eHead; + + /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ + if( f2->next != f2 ) { + f1->prev->next = f2->next; + f2->next->prev = f1->prev; + f2->prev->next = f1; + f1->prev = f2->prev; + } + + if( v2->next != v2 ) { + v1->prev->next = v2->next; + v2->next->prev = v1->prev; + v2->prev->next = v1; + v1->prev = v2->prev; + } + + if( e2->next != e2 ) { + e1->Sym->next->Sym->next = e2->next; + e2->next->Sym->next = e1->Sym->next; + e2->Sym->next->Sym->next = e1; + e1->Sym->next = e2->Sym->next; + } + + memFree( mesh2 ); + return mesh1; +} + + +#ifdef DELETE_BY_ZAPPING + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + + while( fHead->next != fHead ) { + __gl_meshZapFace( fHead->next ); + } + assert( mesh->vHead.next == &mesh->vHead ); + + memFree( mesh ); +} + +#else + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *f, *fNext; + GLUvertex *v, *vNext; + GLUhalfEdge *e, *eNext; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + memFree( f ); + } + + for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) { + vNext = v->next; + memFree( v ); + } + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + /* One call frees both e and e->Sym (see EdgePair above) */ + eNext = e->next; + memFree( e ); + } + + memFree( mesh ); +} + +#endif + +#ifndef NDEBUG + +/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ +void __gl_meshCheckMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + GLUvertex *vHead = &mesh->vHead; + GLUhalfEdge *eHead = &mesh->eHead; + GLUface *f, *fPrev; + GLUvertex *v, *vPrev; + GLUhalfEdge *e, *ePrev; + + fPrev = fHead; + for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) { + assert( f->prev == fPrev ); + e = f->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Lface == f ); + e = e->Lnext; + } while( e != f->anEdge ); + } + assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL ); + + vPrev = vHead; + for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) { + assert( v->prev == vPrev ); + e = v->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Org == v ); + e = e->Onext; + } while( e != v->anEdge ); + } + assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL ); + + ePrev = eHead; + for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) { + assert( e->Sym->next == ePrev->Sym ); + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Org != NULL ); + assert( e->Dst != NULL ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + } + assert( e->Sym->next == ePrev->Sym + && e->Sym == &mesh->eHeadSym + && e->Sym->Sym == e + && e->Org == NULL && e->Dst == NULL + && e->Lface == NULL && e->Rface == NULL ); +} + +#endif diff --git a/cogl/cogl-path/tesselator/mesh.h b/cogl/cogl-path/tesselator/mesh.h new file mode 100644 index 000000000..690c5f2f6 --- /dev/null +++ b/cogl/cogl-path/tesselator/mesh.h @@ -0,0 +1,266 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __mesh_h_ +#define __mesh_h_ + +#include + +typedef struct GLUmesh GLUmesh; + +typedef struct GLUvertex GLUvertex; +typedef struct GLUface GLUface; +typedef struct GLUhalfEdge GLUhalfEdge; + +typedef struct ActiveRegion ActiveRegion; /* Internal data */ + +/* The mesh structure is similar in spirit, notation, and operations + * to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives + * for the manipulation of general subdivisions and the computation of + * Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985). + * For a simplified description, see the course notes for CS348a, + * "Mathematical Foundations of Computer Graphics", available at the + * Stanford bookstore (and taught during the fall quarter). + * The implementation also borrows a tiny subset of the graph-based approach + * use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction + * to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988). + * + * The fundamental data structure is the "half-edge". Two half-edges + * go together to make an edge, but they point in opposite directions. + * Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym), + * its origin vertex (Org), the face on its left side (Lface), and the + * adjacent half-edges in the CCW direction around the origin vertex + * (Onext) and around the left face (Lnext). There is also a "next" + * pointer for the global edge list (see below). + * + * The notation used for mesh navigation: + * Sym = the mate of a half-edge (same edge, but opposite direction) + * Onext = edge CCW around origin vertex (keep same origin) + * Dnext = edge CCW around destination vertex (keep same dest) + * Lnext = edge CCW around left face (dest becomes new origin) + * Rnext = edge CCW around right face (origin becomes new dest) + * + * "prev" means to substitute CW for CCW in the definitions above. + * + * The mesh keeps global lists of all vertices, faces, and edges, + * stored as doubly-linked circular lists with a dummy header node. + * The mesh stores pointers to these dummy headers (vHead, fHead, eHead). + * + * The circular edge list is special; since half-edges always occur + * in pairs (e and e->Sym), each half-edge stores a pointer in only + * one direction. Starting at eHead and following the e->next pointers + * will visit each *edge* once (ie. e or e->Sym, but not both). + * e->Sym stores a pointer in the opposite direction, thus it is + * always true that e->Sym->next->Sym->next == e. + * + * Each vertex has a pointer to next and previous vertices in the + * circular list, and a pointer to a half-edge with this vertex as + * the origin (NULL if this is the dummy header). There is also a + * field "data" for client data. + * + * Each face has a pointer to the next and previous faces in the + * circular list, and a pointer to a half-edge with this face as + * the left face (NULL if this is the dummy header). There is also + * a field "data" for client data. + * + * Note that what we call a "face" is really a loop; faces may consist + * of more than one loop (ie. not simply connected), but there is no + * record of this in the data structure. The mesh may consist of + * several disconnected regions, so it may not be possible to visit + * the entire mesh by starting at a half-edge and traversing the edge + * structure. + * + * The mesh does NOT support isolated vertices; a vertex is deleted along + * with its last edge. Similarly when two faces are merged, one of the + * faces is deleted (see __gl_meshDelete below). For mesh operations, + * all face (loop) and vertex pointers must not be NULL. However, once + * mesh manipulation is finished, __gl_MeshZapFace can be used to delete + * faces of the mesh, one at a time. All external faces can be "zapped" + * before the mesh is returned to the client; then a NULL face indicates + * a region which is not part of the output polygon. + */ + +struct GLUvertex { + GLUvertex *next; /* next vertex (never NULL) */ + GLUvertex *prev; /* previous vertex (never NULL) */ + GLUhalfEdge *anEdge; /* a half-edge with this origin */ + void *data; /* client's data */ + + /* Internal data (keep hidden) */ + GLdouble coords[3]; /* vertex location in 3D */ + GLdouble s, t; /* projection onto the sweep plane */ + long pqHandle; /* to allow deletion from priority queue */ +}; + +struct GLUface { + GLUface *next; /* next face (never NULL) */ + GLUface *prev; /* previous face (never NULL) */ + GLUhalfEdge *anEdge; /* a half edge with this left face */ + void *data; /* room for client's data */ + + /* Internal data (keep hidden) */ + GLUface *trail; /* "stack" for conversion to strips */ + GLboolean marked; /* flag for conversion to strips */ + GLboolean inside; /* this face is in the polygon interior */ +}; + +struct GLUhalfEdge { + GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */ + GLUhalfEdge *Sym; /* same edge, opposite direction */ + GLUhalfEdge *Onext; /* next edge CCW around origin */ + GLUhalfEdge *Lnext; /* next edge CCW around left face */ + GLUvertex *Org; /* origin vertex (Overtex too long) */ + GLUface *Lface; /* left face */ + + /* Internal data (keep hidden) */ + ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */ + int winding; /* change in winding number when crossing + from the right face to the left face */ +}; + +#define Rface Sym->Lface +#define Dst Sym->Org + +#define Oprev Sym->Lnext +#define Lprev Onext->Sym +#define Dprev Lnext->Sym +#define Rprev Sym->Onext +#define Dnext Rprev->Sym /* 3 pointers */ +#define Rnext Oprev->Sym /* 3 pointers */ + + +struct GLUmesh { + GLUvertex vHead; /* dummy header for vertex list */ + GLUface fHead; /* dummy header for face list */ + GLUhalfEdge eHead; /* dummy header for edge list */ + GLUhalfEdge eHeadSym; /* and its symmetric counterpart */ +}; + +/* The mesh operations below have three motivations: completeness, + * convenience, and efficiency. The basic mesh operations are MakeEdge, + * Splice, and Delete. All the other edge operations can be implemented + * in terms of these. The other operations are provided for convenience + * and/or efficiency. + * + * When a face is split or a vertex is added, they are inserted into the + * global list *before* the existing vertex or face (ie. e->Org or e->Lface). + * This makes it easier to process all vertices or faces in the global lists + * without worrying about processing the same data twice. As a convenience, + * when a face is split, the "inside" flag is copied from the old face. + * Other internal data (v->data, v->activeRegion, f->data, f->marked, + * f->trail, e->winding) is set to zero. + * + * ********************** Basic Edge Operations ************************** + * + * __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop. + * The loop (face) consists of the two new half-edges. + * + * __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * ********************** Other Edge Operations ************************** + * + * __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + * + * __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + * + * __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * ************************ Other Operations ***************************** + * + * __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + * + * __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + * + * __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + * + * __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + * + * __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ + +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ); +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); +int __gl_meshDelete( GLUhalfEdge *eDel ); + +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); + +GLUmesh *__gl_meshNewMesh( void ); +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ); +void __gl_meshDeleteMesh( GLUmesh *mesh ); +void __gl_meshZapFace( GLUface *fZap ); + +#ifdef NDEBUG +#define __gl_meshCheckMesh( mesh ) +#else +void __gl_meshCheckMesh( GLUmesh *mesh ); +#endif + +#endif diff --git a/cogl/cogl-path/tesselator/normal.c b/cogl/cogl-path/tesselator/normal.c new file mode 100644 index 000000000..9a3bd43d3 --- /dev/null +++ b/cogl/cogl-path/tesselator/normal.c @@ -0,0 +1,257 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include "mesh.h" +#include "tess.h" +#include "normal.h" +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]) + +#if 0 +static void Normalize( GLdouble v[3] ) +{ + GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + + assert( len > 0 ); + len = sqrt( len ); + v[0] /= len; + v[1] /= len; + v[2] /= len; +} +#endif + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static int LongAxis( GLdouble v[3] ) +{ + int i = 0; + + if( ABS(v[1]) > ABS(v[0]) ) { i = 1; } + if( ABS(v[2]) > ABS(v[i]) ) { i = 2; } + return i; +} + +static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] ) +{ + GLUvertex *v, *v1, *v2; + GLdouble c, tLen2, maxLen2; + GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3]; + GLUvertex *maxVert[3], *minVert[3]; + GLUvertex *vHead = &tess->mesh->vHead; + int i; + + maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD; + minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD; + + for( v = vHead->next; v != vHead; v = v->next ) { + for( i = 0; i < 3; ++i ) { + c = v->coords[i]; + if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; } + if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; } + } + } + + /* Find two vertices separated by at least 1/sqrt(3) of the maximum + * distance between any two vertices + */ + i = 0; + if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; } + if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; } + if( minVal[i] >= maxVal[i] ) { + /* All vertices are the same -- normal doesn't matter */ + norm[0] = 0; norm[1] = 0; norm[2] = 1; + return; + } + + /* Look for a third vertex which forms the triangle with maximum area + * (Length of normal == twice the triangle area) + */ + maxLen2 = 0; + v1 = minVert[i]; + v2 = maxVert[i]; + d1[0] = v1->coords[0] - v2->coords[0]; + d1[1] = v1->coords[1] - v2->coords[1]; + d1[2] = v1->coords[2] - v2->coords[2]; + for( v = vHead->next; v != vHead; v = v->next ) { + d2[0] = v->coords[0] - v2->coords[0]; + d2[1] = v->coords[1] - v2->coords[1]; + d2[2] = v->coords[2] - v2->coords[2]; + tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1]; + tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2]; + tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0]; + tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2]; + if( tLen2 > maxLen2 ) { + maxLen2 = tLen2; + norm[0] = tNorm[0]; + norm[1] = tNorm[1]; + norm[2] = tNorm[2]; + } + } + + if( maxLen2 <= 0 ) { + /* All points lie on a single line -- any decent normal will do */ + norm[0] = norm[1] = norm[2] = 0; + norm[LongAxis(d1)] = 1; + } +} + + +static void CheckOrientation( GLUtesselator *tess ) +{ + GLdouble area; + GLUface *f, *fHead = &tess->mesh->fHead; + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLUhalfEdge *e; + + /* When we compute the normal automatically, we choose the orientation + * so that the sum of the signed areas of all contours is non-negative. + */ + area = 0; + for( f = fHead->next; f != fHead; f = f->next ) { + e = f->anEdge; + if( e->winding <= 0 ) continue; + do { + area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t); + e = e->Lnext; + } while( e != f->anEdge ); + } + if( area < 0 ) { + /* Reverse the orientation by flipping all the t-coordinates */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->t = - v->t; + } + tess->tUnit[0] = - tess->tUnit[0]; + tess->tUnit[1] = - tess->tUnit[1]; + tess->tUnit[2] = - tess->tUnit[2]; + } +} + +#ifdef FOR_TRITE_TEST_PROGRAM +#include +extern int RandomSweep; +#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0) +#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0) +#else +#if defined(SLANTED_SWEEP) +/* The "feature merging" is not intended to be complete. There are + * special cases where edges are nearly parallel to the sweep line + * which are not implemented. The algorithm should still behave + * robustly (ie. produce a reasonable tesselation) in the presence + * of such edges, however it may miss features which could have been + * merged. We could minimize this effect by choosing the sweep line + * direction to be something unusual (ie. not parallel to one of the + * coordinate axes). + */ +#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */ +#define S_UNIT_Y 0.86052074622010633 +#else +#define S_UNIT_X 1.0 +#define S_UNIT_Y 0.0 +#endif +#endif + +/* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ +void __gl_projectPolygon( GLUtesselator *tess ) +{ + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLdouble norm[3]; + GLdouble *sUnit, *tUnit; + int i, computedNormal = FALSE; + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm ); + computedNormal = TRUE; + } + sUnit = tess->sUnit; + tUnit = tess->tUnit; + i = LongAxis( norm ); + +#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT) + /* Choose the initial sUnit vector to be approximately perpendicular + * to the normal. + */ + Normalize( norm ); + + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + /* Now make it exactly perpendicular */ + w = Dot( sUnit, norm ); + sUnit[0] -= w * norm[0]; + sUnit[1] -= w * norm[1]; + sUnit[2] -= w * norm[2]; + Normalize( sUnit ); + + /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */ + tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1]; + tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2]; + tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0]; + Normalize( tUnit ); +#else + /* Project perpendicular to a coordinate axis -- better numerically */ + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + tUnit[i] = 0; + tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y; + tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X; +#endif + + /* Project the vertices onto the sweep plane */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->s = Dot( v->coords, sUnit ); + v->t = Dot( v->coords, tUnit ); + } + if( computedNormal ) { + CheckOrientation( tess ); + } +} diff --git a/cogl/cogl-path/tesselator/normal.h b/cogl/cogl-path/tesselator/normal.h new file mode 100644 index 000000000..c376ca445 --- /dev/null +++ b/cogl/cogl-path/tesselator/normal.h @@ -0,0 +1,45 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __normal_h_ +#define __normal_h_ + +#include "tess.h" + +/* __gl_projectPolygon( tess ) determines the polygon normal + * and project vertices onto the plane of the polygon. + */ +void __gl_projectPolygon( GLUtesselator *tess ); + +#endif diff --git a/cogl/cogl-path/tesselator/priorityq-heap.c b/cogl/cogl-path/tesselator/priorityq-heap.c new file mode 100644 index 000000000..52698b59c --- /dev/null +++ b/cogl/cogl-path/tesselator/priorityq-heap.c @@ -0,0 +1,256 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include +#include +#include "priorityq-heap.h" +#include "memalloc.h" + +#define INIT_SIZE 32 + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef FOR_TRITE_TEST_PROGRAM +#define LEQ(x,y) (*pq->leq)(x,y) +#else +/* Violates modularity, but a little faster */ +#include "geom.h" +#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y) +#endif + +/* really __gl_pqHeapNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->size = 0; + pq->max = INIT_SIZE; + pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) ); + if (pq->nodes == NULL) { + memFree(pq); + return NULL; + } + + pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) ); + if (pq->handles == NULL) { + memFree(pq->nodes); + memFree(pq); + return NULL; + } + + pq->initialized = FALSE; + pq->freeList = 0; + pq->leq = leq; + + pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */ + pq->handles[1].key = NULL; + return pq; +} + +/* really __gl_pqHeapDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + memFree( pq->handles ); + memFree( pq->nodes ); + memFree( pq ); +} + + +static void FloatDown( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hChild; + long child; + + hCurr = n[curr].handle; + for( ;; ) { + child = curr << 1; + if( child < pq->size && LEQ( h[n[child+1].handle].key, + h[n[child].handle].key )) { + ++child; + } + + assert(child <= pq->max); + + hChild = n[child].handle; + if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hChild; + h[hChild].node = curr; + curr = child; + } +} + + +static void FloatUp( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hParent; + long parent; + + hCurr = n[curr].handle; + for( ;; ) { + parent = curr >> 1; + hParent = n[parent].handle; + if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hParent; + h[hParent].node = curr; + curr = parent; + } +} + +/* really __gl_pqHeapInit */ +void pqInit( PriorityQ *pq ) +{ + long i; + + /* This method of building a heap is O(n), rather than O(n lg n). */ + + for( i = pq->size; i >= 1; --i ) { + FloatDown( pq, i ); + } + pq->initialized = TRUE; +} + +/* really __gl_pqHeapInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + PQhandle free_handle; + + curr = ++ pq->size; + if( (curr*2) > pq->max ) { + PQnode *saveNodes= pq->nodes; + PQhandleElem *saveHandles= pq->handles; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->nodes = (PQnode *)memRealloc( pq->nodes, + (size_t) + ((pq->max + 1) * sizeof( pq->nodes[0] ))); + if (pq->nodes == NULL) { + pq->nodes = saveNodes; /* restore ptr to free upon return */ + return LONG_MAX; + } + pq->handles = (PQhandleElem *)memRealloc( pq->handles, + (size_t) + ((pq->max + 1) * + sizeof( pq->handles[0] ))); + if (pq->handles == NULL) { + pq->handles = saveHandles; /* restore ptr to free upon return */ + return LONG_MAX; + } + } + + if( pq->freeList == 0 ) { + free_handle = curr; + } else { + free_handle = pq->freeList; + pq->freeList = pq->handles[free_handle].node; + } + + pq->nodes[curr].handle = free_handle; + pq->handles[free_handle].node = curr; + pq->handles[free_handle].key = keyNew; + + if( pq->initialized ) { + FloatUp( pq, curr ); + } + assert(free_handle != LONG_MAX); + return free_handle; +} + +/* really __gl_pqHeapExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hMin = n[1].handle; + PQkey min = h[hMin].key; + + if( pq->size > 0 ) { + n[1].handle = n[pq->size].handle; + h[n[1].handle].node = 1; + + h[hMin].key = NULL; + h[hMin].node = pq->freeList; + pq->freeList = hMin; + + if( -- pq->size > 0 ) { + FloatDown( pq, 1 ); + } + } + return min; +} + +/* really __gl_pqHeapDelete */ +void pqDelete( PriorityQ *pq, PQhandle hCurr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + long curr; + + assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL ); + + curr = h[hCurr].node; + n[curr].handle = n[pq->size].handle; + h[n[curr].handle].node = curr; + + if( curr <= -- pq->size ) { + if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) { + FloatDown( pq, curr ); + } else { + FloatUp( pq, curr ); + } + } + h[hCurr].key = NULL; + h[hCurr].node = pq->freeList; + pq->freeList = hCurr; +} diff --git a/cogl/cogl-path/tesselator/priorityq-heap.h b/cogl/cogl-path/tesselator/priorityq-heap.h new file mode 100644 index 000000000..dc9aaef87 --- /dev/null +++ b/cogl/cogl-path/tesselator/priorityq-heap.h @@ -0,0 +1,107 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_heap_h_ +#define __priorityq_heap_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQHeapKey +#define PQhandle PQHeapHandle +#define PriorityQ PriorityQHeap + +#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqHeapInit(pq) +#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key) +#define pqMinimum(pq) __gl_pqHeapMinimum(pq) +#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef void *PQkey; +typedef long PQhandle; +typedef struct PriorityQ PriorityQ; + +typedef struct { PQhandle handle; } PQnode; +typedef struct { PQkey key; PQhandle node; } PQhandleElem; + +struct PriorityQ { + PQnode *nodes; + PQhandleElem *handles; + long size, max; + PQhandle freeList; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +void pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + + +#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key) +#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0) + +#endif diff --git a/cogl/cogl-path/tesselator/priorityq-sort.h b/cogl/cogl-path/tesselator/priorityq-sort.h new file mode 100644 index 000000000..746cf5fa6 --- /dev/null +++ b/cogl/cogl-path/tesselator/priorityq-sort.h @@ -0,0 +1,117 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityq-heap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/cogl/cogl-path/tesselator/priorityq.c b/cogl/cogl-path/tesselator/priorityq.c new file mode 100644 index 000000000..db7cd5951 --- /dev/null +++ b/cogl/cogl-path/tesselator/priorityq.c @@ -0,0 +1,261 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include /* LONG_MAX */ +#include "memalloc.h" + +/* Include all the code for the regular heap-based queue here. */ + +#include "priorityq-heap.c" + +/* Now redefine all the function names to map to their "Sort" versions. */ + +#include "priorityq-sort.h" + +/* really __gl_pqSortNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->heap = __gl_pqHeapNewPriorityQ( leq ); + if (pq->heap == NULL) { + memFree(pq); + return NULL; + } + + pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) ); + if (pq->keys == NULL) { + __gl_pqHeapDeletePriorityQ(pq->heap); + memFree(pq); + return NULL; + } + + pq->order = NULL; + pq->size = 0; + pq->max = INIT_SIZE; + pq->initialized = FALSE; + pq->leq = leq; + return pq; +} + +/* really __gl_pqSortDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + assert(pq != NULL); + if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap ); + if (pq->order != NULL) memFree( pq->order ); + if (pq->keys != NULL) memFree( pq->keys ); + memFree( pq ); +} + + +#define LT(x,y) (! LEQ(y,x)) +#define GT(x,y) (! LEQ(x,y)) +#define Swap(a,b) do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0) + +/* really __gl_pqSortInit */ +int pqInit( PriorityQ *pq ) +{ + PQkey **p, **r, **i, **j, *piv; + struct { PQkey **p, **r; } Stack[50], *top = Stack; + unsigned long seed = 2016473283; + + /* Create an array of indirect pointers to the keys, so that we + * the handles we have returned are still valid. + */ +/* + pq->order = (PQHeapKey **)memAlloc( (size_t) + (pq->size * sizeof(pq->order[0])) ); +*/ + pq->order = (PQHeapKey **)memAlloc( (size_t) + ((pq->size+1) * sizeof(pq->order[0])) ); +/* the previous line is a patch to compensate for the fact that IBM */ +/* machines return a null on a malloc of zero bytes (unlike SGI), */ +/* so we have to put in this defense to guard against a memory */ +/* fault four lines down. from fossum@austin.ibm.com. */ + if (pq->order == NULL) return 0; + + p = pq->order; + r = p + pq->size - 1; + for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) { + *i = piv; + } + + /* Sort the indirect pointers in descending order, + * using randomized Quicksort + */ + top->p = p; top->r = r; ++top; + while( --top >= Stack ) { + p = top->p; + r = top->r; + while( r > p + 10 ) { + seed = seed * 1539415821 + 1; + i = p + seed % (r - p + 1); + piv = *i; + *i = *p; + *p = piv; + i = p - 1; + j = r + 1; + do { + do { ++i; } while( GT( **i, *piv )); + do { --j; } while( LT( **j, *piv )); + Swap( i, j ); + } while( i < j ); + Swap( i, j ); /* Undo last swap */ + if( i - p < r - j ) { + top->p = j+1; top->r = r; ++top; + r = i-1; + } else { + top->p = p; top->r = i-1; ++top; + p = j+1; + } + } + /* Insertion sort small lists */ + for( i = p+1; i <= r; ++i ) { + piv = *i; + for( j = i; j > p && LT( **(j-1), *piv ); --j ) { + *j = *(j-1); + } + *j = piv; + } + } + pq->max = pq->size; + pq->initialized = TRUE; + __gl_pqHeapInit( pq->heap ); /* always succeeds */ + +#ifndef NDEBUG + p = pq->order; + r = p + pq->size - 1; + for( i = p; i < r; ++i ) { + assert( LEQ( **(i+1), **i )); + } +#endif + + return 1; +} + +/* really __gl_pqSortInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + + if( pq->initialized ) { + return __gl_pqHeapInsert( pq->heap, keyNew ); + } + curr = pq->size; + if( ++ pq->size >= pq->max ) { + PQkey *saveKey= pq->keys; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->keys = (PQHeapKey *)memRealloc( pq->keys, + (size_t) + (pq->max * sizeof( pq->keys[0] ))); + if (pq->keys == NULL) { + pq->keys = saveKey; /* restore ptr to free upon return */ + return LONG_MAX; + } + } + assert(curr != LONG_MAX); + pq->keys[curr] = keyNew; + + /* Negative handles index the sorted array. */ + return -(curr+1); +} + +/* really __gl_pqSortExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapExtractMin( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return __gl_pqHeapExtractMin( pq->heap ); + } + } + do { + -- pq->size; + } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ); + return sortMin; +} + +/* really __gl_pqSortMinimum */ +PQkey pqMinimum( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapMinimum( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return heapMin; + } + } + return sortMin; +} + +/* really __gl_pqSortIsEmpty */ +int pqIsEmpty( PriorityQ *pq ) +{ + return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap ); +} + +/* really __gl_pqSortDelete */ +void pqDelete( PriorityQ *pq, PQhandle curr ) +{ + if( curr >= 0 ) { + __gl_pqHeapDelete( pq->heap, curr ); + return; + } + curr = -(curr+1); + assert( curr < pq->max && pq->keys[curr] != NULL ); + + pq->keys[curr] = NULL; + while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) { + -- pq->size; + } +} diff --git a/cogl/cogl-path/tesselator/priorityq.h b/cogl/cogl-path/tesselator/priorityq.h new file mode 100644 index 000000000..746cf5fa6 --- /dev/null +++ b/cogl/cogl-path/tesselator/priorityq.h @@ -0,0 +1,117 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityq-heap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/cogl/cogl-path/tesselator/render.c b/cogl/cogl-path/tesselator/render.c new file mode 100644 index 000000000..bca836f04 --- /dev/null +++ b/cogl/cogl-path/tesselator/render.c @@ -0,0 +1,502 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "tess.h" +#include "render.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* This structure remembers the information we need about a primitive + * to be able to render it later, once we have determined which + * primitive is able to use the most triangles. + */ +struct FaceCount { + long size; /* number of triangles used */ + GLUhalfEdge *eStart; /* edge where this primitive starts */ + void (*render)(GLUtesselator *, GLUhalfEdge *, long); + /* routine to render this primitive */ +}; + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ); +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ); + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart, + long size ); + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ); +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head ); + + + +/************************ Strips and Fans decomposition ******************/ + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + + /* Make a list of separate triangles so we can render them all at once */ + tess->lonelyTriList = NULL; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + f->marked = FALSE; + } + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + + /* We examine all faces in an arbitrary order. Whenever we find + * an unprocessed face F, we output a group of faces including F + * whose size is maximum. + */ + if( f->inside && ! f->marked ) { + RenderMaximumFaceGroup( tess, f ); + assert( f->marked ); + } + } + if( tess->lonelyTriList != NULL ) { + RenderLonelyTriangles( tess, tess->lonelyTriList ); + tess->lonelyTriList = NULL; + } +} + + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ) +{ + /* We want to find the largest triangle fan or strip of unmarked faces + * which includes the given face fOrig. There are 3 possible fans + * passing through fOrig (one centered at each vertex), and 3 possible + * strips (one for each CCW permutation of the vertices). Our strategy + * is to try all of these, and take the primitive which uses the most + * triangles (a greedy approach). + */ + GLUhalfEdge *e = fOrig->anEdge; + struct FaceCount max, newFace; + + max.size = 1; + max.eStart = e; + max.render = &RenderTriangle; + + if( ! tess->flagBoundary ) { + newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + + newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + } + (*(max.render))( tess, max.eStart, max.size ); +} + + +/* Macros which keep track of faces we have marked temporarily, and allow + * us to backtrack when necessary. With triangle fans, this is not + * really necessary, since the only awkward case is a loop of triangles + * around a single origin vertex. However with strips the situation is + * more complicated, and we need a general tracking method like the + * one here. + */ +#define Marked(f) (! (f)->inside || (f)->marked) + +#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE) + +#define FreeTrail(t) do { \ + while( (t) != NULL ) { \ + (t)->marked = FALSE; t = (t)->trail; \ + } \ + } while(0) /* absorb trailing semicolon */ + + + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ) +{ + /* eOrig->Lface is the face we want to render. We want to find the size + * of a maximal fan around eOrig->Org. To do this we just walk around + * the origin vertex as far as possible in both directions. + */ + struct FaceCount newFace = { 0, NULL, &RenderFan }; + GLUface *trail = NULL; + GLUhalfEdge *e; + + for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++newFace.size; + } + for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) { + AddToTrail( e->Rface, trail ); + ++newFace.size; + } + newFace.eStart = e; + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +#define IsEven(n) (((n) & 1) == 0) + +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ) +{ + /* Here we are looking for a maximal strip that contains the vertices + * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the + * reverse, such that all triangles are oriented CCW). + * + * Again we walk forward and backward as far as possible. However for + * strips there is a twist: to get CCW orientations, there must be + * an *even* number of triangles in the strip on one side of eOrig. + * We walk the strip starting on a side with an even number of triangles; + * if both side have an odd number, we are forced to shorten one side. + */ + struct FaceCount newFace = { 0, NULL, &RenderStrip }; + long headSize = 0, tailSize = 0; + GLUface *trail = NULL; + GLUhalfEdge *e, *eTail, *eHead; + + for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++tailSize; + e = e->Dprev; + if( Marked( e->Lface )) break; + AddToTrail( e->Lface, trail ); + } + eTail = e; + + for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) { + AddToTrail( e->Rface, trail ); + ++headSize; + e = e->Oprev; + if( Marked( e->Rface )) break; + AddToTrail( e->Rface, trail ); + } + eHead = e; + + newFace.size = tailSize + headSize; + if( IsEven( tailSize )) { + newFace.eStart = eTail->Sym; + } else if( IsEven( headSize )) { + newFace.eStart = eHead; + } else { + /* Both sides have odd length, we must shorten one of them. In fact, + * we must start from eHead to guarantee inclusion of eOrig->Lface. + */ + --newFace.size; + newFace.eStart = eHead->Onext; + } + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Just add the triangle to a triangle list, so we can render all + * the separate triangles at once. + */ + assert( size == 1 ); + AddToTrail( e->Lface, tess->lonelyTriList ); +} + + +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f ) +{ + /* Now we render all the separate triangles which could not be + * grouped into a triangle fan or strip. + */ + GLUhalfEdge *e; + int newState; + int edgeState = -1; /* force edge state output for first vertex */ + + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES ); + + for( ; f != NULL; f = f->trail ) { + /* Loop once for each edge (there will always be 3 edges) */ + + e = f->anEdge; + do { + if( tess->flagBoundary ) { + /* Set the "edge state" to TRUE just before we output the + * first vertex of each edge on the polygon boundary. + */ + newState = ! e->Rface->inside; + if( edgeState != newState ) { + edgeState = newState; + CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState ); + } + } + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + + e = e->Lnext; + } while( e != f->anEdge ); + } + CALL_END_OR_END_DATA(); +} + + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a fan starting from + * edge "e". The fan *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a strip starting from + * edge "e". The strip *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Dprev; + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + if( Marked( e->Lface )) break; + + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +/************************ Boundary contour decomposition ******************/ + +/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one + * contour for each face marked "inside". The rendering output is + * provided as callbacks (see the api). + */ +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + GLUhalfEdge *e; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + if( f->inside ) { + CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP ); + e = f->anEdge; + do { + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + e = e->Lnext; + } while( e != f->anEdge ); + CALL_END_OR_END_DATA(); + } + } +} + + +/************************ Quick-and-dirty decomposition ******************/ + +#define SIGN_INCONSISTENT 2 + +static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check ) +/* + * If check==FALSE, we compute the polygon normal and place it in norm[]. + * If check==TRUE, we check that each triangle in the fan from v0 has a + * consistent orientation with respect to norm[]. If triangles are + * consistently oriented CCW, return 1; if CW, return -1; if all triangles + * are degenerate return 0; otherwise (no consistent orientation) return + * SIGN_INCONSISTENT. + */ +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble dot, xc, yc, zc, xp, yp, zp, n[3]; + int sign = 0; + + /* Find the polygon normal. It is important to get a reasonable + * normal even when the polygon is self-intersecting (eg. a bowtie). + * Otherwise, the computed normal could be very tiny, but perpendicular + * to the true plane of the polygon due to numerical noise. Then all + * the triangles would appear to be degenerate and we would incorrectly + * decompose the polygon as a fan (or simply not render it at all). + * + * We use a sum-of-triangles normal algorithm rather than the more + * efficient sum-of-trapezoids method (used in CheckOrientation() + * in normal.c). This lets us explicitly reverse the signed area + * of some triangles to get a reasonable normal in the self-intersecting + * case. + */ + if( ! check ) { + norm[0] = norm[1] = norm[2] = 0.0; + } + + vc = v0 + 1; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + while( ++vc < vn ) { + xp = xc; yp = yc; zp = zc; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + + /* Compute (vp - v0) cross (vc - v0) */ + n[0] = yp*zc - zp*yc; + n[1] = zp*xc - xp*zc; + n[2] = xp*yc - yp*xc; + + dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2]; + if( ! check ) { + /* Reverse the contribution of back-facing triangles to get + * a reasonable normal for self-intersecting polygons (see above) + */ + if( dot >= 0 ) { + norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2]; + } else { + norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2]; + } + } else if( dot != 0 ) { + /* Check the new orientation for consistency with previous triangles */ + if( dot > 0 ) { + if( sign < 0 ) return SIGN_INCONSISTENT; + sign = 1; + } else { + if( sign > 0 ) return SIGN_INCONSISTENT; + sign = -1; + } + } + } + return sign; +} + +/* __gl_renderCache( tess ) takes a single contour and tries to render it + * as a triangle fan. This handles convex polygons, as well as some + * non-convex polygons if we get lucky. + * + * Returns TRUE if the polygon was successfully rendered. The rendering + * output is provided as callbacks (see the api). + */ +GLboolean __gl_renderCache( GLUtesselator *tess ) +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble norm[3]; + int sign; + + if( tess->cacheCount < 3 ) { + /* Degenerate contour -- no output */ + return TRUE; + } + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm, FALSE ); + } + + sign = ComputeNormal( tess, norm, TRUE ); + if( sign == SIGN_INCONSISTENT ) { + /* Fan triangles did not have a consistent orientation */ + return FALSE; + } + if( sign == 0 ) { + /* All triangles were degenerate */ + return TRUE; + } + + /* Make sure we do the right thing for each winding rule */ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + break; + case GLU_TESS_WINDING_POSITIVE: + if( sign < 0 ) return TRUE; + break; + case GLU_TESS_WINDING_NEGATIVE: + if( sign > 0 ) return TRUE; + break; + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return TRUE; + } + + CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP + : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN + : GL_TRIANGLES ); + + CALL_VERTEX_OR_VERTEX_DATA( v0->data ); + if( sign > 0 ) { + for( vc = v0+1; vc < vn; ++vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } else { + for( vc = vn-1; vc > v0; --vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } + CALL_END_OR_END_DATA(); + return TRUE; +} diff --git a/cogl/cogl-path/tesselator/render.h b/cogl/cogl-path/tesselator/render.h new file mode 100644 index 000000000..a298c9a94 --- /dev/null +++ b/cogl/cogl-path/tesselator/render.h @@ -0,0 +1,52 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __render_h_ +#define __render_h_ + +#include "mesh.h" + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ); +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ); + +GLboolean __gl_renderCache( GLUtesselator *tess ); + +#endif diff --git a/cogl/cogl-path/tesselator/sweep.c b/cogl/cogl-path/tesselator/sweep.c new file mode 100644 index 000000000..eca828ff6 --- /dev/null +++ b/cogl/cogl-path/tesselator/sweep.c @@ -0,0 +1,1361 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include /* longjmp */ +#include /* LONG_MAX */ + +#include "mesh.h" +#include "geom.h" +#include "tess.h" +#include "dict.h" +#include "priorityq.h" +#include "memalloc.h" +#include "sweep.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef FOR_TRITE_TEST_PROGRAM +extern void DebugEvent( GLUtesselator *tess ); +#else +#define DebugEvent( tess ) +#endif + +/* + * Invariants for the Edge Dictionary. + * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) + * at any valid location of the sweep event + * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 + * share a common endpoint + * - for each e, e->Dst has been processed, but not e->Org + * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org) + * where "event" is the current sweep line event. + * - no edge e has zero length + * + * Invariants for the Mesh (the processed portion). + * - the portion of the mesh left of the sweep line is a planar graph, + * ie. there is *some* way to embed it in the plane + * - no processed edge has zero length + * - no two processed vertices have identical coordinates + * - each "inside" region is monotone, ie. can be broken into two chains + * of monotonically increasing vertices according to VertLeq(v1,v2) + * - a non-invariant: these chains may intersect (very slightly) + * + * Invariants for the Sweep. + * - if none of the edges incident to the event vertex have an activeRegion + * (ie. none of these edges are in the edge dictionary), then the vertex + * has only right-going edges. + * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced + * by ConnectRightVertex), then it is the only right-going edge from + * its associated vertex. (This says that these edges exist only + * when it is necessary.) + */ + +#undef MAX +#undef MIN +#define MAX(x,y) ((x) >= (y) ? (x) : (y)) +#define MIN(x,y) ((x) <= (y) ? (x) : (y)) + +/* When we merge two edges into one, we need to compute the combined + * winding of the new edge. + */ +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ); +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ); +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ); + +static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, + ActiveRegion *reg2 ) +/* + * Both edges must be directed from right to left (this is the canonical + * direction for the upper edge of each region). + * + * The strategy is to evaluate a "t" value for each edge at the + * current sweep line position, given by tess->event. The calculations + * are designed to be very stable, but of course they are not perfect. + * + * Special case: if both edge destinations are at the sweep event, + * we sort the edges by slope (they would otherwise compare equally). + */ +{ + GLUvertex *event = tess->event; + GLUhalfEdge *e1, *e2; + GLdouble t1, t2; + + e1 = reg1->eUp; + e2 = reg2->eUp; + + if( e1->Dst == event ) { + if( e2->Dst == event ) { + /* Two edges right of the sweep line which meet at the sweep event. + * Sort them by slope. + */ + if( VertLeq( e1->Org, e2->Org )) { + return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; + } + return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; + } + return EdgeSign( e2->Dst, event, e2->Org ) <= 0; + } + if( e2->Dst == event ) { + return EdgeSign( e1->Dst, event, e1->Org ) >= 0; + } + + /* General case - compute signed distance *from* e1, e2 to event */ + t1 = EdgeEval( e1->Dst, event, e1->Org ); + t2 = EdgeEval( e2->Dst, event, e2->Org ); + return (t1 >= t2); +} + + +static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg ) +{ + if( reg->fixUpperEdge ) { + /* It was created with zero winding number, so it better be + * deleted with zero winding number (ie. it better not get merged + * with a real edge). + */ + assert( reg->eUp->winding == 0 ); + } + reg->eUp->activeRegion = NULL; + dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */ + memFree( reg ); +} + + +static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge ) +/* + * Replace an upper edge which needs fixing (see ConnectRightVertex). + */ +{ + assert( reg->fixUpperEdge ); + if ( !__gl_meshDelete( reg->eUp ) ) return 0; + reg->fixUpperEdge = FALSE; + reg->eUp = newEdge; + newEdge->activeRegion = reg; + + return 1; +} + +static ActiveRegion *TopLeftRegion( ActiveRegion *reg ) +{ + GLUvertex *org = reg->eUp->Org; + GLUhalfEdge *e; + + /* Find the region above the uppermost edge with the same origin */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Org == org ); + + /* If the edge above was a temporary edge introduced by ConnectRightVertex, + * now is the time to fix it. + */ + if( reg->fixUpperEdge ) { + e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); + if (e == NULL) return NULL; + if ( !FixUpperEdge( reg, e ) ) return NULL; + reg = RegionAbove( reg ); + } + return reg; +} + +static ActiveRegion *TopRightRegion( ActiveRegion *reg ) +{ + GLUvertex *dst = reg->eUp->Dst; + + /* Find the region above the uppermost edge with the same destination */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Dst == dst ); + return reg; +} + +static ActiveRegion *AddRegionBelow( GLUtesselator *tess, + ActiveRegion *regAbove, + GLUhalfEdge *eNewUp ) +/* + * Add a new active region to the sweep line, *somewhere* below "regAbove" + * (according to where the new edge belongs in the sweep-line dictionary). + * The upper edge of the new region will be "eNewUp". + * Winding number and "inside" flag are not updated. + */ +{ + ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (regNew == NULL) longjmp(tess->env,1); + + regNew->eUp = eNewUp; + /* __gl_dictListInsertBefore */ + regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew ); + if (regNew->nodeUp == NULL) longjmp(tess->env,1); + regNew->fixUpperEdge = FALSE; + regNew->sentinel = FALSE; + regNew->dirty = FALSE; + + eNewUp->activeRegion = regNew; + return regNew; +} + +static GLboolean IsWindingInside( GLUtesselator *tess, int n ) +{ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + return (n & 1); + case GLU_TESS_WINDING_NONZERO: + return (n != 0); + case GLU_TESS_WINDING_POSITIVE: + return (n > 0); + case GLU_TESS_WINDING_NEGATIVE: + return (n < 0); + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return (n >= 2) || (n <= -2); + } + /*LINTED*/ + assert( FALSE ); + /*NOTREACHED*/ + return GL_FALSE; /* avoid compiler complaints */ +} + + +static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg ) +{ + reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); +} + + +static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg ) +/* + * Delete a region from the sweep line. This happens when the upper + * and lower chains of a region meet (at a vertex on the sweep line). + * The "inside" flag is copied to the appropriate mesh face (we could + * not do this before -- since the structure of the mesh is always + * changing, this face may not have even existed until now). + */ +{ + GLUhalfEdge *e = reg->eUp; + GLUface *f = e->Lface; + + f->inside = reg->inside; + f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ + DeleteRegion( tess, reg ); +} + + +static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, + ActiveRegion *regFirst, ActiveRegion *regLast ) +/* + * We are given a vertex with one or more left-going edges. All affected + * edges should be in the edge dictionary. Starting at regFirst->eUp, + * we walk down deleting all regions where both edges have the same + * origin vOrg. At the same time we copy the "inside" flag from the + * active region to the face, since at this point each face will belong + * to at most one region (this was not necessarily true until this point + * in the sweep). The walk stops at the region above regLast; if regLast + * is NULL we walk as far as possible. At the same time we relink the + * mesh if necessary, so that the ordering of edges around vOrg is the + * same as in the dictionary. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + + regPrev = regFirst; + ePrev = regFirst->eUp; + while( regPrev != regLast ) { + regPrev->fixUpperEdge = FALSE; /* placement was OK */ + reg = RegionBelow( regPrev ); + e = reg->eUp; + if( e->Org != ePrev->Org ) { + if( ! reg->fixUpperEdge ) { + /* Remove the last left-going edge. Even though there are no further + * edges in the dictionary with this origin, there may be further + * such edges in the mesh (if we are adding left edges to a vertex + * that has already been processed). Thus it is important to call + * FinishRegion rather than just DeleteRegion. + */ + FinishRegion( tess, regPrev ); + break; + } + /* If the edge below was a temporary edge introduced by + * ConnectRightVertex, now is the time to fix it. + */ + e = __gl_meshConnect( ePrev->Lprev, e->Sym ); + if (e == NULL) longjmp(tess->env,1); + if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); + } + + /* Relink edges so that ePrev->Onext == e */ + if( ePrev->Onext != e ) { + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); + } + FinishRegion( tess, regPrev ); /* may change reg->eUp */ + ePrev = reg->eUp; + regPrev = reg; + } + return ePrev; +} + + +static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft, + GLboolean cleanUp ) +/* + * Purpose: insert right-going edges into the edge dictionary, and update + * winding numbers and mesh connectivity appropriately. All right-going + * edges share a common origin vOrg. Edges are inserted CCW starting at + * eFirst; the last edge inserted is eLast->Oprev. If vOrg has any + * left-going edges already processed, then eTopLeft must be the edge + * such that an imaginary upward vertical segment from vOrg would be + * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft + * should be NULL. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + int firstTime = TRUE; + + /* Insert the new right-going edges in the dictionary */ + e = eFirst; + do { + assert( VertLeq( e->Org, e->Dst )); + AddRegionBelow( tess, regUp, e->Sym ); + e = e->Onext; + } while ( e != eLast ); + + /* Walk *all* right-going edges from e->Org, in the dictionary order, + * updating the winding numbers of each region, and re-linking the mesh + * edges to match the dictionary ordering (if necessary). + */ + if( eTopLeft == NULL ) { + eTopLeft = RegionBelow( regUp )->eUp->Rprev; + } + regPrev = regUp; + ePrev = eTopLeft; + for( ;; ) { + reg = RegionBelow( regPrev ); + e = reg->eUp->Sym; + if( e->Org != ePrev->Org ) break; + + if( e->Onext != ePrev ) { + /* Unlink e from its current position, and relink below ePrev */ + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1); + } + /* Compute the winding number and "inside" flag for the new regions */ + reg->windingNumber = regPrev->windingNumber - e->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); + + /* Check for two outgoing edges with same slope -- process these + * before any intersection tests (see example in __gl_computeInterior). + */ + regPrev->dirty = TRUE; + if( ! firstTime && CheckForRightSplice( tess, regPrev )) { + AddWinding( e, ePrev ); + DeleteRegion( tess, regPrev ); + if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1); + } + firstTime = FALSE; + regPrev = reg; + ePrev = e; + } + regPrev->dirty = TRUE; + assert( regPrev->windingNumber - e->winding == reg->windingNumber ); + + if( cleanUp ) { + /* Check for intersections between newly adjacent edges. */ + WalkDirtyRegions( tess, regPrev ); + } +} + + +static void CallCombine( GLUtesselator *tess, GLUvertex *isect, + void *data[4], GLfloat weights[4], int needed ) +{ + GLdouble coords[3]; + + /* Copy coord data in case the callback changes it. */ + coords[0] = isect->coords[0]; + coords[1] = isect->coords[1]; + coords[2] = isect->coords[2]; + + isect->data = NULL; + CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data ); + if( isect->data == NULL ) { + if( ! needed ) { + isect->data = data[0]; + } else if( ! tess->fatalError ) { + /* The only way fatal error is when two edges are found to intersect, + * but the user has not provided the callback necessary to handle + * generated intersection points. + */ + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK ); + tess->fatalError = TRUE; + } + } +} + +static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1, + GLUhalfEdge *e2 ) +/* + * Two vertices with idential coordinates are combined into one. + * e1->Org is kept, while e2->Org is discarded. + */ +{ + void *data[4] = { NULL, NULL, NULL, NULL }; + GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 }; + + data[0] = e1->Org->data; + data[1] = e2->Org->data; + CallCombine( tess, e1->Org, data, weights, FALSE ); + if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1); +} + +static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst, + GLfloat *weights ) +/* + * Find some weights which describe how the intersection vertex is + * a linear combination of "org" and "dest". Each of the two edges + * which generated "isect" is allocated 50% of the weight; each edge + * splits the weight between its org and dst according to the + * relative distance to "isect". + */ +{ + GLdouble t1 = VertL1dist( org, isect ); + GLdouble t2 = VertL1dist( dst, isect ); + + weights[0] = 0.5 * t2 / (t1 + t2); + weights[1] = 0.5 * t1 / (t1 + t2); + isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0]; + isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1]; + isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2]; +} + + +static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect, + GLUvertex *orgUp, GLUvertex *dstUp, + GLUvertex *orgLo, GLUvertex *dstLo ) +/* + * We've computed a new intersection point, now we need a "data" pointer + * from the user so that we can refer to this new vertex in the + * rendering callbacks. + */ +{ + void *data[4]; + GLfloat weights[4]; + + data[0] = orgUp->data; + data[1] = dstUp->data; + data[2] = orgLo->data; + data[3] = dstLo->data; + + isect->coords[0] = isect->coords[1] = isect->coords[2] = 0; + VertexWeights( isect, orgUp, dstUp, &weights[0] ); + VertexWeights( isect, orgLo, dstLo, &weights[2] ); + + CallCombine( tess, isect, data, weights, TRUE ); +} + +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which + * origin is leftmost). + * + * The main purpose is to splice right-going edges with the same + * dest vertex and nearly identical slopes (ie. we can't distinguish + * the slopes numerically). However the splicing can also help us + * to recover from numerical errors. For example, suppose at one + * point we checked eUp and eLo, and decided that eUp->Org is barely + * above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * our test so that now eUp->Org is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants. + * + * One possibility is to check these edges for intersection again + * (ie. CheckForIntersect). This is what we do if possible. However + * CheckForIntersect requires that tess->event lies between eUp and eLo, + * so that it has something to fall back on when the intersection + * calculation gives us an unusable answer. So, for those cases where + * we can't check for intersection, this routine fixes the problem + * by just splicing the offending vertex into the other edge. + * This is a guaranteed solution, no matter how degenerate things get. + * Basically this is a combinatorial solution to a numerical problem. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + + if( VertLeq( eUp->Org, eLo->Org )) { + if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Org appears to be below eLo */ + if( ! VertEq( eUp->Org, eLo->Org )) { + /* Splice eUp->Org into eLo */ + if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1); + regUp->dirty = regLo->dirty = TRUE; + + } else if( eUp->Org != eLo->Org ) { + /* merge the two vertices, discarding eUp->Org */ + pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */ + SpliceMergeVertices( tess, eLo->Oprev, eUp ); + } + } else { + if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + } + return TRUE; +} + +static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which + * destination is rightmost). + * + * Theoretically, this should always be true. However, splitting an edge + * into two pieces can change the results of previous tests. For example, + * suppose at one point we checked eUp and eLo, and decided that eUp->Dst + * is barely above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * the test so that now eUp->Dst is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants + * (otherwise new edges might get inserted in the wrong place in the + * dictionary, and bad stuff will happen). + * + * We fix the problem by just splicing the offending vertex into the + * other edge. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUhalfEdge *e; + + assert( ! VertEq( eUp->Dst, eLo->Dst )); + + if( VertLeq( eUp->Dst, eLo->Dst )) { + if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + e = __gl_meshSplitEdge( eUp ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1); + e->Lface->inside = regUp->inside; + } else { + if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */ + regUp->dirty = regLo->dirty = TRUE; + e = __gl_meshSplitEdge( eLo ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1); + e->Rface->inside = regUp->inside; + } + return TRUE; +} + + +static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edges of the given region to see if + * they intersect. If so, create the intersection and add it + * to the data structures. + * + * Returns TRUE if adding the new intersection resulted in a recursive + * call to AddRightEdges(); in this case all "dirty" regions have been + * checked for intersections, and possibly regUp has been deleted. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUvertex *orgUp = eUp->Org; + GLUvertex *orgLo = eLo->Org; + GLUvertex *dstUp = eUp->Dst; + GLUvertex *dstLo = eLo->Dst; + GLdouble tMinUp, tMaxLo; + GLUvertex isect, *orgMin; + GLUhalfEdge *e; + + assert( ! VertEq( dstLo, dstUp )); + assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 ); + assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 ); + assert( orgUp != tess->event && orgLo != tess->event ); + assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge ); + + if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */ + + tMinUp = MIN( orgUp->t, dstUp->t ); + tMaxLo = MAX( orgLo->t, dstLo->t ); + if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */ + + if( VertLeq( orgUp, orgLo )) { + if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE; + } else { + if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE; + } + + /* At this point the edges intersect, at least marginally */ + DebugEvent( tess ); + + __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect ); + /* The following properties are guaranteed: */ + assert( MIN( orgUp->t, dstUp->t ) <= isect.t ); + assert( isect.t <= MAX( orgLo->t, dstLo->t )); + assert( MIN( dstLo->s, dstUp->s ) <= isect.s ); + assert( isect.s <= MAX( orgLo->s, orgUp->s )); + + if( VertLeq( &isect, tess->event )) { + /* The intersection point lies slightly to the left of the sweep line, + * so move it until it''s slightly to the right of the sweep line. + * (If we had perfect numerical precision, this would never happen + * in the first place). The easiest and safest thing to do is + * replace the intersection by tess->event. + */ + isect.s = tess->event->s; + isect.t = tess->event->t; + } + /* Similarly, if the computed intersection lies to the right of the + * rightmost origin (which should rarely happen), it can cause + * unbelievable inefficiency on sufficiently degenerate inputs. + * (If you have the test program, try running test54.d with the + * "X zoom" option turned on). + */ + orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo; + if( VertLeq( orgMin, &isect )) { + isect.s = orgMin->s; + isect.t = orgMin->t; + } + + if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) { + /* Easy case -- intersection at one of the right endpoints */ + (void) CheckForRightSplice( tess, regUp ); + return FALSE; + } + + if( (! VertEq( dstUp, tess->event ) + && EdgeSign( dstUp, tess->event, &isect ) >= 0) + || (! VertEq( dstLo, tess->event ) + && EdgeSign( dstLo, tess->event, &isect ) <= 0 )) + { + /* Very unusual -- the new upper or lower edge would pass on the + * wrong side of the sweep event, or through it. This can happen + * due to very small numerical errors in the intersection calculation. + */ + if( dstLo == tess->event ) { + /* Splice dstLo into eUp, and process the new region(s) */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eUp = RegionBelow(regUp)->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE ); + return TRUE; + } + if( dstUp == tess->event ) { + /* Splice dstUp into eLo, and process the new region(s) */ + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1); + regLo = regUp; + regUp = TopRightRegion( regUp ); + e = RegionBelow(regUp)->eUp->Rprev; + regLo->eUp = eLo->Oprev; + eLo = FinishLeftRegions( tess, regLo, NULL ); + AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE ); + return TRUE; + } + /* Special case: called from ConnectRightVertex. If either + * edge passes on the wrong side of tess->event, split it + * (and wait for ConnectRightVertex to splice it appropriately). + */ + if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) { + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + eUp->Org->s = tess->event->s; + eUp->Org->t = tess->event->t; + } + if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) { + regUp->dirty = regLo->dirty = TRUE; + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + eLo->Org->s = tess->event->s; + eLo->Org->t = tess->event->t; + } + /* leave the rest for ConnectRightVertex */ + return FALSE; + } + + /* General case -- split both edges, splice into new vertex. + * When we do the splice operation, the order of the arguments is + * arbitrary as far as correctness goes. However, when the operation + * creates a new face, the work done is proportional to the size of + * the new face. We expect the faces in the processed part of + * the mesh (ie. eUp->Lface) to be smaller than the faces in the + * unprocessed original contours (which will be eLo->Oprev->Lface). + */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + eUp->Org->s = isect.s; + eUp->Org->t = isect.t; + eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */ + if (eUp->Org->pqHandle == LONG_MAX) { + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + longjmp(tess->env,1); + } + GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo ); + RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE; + return FALSE; +} + +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * When the upper or lower edge of any region changes, the region is + * marked "dirty". This routine walks through all the dirty regions + * and makes sure that the dictionary invariants are satisfied + * (see the comments at the beginning of this file). Of course + * new dirty regions can be created as we make changes to restore + * the invariants. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp, *eLo; + + for( ;; ) { + /* Find the lowest dirty region (we walk from the bottom up). */ + while( regLo->dirty ) { + regUp = regLo; + regLo = RegionBelow(regLo); + } + if( ! regUp->dirty ) { + regLo = regUp; + regUp = RegionAbove( regUp ); + if( regUp == NULL || ! regUp->dirty ) { + /* We've walked all the dirty regions */ + return; + } + } + regUp->dirty = FALSE; + eUp = regUp->eUp; + eLo = regLo->eUp; + + if( eUp->Dst != eLo->Dst ) { + /* Check that the edge ordering is obeyed at the Dst vertices. */ + if( CheckForLeftSplice( tess, regUp )) { + + /* If the upper or lower edge was marked fixUpperEdge, then + * we no longer need it (since these edges are needed only for + * vertices which otherwise have no right-going edges). + */ + if( regLo->fixUpperEdge ) { + DeleteRegion( tess, regLo ); + if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1); + regLo = RegionBelow( regUp ); + eLo = regLo->eUp; + } else if( regUp->fixUpperEdge ) { + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + eUp = regUp->eUp; + } + } + } + if( eUp->Org != eLo->Org ) { + if( eUp->Dst != eLo->Dst + && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge + && (eUp->Dst == tess->event || eLo->Dst == tess->event) ) + { + /* When all else fails in CheckForIntersect(), it uses tess->event + * as the intersection location. To make this possible, it requires + * that tess->event lie between the upper and lower edges, and also + * that neither of these is marked fixUpperEdge (since in the worst + * case it might splice one of these edges into tess->event, and + * violate the invariant that fixable edges are the only right-going + * edge from their associated vertex). + */ + if( CheckForIntersect( tess, regUp )) { + /* WalkDirtyRegions() was called recursively; we're done */ + return; + } + } else { + /* Even though we can't use CheckForIntersect(), the Org vertices + * may violate the dictionary edge ordering. Check and correct this. + */ + (void) CheckForRightSplice( tess, regUp ); + } + } + if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) { + /* A degenerate loop consisting of only two edges -- delete it. */ + AddWinding( eLo, eUp ); + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + } + } +} + + +static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eBottomLeft ) +/* + * Purpose: connect a "right" vertex vEvent (one where all edges go left) + * to the unprocessed portion of the mesh. Since there are no right-going + * edges, two regions (one above vEvent and one below) are being merged + * into one. "regUp" is the upper of these two regions. + * + * There are two reasons for doing this (adding a right-going edge): + * - if the two regions being merged are "inside", we must add an edge + * to keep them separated (the combined region would not be monotone). + * - in any case, we must leave some record of vEvent in the dictionary, + * so that we can merge vEvent with features that we have not seen yet. + * For example, maybe there is a vertical edge which passes just to + * the right of vEvent; we would like to splice vEvent into this edge. + * + * However, we don't want to connect vEvent to just any vertex. We don''t + * want the new edge to cross any other edges; otherwise we will create + * intersection vertices even when the input data had no self-intersections. + * (This is a bad thing; if the user's input data has no intersections, + * we don't want to generate any false intersections ourselves.) + * + * Our eventual goal is to connect vEvent to the leftmost unprocessed + * vertex of the combined region (the union of regUp and regLo). + * But because of unseen vertices with all right-going edges, and also + * new vertices which may be created by edge intersections, we don''t + * know where that leftmost unprocessed vertex is. In the meantime, we + * connect vEvent to the closest vertex of either chain, and mark the region + * as "fixUpperEdge". This flag says to delete and reconnect this edge + * to the next processed vertex on the boundary of the combined region. + * Quite possibly the vertex we connected to will turn out to be the + * closest one, in which case we won''t need to make any changes. + */ +{ + GLUhalfEdge *eNew; + GLUhalfEdge *eTopLeft = eBottomLeft->Onext; + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + int degenerate = FALSE; + + if( eUp->Dst != eLo->Dst ) { + (void) CheckForIntersect( tess, regUp ); + } + + /* Possible new degeneracies: upper or lower edge of regUp may pass + * through vEvent, or may coincide with new intersection vertex + */ + if( VertEq( eUp->Org, tess->event )) { + if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eTopLeft = RegionBelow( regUp )->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + degenerate = TRUE; + } + if( VertEq( eLo->Org, tess->event )) { + if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1); + eBottomLeft = FinishLeftRegions( tess, regLo, NULL ); + degenerate = TRUE; + } + if( degenerate ) { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + return; + } + + /* Non-degenerate situation -- need to add a temporary, fixable edge. + * Connect to the closer of eLo->Org, eUp->Org. + */ + if( VertLeq( eLo->Org, eUp->Org )) { + eNew = eLo->Oprev; + } else { + eNew = eUp; + } + eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew ); + if (eNew == NULL) longjmp(tess->env,1); + + /* Prevent cleanup, otherwise eNew might disappear before we've even + * had a chance to mark it as a temporary edge. + */ + AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE ); + eNew->Sym->activeRegion->fixUpperEdge = TRUE; + WalkDirtyRegions( tess, regUp ); +} + +/* Because vertices at exactly the same location are merged together + * before we process the sweep event, some degenerate cases can't occur. + * However if someone eventually makes the modifications required to + * merge features which are close together, the cases below marked + * TOLERANCE_NONZERO will be useful. They were debugged before the + * code to merge identical vertices in the main loop was added. + */ +#define TOLERANCE_NONZERO FALSE + +static void ConnectLeftDegenerate( GLUtesselator *tess, + ActiveRegion *regUp, GLUvertex *vEvent ) +/* + * The event vertex lies exacty on an already-processed edge or vertex. + * Adding the new vertex involves splicing it into the already-processed + * part of the mesh. + */ +{ + GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast; + ActiveRegion *reg; + + e = regUp->eUp; + if( VertEq( e->Org, vEvent )) { + /* e->Org is an unprocessed vertex - just combine them, and wait + * for e->Org to be pulled from the queue + */ + assert( TOLERANCE_NONZERO ); + SpliceMergeVertices( tess, e, vEvent->anEdge ); + return; + } + + if( ! VertEq( e->Dst, vEvent )) { + /* General case -- splice vEvent into edge e which passes through it */ + if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1); + if( regUp->fixUpperEdge ) { + /* This edge was fixable -- delete unused portion of original edge */ + if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1); + regUp->fixUpperEdge = FALSE; + } + if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1); + SweepEvent( tess, vEvent ); /* recurse */ + return; + } + + /* vEvent coincides with e->Dst, which has already been processed. + * Splice in the additional right-going edges. + */ + assert( TOLERANCE_NONZERO ); + regUp = TopRightRegion( regUp ); + reg = RegionBelow( regUp ); + eTopRight = reg->eUp->Sym; + eTopLeft = eLast = eTopRight->Onext; + if( reg->fixUpperEdge ) { + /* Here e->Dst has only a single fixable edge going right. + * We can delete it since now we have some real right-going edges. + */ + assert( eTopLeft != eTopRight ); /* there are some left edges too */ + DeleteRegion( tess, reg ); + if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1); + eTopRight = eTopLeft->Oprev; + } + if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1); + if( ! EdgeGoesLeft( eTopLeft )) { + /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */ + eTopLeft = NULL; + } + AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE ); +} + + +static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Purpose: connect a "left" vertex (one where both edges go right) + * to the processed portion of the mesh. Let R be the active region + * containing vEvent, and let U and L be the upper and lower edge + * chains of R. There are two possibilities: + * + * - the normal case: split R into two regions, by connecting vEvent to + * the rightmost vertex of U or L lying to the left of the sweep line + * + * - the degenerate case: if vEvent is close enough to U or L, we + * merge vEvent into that edge chain. The subcases are: + * - merging with the rightmost vertex of U or L + * - merging with the active edge of U or L + * - merging with an already-processed portion of U or L + */ +{ + ActiveRegion *regUp, *regLo, *reg; + GLUhalfEdge *eUp, *eLo, *eNew; + ActiveRegion tmp; + + /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */ + + /* Get a pointer to the active region containing vEvent */ + tmp.eUp = vEvent->anEdge->Sym; + /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ + regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); + regLo = RegionBelow( regUp ); + eUp = regUp->eUp; + eLo = regLo->eUp; + + /* Try merging with U or L first */ + if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) { + ConnectLeftDegenerate( tess, regUp, vEvent ); + return; + } + + /* Connect vEvent to rightmost processed vertex of either chain. + * e->Dst is the vertex that we will connect to vEvent. + */ + reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo; + + if( regUp->inside || reg->fixUpperEdge) { + if( reg == regUp ) { + eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext ); + if (eNew == NULL) longjmp(tess->env,1); + } else { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge); + if (tempHalfEdge == NULL) longjmp(tess->env,1); + + eNew = tempHalfEdge->Sym; + } + if( reg->fixUpperEdge ) { + if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1); + } else { + ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew )); + } + SweepEvent( tess, vEvent ); + } else { + /* The new vertex is in a region which does not belong to the polygon. + * We don''t need to connect this vertex to the rest of the mesh. + */ + AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE ); + } +} + + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Does everything necessary when the sweep line crosses a vertex. + * Updates the mesh and the edge dictionary. + */ +{ + ActiveRegion *regUp, *reg; + GLUhalfEdge *e, *eTopLeft, *eBottomLeft; + + tess->event = vEvent; /* for access in EdgeLeq() */ + DebugEvent( tess ); + + /* Check if this vertex is the right endpoint of an edge that is + * already in the dictionary. In this case we don't need to waste + * time searching for the location to insert new edges. + */ + e = vEvent->anEdge; + while( e->activeRegion == NULL ) { + e = e->Onext; + if( e == vEvent->anEdge ) { + /* All edges go right -- not incident to any processed edges */ + ConnectLeftVertex( tess, vEvent ); + return; + } + } + + /* Processing consists of two phases: first we "finish" all the + * active regions where both the upper and lower edges terminate + * at vEvent (ie. vEvent is closing off these regions). + * We mark these faces "inside" or "outside" the polygon according + * to their winding number, and delete the edges from the dictionary. + * This takes care of all the left-going edges from vEvent. + */ + regUp = TopLeftRegion( e->activeRegion ); + if (regUp == NULL) longjmp(tess->env,1); + reg = RegionBelow( regUp ); + eTopLeft = reg->eUp; + eBottomLeft = FinishLeftRegions( tess, reg, NULL ); + + /* Next we process all the right-going edges from vEvent. This + * involves adding the edges to the dictionary, and creating the + * associated "active regions" which record information about the + * regions between adjacent dictionary edges. + */ + if( eBottomLeft->Onext == eTopLeft ) { + /* No right-going edges -- add a temporary "fixable" edge */ + ConnectRightVertex( tess, regUp, eBottomLeft ); + } else { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + } +} + + +/* Make the sentinel coordinates big enough that they will never be + * merged with real input features. (Even with the largest possible + * input contour and the maximum tolerance of 1.0, no merging will be + * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). + */ +#define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD) + +static void AddSentinel( GLUtesselator *tess, GLdouble t ) +/* + * We add two sentinel edges above and below all other edges, + * to avoid special cases at the top and bottom. + */ +{ + GLUhalfEdge *e; + ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (reg == NULL) longjmp(tess->env,1); + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) longjmp(tess->env,1); + + e->Org->s = SENTINEL_COORD; + e->Org->t = t; + e->Dst->s = -SENTINEL_COORD; + e->Dst->t = t; + tess->event = e->Dst; /* initialize it */ + + reg->eUp = e; + reg->windingNumber = 0; + reg->inside = FALSE; + reg->fixUpperEdge = FALSE; + reg->sentinel = TRUE; + reg->dirty = FALSE; + reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */ + if (reg->nodeUp == NULL) longjmp(tess->env,1); +} + + +static void InitEdgeDict( GLUtesselator *tess ) +/* + * We maintain an ordering of edge intersections with the sweep line. + * This order is maintained in a dynamic dictionary. + */ +{ + /* __gl_dictListNewDict */ + tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq ); + if (tess->dict == NULL) longjmp(tess->env,1); + + AddSentinel( tess, -SENTINEL_COORD ); + AddSentinel( tess, SENTINEL_COORD ); +} + + +static void DoneEdgeDict( GLUtesselator *tess ) +{ + ActiveRegion *reg; +#ifndef NDEBUG + int fixedEdges = 0; +#endif + + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) { + /* + * At the end of all processing, the dictionary should contain + * only the two sentinel edges, plus at most one "fixable" edge + * created by ConnectRightVertex(). + */ + if( ! reg->sentinel ) { + assert( reg->fixUpperEdge ); + assert( ++fixedEdges == 1 ); + } + assert( reg->windingNumber == 0 ); + DeleteRegion( tess, reg ); +/* __gl_meshDelete( reg->eUp );*/ + } + dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */ +} + + +static void RemoveDegenerateEdges( GLUtesselator *tess ) +/* + * Remove zero-length edges, and contours with fewer than 3 vertices. + */ +{ + GLUhalfEdge *e, *eNext, *eLnext; + GLUhalfEdge *eHead = &tess->mesh->eHead; + + /*LINTED*/ + for( e = eHead->next; e != eHead; e = eNext ) { + eNext = e->next; + eLnext = e->Lnext; + + if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) { + /* Zero-length edge, contour has at least 3 edges */ + + SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */ + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */ + e = eLnext; + eLnext = e->Lnext; + } + if( eLnext->Lnext == e ) { + /* Degenerate contour (one or two edges) */ + + if( eLnext != e ) { + if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1); + } + if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); + } + } +} + +static int InitPriorityQ( GLUtesselator *tess ) +/* + * Insert all vertices into the priority queue which determines the + * order in which vertices cross the sweep line. + */ +{ + PriorityQ *pq; + GLUvertex *v, *vHead; + + /* __gl_pqSortNewPriorityQ */ + pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq ); + if (pq == NULL) return 0; + + vHead = &tess->mesh->vHead; + for( v = vHead->next; v != vHead; v = v->next ) { + v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */ + if (v->pqHandle == LONG_MAX) break; + } + if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */ + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + return 0; + } + + return 1; +} + + +static void DonePriorityQ( GLUtesselator *tess ) +{ + pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */ +} + + +static int RemoveDegenerateFaces( GLUmesh *mesh ) +/* + * Delete any degenerate faces with only two edges. WalkDirtyRegions() + * will catch almost all of these, but it won't catch degenerate faces + * produced by splice operations on already-processed edges. + * The two places this can happen are in FinishLeftRegions(), when + * we splice in a "temporary" edge produced by ConnectRightVertex(), + * and in CheckForLeftSplice(), where we splice already-processed + * edges to ensure that our dictionary invariants are not violated + * by numerical errors. + * + * In both these cases it is *very* dangerous to delete the offending + * edge at the time, since one of the routines further up the stack + * will sometimes be keeping a pointer to that edge. + */ +{ + GLUface *f, *fNext; + GLUhalfEdge *e; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + e = f->anEdge; + assert( e->Lnext != e ); + + if( e->Lnext->Lnext == e ) { + /* A face with only two edges */ + AddWinding( e->Onext, e ); + if ( !__gl_meshDelete( e ) ) return 0; + } + } + return 1; +} + +int __gl_computeInterior( GLUtesselator *tess ) +/* + * __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +{ + GLUvertex *v, *vNext; + + tess->fatalError = FALSE; + + /* Each vertex defines an event for our sweep line. Start by inserting + * all the vertices in a priority queue. Events are processed in + * lexicographic order, ie. + * + * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) + */ + RemoveDegenerateEdges( tess ); + if ( !InitPriorityQ( tess ) ) return 0; /* if error */ + InitEdgeDict( tess ); + + /* __gl_pqSortExtractMin */ + while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) { + for( ;; ) { + vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */ + if( vNext == NULL || ! VertEq( vNext, v )) break; + + /* Merge together all vertices at exactly the same location. + * This is more efficient than processing them one at a time, + * simplifies the code (see ConnectLeftDegenerate), and is also + * important for correct handling of certain degenerate cases. + * For example, suppose there are two identical edges A and B + * that belong to different contours (so without this code they would + * be processed by separate sweep events). Suppose another edge C + * crosses A and B from above. When A is processed, we split it + * at its intersection point with C. However this also splits C, + * so when we insert B we may compute a slightly different + * intersection point. This might leave two edges with a small + * gap between them. This kind of error is especially obvious + * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). + */ + vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/ + SpliceMergeVertices( tess, v->anEdge, vNext->anEdge ); + } + SweepEvent( tess, v ); + } + + /* Set tess->event for debugging purposes */ + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org; + DebugEvent( tess ); + DoneEdgeDict( tess ); + DonePriorityQ( tess ); + + if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0; + __gl_meshCheckMesh( tess->mesh ); + + return 1; +} diff --git a/cogl/cogl-path/tesselator/sweep.h b/cogl/cogl-path/tesselator/sweep.h new file mode 100644 index 000000000..feb68b0ff --- /dev/null +++ b/cogl/cogl-path/tesselator/sweep.h @@ -0,0 +1,77 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __sweep_h_ +#define __sweep_h_ + +#include "mesh.h" + +/* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +int __gl_computeInterior( GLUtesselator *tess ); + + +/* The following is here *only* for access by debugging routines */ + +#include "dict.h" + +/* For each pair of adjacent edges crossing the sweep line, there is + * an ActiveRegion to represent the region between them. The active + * regions are kept in sorted order in a dynamic dictionary. As the + * sweep line crosses each vertex, we update the affected regions. + */ + +struct ActiveRegion { + GLUhalfEdge *eUp; /* upper edge, directed right to left */ + DictNode *nodeUp; /* dictionary node corresponding to eUp */ + int windingNumber; /* used to determine which regions are + * inside the polygon */ + GLboolean inside; /* is this region inside the polygon? */ + GLboolean sentinel; /* marks fake edges at t = +/-infinity */ + GLboolean dirty; /* marks regions where the upper or lower + * edge has changed, but we haven't checked + * whether they intersect yet */ + GLboolean fixUpperEdge; /* marks temporary edges introduced when + * we process a "right vertex" (one without + * any edges leaving to the right) */ +}; + +#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp))) +#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp))) + +#endif diff --git a/cogl/cogl-path/tesselator/tess.c b/cogl/cogl-path/tesselator/tess.c new file mode 100644 index 000000000..4a0e8dea7 --- /dev/null +++ b/cogl/cogl-path/tesselator/tess.c @@ -0,0 +1,632 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include +#include "memalloc.h" +#include "tess.h" +#include "mesh.h" +#include "normal.h" +#include "sweep.h" +#include "tessmono.h" +#include "render.h" + +#define GLU_TESS_DEFAULT_TOLERANCE 0.0 +#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {} +/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {} +/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {} +/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **dataOut ) {} +/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {} + + +/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], + void *data[4], + GLfloat weight[4], + void **outData, + void *polygonData ) {} + +/* Half-edges are allocated in pairs (see mesh.c) */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \ + MAX(sizeof(GLUvertex),sizeof(GLUface)))) + + +GLUtesselator * GLAPIENTRY +gluNewTess( void ) +{ + GLUtesselator *tess; + + /* Only initialize fields which can be changed by the api. Other fields + * are initialized where they are used. + */ + + if (memInit( MAX_FAST_ALLOC ) == 0) { + return 0; /* out of memory */ + } + tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator )); + if (tess == NULL) { + return 0; /* out of memory */ + } + + tess->state = T_DORMANT; + + tess->normal[0] = 0; + tess->normal[1] = 0; + tess->normal[2] = 0; + + tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE; + tess->windingRule = GLU_TESS_WINDING_ODD; + tess->flagBoundary = FALSE; + tess->boundaryOnly = FALSE; + + tess->callBegin = &noBegin; + tess->callEdgeFlag = &noEdgeFlag; + tess->callVertex = &noVertex; + tess->callEnd = &noEnd; + + tess->callError = &noError; + tess->callCombine = &noCombine; + tess->callMesh = &noMesh; + + tess->callBeginData= &__gl_noBeginData; + tess->callEdgeFlagData= &__gl_noEdgeFlagData; + tess->callVertexData= &__gl_noVertexData; + tess->callEndData= &__gl_noEndData; + tess->callErrorData= &__gl_noErrorData; + tess->callCombineData= &__gl_noCombineData; + + tess->polygonData= NULL; + + return tess; +} + +static void MakeDormant( GLUtesselator *tess ) +{ + /* Return the tessellator to its original dormant state. */ + + if( tess->mesh != NULL ) { + __gl_meshDeleteMesh( tess->mesh ); + } + tess->state = T_DORMANT; + tess->lastEdge = NULL; + tess->mesh = NULL; +} + +#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s) + +static void GotoState( GLUtesselator *tess, enum TessState newState ) +{ + while( tess->state != newState ) { + /* We change the current state one level at a time, to get to + * the desired state. + */ + if( tess->state < newState ) { + switch( tess->state ) { + case T_DORMANT: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON ); + gluTessBeginPolygon( tess, NULL ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR ); + gluTessBeginContour( tess ); + break; + default: + ; + } + } else { + switch( tess->state ) { + case T_IN_CONTOUR: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR ); + gluTessEndContour( tess ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON ); + /* gluTessEndPolygon( tess ) is too much work! */ + MakeDormant( tess ); + break; + default: + ; + } + } + } +} + + +void GLAPIENTRY +gluDeleteTess( GLUtesselator *tess ) +{ + RequireState( tess, T_DORMANT ); + memFree( tess ); +} + + +void GLAPIENTRY +gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value ) +{ + GLenum windingRule; + + switch( which ) { + case GLU_TESS_TOLERANCE: + if( value < 0.0 || value > 1.0 ) break; + tess->relTolerance = value; + return; + + case GLU_TESS_WINDING_RULE: + windingRule = (GLenum) value; + if( windingRule != value ) break; /* not an integer */ + + switch( windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + case GLU_TESS_WINDING_POSITIVE: + case GLU_TESS_WINDING_NEGATIVE: + case GLU_TESS_WINDING_ABS_GEQ_TWO: + tess->windingRule = windingRule; + return; + default: + break; + } + + case GLU_TESS_BOUNDARY_ONLY: + tess->boundaryOnly = (value != 0); + return; + + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE ); +} + +/* Returns tessellator property */ +void GLAPIENTRY +gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value ) +{ + switch (which) { + case GLU_TESS_TOLERANCE: + /* tolerance should be in range [0..1] */ + assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0); + *value= tess->relTolerance; + break; + case GLU_TESS_WINDING_RULE: + assert(tess->windingRule == GLU_TESS_WINDING_ODD || + tess->windingRule == GLU_TESS_WINDING_NONZERO || + tess->windingRule == GLU_TESS_WINDING_POSITIVE || + tess->windingRule == GLU_TESS_WINDING_NEGATIVE || + tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO); + *value= tess->windingRule; + break; + case GLU_TESS_BOUNDARY_ONLY: + assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE); + *value= tess->boundaryOnly; + break; + default: + *value= 0.0; + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + break; + } +} /* gluGetTessProperty() */ + +void GLAPIENTRY +gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z ) +{ + tess->normal[0] = x; + tess->normal[1] = y; + tess->normal[2] = z; +} + +void GLAPIENTRY +gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn) +{ + switch( which ) { + case GLU_TESS_BEGIN: + tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_BEGIN_DATA: + tess->callBeginData = (fn == NULL) ? + &__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_EDGE_FLAG: + tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag : + (void (GLAPIENTRY *)(GLboolean)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_EDGE_FLAG_DATA: + tess->callEdgeFlagData= (fn == NULL) ? + &__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_VERTEX: + tess->callVertex = (fn == NULL) ? &noVertex : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_VERTEX_DATA: + tess->callVertexData = (fn == NULL) ? + &__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn; + return; + case GLU_TESS_END: + tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn; + return; + case GLU_TESS_END_DATA: + tess->callEndData = (fn == NULL) ? &__gl_noEndData : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_ERROR: + tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_ERROR_DATA: + tess->callErrorData = (fn == NULL) ? + &__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_COMBINE: + tess->callCombine = (fn == NULL) ? &noCombine : + (void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn; + return; + case GLU_TESS_COMBINE_DATA: + tess->callCombineData = (fn == NULL) ? &__gl_noCombineData : + (void (GLAPIENTRY *)(GLdouble [3], + void *[4], + GLfloat [4], + void **, + void *)) fn; + return; + case GLU_TESS_MESH: + tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn; + return; + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } +} + +static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + GLUhalfEdge *e; + + e = tess->lastEdge; + if( e == NULL ) { + /* Make a self-loop (one vertex, one edge). */ + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) return 0; + if ( !__gl_meshSplice( e, e->Sym ) ) return 0; + } else { + /* Create a new vertex and edge which immediately follow e + * in the ordering around the left face. + */ + if (__gl_meshSplitEdge( e ) == NULL) return 0; + e = e->Lnext; + } + + /* The new vertex is now e->Org. */ + e->Org->data = data; + e->Org->coords[0] = coords[0]; + e->Org->coords[1] = coords[1]; + e->Org->coords[2] = coords[2]; + + /* The winding of an edge says how the winding number changes as we + * cross from the edge''s right face to its left face. We add the + * vertices in such an order that a CCW contour will add +1 to + * the winding number of the region inside the contour. + */ + e->winding = 1; + e->Sym->winding = -1; + + tess->lastEdge = e; + + return 1; +} + + +static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + CachedVertex *v = &tess->cache[tess->cacheCount]; + + v->data = data; + v->coords[0] = coords[0]; + v->coords[1] = coords[1]; + v->coords[2] = coords[2]; + ++tess->cacheCount; +} + + +static int EmptyCache( GLUtesselator *tess ) +{ + CachedVertex *v = tess->cache; + CachedVertex *vLast; + + tess->mesh = __gl_meshNewMesh(); + if (tess->mesh == NULL) return 0; + + for( vLast = v + tess->cacheCount; v < vLast; ++v ) { + if ( !AddVertex( tess, v->coords, v->data ) ) return 0; + } + tess->cacheCount = 0; + tess->emptyCache = FALSE; + + return 1; +} + + +void GLAPIENTRY +gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + int i, tooLarge = FALSE; + GLdouble x, clamped[3]; + + RequireState( tess, T_IN_CONTOUR ); + + if( tess->emptyCache ) { + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + tess->lastEdge = NULL; + } + for( i = 0; i < 3; ++i ) { + x = coords[i]; + if( x < - GLU_TESS_MAX_COORD ) { + x = - GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + if( x > GLU_TESS_MAX_COORD ) { + x = GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + clamped[i] = x; + } + if( tooLarge ) { + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE ); + } + + if( tess->mesh == NULL ) { + if( tess->cacheCount < TESS_MAX_CACHE ) { + CacheVertex( tess, clamped, data ); + return; + } + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + } + if ( !AddVertex( tess, clamped, data ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + } +} + + +void GLAPIENTRY +gluTessBeginPolygon( GLUtesselator *tess, void *data ) +{ + RequireState( tess, T_DORMANT ); + + tess->state = T_IN_POLYGON; + tess->cacheCount = 0; + tess->emptyCache = FALSE; + tess->mesh = NULL; + + tess->polygonData= data; +} + + +void GLAPIENTRY +gluTessBeginContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_POLYGON ); + + tess->state = T_IN_CONTOUR; + tess->lastEdge = NULL; + if( tess->cacheCount > 0 ) { + /* Just set a flag so we don't get confused by empty contours + * -- these can be generated accidentally with the obsolete + * NextContour() interface. + */ + tess->emptyCache = TRUE; + } +} + + +void GLAPIENTRY +gluTessEndContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_CONTOUR ); + tess->state = T_IN_POLYGON; +} + +void GLAPIENTRY +gluTessEndPolygon( GLUtesselator *tess ) +{ + GLUmesh *mesh; + + if (setjmp(tess->env) != 0) { + /* come back here if out of memory */ + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + + RequireState( tess, T_IN_POLYGON ); + tess->state = T_DORMANT; + + if( tess->mesh == NULL ) { + if( ! tess->flagBoundary && tess->callMesh == &noMesh ) { + + /* Try some special code to make the easy cases go quickly + * (eg. convex polygons). This code does NOT handle multiple contours, + * intersections, edge flags, and of course it does not generate + * an explicit mesh either. + */ + if( __gl_renderCache( tess )) { + tess->polygonData= NULL; + return; + } + } + if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/ + } + + /* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + __gl_projectPolygon( tess ); + + /* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ + if ( !__gl_computeInterior( tess ) ) { + longjmp(tess->env,1); /* could've used a label */ + } + + mesh = tess->mesh; + if( ! tess->fatalError ) { + int rc = 1; + + /* If the user wants only the boundary contours, we throw away all edges + * except those which separate the interior from the exterior. + * Otherwise we tessellate all the regions marked "inside". + */ + if( tess->boundaryOnly ) { + rc = __gl_meshSetWindingNumber( mesh, 1, TRUE ); + } else { + rc = __gl_meshTessellateInterior( mesh ); + } + if (rc == 0) longjmp(tess->env,1); /* could've used a label */ + + __gl_meshCheckMesh( mesh ); + + if( tess->callBegin != &noBegin || tess->callEnd != &noEnd + || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag + || tess->callBeginData != &__gl_noBeginData + || tess->callEndData != &__gl_noEndData + || tess->callVertexData != &__gl_noVertexData + || tess->callEdgeFlagData != &__gl_noEdgeFlagData ) + { + if( tess->boundaryOnly ) { + __gl_renderBoundary( tess, mesh ); /* output boundary contours */ + } else { + __gl_renderMesh( tess, mesh ); /* output strips and fans */ + } + } + if( tess->callMesh != &noMesh ) { + + /* Throw away the exterior faces, so that all faces are interior. + * This way the user doesn't have to check the "inside" flag, + * and we don't need to even reveal its existence. It also leaves + * the freedom for an implementation to not generate the exterior + * faces in the first place. + */ + __gl_meshDiscardExterior( mesh ); + (*tess->callMesh)( mesh ); /* user wants the mesh itself */ + tess->mesh = NULL; + tess->polygonData= NULL; + return; + } + } + __gl_meshDeleteMesh( mesh ); + tess->polygonData= NULL; + tess->mesh = NULL; +} + + +/*XXXblythe unused function*/ +#if 0 +void GLAPIENTRY +gluDeleteMesh( GLUmesh *mesh ) +{ + __gl_meshDeleteMesh( mesh ); +} +#endif + + + +/*******************************************************/ + +/* Obsolete calls -- for backward compatibility */ + +void GLAPIENTRY +gluBeginPolygon( GLUtesselator *tess ) +{ + gluTessBeginPolygon( tess, NULL ); + gluTessBeginContour( tess ); +} + + +/*ARGSUSED*/ +void GLAPIENTRY +gluNextContour( GLUtesselator *tess, GLenum type ) +{ + gluTessEndContour( tess ); + gluTessBeginContour( tess ); +} + + +void GLAPIENTRY +gluEndPolygon( GLUtesselator *tess ) +{ + gluTessEndContour( tess ); + gluTessEndPolygon( tess ); +} diff --git a/cogl/cogl-path/tesselator/tess.h b/cogl/cogl-path/tesselator/tess.h new file mode 100644 index 000000000..162496088 --- /dev/null +++ b/cogl/cogl-path/tesselator/tess.h @@ -0,0 +1,165 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __tess_h_ +#define __tess_h_ + +#include +#include +#include "mesh.h" +#include "dict.h" +#include "priorityq.h" + +/* The begin/end calls must be properly nested. We keep track of + * the current state to enforce the ordering. + */ +enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR }; + +/* We cache vertex data for single-contour polygons so that we can + * try a quick-and-dirty decomposition first. + */ +#define TESS_MAX_CACHE 100 + +typedef struct CachedVertex { + GLdouble coords[3]; + void *data; +} CachedVertex; + +struct GLUtesselator { + + /*** state needed for collecting the input data ***/ + + enum TessState state; /* what begin/end calls have we seen? */ + + GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */ + GLUmesh *mesh; /* stores the input contours, and eventually + the tessellation itself */ + + void (GLAPIENTRY *callError)( GLenum errnum ); + + /*** state needed for projecting onto the sweep plane ***/ + + GLdouble normal[3]; /* user-specified normal (if provided) */ + GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */ + GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */ + + /*** state needed for the line sweep ***/ + + GLdouble relTolerance; /* tolerance for merging features */ + GLenum windingRule; /* rule for determining polygon interior */ + GLboolean fatalError; /* fatal error: needed combine callback */ + + Dict *dict; /* edge dictionary for sweep line */ + PriorityQ *pq; /* priority queue of vertex events */ + GLUvertex *event; /* current sweep event being processed */ + + void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData ); + + /*** state needed for rendering callbacks (see render.c) ***/ + + GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */ + GLboolean boundaryOnly; /* Extract contours, not triangles */ + GLUface *lonelyTriList; + /* list of triangles which could not be rendered as strips or fans */ + + void (GLAPIENTRY *callBegin)( GLenum type ); + void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge ); + void (GLAPIENTRY *callVertex)( void *data ); + void (GLAPIENTRY *callEnd)( void ); + void (GLAPIENTRY *callMesh)( GLUmesh *mesh ); + + + /*** state needed to cache single-contour polygons for renderCache() */ + + GLboolean emptyCache; /* empty cache on next vertex() call */ + int cacheCount; /* number of cached vertices */ + CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */ + + /*** rendering callbacks that also pass polygon data ***/ + void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData ); + void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge, + void *polygonData ); + void (GLAPIENTRY *callVertexData)( void *data, void *polygonData ); + void (GLAPIENTRY *callEndData)( void *polygonData ); + void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData ); + void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + + jmp_buf env; /* place to jump to when memAllocs fail */ + + void *polygonData; /* client data for current polygon */ +}; + +void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData ); +void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData ); +void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData ); +void GLAPIENTRY __gl_noEndData( void *polygonData ); +void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData ); +void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + +#define CALL_BEGIN_OR_BEGIN_DATA(a) \ + if (tess->callBeginData != &__gl_noBeginData) \ + (*tess->callBeginData)((a),tess->polygonData); \ + else (*tess->callBegin)((a)); + +#define CALL_VERTEX_OR_VERTEX_DATA(a) \ + if (tess->callVertexData != &__gl_noVertexData) \ + (*tess->callVertexData)((a),tess->polygonData); \ + else (*tess->callVertex)((a)); + +#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \ + if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \ + (*tess->callEdgeFlagData)((a),tess->polygonData); \ + else (*tess->callEdgeFlag)((a)); + +#define CALL_END_OR_END_DATA() \ + if (tess->callEndData != &__gl_noEndData) \ + (*tess->callEndData)(tess->polygonData); \ + else (*tess->callEnd)(); + +#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \ + if (tess->callCombineData != &__gl_noCombineData) \ + (*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \ + else (*tess->callCombine)((a),(b),(c),(d)); + +#define CALL_ERROR_OR_ERROR_DATA(a) \ + if (tess->callErrorData != &__gl_noErrorData) \ + (*tess->callErrorData)((a),tess->polygonData); \ + else (*tess->callError)((a)); + +#endif diff --git a/cogl/cogl-path/tesselator/tesselator.h b/cogl/cogl-path/tesselator/tesselator.h new file mode 100644 index 000000000..5b651be1d --- /dev/null +++ b/cogl/cogl-path/tesselator/tesselator.h @@ -0,0 +1,122 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#ifndef __TESSELATOR_H__ +#define __TESSELATOR_H__ + +/* This just includes the defines needed by the tesselator code */ + +#include "cogl/cogl-defines.h" +#include "cogl/cogl-gl-header.h" + +typedef struct GLUtesselator GLUtesselator; + +#define GLU_TESS_MAX_COORD 1.0e150 + +void gluBeginPolygon (GLUtesselator* tess); +void gluDeleteTess (GLUtesselator* tess); +void gluEndPolygon (GLUtesselator* tess); + +typedef void (_GLUfuncptr)(); + +void gluGetTessProperty (GLUtesselator* tess, GLenum which, double* data); + +GLUtesselator *gluNewTess (void); +void gluNextContour (GLUtesselator* tess, GLenum type); + +void gluTessBeginContour (GLUtesselator* tess); +void gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data); +void gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc); +void gluTessEndContour (GLUtesselator* tess); +void gluTessEndPolygon (GLUtesselator* tess); +void gluTessNormal (GLUtesselator* tess, double valueX, double valueY, double valueZ); +void gluTessProperty (GLUtesselator* tess, GLenum which, double data); +void gluTessVertex (GLUtesselator* tess, double *location, GLvoid* data); + +/* ErrorCode */ +#define GLU_INVALID_ENUM 100900 +#define GLU_INVALID_VALUE 100901 +#define GLU_OUT_OF_MEMORY 100902 + +/* TessCallback */ +#define GLU_TESS_BEGIN 100100 +#define GLU_BEGIN 100100 +#define GLU_TESS_VERTEX 100101 +#define GLU_VERTEX 100101 +#define GLU_TESS_END 100102 +#define GLU_END 100102 +#define GLU_TESS_ERROR 100103 +#define GLU_TESS_EDGE_FLAG 100104 +#define GLU_EDGE_FLAG 100104 +#define GLU_TESS_COMBINE 100105 +#define GLU_TESS_BEGIN_DATA 100106 +#define GLU_TESS_VERTEX_DATA 100107 +#define GLU_TESS_END_DATA 100108 +#define GLU_TESS_ERROR_DATA 100109 +#define GLU_TESS_EDGE_FLAG_DATA 100110 +#define GLU_TESS_COMBINE_DATA 100111 + +/* TessContour */ +#define GLU_CW 100120 +#define GLU_CCW 100121 +#define GLU_INTERIOR 100122 +#define GLU_EXTERIOR 100123 +#define GLU_UNKNOWN 100124 + +/* TessProperty */ +#define GLU_TESS_WINDING_RULE 100140 +#define GLU_TESS_BOUNDARY_ONLY 100141 +#define GLU_TESS_TOLERANCE 100142 + +/* TessError */ +#define GLU_TESS_ERROR1 100151 +#define GLU_TESS_ERROR2 100152 +#define GLU_TESS_ERROR3 100153 +#define GLU_TESS_ERROR4 100154 +#define GLU_TESS_ERROR5 100155 +#define GLU_TESS_ERROR6 100156 +#define GLU_TESS_ERROR7 100157 +#define GLU_TESS_ERROR8 100158 +#define GLU_TESS_MISSING_BEGIN_POLYGON 100151 +#define GLU_TESS_MISSING_BEGIN_CONTOUR 100152 +#define GLU_TESS_MISSING_END_POLYGON 100153 +#define GLU_TESS_MISSING_END_CONTOUR 100154 +#define GLU_TESS_COORD_TOO_LARGE 100155 +#define GLU_TESS_NEED_COMBINE_CALLBACK 100156 + +/* TessWinding */ +#define GLU_TESS_WINDING_ODD 100130 +#define GLU_TESS_WINDING_NONZERO 100131 +#define GLU_TESS_WINDING_POSITIVE 100132 +#define GLU_TESS_WINDING_NEGATIVE 100133 +#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134 + +#endif /* __TESSELATOR_H__ */ diff --git a/cogl/cogl-path/tesselator/tessmono.c b/cogl/cogl-path/tesselator/tessmono.c new file mode 100644 index 000000000..4d0844005 --- /dev/null +++ b/cogl/cogl-path/tesselator/tessmono.c @@ -0,0 +1,201 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include "geom.h" +#include "mesh.h" +#include "tessmono.h" +#include + +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * The basic idea is explained in Preparata and Shamos (which I don''t + * have handy right now), although their implementation is more + * complicated than this one. The are two edge chains, an upper chain + * and a lower chain. We process all vertices from both chains in order, + * from right to left. + * + * The algorithm ensures that the following invariant holds after each + * vertex is processed: the untessellated region consists of two + * chains, where one chain (say the upper) is a single edge, and + * the other chain is concave. The left vertex of the single edge + * is always to the left of all vertices in the concave chain. + * + * Each step consists of adding the rightmost unprocessed vertex to one + * of the two chains, and forming a fan of triangles from the rightmost + * of two chain endpoints. Determining whether we can add each triangle + * to the fan is a simple orientation test. By making the fan as large + * as possible, we restore the invariant (check it yourself). + */ +int __gl_meshTessellateMonoRegion( GLUface *face ) +{ + GLUhalfEdge *up, *lo; + + /* All edges are oriented CCW around the boundary of the region. + * First, find the half-edge whose origin vertex is rightmost. + * Since the sweep goes from left to right, face->anEdge should + * be close to the edge we want. + */ + up = face->anEdge; + assert( up->Lnext != up && up->Lnext->Lnext != up ); + + for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev ) + ; + for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext ) + ; + lo = up->Lprev; + + while( up->Lnext != lo ) { + if( VertLeq( up->Dst, lo->Org )) { + /* up->Dst is on the left. It is safe to form triangles from lo->Org. + * The EdgeGoesLeft test guarantees progress even when some triangles + * are CW, given that the upper and lower chains are truly monotone. + */ + while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext ) + || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + lo = lo->Lprev; + } else { + /* lo->Org is on the left. We can make CCW triangles from up->Dst. */ + while( lo->Lnext != up && (EdgeGoesRight( up->Lprev ) + || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev ); + if (tempHalfEdge == NULL) return 0; + up = tempHalfEdge->Sym; + } + up = up->Lnext; + } + } + + /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region + * can be tessellated in a fan from this leftmost vertex. + */ + assert( lo->Lnext != up ); + while( lo->Lnext->Lnext != up ) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + + return 1; +} + + +/* __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + */ +int __gl_meshTessellateInterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Make sure we don''t try to tessellate the new triangles. */ + next = f->next; + if( f->inside ) { + if ( !__gl_meshTessellateMonoRegion( f ) ) return 0; + } + } + + return 1; +} + + +/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + */ +void __gl_meshDiscardExterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Since f will be destroyed, save its next pointer. */ + next = f->next; + if( ! f->inside ) { + __gl_meshZapFace( f ); + } + } +} + +#define MARKED_FOR_DELETION 0x7fffffff + +/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ) +{ + GLUhalfEdge *e, *eNext; + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + eNext = e->next; + if( e->Rface->inside != e->Lface->inside ) { + + /* This is a boundary edge (one side is interior, one is exterior). */ + e->winding = (e->Lface->inside) ? value : -value; + } else { + + /* Both regions are interior, or both are exterior. */ + if( ! keepOnlyBoundary ) { + e->winding = 0; + } else { + if ( !__gl_meshDelete( e ) ) return 0; + } + } + } + return 1; +} diff --git a/cogl/cogl-path/tesselator/tessmono.h b/cogl/cogl-path/tesselator/tessmono.h new file mode 100644 index 000000000..8ee1b2fe3 --- /dev/null +++ b/cogl/cogl-path/tesselator/tessmono.h @@ -0,0 +1,71 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __tessmono_h_ +#define __tessmono_h_ + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + * + * __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + * + * __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ + +int __gl_meshTessellateMonoRegion( GLUface *face ); +int __gl_meshTessellateInterior( GLUmesh *mesh ); +void __gl_meshDiscardExterior( GLUmesh *mesh ); +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ); + +#endif diff --git a/cogl/cogl/Makefile.am b/cogl/cogl/Makefile.am new file mode 100644 index 000000000..e34c76806 --- /dev/null +++ b/cogl/cogl/Makefile.am @@ -0,0 +1,547 @@ +# preamble + +NULL = + +SUBDIRS = + +BUILT_SOURCES = + +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(srcdir)/deprecated \ + -I$(srcdir)/winsys \ + -I$(srcdir)/driver/gl \ + -I$(srcdir)/driver/gl/gl \ + -I$(srcdir)/driver/gl/gles \ + $(NULL) + +AM_CPPFLAGS += \ + -DG_LOG_DOMAIN=\"Cogl\" \ + -DCOGL_COMPILATION \ + -DCOGL_GL_LIBNAME=\"$(COGL_GL_LIBNAME)\" \ + -DCOGL_GLES1_LIBNAME=\"$(COGL_GLES1_LIBNAME)\" \ + -DCOGL_GLES2_LIBNAME=\"$(COGL_GLES2_LIBNAME)\" \ + -DCOGL_LOCALEDIR=\""$(localedir)"\" \ + $(NULL) + +if HAVE_COGL_DEFAULT_DRIVER +AM_CPPFLAGS += \ + -DCOGL_DEFAULT_DRIVER=\"$(COGL_DEFAULT_DRIVER)\" +endif + + +AM_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS) + +BUILT_SOURCES += cogl-defines.h cogl-egl-defines.h cogl-gl-header.h +DISTCLEANFILES += cogl-defines.h cogl-egl-defines.h cogl-gl-header.h +EXTRA_DIST += cogl-defines.h.in cogl-egl-defines.h.in cogl-gl-header.h.in + +pc_files = mutter-cogl-1.0.pc + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pc_files) + +DISTCLEANFILES += $(pc_files) + +cogl_deprecated_h = \ + deprecated/cogl-clip-state.h \ + deprecated/cogl-fixed.h \ + deprecated/cogl-material-compat.h \ + deprecated/cogl-vertex-buffer.h \ + deprecated/cogl-shader.h \ + deprecated/cogl-clutter.h \ + deprecated/cogl-type-casts.h \ + deprecated/cogl-framebuffer-deprecated.h \ + deprecated/cogl-texture-deprecated.h \ + deprecated/cogl-auto-texture.h \ + $(NULL) + +# public 1.x api headers +cogl_1_public_h = \ + $(cogl_deprecated_h) \ + cogl1-context.h \ + cogl-bitmap.h \ + cogl-color.h \ + cogl-matrix.h \ + cogl-offscreen.h \ + cogl-primitives.h \ + cogl-texture.h \ + cogl-types.h \ + cogl.h \ + $(NULL) + +# experimental 2.0 api headers +# Note: we don't run glib-mkenums over these headers +cogl_experimental_h = \ + cogl-object.h \ + cogl-renderer.h \ + cogl-swap-chain.h \ + cogl-onscreen-template.h \ + cogl-display.h \ + cogl-context.h \ + cogl-pipeline.h \ + cogl-pipeline-state.h \ + cogl-pipeline-layer-state.h \ + cogl-snippet.h \ + cogl-gles2.h \ + cogl-gles2-types.h \ + cogl-index-buffer.h \ + cogl-attribute-buffer.h \ + cogl-indices.h \ + cogl-attribute.h \ + cogl-primitive.h \ + cogl-framebuffer.h \ + cogl-onscreen.h \ + cogl-frame-info.h \ + cogl-vector.h \ + cogl-euler.h \ + cogl-output.h \ + cogl-quaternion.h \ + cogl-matrix-stack.h \ + cogl-poll.h \ + cogl-texture-3d.h \ + cogl-texture-2d.h \ + cogl-texture-2d-gl.h \ + cogl-texture-rectangle.h \ + cogl-texture-2d-sliced.h \ + cogl-sub-texture.h \ + cogl-atlas-texture.h \ + cogl-meta-texture.h \ + cogl-primitive-texture.h \ + cogl-depth-state.h \ + cogl-buffer.h \ + cogl-pixel-buffer.h \ + cogl2-experimental.h \ + cogl-macros.h \ + cogl-fence.h \ + cogl-version.h \ + cogl-error.h \ + $(NULL) + +cogl_additional_experimental_h = \ + cogl-bitmap.h \ + cogl-color.h \ + cogl-matrix.h \ + cogl-texture.h \ + cogl-types.h \ + cogl-gtype-private.h \ + $(NULL) + +cogl_nodist_experimental_h = \ + $(NULL) + +# nop driver +cogl_driver_sources = \ + driver/nop/cogl-driver-nop.c \ + driver/nop/cogl-framebuffer-nop-private.h \ + driver/nop/cogl-framebuffer-nop.c \ + driver/nop/cogl-attribute-nop-private.h \ + driver/nop/cogl-attribute-nop.c \ + driver/nop/cogl-clip-stack-nop-private.h \ + driver/nop/cogl-clip-stack-nop.c \ + driver/nop/cogl-texture-2d-nop-private.h \ + driver/nop/cogl-texture-2d-nop.c \ + $(NULL) + +# gl driver sources +cogl_gl_prototypes_h = \ + gl-prototypes/cogl-gles2-functions.h \ + gl-prototypes/cogl-core-functions.h \ + gl-prototypes/cogl-in-gles-core-functions.h \ + gl-prototypes/cogl-in-gles2-core-functions.h \ + gl-prototypes/cogl-glsl-functions.h \ + $(NULL) + +cogl_driver_sources += \ + driver/gl/cogl-util-gl-private.h \ + driver/gl/cogl-util-gl.c \ + driver/gl/cogl-framebuffer-gl-private.h \ + driver/gl/cogl-framebuffer-gl.c \ + driver/gl/cogl-texture-gl-private.h \ + driver/gl/cogl-texture-gl.c \ + driver/gl/cogl-texture-2d-gl-private.h \ + driver/gl/cogl-texture-2d-gl.c \ + driver/gl/cogl-attribute-gl-private.h \ + driver/gl/cogl-attribute-gl.c \ + driver/gl/cogl-clip-stack-gl-private.h \ + driver/gl/cogl-clip-stack-gl.c \ + driver/gl/cogl-buffer-gl-private.h \ + driver/gl/cogl-buffer-gl.c \ + driver/gl/cogl-pipeline-opengl.c \ + driver/gl/cogl-pipeline-opengl-private.h \ + driver/gl/cogl-pipeline-fragend-glsl.c \ + driver/gl/cogl-pipeline-fragend-glsl-private.h \ + driver/gl/gl/cogl-pipeline-fragend-arbfp.c \ + driver/gl/gl/cogl-pipeline-fragend-arbfp-private.h \ + driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c \ + driver/gl/gl/cogl-pipeline-progend-fixed-arbfp-private.h \ + driver/gl/cogl-pipeline-fragend-fixed.c \ + driver/gl/cogl-pipeline-fragend-fixed-private.h \ + driver/gl/cogl-pipeline-vertend-glsl.c \ + driver/gl/cogl-pipeline-vertend-glsl-private.h \ + driver/gl/cogl-pipeline-vertend-fixed.c \ + driver/gl/cogl-pipeline-vertend-fixed-private.h \ + driver/gl/cogl-pipeline-progend-fixed.c \ + driver/gl/cogl-pipeline-progend-fixed-private.h \ + driver/gl/cogl-pipeline-progend-glsl.c \ + driver/gl/cogl-pipeline-progend-glsl-private.h \ + $(NULL) + +if COGL_DRIVER_GL_SUPPORTED +cogl_driver_sources += \ + driver/gl/gl/cogl-driver-gl.c \ + driver/gl/gl/cogl-texture-driver-gl.c \ + $(NULL) +endif + +if COGL_DRIVER_GLES_SUPPORTED +cogl_driver_sources += \ + driver/gl/gles/cogl-driver-gles.c \ + driver/gl/gles/cogl-texture-driver-gles.c \ + $(NULL) +endif + +# winsys sources, common to all backends +cogl_winsys_common_sources = \ + winsys/cogl-winsys-private.h \ + winsys/cogl-winsys.c \ + $(NULL) + +# sources +cogl_sources_c = \ + $(cogl_driver_sources) \ + $(cogl_winsys_common_sources) \ + cogl-private.h \ + cogl-i18n-private.h \ + cogl-debug.h \ + cogl-debug-options.h \ + cogl-gpu-info.c \ + cogl-gpu-info-private.h \ + cogl-context-private.h \ + cogl-context.c \ + cogl-renderer-private.h \ + cogl-renderer.h \ + cogl-renderer.c \ + cogl-swap-chain-private.h \ + cogl-swap-chain.h \ + cogl-swap-chain.c \ + cogl-onscreen-template-private.h \ + cogl-onscreen-template.h \ + cogl-onscreen-template.c \ + cogl-display-private.h \ + cogl-display.h \ + cogl-display.c \ + cogl-driver.h \ + cogl.c \ + cogl-object-private.h \ + cogl-object.h \ + cogl-object.c \ + cogl-util.h \ + cogl-util.c \ + cogl-bitmap-private.h \ + cogl-bitmap.c \ + cogl-bitmap-conversion.c \ + cogl-bitmap-packing.h \ + cogl-primitives-private.h \ + cogl-primitives.h \ + cogl-primitives.c \ + cogl-bitmap-pixbuf.c \ + cogl-clip-stack.h \ + cogl-clip-stack.c \ + cogl-feature-private.h \ + cogl-feature-private.c \ + cogl-color-private.h \ + cogl-color.c \ + cogl-buffer-private.h \ + cogl-buffer.c \ + cogl-pixel-buffer-private.h \ + cogl-pixel-buffer.c \ + cogl-index-buffer-private.h \ + cogl-index-buffer.c \ + cogl-attribute-buffer-private.h \ + cogl-attribute-buffer.c \ + cogl-indices-private.h \ + cogl-indices.c \ + cogl-attribute-private.h \ + cogl-attribute.c \ + cogl-primitive-private.h \ + cogl-primitive.c \ + cogl-matrix.c \ + cogl-vector.c \ + cogl-euler.c \ + cogl-quaternion-private.h \ + cogl-quaternion.c \ + cogl-matrix-private.h \ + cogl-matrix-stack.c \ + cogl-matrix-stack-private.h \ + cogl-depth-state.c \ + cogl-depth-state-private.h \ + cogl-node.c \ + cogl-node-private.h \ + cogl-pipeline.c \ + cogl-pipeline-private.h \ + cogl-pipeline-layer.c \ + cogl-pipeline-layer-private.h \ + cogl-pipeline-state.c \ + cogl-pipeline-layer-state-private.h \ + cogl-pipeline-layer-state.c \ + cogl-pipeline-state-private.h \ + cogl-pipeline-debug.c \ + cogl-glsl-shader.c \ + cogl-glsl-shader-private.h \ + cogl-glsl-shader-boilerplate.h \ + cogl-pipeline-snippet-private.h \ + cogl-pipeline-snippet.c \ + cogl-pipeline-cache.h \ + cogl-pipeline-cache.c \ + cogl-pipeline-hash-table.h \ + cogl-pipeline-hash-table.c \ + cogl-sampler-cache.c \ + cogl-sampler-cache-private.h \ + cogl-blend-string.c \ + cogl-blend-string.h \ + cogl-debug.c \ + cogl-sub-texture-private.h \ + cogl-texture-private.h \ + cogl-texture-2d-private.h \ + cogl-texture-2d-sliced-private.h \ + cogl-texture-3d-private.h \ + cogl-texture-driver.h \ + cogl-sub-texture.c \ + cogl-texture.c \ + cogl-texture-2d.c \ + cogl-texture-2d-sliced.c \ + cogl-texture-3d.c \ + cogl-texture-rectangle-private.h \ + cogl-texture-rectangle.c \ + cogl-rectangle-map.h \ + cogl-rectangle-map.c \ + cogl-atlas.h \ + cogl-atlas.c \ + cogl-atlas-texture-private.h \ + cogl-atlas-texture.c \ + cogl-meta-texture.c \ + cogl-primitive-texture.c \ + cogl-blit.h \ + cogl-blit.c \ + cogl-spans.h \ + cogl-spans.c \ + cogl-journal-private.h \ + cogl-journal.c \ + cogl-frame-info-private.h \ + cogl-frame-info.c \ + cogl-framebuffer-private.h \ + cogl-framebuffer.c \ + cogl-onscreen-private.h \ + cogl-onscreen.c \ + cogl-output-private.h \ + cogl-output.c \ + cogl-profile.h \ + cogl-profile.c \ + cogl-flags.h \ + cogl-bitmask.h \ + cogl-bitmask.c \ + cogl-gtype.c \ + cogl-gtype-private.h \ + cogl-point-in-poly-private.h \ + cogl-point-in-poly.c \ + cogl-list.c \ + cogl-list.h \ + winsys/cogl-winsys-stub-private.h \ + winsys/cogl-winsys-stub.c \ + cogl-config-private.h \ + cogl-config.c \ + cogl-boxed-value.h \ + cogl-boxed-value.c \ + cogl-snippet-private.h \ + cogl-snippet.c \ + cogl-poll-private.h \ + cogl-poll.c \ + gl-prototypes/cogl-all-functions.h \ + gl-prototypes/cogl-gles1-functions.h \ + gl-prototypes/cogl-gles2-functions.h \ + gl-prototypes/cogl-core-functions.h \ + gl-prototypes/cogl-in-gles-core-functions.h \ + gl-prototypes/cogl-in-gles1-core-functions.h \ + gl-prototypes/cogl-in-gles2-core-functions.h \ + gl-prototypes/cogl-fixed-functions.h \ + gl-prototypes/cogl-glsl-functions.h \ + cogl-memory-stack-private.h \ + cogl-memory-stack.c \ + cogl-magazine-private.h \ + cogl-magazine.c \ + cogl-gles2-context-private.h \ + cogl-gles2-context.c \ + cogl-error-private.h \ + cogl-error.c \ + cogl-closure-list-private.h \ + cogl-closure-list.c \ + cogl-fence.c \ + cogl-fence-private.h \ + deprecated/cogl-clip-state.c \ + deprecated/cogl-fixed.c \ + deprecated/cogl-vertex-buffer-private.h \ + deprecated/cogl-vertex-buffer.c \ + deprecated/cogl-material-compat.c \ + deprecated/cogl-program.c \ + deprecated/cogl-program-private.h \ + deprecated/cogl-auto-texture.c \ + deprecated/cogl-shader-private.h \ + deprecated/cogl-shader.c \ + deprecated/cogl-clutter.c \ + deprecated/cogl-framebuffer-deprecated.c \ + deprecated/cogl-texture-deprecated.c \ + $(NULL) + +cogl_experimental_h += cogl-glib-source.h +cogl_sources_c += cogl-glib-source.c + +if SUPPORT_XLIB +cogl_deprecated_h += deprecated/cogl-clutter-xlib.h +cogl_1_public_h += cogl-xlib-renderer.h + +cogl_experimental_h += \ + winsys/cogl-texture-pixmap-x11.h \ + cogl-xlib.h + +cogl_sources_c += \ + cogl-x11-renderer-private.h \ + cogl-xlib-renderer-private.h \ + cogl-xlib-renderer.c \ + cogl-xlib.c \ + cogl-xlib-private.h \ + winsys/cogl-texture-pixmap-x11.c \ + winsys/cogl-texture-pixmap-x11-private.h +endif +if SUPPORT_GLX +cogl_experimental_h += cogl-glx.h +cogl_sources_c += \ + cogl-glx-renderer-private.h \ + cogl-glx-display-private.h \ + winsys/cogl-winsys-glx-feature-functions.h \ + winsys/cogl-winsys-glx-private.h \ + winsys/cogl-winsys-glx.c +endif +if SUPPORT_WAYLAND_EGL_SERVER +cogl_experimental_h += cogl-wayland-server.h +endif +if SUPPORT_EGL_PLATFORM_KMS +cogl_experimental_h += \ + cogl-kms-renderer.h \ + cogl-kms-display.h +cogl_sources_c += \ + winsys/cogl-winsys-egl-kms.c \ + winsys/cogl-winsys-egl-kms-private.h +endif +if SUPPORT_EGL_PLATFORM_XLIB +cogl_sources_c += \ + winsys/cogl-winsys-egl-x11.c \ + winsys/cogl-winsys-egl-x11-private.h +endif +if SUPPORT_EGL +cogl_experimental_h += cogl-egl.h +cogl_nodist_experimental_h += cogl-egl-defines.h + +cogl_sources_c += \ + cogl-egl-private.h \ + winsys/cogl-winsys-egl.c \ + winsys/cogl-winsys-egl-feature-functions.h \ + winsys/cogl-winsys-egl-private.h +endif + +# glib-mkenums rules +glib_enum_h = cogl-enum-types.h +glib_enum_c = cogl-enum-types.c +glib_enum_headers = $(cogl_1_public_h) +include $(top_srcdir)/build/autotools/Makefile.am.enums + +mutterlibdir = $(libdir)/mutter +mutterlib_LTLIBRARIES = libmutter-cogl.la + +libmutter_cogl_la_LIBADD = $(LIBM) $(COGL_DEP_LIBS) $(COGL_EXTRA_LDFLAGS) +if UNIT_TESTS +libmutter_cogl_la_LIBADD += $(top_builddir)/test-fixtures/libtest-fixtures.la +endif +# XXX: The aim is to eventually get rid of all private API exports +# for cogl-pango. +libmutter_cogl_la_LDFLAGS = \ + -no-undefined \ + -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \ + -export-dynamic \ + -rpath $(mutterlibdir) \ + -export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_framebuffer_draw_primitive|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|_cogl_primitive_draw|test_|unit_test_).*" + +libmutter_cogl_la_SOURCES = $(cogl_sources_c) +nodist_libmutter_cogl_la_SOURCES = $(BUILT_SOURCES) + +# Cogl installed headers +cogl_headers = \ + $(cogl_1_public_h) \ + cogl-deprecated.h \ + cogl-pango.h \ + $(NULL) + +cogl_base_includedir = $(includedir)/mutter +cogldeprecatedincludedir = $(cogl_base_includedir)/cogl/cogl/deprecated +cogldeprecatedinclude_HEADERS = $(cogl_deprecated_h) + +coglincludedir = $(cogl_base_includedir)/cogl/cogl +coglinclude_HEADERS = $(cogl_headers) $(cogl_experimental_h) +nodist_coglinclude_HEADERS = $(cogl_nodist_experimental_h) cogl-defines.h cogl-enum-types.h + +cogl_proto_includedir = $(cogl_base_includedir)/cogl/cogl/gl-prototypes +cogl_proto_include_HEADERS = $(cogl_gl_prototypes_h) + +EXTRA_DIST += \ + cogl.symbols + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_GIRS = + +if HAVE_INTROSPECTION +Cogl-1.0.gir: libmutter-cogl.la Makefile + +Cogl_1_0_gir_NAMESPACE = Cogl +Cogl_1_0_gir_VERSION = 1.0 +Cogl_1_0_gir_LIBS = libmutter-cogl.la +if UNIT_TESTS +Cogl_1_0_gir_LIBS += $(top_builddir)/test-fixtures/libtest-fixtures.la +endif +Cogl_1_0_gir_FILES = $(cogl_1_public_h) cogl-enum-types.h + +Cogl-2.0.gir: libmutter-cogl.la Makefile + +Cogl_2_0_gir_NAMESPACE = Cogl +Cogl_2_0_gir_VERSION = 2.0 +Cogl_2_0_gir_LIBS = libmutter-cogl.la +if UNIT_TESTS +Cogl_2_0_gir_LIBS += $(top_builddir)/test-fixtures/libtest-fixtures.la +endif +Cogl_2_0_gir_FILES = $(cogl_experimental_h) $(cogl_additional_experimental_h) cogl-enum-types.h + +Cogl_1_0_gir_CFLAGS = $(AM_CPPFLAGS) $(COGL_DEP_CFLAGS) -UCOGL_ENABLE_EXPERIMENTAL_API -UCOGL_ENABLE_EXPERIMENTAL_2_0_API -UCOGL_COMPILATION -D__COGL_H_INSIDE__ -D__COGL_XLIB_H_INSIDE__ -D__COGL_EGL_H_INSIDE__ -D__COGL_GLX_H_INSIDE__ -DCOGL_GIR_SCANNING +Cogl_1_0_gir_INCLUDES = GL-1.0 GObject-2.0 +Cogl_1_0_gir_EXPORT_PACKAGES = cogl-1.0 +Cogl_1_0_gir_SCANNERFLAGS = --warn-all --c-include='cogl/cogl.h' + +Cogl_2_0_gir_CFLAGS = $(AM_CPPFLAGS) $(COGL_DEP_CFLAGS) -DCOGL_ENABLE_EXPERIMENTAL_API=1 -UCOGL_COMPILATION -D__COGL_H_INSIDE__ -D__COGL_XLIB_H_INSIDE__ -DCOGL_GIR_SCANNING +Cogl_2_0_gir_INCLUDES = GL-1.0 GObject-2.0 +Cogl_2_0_gir_EXPORT_PACKAGES = cogl-2.0-experimental +Cogl_2_0_gir_SCANNERFLAGS = --warn-all --c-include='cogl/cogl.h' --symbol-prefix=cogl --symbol-prefix=cogl2 + +INTROSPECTION_GIRS += Cogl-1.0.gir Cogl-2.0.gir + +girdir = $(mutterlibdir) +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(mutterlibdir) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) +endif diff --git a/cogl/cogl/cogl-atlas-texture-private.h b/cogl/cogl/cogl-atlas-texture-private.h new file mode 100644 index 000000000..ba83bf9eb --- /dev/null +++ b/cogl/cogl/cogl-atlas-texture-private.h @@ -0,0 +1,81 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef _COGL_ATLAS_TEXTURE_PRIVATE_H_ +#define _COGL_ATLAS_TEXTURE_PRIVATE_H_ + +#include "cogl-object-private.h" +#include "cogl-texture-private.h" +#include "cogl-rectangle-map.h" +#include "cogl-atlas.h" +#include "cogl-atlas-texture.h" + +struct _CoglAtlasTexture +{ + CoglTexture _parent; + + /* The format that the texture is in. This isn't necessarily the + same format as the atlas texture because we can store + pre-multiplied and non-pre-multiplied textures together */ + CoglPixelFormat internal_format; + + /* The rectangle that was used to add this texture to the + atlas. This includes the 1-pixel border */ + CoglRectangleMapEntry rectangle; + + /* The atlas that this texture is in. If the texture is no longer in + an atlas then this will be NULL. A reference is taken on the + atlas by the texture (but not vice versa so there is no cycle) */ + CoglAtlas *atlas; + + /* Either a CoglSubTexture representing the atlas region for easy + * rendering or if the texture has been migrated out of the atlas it + * may be some other texture type such as CoglTexture2D */ + CoglTexture *sub_texture; +}; + +CoglAtlasTexture * +_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, + CoglBool can_convert_in_place); + +void +_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, + GHookFunc callback, + void *user_data); + +void +_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, + GHookFunc callback, + void *user_data); + +CoglBool +_cogl_is_atlas_texture (void *object); + +#endif /* _COGL_ATLAS_TEXTURE_PRIVATE_H_ */ diff --git a/cogl/cogl/cogl-atlas-texture.c b/cogl/cogl/cogl-atlas-texture.c new file mode 100644 index 000000000..1c8b56972 --- /dev/null +++ b/cogl/cogl/cogl-atlas-texture.c @@ -0,0 +1,1047 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-atlas-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-texture-driver.h" +#include "cogl-rectangle-map.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-atlas.h" +#include "cogl1-context.h" +#include "cogl-sub-texture.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-gtype-private.h" + +#include + +static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); + +COGL_TEXTURE_DEFINE (AtlasTexture, atlas_texture); +COGL_GTYPE_DEFINE_CLASS (AtlasTexture, atlas_texture); + +static const CoglTextureVtable cogl_atlas_texture_vtable; + +static CoglSubTexture * +_cogl_atlas_texture_create_sub_texture (CoglTexture *full_texture, + const CoglRectangleMapEntry *rectangle) +{ + CoglContext *ctx = full_texture->context; + /* Create a subtexture for the given rectangle not including the + 1-pixel border */ + return cogl_sub_texture_new (ctx, + full_texture, + rectangle->x + 1, + rectangle->y + 1, + rectangle->width - 2, + rectangle->height - 2); +} + +static void +_cogl_atlas_texture_update_position_cb (void *user_data, + CoglTexture *new_texture, + const CoglRectangleMapEntry *rectangle) +{ + CoglAtlasTexture *atlas_tex = user_data; + + /* Update the sub texture */ + if (atlas_tex->sub_texture) + cogl_object_unref (atlas_tex->sub_texture); + atlas_tex->sub_texture = COGL_TEXTURE ( + _cogl_atlas_texture_create_sub_texture (new_texture, rectangle)); + + /* Update the position */ + atlas_tex->rectangle = *rectangle; +} + +static void +_cogl_atlas_texture_pre_reorganize_foreach_cb + (const CoglRectangleMapEntry *entry, + void *rectangle_data, + void *user_data) +{ + CoglAtlasTexture *atlas_tex = rectangle_data; + + /* Keep a reference to the texture because we don't want it to be + destroyed during the reorganization */ + cogl_object_ref (atlas_tex); + + /* Notify cogl-pipeline.c that the texture's underlying GL texture + * storage is changing so it knows it may need to bind a new texture + * if the CoglTexture is reused with the same texture unit. */ + _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex)); +} + +static void +_cogl_atlas_texture_pre_reorganize_cb (void *data) +{ + CoglAtlas *atlas = data; + + /* We don't know if any journal entries currently depend on OpenGL + * texture coordinates that would be invalidated by reorganizing + * this atlas so we flush all journals before migrating. + * + * We are assuming that texture atlas migration never happens + * during a flush so we don't have to consider recursion here. + */ + cogl_flush (); + + if (atlas->map) + _cogl_rectangle_map_foreach (atlas->map, + _cogl_atlas_texture_pre_reorganize_foreach_cb, + NULL); +} + +typedef struct +{ + CoglAtlasTexture **textures; + /* Number of textures found so far */ + unsigned int n_textures; +} CoglAtlasTextureGetRectanglesData; + +static void +_cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *entry, + void *rectangle_data, + void *user_data) +{ + CoglAtlasTextureGetRectanglesData *data = user_data; + + data->textures[data->n_textures++] = rectangle_data; +} + +static void +_cogl_atlas_texture_post_reorganize_cb (void *user_data) +{ + CoglAtlas *atlas = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (atlas->map) + { + CoglAtlasTextureGetRectanglesData data; + unsigned int i; + + data.textures = g_new (CoglAtlasTexture *, + _cogl_rectangle_map_get_n_rectangles (atlas->map)); + data.n_textures = 0; + + /* We need to remove all of the references that we took during + the preorganize callback. We have to get a separate array of + the textures because CoglRectangleMap doesn't support + removing rectangles during iteration */ + _cogl_rectangle_map_foreach (atlas->map, + _cogl_atlas_texture_get_rectangles_cb, + &data); + + for (i = 0; i < data.n_textures; i++) + { + /* Ignore textures that don't have an atlas yet. This will + happen when a new texture is added because we allocate + the structure for the texture so that it can get stored + in the atlas but it isn't a valid object yet */ + if (data.textures[i]->atlas) + cogl_object_unref (data.textures[i]); + } + + g_free (data.textures); + } + + /* Notify any listeners that an atlas has changed */ + g_hook_list_invoke (&ctx->atlas_reorganize_callbacks, FALSE); +} + +static void +_cogl_atlas_texture_atlas_destroyed_cb (void *user_data) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Remove the atlas from the global list */ + ctx->atlases = g_slist_remove (ctx->atlases, user_data); +} + +static CoglAtlas * +_cogl_atlas_texture_create_atlas (CoglContext *ctx) +{ + static CoglUserDataKey atlas_private_key; + + CoglAtlas *atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888, + 0, + _cogl_atlas_texture_update_position_cb); + + _cogl_atlas_add_reorganize_callback (atlas, + _cogl_atlas_texture_pre_reorganize_cb, + _cogl_atlas_texture_post_reorganize_cb, + atlas); + + ctx->atlases = g_slist_prepend (ctx->atlases, atlas); + + /* Set some data on the atlas so we can get notification when it is + destroyed in order to remove it from the list. ctx->atlases + effectively holds a weak reference. We don't need a strong + reference because the atlas textures take a reference on the + atlas so it will stay alive */ + cogl_object_set_user_data (COGL_OBJECT (atlas), &atlas_private_key, atlas, + _cogl_atlas_texture_atlas_destroyed_cb); + + return atlas; +} + +static void +_cogl_atlas_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + CoglMetaTexture *meta_texture = COGL_META_TEXTURE (atlas_tex->sub_texture); + + /* Forward on to the sub texture */ + cogl_meta_texture_foreach_in_region (meta_texture, + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + callback, + user_data); +} + +static void +_cogl_atlas_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_gl_flush_legacy_texobj_wrap_modes (atlas_tex->sub_texture, + wrap_mode_s, + wrap_mode_t, + wrap_mode_p); +} + +static void +_cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) +{ + if (atlas_tex->atlas) + { + _cogl_atlas_remove (atlas_tex->atlas, + &atlas_tex->rectangle); + + cogl_object_unref (atlas_tex->atlas); + atlas_tex->atlas = NULL; + } +} + +static void +_cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) +{ + _cogl_atlas_texture_remove_from_atlas (atlas_tex); + + if (atlas_tex->sub_texture) + cogl_object_unref (atlas_tex->sub_texture); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (atlas_tex)); +} + +static int +_cogl_atlas_texture_get_max_waste (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_max_waste (atlas_tex->sub_texture); +} + +static CoglBool +_cogl_atlas_texture_is_sliced (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_is_sliced (atlas_tex->sub_texture); +} + +static CoglBool +_cogl_atlas_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_can_hardware_repeat (atlas_tex->sub_texture); +} + +static void +_cogl_atlas_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_transform_coords_to_gl (atlas_tex->sub_texture, s, t); +} + +static CoglTransformResult +_cogl_atlas_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_transform_quad_coords_to_gl (atlas_tex->sub_texture, + coords); +} + +static CoglBool +_cogl_atlas_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_gl_texture (atlas_tex->sub_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_atlas_texture_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_gl_flush_legacy_texobj_filters (atlas_tex->sub_texture, + min_filter, mag_filter); +} + +static void +_cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) +{ + CoglTexture *standalone_tex; + + /* Make sure this texture is not in the atlas */ + if (!atlas_tex->atlas) + return; + + COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); + + /* We don't know if any journal entries currently depend on + * OpenGL texture coordinates that would be invalidated by + * migrating textures in this atlas so we flush all journals + * before migrating. + * + * We are assuming that texture atlas migration never happens + * during a flush so we don't have to consider recursion here. + */ + cogl_flush (); + + standalone_tex = + _cogl_atlas_copy_rectangle (atlas_tex->atlas, + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + 1, + atlas_tex->rectangle.width - 2, + atlas_tex->rectangle.height - 2, + atlas_tex->internal_format); + /* Note: we simply silently ignore failures to migrate a texture + * out (most likely due to lack of memory) and hope for the + * best. + * + * Maybe we should find a way to report the problem back to the + * app. + */ + if (!standalone_tex) + return; + + /* Notify cogl-pipeline.c that the texture's underlying GL texture + * storage is changing so it knows it may need to bind a new texture + * if the CoglTexture is reused with the same texture unit. */ + _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex)); + + /* We need to unref the sub texture after doing the copy because + the copy can involve rendering which might cause the texture + to be used if it is used from a layer that is left in a + texture unit */ + cogl_object_unref (atlas_tex->sub_texture); + atlas_tex->sub_texture = standalone_tex; + + _cogl_atlas_texture_remove_from_atlas (atlas_tex); +} + +static void +_cogl_atlas_texture_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + if ((flags & COGL_TEXTURE_NEEDS_MIPMAP)) + /* Mipmaps do not work well with the current atlas so instead + we'll just migrate the texture out and use a regular texture */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* Forward on to the sub texture */ + _cogl_texture_pre_paint (atlas_tex->sub_texture, flags); +} + +static void +_cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Sub textures can't support non-quad rendering so we'll just + migrate the texture out */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* Forward on to the sub texture */ + _cogl_texture_ensure_non_quad_rendering (atlas_tex->sub_texture); +} + +static CoglBool +_cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + CoglBitmap *bmp, + CoglError **error) +{ + CoglAtlas *atlas = atlas_tex->atlas; + + /* Copy the central data */ + if (!_cogl_texture_set_region_from_bitmap (atlas->texture, + src_x, src_y, + dst_width, + dst_height, + bmp, + dst_x + atlas_tex->rectangle.x + 1, + dst_y + atlas_tex->rectangle.y + 1, + 0, /* level 0 */ + error)) + return FALSE; + + /* Update the left edge pixels */ + if (dst_x == 0 && + !_cogl_texture_set_region_from_bitmap (atlas->texture, + src_x, src_y, + 1, dst_height, + bmp, + atlas_tex->rectangle.x, + dst_y + atlas_tex->rectangle.y + 1, + 0, /* level 0 */ + error)) + return FALSE; + /* Update the right edge pixels */ + if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && + !_cogl_texture_set_region_from_bitmap (atlas->texture, + src_x + dst_width - 1, src_y, + 1, dst_height, + bmp, + atlas_tex->rectangle.x + + atlas_tex->rectangle.width - 1, + dst_y + atlas_tex->rectangle.y + 1, + 0, /* level 0 */ + error)) + return FALSE; + /* Update the top edge pixels */ + if (dst_y == 0 && + !_cogl_texture_set_region_from_bitmap (atlas->texture, + src_x, src_y, + dst_width, 1, + bmp, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y, + 0, /* level 0 */ + error)) + return FALSE; + /* Update the bottom edge pixels */ + if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && + !_cogl_texture_set_region_from_bitmap (atlas->texture, + src_x, src_y + dst_height - 1, + dst_width, 1, + bmp, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + + atlas_tex->rectangle.height - 1, + 0, /* level 0 */ + error)) + return FALSE; + + return TRUE; +} + +static CoglBitmap * +_cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex, + CoglBitmap *bmp, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error) +{ + CoglBitmap *upload_bmp; + CoglBitmap *override_bmp; + + /* We'll prepare to upload using the format of the actual texture of + the atlas texture instead of the format reported by + _cogl_texture_get_format which would be the original internal + format specified when the texture was created. However we'll + preserve the premult status of the internal format because the + images are all stored in the original premult format of the + orignal format so we do need to trigger the conversion */ + + internal_format = (COGL_PIXEL_FORMAT_RGBA_8888 | + (internal_format & COGL_PREMULT_BIT)); + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return NULL; + + /* We'll create another bitmap which uses the same data but + overrides the format to remove the premult flag so that uploads + to the atlas texture won't trigger the conversion again */ + + override_bmp = + _cogl_bitmap_new_shared (upload_bmp, + cogl_bitmap_get_format (upload_bmp) & + ~COGL_PREMULT_BIT, + cogl_bitmap_get_width (upload_bmp), + cogl_bitmap_get_height (upload_bmp), + cogl_bitmap_get_rowstride (upload_bmp)); + + cogl_object_unref (upload_bmp); + + return override_bmp; +} + +static CoglBool +_cogl_atlas_texture_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + if (level != 0 && atlas_tex->atlas) + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* If the texture is in the atlas then we need to copy the edge + pixels to the border */ + if (atlas_tex->atlas) + { + CoglBool ret; + CoglBitmap *upload_bmp = + _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, + bmp, + atlas_tex->internal_format, + FALSE, /* can't convert + in place */ + error); + if (!upload_bmp) + return FALSE; + + /* Upload the data ignoring the premult bit */ + ret = _cogl_atlas_texture_set_region_with_border (atlas_tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + upload_bmp, + error); + + cogl_object_unref (upload_bmp); + + return ret; + } + else + /* Otherwise we can just forward on to the sub texture */ + return _cogl_texture_set_region_from_bitmap (atlas_tex->sub_texture, + src_x, src_y, + dst_width, dst_height, + bmp, + dst_x, dst_y, + level, + error); +} + +static CoglPixelFormat +_cogl_atlas_texture_get_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* We don't want to forward this on the sub-texture because it isn't + the necessarily the same format. This will happen if the texture + isn't pre-multiplied */ + return atlas_tex->internal_format; +} + +static GLenum +_cogl_atlas_texture_get_gl_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_gl_get_format (atlas_tex->sub_texture); +} + +static CoglBool +_cogl_atlas_texture_can_use_format (CoglPixelFormat format) +{ + /* We don't care about the ordering or the premult status and we can + accept RGBA or RGB textures. Although we could also accept + luminance and alpha only textures or 16-bit formats it seems that + if the application is explicitly using these formats then they've + got a reason to want the lower memory requirements so putting + them in the atlas might not be a good idea */ + format &= ~(COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT); + return (format == COGL_PIXEL_FORMAT_RGB_888 || + format == COGL_PIXEL_FORMAT_RGBA_8888); +} + +static CoglAtlasTexture * +_cogl_atlas_texture_create_base (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglAtlasTexture *atlas_tex; + + COGL_NOTE (ATLAS, "Adding texture of size %ix%i", width, height); + + /* We need to allocate the texture now because we need the pointer + to set as the data for the rectangle in the atlas */ + atlas_tex = g_new0 (CoglAtlasTexture, 1); + /* Mark it as having no atlas so we don't try to unref it in + _cogl_atlas_texture_post_reorganize_cb */ + atlas_tex->atlas = NULL; + + _cogl_texture_init (COGL_TEXTURE (atlas_tex), + ctx, + width, height, + internal_format, + loader, + &cogl_atlas_texture_vtable); + + atlas_tex->sub_texture = NULL; + + atlas_tex->atlas = NULL; + + return _cogl_atlas_texture_object_new (atlas_tex); +} + +CoglAtlasTexture * +cogl_atlas_texture_new_with_size (CoglContext *ctx, + int width, + int height) +{ + CoglTextureLoader *loader; + + /* We can't atlas zero-sized textures because it breaks the atlas + * data structure */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_atlas_texture_create_base (ctx, width, height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + loader); +} + +static CoglBool +allocate_space (CoglAtlasTexture *atlas_tex, + int width, + int height, + CoglPixelFormat internal_format, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (atlas_tex); + CoglContext *ctx = tex->context; + CoglAtlas *atlas; + GSList *l; + + /* If the texture is in a strange format then we won't use it */ + if (!_cogl_atlas_texture_can_use_format (internal_format)) + { + COGL_NOTE (ATLAS, "Texture can not be added because the " + "format is unsupported"); + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_FORMAT, + "Texture format unsuitable for atlasing"); + return FALSE; + } + + /* If we can't use FBOs then it will be too slow to migrate textures + and we shouldn't use the atlas */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Atlasing disabled because migrations " + "would be too slow"); + return FALSE; + } + + /* Look for an existing atlas that can hold the texture */ + for (l = ctx->atlases; l; l = l->next) + { + /* We need to take a reference on the atlas before trying to + * reserve space because in some circumstances atlas migration + * can cause the atlas to be freed */ + atlas = cogl_object_ref (l->data); + /* Try to make some space in the atlas for the texture */ + if (_cogl_atlas_reserve_space (atlas, + /* Add two pixels for the border */ + width + 2, height + 2, + atlas_tex)) + { + /* keep the atlas reference */ + break; + } + else + { + cogl_object_unref (atlas); + } + } + + /* If we couldn't find a suitable atlas then start another */ + if (l == NULL) + { + atlas = _cogl_atlas_texture_create_atlas (ctx); + COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas); + if (!_cogl_atlas_reserve_space (atlas, + /* Add two pixels for the border */ + width + 2, height + 2, + atlas_tex)) + { + /* Ok, this means we really can't add it to the atlas */ + cogl_object_unref (atlas); + + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY, + "Not enough memory to atlas texture"); + return FALSE; + } + } + + atlas_tex->internal_format = internal_format; + + atlas_tex->atlas = atlas; + + return TRUE; +} + +static CoglBool +allocate_with_size (CoglAtlasTexture *atlas_tex, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (atlas_tex); + CoglPixelFormat internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (allocate_space (atlas_tex, + loader->src.sized.width, + loader->src.sized.height, + internal_format, + error)) + { + _cogl_texture_set_allocated (COGL_TEXTURE (atlas_tex), + internal_format, + loader->src.sized.width, + loader->src.sized.height); + return TRUE; + } + else + return FALSE; +} + +static CoglBool +allocate_from_bitmap (CoglAtlasTexture *atlas_tex, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (atlas_tex); + CoglBitmap *bmp = loader->src.bitmap.bitmap; + CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglPixelFormat internal_format; + CoglBitmap *upload_bmp; + + _COGL_RETURN_VAL_IF_FAIL (atlas_tex->atlas == NULL, FALSE); + + internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); + + upload_bmp = + _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, + bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + if (!allocate_space (atlas_tex, + width, + height, + internal_format, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + /* Defer to set_region so that we can share the code for copying the + edge pixels to the border. */ + if (!_cogl_atlas_texture_set_region_with_border (atlas_tex, + 0, /* src_x */ + 0, /* src_y */ + 0, /* dst_x */ + 0, /* dst_y */ + width, /* dst_width */ + height, /* dst_height */ + upload_bmp, + error)) + { + _cogl_atlas_texture_remove_from_atlas (atlas_tex); + cogl_object_unref (upload_bmp); + return FALSE; + } + + cogl_object_unref (upload_bmp); + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +_cogl_atlas_texture_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (atlas_tex, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (atlas_tex, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +CoglAtlasTexture * +_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, + CoglBool can_convert_in_place) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; + + return _cogl_atlas_texture_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + cogl_bitmap_get_format (bmp), + loader); +} + +CoglAtlasTexture * +cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp) +{ + return _cogl_atlas_texture_new_from_bitmap (bmp, FALSE); +} + +CoglAtlasTexture * +cogl_atlas_texture_new_from_data (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error) +{ + CoglBitmap *bmp; + CoglAtlasTexture *atlas_tex; + + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + atlas_tex = cogl_atlas_texture_new_from_bitmap (bmp); + + cogl_object_unref (bmp); + + if (atlas_tex && + !cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error)) + { + cogl_object_unref (atlas_tex); + return NULL; + } + + return atlas_tex; +} + +CoglAtlasTexture * +cogl_atlas_texture_new_from_file (CoglContext *ctx, + const char *filename, + CoglError **error) +{ + CoglBitmap *bmp; + CoglAtlasTexture *atlas_tex = NULL; + + _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL); + + bmp = cogl_bitmap_new_from_file (filename, error); + if (bmp == NULL) + return NULL; + + atlas_tex = _cogl_atlas_texture_new_from_bitmap (bmp, + TRUE); /* convert in-place */ + + cogl_object_unref (bmp); + + return atlas_tex; +} + +void +_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, + GHookFunc callback, + void *user_data) +{ + GHook *hook = g_hook_alloc (&ctx->atlas_reorganize_callbacks); + hook->func = callback; + hook->data = user_data; + g_hook_prepend (&ctx->atlas_reorganize_callbacks, hook); +} + +void +_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, + GHookFunc callback, + void *user_data) +{ + GHook *hook = g_hook_find_func_data (&ctx->atlas_reorganize_callbacks, + FALSE, + callback, + user_data); + + if (hook) + g_hook_destroy_link (&ctx->atlas_reorganize_callbacks, hook); +} + +static CoglTextureType +_cogl_atlas_texture_get_type (CoglTexture *tex) +{ + return COGL_TEXTURE_TYPE_2D; +} + +static const CoglTextureVtable +cogl_atlas_texture_vtable = + { + FALSE, /* not primitive */ + _cogl_atlas_texture_allocate, + _cogl_atlas_texture_set_region, + NULL, /* get_data */ + _cogl_atlas_texture_foreach_sub_texture_in_region, + _cogl_atlas_texture_get_max_waste, + _cogl_atlas_texture_is_sliced, + _cogl_atlas_texture_can_hardware_repeat, + _cogl_atlas_texture_transform_coords_to_gl, + _cogl_atlas_texture_transform_quad_coords_to_gl, + _cogl_atlas_texture_get_gl_texture, + _cogl_atlas_texture_gl_flush_legacy_texobj_filters, + _cogl_atlas_texture_pre_paint, + _cogl_atlas_texture_ensure_non_quad_rendering, + _cogl_atlas_texture_gl_flush_legacy_texobj_wrap_modes, + _cogl_atlas_texture_get_format, + _cogl_atlas_texture_get_gl_format, + _cogl_atlas_texture_get_type, + NULL, /* is_foreign */ + NULL /* set_auto_mipmap */ + }; diff --git a/cogl/cogl/cogl-atlas-texture.h b/cogl/cogl/cogl-atlas-texture.h new file mode 100644 index 000000000..79c15b9e9 --- /dev/null +++ b/cogl/cogl/cogl-atlas-texture.h @@ -0,0 +1,258 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef _COGL_ATLAS_TEXTURE_H_ +#define _COGL_ATLAS_TEXTURE_H_ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-atlas-texture + * @short_description: Functions for managing textures in Cogl's global + * set of texture atlases + * + * A texture atlas is a texture that contains many smaller images that + * an application is interested in. These are packed together as a way + * of optimizing drawing with those images by avoiding the costs of + * repeatedly telling the hardware to change what texture it should + * sample from. This can enable more geometry to be batched together + * into few draw calls. + * + * Each #CoglContext has an shared, pool of texture atlases that are + * are managed by Cogl. + * + * This api lets applications upload texture data into one of Cogl's + * shared texture atlases using a high-level #CoglAtlasTexture which + * represents a sub-region of one of these atlases. + * + * A #CoglAtlasTexture is a high-level meta texture which has + * some limitations to be aware of. Please see the documentation for + * #CoglMetaTexture for more details. + */ + + +typedef struct _CoglAtlasTexture CoglAtlasTexture; +#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_atlas_texture_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_atlas_texture_get_gtype (void); +#endif + +/** + * cogl_atlas_texture_new_with_size: + * @ctx: A #CoglContext + * @width: The width of your atlased texture. + * @height: The height of your atlased texture. + * + * Creates a #CoglAtlasTexture with a given @width and @height. A + * #CoglAtlasTexture represents a sub-region within one of Cogl's + * shared texture atlases. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Allocate call can fail if Cogl considers the internal + * format to be incompatible with the format of its internal + * atlases. + * + * The returned #CoglAtlasTexture is a high-level meta-texture + * with some limitations. See the documentation for #CoglMetaTexture + * for more details. + * + * Returns: (transfer full): A new #CoglAtlasTexture object. + * Since: 1.16 + * Stability: unstable + */ +CoglAtlasTexture * +cogl_atlas_texture_new_with_size (CoglContext *ctx, + int width, + int height); + +/** + * cogl_atlas_texture_new_from_file: + * @ctx: A #CoglContext + * @filename: the file to load + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a #CoglAtlasTexture from an image file. A #CoglAtlasTexture + * represents a sub-region within one of Cogl's shared texture + * atlases. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Allocate call can fail if Cogl considers the internal + * format to be incompatible with the format of its internal + * atlases. + * + * The returned #CoglAtlasTexture is a high-level meta-texture + * with some limitations. See the documentation for #CoglMetaTexture + * for more details. + * + * Return value: (transfer full): A new #CoglAtlasTexture object or + * %NULL on failure and @error will be updated. + * Since: 1.16 + * Stability: unstable + */ +CoglAtlasTexture * +cogl_atlas_texture_new_from_file (CoglContext *ctx, + const char *filename, + CoglError **error); + +/** + * cogl_atlas_texture_new_from_data: + * @ctx: A #CoglContext + * @width: width of texture in pixels + * @height: height of texture in pixels + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @rowstride: the memory offset in bytes between the start of each + * row in @data. A value of 0 will make Cogl automatically + * calculate @rowstride from @width and @format. + * @data: pointer to the memory region where the source buffer resides + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a new #CoglAtlasTexture texture based on data residing in + * memory. A #CoglAtlasTexture represents a sub-region within one of + * Cogl's shared texture atlases. + * + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_atlas_texture_new_from_bitmap() or use + * cogl_atlas_texture_new_with_size() and then upload data using + * cogl_texture_set_data() + * + * Allocate call can fail if Cogl considers the internal + * format to be incompatible with the format of its internal + * atlases. + * + * The returned #CoglAtlasTexture is a high-level + * meta-texture with some limitations. See the documentation for + * #CoglMetaTexture for more details. + * + * Return value: (transfer full): A new #CoglAtlasTexture object or + * %NULL on failure and @error will be updated. + * Since: 1.16 + * Stability: unstable + */ +CoglAtlasTexture * +cogl_atlas_texture_new_from_data (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error); + +/** + * cogl_atlas_texture_new_from_bitmap: + * @bitmap: A #CoglBitmap + * + * Creates a new #CoglAtlasTexture texture based on data residing in a + * @bitmap. A #CoglAtlasTexture represents a sub-region within one of + * Cogl's shared texture atlases. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Allocate call can fail if Cogl considers the internal + * format to be incompatible with the format of its internal + * atlases. + * + * The returned #CoglAtlasTexture is a high-level meta-texture + * with some limitations. See the documentation for #CoglMetaTexture + * for more details. + * + * Returns: (transfer full): A new #CoglAtlasTexture object. + * Since: 1.16 + * Stability: unstable + */ +CoglAtlasTexture * +cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp); + +/** + * cogl_is_atlas_texture: + * @object: a #CoglObject + * + * Checks whether the given object references a #CoglAtlasTexture + * + * Return value: %TRUE if the passed object represents an atlas + * texture and %FALSE otherwise + * + * Since: 1.16 + * Stability: Unstable + */ +CoglBool +cogl_is_atlas_texture (void *object); + +COGL_END_DECLS + +#endif /* _COGL_ATLAS_TEXTURE_H_ */ diff --git a/cogl/cogl/cogl-atlas.c b/cogl/cogl/cogl-atlas.c new file mode 100644 index 000000000..0fd8b7227 --- /dev/null +++ b/cogl/cogl/cogl-atlas.c @@ -0,0 +1,690 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-atlas.h" +#include "cogl-rectangle-map.h" +#include "cogl-context-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-2d-sliced.h" +#include "cogl-texture-driver.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-debug.h" +#include "cogl-framebuffer-private.h" +#include "cogl-blit.h" +#include "cogl-private.h" + +#include + +static void _cogl_atlas_free (CoglAtlas *atlas); + +COGL_OBJECT_INTERNAL_DEFINE (Atlas, atlas); + +CoglAtlas * +_cogl_atlas_new (CoglPixelFormat texture_format, + CoglAtlasFlags flags, + CoglAtlasUpdatePositionCallback update_position_cb) +{ + CoglAtlas *atlas = g_new (CoglAtlas, 1); + + atlas->update_position_cb = update_position_cb; + atlas->map = NULL; + atlas->texture = NULL; + atlas->flags = flags; + atlas->texture_format = texture_format; + g_hook_list_init (&atlas->pre_reorganize_callbacks, sizeof (GHook)); + g_hook_list_init (&atlas->post_reorganize_callbacks, sizeof (GHook)); + + return _cogl_atlas_object_new (atlas); +} + +static void +_cogl_atlas_free (CoglAtlas *atlas) +{ + COGL_NOTE (ATLAS, "%p: Atlas destroyed", atlas); + + if (atlas->texture) + cogl_object_unref (atlas->texture); + if (atlas->map) + _cogl_rectangle_map_free (atlas->map); + + g_hook_list_clear (&atlas->pre_reorganize_callbacks); + g_hook_list_clear (&atlas->post_reorganize_callbacks); + + g_free (atlas); +} + +typedef struct _CoglAtlasRepositionData +{ + /* The current user data for this texture */ + void *user_data; + /* The old and new positions of the texture */ + CoglRectangleMapEntry old_position; + CoglRectangleMapEntry new_position; +} CoglAtlasRepositionData; + +static void +_cogl_atlas_migrate (CoglAtlas *atlas, + unsigned int n_textures, + CoglAtlasRepositionData *textures, + CoglTexture *old_texture, + CoglTexture *new_texture, + void *skip_user_data) +{ + unsigned int i; + CoglBlitData blit_data; + + /* If the 'disable migrate' flag is set then we won't actually copy + the textures to their new location. Instead we'll just invoke the + callback to update the position */ + if ((atlas->flags & COGL_ATLAS_DISABLE_MIGRATION)) + for (i = 0; i < n_textures; i++) + /* Update the texture position */ + atlas->update_position_cb (textures[i].user_data, + new_texture, + &textures[i].new_position); + else + { + _cogl_blit_begin (&blit_data, new_texture, old_texture); + + for (i = 0; i < n_textures; i++) + { + /* Skip the texture that is being added because it doesn't contain + any data yet */ + if (textures[i].user_data != skip_user_data) + _cogl_blit (&blit_data, + textures[i].old_position.x, + textures[i].old_position.y, + textures[i].new_position.x, + textures[i].new_position.y, + textures[i].new_position.width, + textures[i].new_position.height); + + /* Update the texture position */ + atlas->update_position_cb (textures[i].user_data, + new_texture, + &textures[i].new_position); + } + + _cogl_blit_end (&blit_data); + } +} + +typedef struct _CoglAtlasGetRectanglesData +{ + CoglAtlasRepositionData *textures; + /* Number of textures found so far */ + unsigned int n_textures; +} CoglAtlasGetRectanglesData; + +static void +_cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle, + void *rect_data, + void *user_data) +{ + CoglAtlasGetRectanglesData *data = user_data; + + data->textures[data->n_textures].old_position = *rectangle; + data->textures[data->n_textures++].user_data = rect_data; +} + +static void +_cogl_atlas_get_next_size (unsigned int *map_width, + unsigned int *map_height) +{ + /* Double the size of the texture by increasing whichever dimension + is smaller */ + if (*map_width < *map_height) + *map_width <<= 1; + else + *map_height <<= 1; +} + +static void +_cogl_atlas_get_initial_size (CoglPixelFormat format, + unsigned int *map_width, + unsigned int *map_height) +{ + unsigned int size; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_intformat, + &gl_format, + &gl_type); + + /* At least on Intel hardware, the texture size will be rounded up + to at least 1MB so we might as well try to aim for that as an + initial minimum size. If the format is only 1 byte per pixel we + can use 1024x1024, otherwise we'll assume it will take 4 bytes + per pixel and use 512x512. */ + if (_cogl_pixel_format_get_bytes_per_pixel (format) == 1) + size = 1024; + else + size = 512; + + /* Some platforms might not support this large size so we'll + decrease the size until it can */ + while (size > 1 && + !ctx->texture_driver->size_supported (ctx, + GL_TEXTURE_2D, + gl_intformat, + gl_format, + gl_type, + size, size)) + size >>= 1; + + *map_width = size; + *map_height = size; +} + +static CoglRectangleMap * +_cogl_atlas_create_map (CoglPixelFormat format, + unsigned int map_width, + unsigned int map_height, + unsigned int n_textures, + CoglAtlasRepositionData *textures) +{ + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + _COGL_GET_CONTEXT (ctx, NULL); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_intformat, + &gl_format, + &gl_type); + + /* Keep trying increasingly larger atlases until we can fit all of + the textures */ + while (ctx->texture_driver->size_supported (ctx, + GL_TEXTURE_2D, + gl_intformat, + gl_format, + gl_type, + map_width, map_height)) + { + CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width, + map_height, + NULL); + unsigned int i; + + COGL_NOTE (ATLAS, "Trying to resize the atlas to %ux%u", + map_width, map_height); + + /* Add all of the textures and keep track of the new position */ + for (i = 0; i < n_textures; i++) + if (!_cogl_rectangle_map_add (new_atlas, + textures[i].old_position.width, + textures[i].old_position.height, + textures[i].user_data, + &textures[i].new_position)) + break; + + /* If the atlas can contain all of the textures then we have a + winner */ + if (i >= n_textures) + return new_atlas; + else + COGL_NOTE (ATLAS, "Atlas size abandoned after trying " + "%u out of %u textures", + i, n_textures); + + _cogl_rectangle_map_free (new_atlas); + _cogl_atlas_get_next_size (&map_width, &map_height); + } + + /* If we get here then there's no atlas that can accommodate all of + the rectangles */ + + return NULL; +} + +static CoglTexture2D * +_cogl_atlas_create_texture (CoglAtlas *atlas, + int width, + int height) +{ + CoglTexture2D *tex; + CoglError *ignore_error = NULL; + + _COGL_GET_CONTEXT (ctx, NULL); + + if ((atlas->flags & COGL_ATLAS_CLEAR_TEXTURE)) + { + uint8_t *clear_data; + CoglBitmap *clear_bmp; + int bpp = _cogl_pixel_format_get_bytes_per_pixel (atlas->texture_format); + + /* Create a buffer of zeroes to initially clear the texture */ + clear_data = g_malloc0 (width * height * bpp); + clear_bmp = cogl_bitmap_new_for_data (ctx, + width, + height, + atlas->texture_format, + width * bpp, + clear_data); + + tex = cogl_texture_2d_new_from_bitmap (clear_bmp); + + _cogl_texture_set_internal_format (COGL_TEXTURE (tex), + atlas->texture_format); + + if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) + { + cogl_error_free (ignore_error); + cogl_object_unref (tex); + tex = NULL; + } + + cogl_object_unref (clear_bmp); + + g_free (clear_data); + } + else + { + tex = cogl_texture_2d_new_with_size (ctx, width, height); + + _cogl_texture_set_internal_format (COGL_TEXTURE (tex), + atlas->texture_format); + + if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) + { + cogl_error_free (ignore_error); + cogl_object_unref (tex); + tex = NULL; + } + } + + return tex; +} + +static int +_cogl_atlas_compare_size_cb (const void *a, + const void *b) +{ + const CoglAtlasRepositionData *ta = a; + const CoglAtlasRepositionData *tb = b; + unsigned int a_size, b_size; + + a_size = ta->old_position.width * ta->old_position.height; + b_size = tb->old_position.width * tb->old_position.height; + + return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; +} + +static void +_cogl_atlas_notify_pre_reorganize (CoglAtlas *atlas) +{ + g_hook_list_invoke (&atlas->pre_reorganize_callbacks, FALSE); +} + +static void +_cogl_atlas_notify_post_reorganize (CoglAtlas *atlas) +{ + g_hook_list_invoke (&atlas->post_reorganize_callbacks, FALSE); +} + +CoglBool +_cogl_atlas_reserve_space (CoglAtlas *atlas, + unsigned int width, + unsigned int height, + void *user_data) +{ + CoglAtlasGetRectanglesData data; + CoglRectangleMap *new_map; + CoglTexture2D *new_tex; + unsigned int map_width = 0, map_height = 0; + CoglBool ret; + CoglRectangleMapEntry new_position; + + /* Check if we can fit the rectangle into the existing map */ + if (atlas->map && + _cogl_rectangle_map_add (atlas->map, width, height, + user_data, + &new_position)) + { + COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + /* waste as a percentage */ + _cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map))); + + atlas->update_position_cb (user_data, + atlas->texture, + &new_position); + + return TRUE; + } + + /* If we make it here then we need to reorganize the atlas. First + we'll notify any users of the atlas that this is going to happen + so that for example in CoglAtlasTexture it can notify that the + storage has changed and cause a flush */ + _cogl_atlas_notify_pre_reorganize (atlas); + + /* Get an array of all the textures currently in the atlas. */ + data.n_textures = 0; + if (atlas->map == NULL) + data.textures = g_malloc (sizeof (CoglAtlasRepositionData)); + else + { + unsigned int n_rectangles = + _cogl_rectangle_map_get_n_rectangles (atlas->map); + data.textures = g_malloc (sizeof (CoglAtlasRepositionData) * + (n_rectangles + 1)); + _cogl_rectangle_map_foreach (atlas->map, + _cogl_atlas_get_rectangles_cb, + &data); + } + + /* Add the new rectangle as a dummy texture so that it can be + positioned with the rest */ + data.textures[data.n_textures].old_position.x = 0; + data.textures[data.n_textures].old_position.y = 0; + data.textures[data.n_textures].old_position.width = width; + data.textures[data.n_textures].old_position.height = height; + data.textures[data.n_textures++].user_data = user_data; + + /* The atlasing algorithm works a lot better if the rectangles are + added in decreasing order of size so we'll first sort the + array */ + qsort (data.textures, data.n_textures, + sizeof (CoglAtlasRepositionData), + _cogl_atlas_compare_size_cb); + + /* Try to create a new atlas that can contain all of the textures */ + if (atlas->map) + { + map_width = _cogl_rectangle_map_get_width (atlas->map); + map_height = _cogl_rectangle_map_get_height (atlas->map); + + /* If there is enough space in for the new rectangle in the + existing atlas with at least 6% waste we'll start with the + same size, otherwise we'll immediately double it */ + if ((map_width * map_height - + _cogl_rectangle_map_get_remaining_space (atlas->map) + + width * height) * 53 / 50 > + map_width * map_height) + _cogl_atlas_get_next_size (&map_width, &map_height); + } + else + _cogl_atlas_get_initial_size (atlas->texture_format, + &map_width, &map_height); + + new_map = _cogl_atlas_create_map (atlas->texture_format, + map_width, map_height, + data.n_textures, data.textures); + + /* If we can't create a map with the texture then give up */ + if (new_map == NULL) + { + COGL_NOTE (ATLAS, "%p: Could not fit texture in the atlas", atlas); + ret = FALSE; + } + /* We need to migrate the existing textures into a new texture */ + else if ((new_tex = _cogl_atlas_create_texture + (atlas, + _cogl_rectangle_map_get_width (new_map), + _cogl_rectangle_map_get_height (new_map))) == NULL) + { + COGL_NOTE (ATLAS, "%p: Could not create a CoglTexture2D", atlas); + _cogl_rectangle_map_free (new_map); + ret = FALSE; + } + else + { + int waste; + + COGL_NOTE (ATLAS, + "%p: Atlas %s with size %ix%i", + atlas, + atlas->map == NULL || + _cogl_rectangle_map_get_width (atlas->map) != + _cogl_rectangle_map_get_width (new_map) || + _cogl_rectangle_map_get_height (atlas->map) != + _cogl_rectangle_map_get_height (new_map) ? + "resized" : "reorganized", + _cogl_rectangle_map_get_width (new_map), + _cogl_rectangle_map_get_height (new_map)); + + if (atlas->map) + { + /* Move all the textures to the right position in the new + texture. This will also update the texture's rectangle */ + _cogl_atlas_migrate (atlas, + data.n_textures, + data.textures, + atlas->texture, + COGL_TEXTURE (new_tex), + user_data); + _cogl_rectangle_map_free (atlas->map); + cogl_object_unref (atlas->texture); + } + else + /* We know there's only one texture so we can just directly + update the rectangle from its new position */ + atlas->update_position_cb (data.textures[0].user_data, + COGL_TEXTURE (new_tex), + &data.textures[0].new_position); + + atlas->map = new_map; + atlas->texture = COGL_TEXTURE (new_tex); + + waste = (_cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map))); + + COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + waste); + + ret = TRUE; + } + + g_free (data.textures); + + _cogl_atlas_notify_post_reorganize (atlas); + + return ret; +} + +void +_cogl_atlas_remove (CoglAtlas *atlas, + const CoglRectangleMapEntry *rectangle) +{ + _cogl_rectangle_map_remove (atlas->map, rectangle); + + COGL_NOTE (ATLAS, "%p: Removed rectangle sized %ix%i", + atlas, + rectangle->width, + rectangle->height); + COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + _cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map))); +}; + +static CoglTexture * +create_migration_texture (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + CoglError *skip_error = NULL; + + if ((_cogl_util_is_pot (width) && _cogl_util_is_pot (height)) || + (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + /* First try creating a fast-path non-sliced texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, + width, height)); + + _cogl_texture_set_internal_format (tex, internal_format); + + /* TODO: instead of allocating storage here it would be better + * if we had some api that let us just check that the size is + * supported by the hardware so storage could be allocated + * lazily when uploading data. */ + if (!cogl_texture_allocate (tex, &skip_error)) + { + cogl_error_free (skip_error); + cogl_object_unref (tex); + tex = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_with_size (ctx, + width, + height, + COGL_TEXTURE_MAX_WASTE); + + _cogl_texture_set_internal_format (COGL_TEXTURE (tex_2ds), + internal_format); + + tex = COGL_TEXTURE (tex_2ds); + } + + return tex; +} + +CoglTexture * +_cogl_atlas_copy_rectangle (CoglAtlas *atlas, + int x, + int y, + int width, + int height, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + CoglBlitData blit_data; + CoglError *ignore_error = NULL; + + _COGL_GET_CONTEXT (ctx, NULL); + + /* Create a new texture at the right size */ + tex = create_migration_texture (ctx, width, height, internal_format); + if (!cogl_texture_allocate (tex, &ignore_error)) + { + cogl_error_free (ignore_error); + cogl_object_unref (tex); + return NULL; + } + + /* Blit the data out of the atlas to the new texture. If FBOs + aren't available this will end up having to copy the entire + atlas texture */ + _cogl_blit_begin (&blit_data, tex, atlas->texture); + _cogl_blit (&blit_data, + x, y, + 0, 0, + width, height); + _cogl_blit_end (&blit_data); + + return tex; +} + +void +_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, + GHookFunc pre_callback, + GHookFunc post_callback, + void *user_data) +{ + if (pre_callback) + { + GHook *hook = g_hook_alloc (&atlas->post_reorganize_callbacks); + hook->func = pre_callback; + hook->data = user_data; + g_hook_prepend (&atlas->pre_reorganize_callbacks, hook); + } + if (post_callback) + { + GHook *hook = g_hook_alloc (&atlas->pre_reorganize_callbacks); + hook->func = post_callback; + hook->data = user_data; + g_hook_prepend (&atlas->post_reorganize_callbacks, hook); + } +} + +void +_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, + GHookFunc pre_callback, + GHookFunc post_callback, + void *user_data) +{ + if (pre_callback) + { + GHook *hook = g_hook_find_func_data (&atlas->pre_reorganize_callbacks, + FALSE, + pre_callback, + user_data); + if (hook) + g_hook_destroy_link (&atlas->pre_reorganize_callbacks, hook); + } + if (post_callback) + { + GHook *hook = g_hook_find_func_data (&atlas->post_reorganize_callbacks, + FALSE, + post_callback, + user_data); + if (hook) + g_hook_destroy_link (&atlas->post_reorganize_callbacks, hook); + } +} diff --git a/cogl/cogl/cogl-atlas.h b/cogl/cogl/cogl-atlas.h new file mode 100644 index 000000000..e9b3fb069 --- /dev/null +++ b/cogl/cogl/cogl-atlas.h @@ -0,0 +1,105 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_ATLAS_H +#define __COGL_ATLAS_H + +#include "cogl-rectangle-map.h" +#include "cogl-object-private.h" +#include "cogl-texture.h" + +typedef void +(* CoglAtlasUpdatePositionCallback) (void *user_data, + CoglTexture *new_texture, + const CoglRectangleMapEntry *rect); + +typedef enum +{ + COGL_ATLAS_CLEAR_TEXTURE = (1 << 0), + COGL_ATLAS_DISABLE_MIGRATION = (1 << 1) +} CoglAtlasFlags; + +typedef struct _CoglAtlas CoglAtlas; + +#define COGL_ATLAS(object) ((CoglAtlas *) object) + +struct _CoglAtlas +{ + CoglObject _parent; + + CoglRectangleMap *map; + + CoglTexture *texture; + CoglPixelFormat texture_format; + CoglAtlasFlags flags; + + CoglAtlasUpdatePositionCallback update_position_cb; + + GHookList pre_reorganize_callbacks; + GHookList post_reorganize_callbacks; +}; + +CoglAtlas * +_cogl_atlas_new (CoglPixelFormat texture_format, + CoglAtlasFlags flags, + CoglAtlasUpdatePositionCallback update_position_cb); + +CoglBool +_cogl_atlas_reserve_space (CoglAtlas *atlas, + unsigned int width, + unsigned int height, + void *user_data); + +void +_cogl_atlas_remove (CoglAtlas *atlas, + const CoglRectangleMapEntry *rectangle); + +CoglTexture * +_cogl_atlas_copy_rectangle (CoglAtlas *atlas, + int x, + int y, + int width, + int height, + CoglPixelFormat format); + +void +_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, + GHookFunc pre_callback, + GHookFunc post_callback, + void *user_data); + +void +_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, + GHookFunc pre_callback, + GHookFunc post_callback, + void *user_data); + +CoglBool +_cogl_is_atlas (void *object); + +#endif /* __COGL_ATLAS_H */ diff --git a/cogl/cogl/cogl-attribute-buffer-private.h b/cogl/cogl/cogl-attribute-buffer-private.h new file mode 100644 index 000000000..c5e8a276d --- /dev/null +++ b/cogl/cogl/cogl-attribute-buffer-private.h @@ -0,0 +1,44 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_ATTRIBUTE_BUFFER_PRIVATE_H +#define __COGL_ATTRIBUTE_BUFFER_PRIVATE_H + +#include "cogl-buffer-private.h" + +struct _CoglAttributeBuffer +{ + CoglBuffer _parent; +}; + +#endif /* __COGL_ATTRIBUTE_BUFFER_PRIVATE_H */ diff --git a/cogl/cogl/cogl-attribute-buffer.c b/cogl/cogl/cogl-attribute-buffer.c new file mode 100644 index 000000000..8d92d29da --- /dev/null +++ b/cogl/cogl/cogl-attribute-buffer.c @@ -0,0 +1,104 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-object-private.h" +#include "cogl-attribute-buffer.h" +#include "cogl-attribute-buffer-private.h" +#include "cogl-context-private.h" +#include "cogl-gtype-private.h" + +static void _cogl_attribute_buffer_free (CoglAttributeBuffer *array); + +COGL_BUFFER_DEFINE (AttributeBuffer, attribute_buffer); +COGL_GTYPE_DEFINE_CLASS (AttributeBuffer, attribute_buffer); + +CoglAttributeBuffer * +cogl_attribute_buffer_new_with_size (CoglContext *context, + size_t bytes) +{ + CoglAttributeBuffer *buffer = g_slice_new (CoglAttributeBuffer); + + /* parent's constructor */ + _cogl_buffer_initialize (COGL_BUFFER (buffer), + context, + bytes, + COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, + COGL_BUFFER_USAGE_HINT_ATTRIBUTE_BUFFER, + COGL_BUFFER_UPDATE_HINT_STATIC); + + return _cogl_attribute_buffer_object_new (buffer); +} + +CoglAttributeBuffer * +cogl_attribute_buffer_new (CoglContext *context, + size_t bytes, + const void *data) +{ + CoglAttributeBuffer *buffer; + + buffer = cogl_attribute_buffer_new_with_size (context, bytes); + + /* Note: to keep the common cases simple this API doesn't throw + * CoglErrors, so developers can assume this function never returns + * NULL and we will simply abort on error. + * + * Developers wanting to catch errors can use + * cogl_attribute_buffer_new_with_size() and catch errors when later + * calling cogl_buffer_set_data() or cogl_buffer_map(). + */ + + /* XXX: NB: for Cogl 2.0 we don't allow NULL data here but we can't + * break the api for 1.x and so we keep the check for now. */ + if (data) + _cogl_buffer_set_data (COGL_BUFFER (buffer), + 0, + data, + bytes, + NULL); + + return buffer; +} + +static void +_cogl_attribute_buffer_free (CoglAttributeBuffer *array) +{ + /* parent's destructor */ + _cogl_buffer_fini (COGL_BUFFER (array)); + + g_slice_free (CoglAttributeBuffer, array); +} + diff --git a/cogl/cogl/cogl-attribute-buffer.h b/cogl/cogl/cogl-attribute-buffer.h new file mode 100644 index 000000000..189d81ebe --- /dev/null +++ b/cogl/cogl/cogl-attribute-buffer.h @@ -0,0 +1,152 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_ATTRIBUTE_BUFFER_H__ +#define __COGL_ATTRIBUTE_BUFFER_H__ + +/* We forward declare the CoglAttributeBuffer type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglAttributeBuffer CoglAttributeBuffer; + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-attribute-buffer + * @short_description: Functions for creating and manipulating attribute + * buffers + * + * FIXME + */ + +#define COGL_ATTRIBUTE_BUFFER(buffer) ((CoglAttributeBuffer *)(buffer)) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_attribute_buffer_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_attribute_buffer_get_gtype (void); +#endif + +/** + * cogl_attribute_buffer_new_with_size: + * @context: A #CoglContext + * @bytes: The number of bytes to allocate for vertex attribute data. + * + * Describes a new #CoglAttributeBuffer of @size bytes to contain + * arrays of vertex attribute data. Afterwards data can be set using + * cogl_buffer_set_data() or by mapping it into the application's + * address space using cogl_buffer_map(). + * + * The underlying storage of this buffer isn't allocated by this + * function so that you have an opportunity to use the + * cogl_buffer_set_update_hint() and cogl_buffer_set_usage_hint() + * functions which may influence how the storage is allocated. The + * storage will be allocated once you upload data to the buffer. + * + * Note: You can assume this function always succeeds and won't return + * %NULL + * + * Return value: (transfer full): A newly allocated #CoglAttributeBuffer. Never %NULL. + * + * Stability: Unstable + */ +CoglAttributeBuffer * +cogl_attribute_buffer_new_with_size (CoglContext *context, + size_t bytes); + +/** + * cogl_attribute_buffer_new: + * @context: A #CoglContext + * @bytes: The number of bytes to allocate for vertex attribute data. + * @data: (array length=bytes): An optional pointer to vertex data to + * upload immediately. + * + * Describes a new #CoglAttributeBuffer of @size bytes to contain + * arrays of vertex attribute data and also uploads @size bytes read + * from @data to the new buffer. + * + * You should never pass a %NULL data pointer. + * + * This function does not report out-of-memory errors back to + * the caller by returning %NULL and so you can assume this function + * always succeeds. + * + * In the unlikely case that there is an out of memory problem + * then Cogl will abort the application with a message. If your + * application needs to gracefully handle out-of-memory errors then + * you can use cogl_attribute_buffer_new_with_size() and then + * explicitly catch errors with cogl_buffer_set_data() or + * cogl_buffer_map(). + * + * Return value: (transfer full): A newly allocated #CoglAttributeBuffer (never %NULL) + * + * Since: 1.4 + * Stability: Unstable + */ +CoglAttributeBuffer * +cogl_attribute_buffer_new (CoglContext *context, + size_t bytes, + const void *data); + +/** + * cogl_is_attribute_buffer: + * @object: A #CoglObject + * + * Gets whether the given object references a #CoglAttributeBuffer. + * + * Returns: %TRUE if @object references a #CoglAttributeBuffer, + * %FALSE otherwise + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_is_attribute_buffer (void *object); + +COGL_END_DECLS + +#endif /* __COGL_ATTRIBUTE_BUFFER_H__ */ + diff --git a/cogl/cogl/cogl-attribute-private.h b/cogl/cogl/cogl-attribute-private.h new file mode 100644 index 000000000..aac4a887d --- /dev/null +++ b/cogl/cogl/cogl-attribute-private.h @@ -0,0 +1,140 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_ATTRIBUTE_PRIVATE_H +#define __COGL_ATTRIBUTE_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-attribute.h" +#include "cogl-framebuffer.h" +#include "cogl-pipeline-private.h" +#include "cogl-boxed-value.h" + +typedef enum +{ + COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY, + COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY, + COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY, + COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY, + COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY, + COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY +} CoglAttributeNameID; + +typedef struct _CoglAttributeNameState +{ + char *name; + CoglAttributeNameID name_id; + int name_index; + CoglBool normalized_default; + int layer_number; +} CoglAttributeNameState; + +struct _CoglAttribute +{ + CoglObject _parent; + + const CoglAttributeNameState *name_state; + CoglBool normalized; + + CoglBool is_buffered; + + union { + struct { + CoglAttributeBuffer *attribute_buffer; + size_t stride; + size_t offset; + int n_components; + CoglAttributeType type; + } buffered; + struct { + CoglContext *context; + CoglBoxedValue boxed; + } constant; + } d; + + int immutable_ref; +}; + +typedef enum +{ + COGL_DRAW_SKIP_JOURNAL_FLUSH = 1 << 0, + COGL_DRAW_SKIP_PIPELINE_VALIDATION = 1 << 1, + COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH = 1 << 2, + COGL_DRAW_SKIP_LEGACY_STATE = 1 << 3, + /* By default the vertex attribute drawing code will assume that if + there is a color attribute array enabled then we can't determine + if the colors will be opaque so we need to enabling + blending. However when drawing from the journal we know what the + contents of the color array is so we can override this by passing + this flag. */ + COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE = 1 << 4, + /* This forcibly disables the debug option to divert all drawing to + * wireframes */ + COGL_DRAW_SKIP_DEBUG_WIREFRAME = 1 << 5 +} CoglDrawFlags; + +/* During CoglContext initialization we register the "cogl_color_in" + * attribute name so it gets a global name_index of 0. We need to know + * the name_index for "cogl_color_in" in + * _cogl_pipeline_flush_gl_state() */ +#define COGL_ATTRIBUTE_COLOR_NAME_INDEX 0 + +CoglAttributeNameState * +_cogl_attribute_register_attribute_name (CoglContext *context, + const char *name); + +CoglAttribute * +_cogl_attribute_immutable_ref (CoglAttribute *attribute); + +void +_cogl_attribute_immutable_unref (CoglAttribute *attribute); + +typedef struct +{ + int unit; + CoglPipelineFlushOptions options; + uint32_t fallback_layers; +} CoglFlushLayerState; + +void +_cogl_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes); + +int +_cogl_attribute_get_n_components (CoglAttribute *attribute); + +#endif /* __COGL_ATTRIBUTE_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-attribute.c b/cogl/cogl/cogl-attribute.c new file mode 100644 index 000000000..bcfbf78c6 --- /dev/null +++ b/cogl/cogl/cogl-attribute.c @@ -0,0 +1,687 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-journal-private.h" +#include "cogl-attribute.h" +#include "cogl-attribute-private.h" +#include "cogl-pipeline.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-texture-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-indices-private.h" +#ifdef COGL_PIPELINE_PROGEND_GLSL +#include "cogl-pipeline-progend-glsl-private.h" +#endif +#include "cogl-private.h" +#include "cogl-gtype-private.h" + +#include +#include +#include + +/* This isn't defined in the GLES headers */ +#ifndef GL_UNSIGNED_INT +#define GL_UNSIGNED_INT 0x1405 +#endif + +static void _cogl_attribute_free (CoglAttribute *attribute); + +COGL_OBJECT_DEFINE (Attribute, attribute); +COGL_GTYPE_DEFINE_CLASS (Attribute, attribute); + +static CoglBool +validate_cogl_attribute_name (const char *name, + char **real_attribute_name, + CoglAttributeNameID *name_id, + CoglBool *normalized, + int *layer_number) +{ + name = name + 5; /* skip "cogl_" */ + + *normalized = FALSE; + *layer_number = 0; + + if (strcmp (name, "position_in") == 0) + *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY; + else if (strcmp (name, "color_in") == 0) + { + *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY; + *normalized = TRUE; + } + else if (strcmp (name, "tex_coord_in") == 0) + { + *real_attribute_name = "cogl_tex_coord0_in"; + *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY; + } + else if (strncmp (name, "tex_coord", strlen ("tex_coord")) == 0) + { + char *endptr; + *layer_number = strtoul (name + 9, &endptr, 10); + if (strcmp (endptr, "_in") != 0) + { + g_warning ("Texture coordinate attributes should either be named " + "\"cogl_tex_coord_in\" or named with a texture unit index " + "like \"cogl_tex_coord2_in\"\n"); + return FALSE; + } + *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY; + } + else if (strcmp (name, "normal_in") == 0) + { + *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY; + *normalized = TRUE; + } + else if (strcmp (name, "point_size_in") == 0) + *name_id = COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY; + else + { + g_warning ("Unknown cogl_* attribute name cogl_%s\n", name); + return FALSE; + } + + return TRUE; +} + +CoglAttributeNameState * +_cogl_attribute_register_attribute_name (CoglContext *context, + const char *name) +{ + CoglAttributeNameState *name_state = g_new (CoglAttributeNameState, 1); + int name_index = context->n_attribute_names++; + char *name_copy = g_strdup (name); + + name_state->name = NULL; + name_state->name_index = name_index; + if (strncmp (name, "cogl_", 5) == 0) + { + if (!validate_cogl_attribute_name (name, + &name_state->name, + &name_state->name_id, + &name_state->normalized_default, + &name_state->layer_number)) + goto error; + } + else + { + name_state->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY; + name_state->normalized_default = FALSE; + name_state->layer_number = 0; + } + + if (name_state->name == NULL) + name_state->name = name_copy; + + g_hash_table_insert (context->attribute_name_states_hash, + name_copy, name_state); + + if (G_UNLIKELY (context->attribute_name_index_map == NULL)) + context->attribute_name_index_map = + g_array_new (FALSE, FALSE, sizeof (void *)); + + g_array_set_size (context->attribute_name_index_map, name_index + 1); + + g_array_index (context->attribute_name_index_map, + CoglAttributeNameState *, name_index) = name_state; + + return name_state; + +error: + g_free (name_state); + return NULL; +} + +static CoglBool +validate_n_components (const CoglAttributeNameState *name_state, + int n_components) +{ + switch (name_state->name_id) + { + case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY: + if (G_UNLIKELY (n_components == 1)) + { + g_critical ("glVertexPointer doesn't allow 1 component vertex " + "positions so we currently only support \"cogl_vertex\" " + "attributes where n_components == 2, 3 or 4"); + return FALSE; + } + break; + case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: + if (G_UNLIKELY (n_components != 3 && n_components != 4)) + { + g_critical ("glColorPointer expects 3 or 4 component colors so we " + "currently only support \"cogl_color\" attributes where " + "n_components == 3 or 4"); + return FALSE; + } + break; + case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY: + break; + case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY: + if (G_UNLIKELY (n_components != 3)) + { + g_critical ("glNormalPointer expects 3 component normals so we " + "currently only support \"cogl_normal\" attributes " + "where n_components == 3"); + return FALSE; + } + break; + case COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY: + if (G_UNLIKELY (n_components != 1)) + { + g_critical ("The point size attribute can only have one " + "component"); + return FALSE; + } + break; + case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY: + return TRUE; + } + + return TRUE; +} + +CoglAttribute * +cogl_attribute_new (CoglAttributeBuffer *attribute_buffer, + const char *name, + size_t stride, + size_t offset, + int n_components, + CoglAttributeType type) +{ + CoglAttribute *attribute = g_slice_new (CoglAttribute); + CoglBuffer *buffer = COGL_BUFFER (attribute_buffer); + CoglContext *ctx = buffer->context; + + attribute->is_buffered = TRUE; + + attribute->name_state = + g_hash_table_lookup (ctx->attribute_name_states_hash, name); + if (!attribute->name_state) + { + CoglAttributeNameState *name_state = + _cogl_attribute_register_attribute_name (ctx, name); + if (!name_state) + goto error; + attribute->name_state = name_state; + } + + attribute->d.buffered.attribute_buffer = cogl_object_ref (attribute_buffer); + attribute->d.buffered.stride = stride; + attribute->d.buffered.offset = offset; + attribute->d.buffered.n_components = n_components; + attribute->d.buffered.type = type; + + attribute->immutable_ref = 0; + + if (attribute->name_state->name_id != COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY) + { + if (!validate_n_components (attribute->name_state, n_components)) + return NULL; + attribute->normalized = + attribute->name_state->normalized_default; + } + else + attribute->normalized = FALSE; + + return _cogl_attribute_object_new (attribute); + +error: + _cogl_attribute_free (attribute); + return NULL; +} + +static CoglAttribute * +_cogl_attribute_new_const (CoglContext *context, + const char *name, + int n_components, + int n_columns, + CoglBool transpose, + const float *value) +{ + CoglAttribute *attribute = g_slice_new (CoglAttribute); + + attribute->name_state = + g_hash_table_lookup (context->attribute_name_states_hash, name); + if (!attribute->name_state) + { + CoglAttributeNameState *name_state = + _cogl_attribute_register_attribute_name (context, name); + if (!name_state) + goto error; + attribute->name_state = name_state; + } + + if (!validate_n_components (attribute->name_state, n_components)) + goto error; + + attribute->is_buffered = FALSE; + attribute->normalized = FALSE; + + attribute->d.constant.context = cogl_object_ref (context); + + attribute->d.constant.boxed.v.array = NULL; + + if (n_columns == 1) + { + _cogl_boxed_value_set_float (&attribute->d.constant.boxed, + n_components, + 1, + value); + } + else + { + /* FIXME: Up until GL[ES] 3 only square matrices were supported + * and we don't currently expose non-square matrices in Cogl. + */ + _COGL_RETURN_VAL_IF_FAIL (n_columns == n_components, NULL); + _cogl_boxed_value_set_matrix (&attribute->d.constant.boxed, + n_columns, + 1, + transpose, + value); + } + + return _cogl_attribute_object_new (attribute); + +error: + _cogl_attribute_free (attribute); + return NULL; +} + +CoglAttribute * +cogl_attribute_new_const_1f (CoglContext *context, + const char *name, + float value) +{ + return _cogl_attribute_new_const (context, + name, + 1, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + &value); +} + +CoglAttribute * +cogl_attribute_new_const_2fv (CoglContext *context, + const char *name, + const float *value) +{ + return _cogl_attribute_new_const (context, + name, + 2, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + value); +} + +CoglAttribute * +cogl_attribute_new_const_3fv (CoglContext *context, + const char *name, + const float *value) +{ + return _cogl_attribute_new_const (context, + name, + 3, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + value); +} + +CoglAttribute * +cogl_attribute_new_const_4fv (CoglContext *context, + const char *name, + const float *value) +{ + return _cogl_attribute_new_const (context, + name, + 4, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + value); +} + +CoglAttribute * +cogl_attribute_new_const_2f (CoglContext *context, + const char *name, + float component0, + float component1) +{ + float vec2[2] = { component0, component1 }; + return _cogl_attribute_new_const (context, + name, + 2, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + vec2); +} + +CoglAttribute * +cogl_attribute_new_const_3f (CoglContext *context, + const char *name, + float component0, + float component1, + float component2) +{ + float vec3[3] = { component0, component1, component2 }; + return _cogl_attribute_new_const (context, + name, + 3, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + vec3); +} + +CoglAttribute * +cogl_attribute_new_const_4f (CoglContext *context, + const char *name, + float component0, + float component1, + float component2, + float component3) +{ + float vec4[4] = { component0, component1, component2, component3 }; + return _cogl_attribute_new_const (context, + name, + 4, /* n_components */ + 1, /* 1 column vector */ + FALSE, /* no transpose */ + vec4); +} + +CoglAttribute * +cogl_attribute_new_const_2x2fv (CoglContext *context, + const char *name, + const float *matrix2x2, + CoglBool transpose) +{ + return _cogl_attribute_new_const (context, + name, + 2, /* n_components */ + 2, /* 2 column vector */ + FALSE, /* no transpose */ + matrix2x2); +} + +CoglAttribute * +cogl_attribute_new_const_3x3fv (CoglContext *context, + const char *name, + const float *matrix3x3, + CoglBool transpose) +{ + return _cogl_attribute_new_const (context, + name, + 3, /* n_components */ + 3, /* 3 column vector */ + FALSE, /* no transpose */ + matrix3x3); +} + +CoglAttribute * +cogl_attribute_new_const_4x4fv (CoglContext *context, + const char *name, + const float *matrix4x4, + CoglBool transpose) +{ + return _cogl_attribute_new_const (context, + name, + 4, /* n_components */ + 4, /* 4 column vector */ + FALSE, /* no transpose */ + matrix4x4); +} + +CoglBool +cogl_attribute_get_normalized (CoglAttribute *attribute) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), FALSE); + + return attribute->normalized; +} + +static void +warn_about_midscene_changes (void) +{ + static CoglBool seen = FALSE; + if (!seen) + { + g_warning ("Mid-scene modification of attributes has " + "undefined results\n"); + seen = TRUE; + } +} + +void +cogl_attribute_set_normalized (CoglAttribute *attribute, + CoglBool normalized) +{ + _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute)); + + if (G_UNLIKELY (attribute->immutable_ref)) + warn_about_midscene_changes (); + + attribute->normalized = normalized; +} + +CoglAttributeBuffer * +cogl_attribute_get_buffer (CoglAttribute *attribute) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL); + _COGL_RETURN_VAL_IF_FAIL (attribute->is_buffered, NULL); + + return attribute->d.buffered.attribute_buffer; +} + +void +cogl_attribute_set_buffer (CoglAttribute *attribute, + CoglAttributeBuffer *attribute_buffer) +{ + _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute)); + _COGL_RETURN_IF_FAIL (attribute->is_buffered); + + if (G_UNLIKELY (attribute->immutable_ref)) + warn_about_midscene_changes (); + + cogl_object_ref (attribute_buffer); + + cogl_object_unref (attribute->d.buffered.attribute_buffer); + attribute->d.buffered.attribute_buffer = attribute_buffer; +} + +CoglAttribute * +_cogl_attribute_immutable_ref (CoglAttribute *attribute) +{ + CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer); + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL); + + attribute->immutable_ref++; + _cogl_buffer_immutable_ref (buffer); + return attribute; +} + +void +_cogl_attribute_immutable_unref (CoglAttribute *attribute) +{ + CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer); + + _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute)); + _COGL_RETURN_IF_FAIL (attribute->immutable_ref > 0); + + attribute->immutable_ref--; + _cogl_buffer_immutable_unref (buffer); +} + +static void +_cogl_attribute_free (CoglAttribute *attribute) +{ + if (attribute->is_buffered) + cogl_object_unref (attribute->d.buffered.attribute_buffer); + else + _cogl_boxed_value_destroy (&attribute->d.constant.boxed); + + g_slice_free (CoglAttribute, attribute); +} + +static CoglBool +validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + CoglTexture *texture = + cogl_pipeline_get_layer_texture (pipeline, layer_index); + CoglFlushLayerState *state = user_data; + CoglBool status = TRUE; + + /* invalid textures will be handled correctly in + * _cogl_pipeline_flush_layers_gl_state */ + if (texture == NULL) + goto validated; + + _cogl_texture_flush_journal_rendering (texture); + + /* Give the texture a chance to know that we're rendering + non-quad shaped primitives. If the texture is in an atlas it + will be migrated */ + _cogl_texture_ensure_non_quad_rendering (texture); + + /* We need to ensure the mipmaps are ready before deciding + * anything else about the texture because the texture storate + * could completely change if it needs to be migrated out of the + * atlas and will affect how we validate the layer. + */ + _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); + + if (!_cogl_texture_can_hardware_repeat (texture)) + { + g_warning ("Disabling layer %d of the current source material, " + "because texturing with the vertex buffer API is not " + "currently supported using sliced textures, or textures " + "with waste\n", layer_index); + + /* XXX: maybe we can add a mechanism for users to forcibly use + * textures with waste where it would be their responsability to use + * texture coords in the range [0,1] such that sampling outside isn't + * required. We can then use a texture matrix (or a modification of + * the users own matrix) to map 1 to the edge of the texture data. + * + * Potentially, given the same guarantee as above we could also + * support a single sliced layer too. We would have to redraw the + * vertices once for each layer, each time with a fiddled texture + * matrix. + */ + state->fallback_layers |= (1 << state->unit); + state->options.flags |= COGL_PIPELINE_FLUSH_FALLBACK_MASK; + } + +validated: + state->unit++; + return status; +} + +void +_cogl_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes) +{ + CoglContext *ctx = framebuffer->context; + CoglFlushLayerState layers_state; + CoglPipeline *copy = NULL; + + if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH)) + _cogl_journal_flush (framebuffer->journal); + + layers_state.unit = 0; + layers_state.options.flags = 0; + layers_state.fallback_layers = 0; + + if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION)) + cogl_pipeline_foreach_layer (pipeline, + validate_layer_cb, + &layers_state); + + /* NB: _cogl_framebuffer_flush_state may disrupt various state (such + * as the pipeline state) when flushing the clip stack, so should + * always be done first when preparing to draw. We need to do this + * before setting up the array pointers because setting up the clip + * stack can cause some drawing which would change the array + * pointers. */ + if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH)) + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_ALL); + + /* In cogl_read_pixels we have a fast-path when reading a single + * pixel and the scene is just comprised of simple rectangles still + * in the journal. For this optimization to work we need to track + * when the framebuffer really does get drawn to. */ + _cogl_framebuffer_mark_mid_scene (framebuffer); + _cogl_framebuffer_mark_clear_clip_dirty (framebuffer); + + if (G_UNLIKELY (!(flags & COGL_DRAW_SKIP_LEGACY_STATE)) && + G_UNLIKELY (ctx->legacy_state_set) && + _cogl_get_enable_legacy_state ()) + { + copy = cogl_pipeline_copy (pipeline); + pipeline = copy; + _cogl_pipeline_apply_legacy_state (pipeline); + } + + ctx->driver_vtable->flush_attributes_state (framebuffer, + pipeline, + &layers_state, + flags, + attributes, + n_attributes); + + if (copy) + cogl_object_unref (copy); +} + +int +_cogl_attribute_get_n_components (CoglAttribute *attribute) +{ + if (attribute->is_buffered) + return attribute->d.buffered.n_components; + else + return attribute->d.constant.boxed.size; +} diff --git a/cogl/cogl/cogl-attribute.h b/cogl/cogl/cogl-attribute.h new file mode 100644 index 000000000..736b0c675 --- /dev/null +++ b/cogl/cogl/cogl-attribute.h @@ -0,0 +1,558 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_ATTRIBUTE_H__ +#define __COGL_ATTRIBUTE_H__ + +/* We forward declare the CoglAttribute type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglAttribute CoglAttribute; + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-attribute + * @short_description: Functions for declaring and drawing vertex + * attributes + * + * FIXME + */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_attribute_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_attribute_get_gtype (void); +#endif + +/** + * cogl_attribute_new: (constructor) + * @attribute_buffer: The #CoglAttributeBuffer containing the actual + * attribute data + * @name: The name of the attribute (used to reference it from GLSL) + * @stride: The number of bytes to jump to get to the next attribute + * value for the next vertex. (Usually + * sizeof (MyVertex)) + * @offset: The byte offset from the start of @attribute_buffer for + * the first attribute value. (Usually + * offsetof (MyVertex, component0) + * @components: The number of components (e.g. 4 for an rgba color or + * 3 for and (x,y,z) position) + * @type: FIXME + * + * Describes the layout for a list of vertex attribute values (For + * example, a list of texture coordinates or colors). + * + * The @name is used to access the attribute inside a GLSL vertex + * shader and there are some special names you should use if they are + * applicable: + * + * "cogl_position_in" (used for vertex positions) + * "cogl_color_in" (used for vertex colors) + * "cogl_tex_coord0_in", "cogl_tex_coord1", ... + * (used for vertex texture coordinates) + * "cogl_normal_in" (used for vertex normals) + * "cogl_point_size_in" (used to set the size of points + * per-vertex. Note this can only be used if + * %COGL_FEATURE_ID_POINT_SIZE_ATTRIBUTE is advertised and + * cogl_pipeline_set_per_vertex_point_size() is called on the pipeline. + * + * + * + * The attribute values corresponding to different vertices can either + * be tightly packed or interleaved with other attribute values. For + * example it's common to define a structure for a single vertex like: + * |[ + * typedef struct + * { + * float x, y, z; /* position attribute */ + * float s, t; /* texture coordinate attribute */ + * } MyVertex; + * ]| + * + * And then create an array of vertex data something like: + * |[ + * MyVertex vertices[100] = { .... } + * ]| + * + * In this case, to describe either the position or texture coordinate + * attribute you have to move sizeof (MyVertex) bytes to + * move from one vertex to the next. This is called the attribute + * @stride. If you weren't interleving attributes and you instead had + * a packed array of float x, y pairs then the attribute stride would + * be (2 * sizeof (float)). So the @stride is the number of + * bytes to move to find the attribute value of the next vertex. + * + * Normally a list of attributes starts at the beginning of an array. + * So for the MyVertex example above the @offset is the + * offset inside the MyVertex structure to the first + * component of the attribute. For the texture coordinate attribute + * the offset would be offsetof (MyVertex, s) or instead of + * using the offsetof macro you could use sizeof (float) * + * 3. If you've divided your @array into blocks of non-interleved + * attributes then you will need to calculate the @offset as the number of + * bytes in blocks preceding the attribute you're describing. + * + * An attribute often has more than one component. For example a color + * is often comprised of 4 red, green, blue and alpha @components, and a + * position may be comprised of 2 x and y @components. You should aim + * to keep the number of components to a minimum as more components + * means more data needs to be mapped into the GPU which can be a + * bottlneck when dealing with a large number of vertices. + * + * Finally you need to specify the component data type. Here you + * should aim to use the smallest type that meets your precision + * requirements. Again the larger the type then more data needs to be + * mapped into the GPU which can be a bottlneck when dealing with + * a large number of vertices. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * describing the layout for a list of attribute values + * stored in @array. + * + * Since: 1.4 + * Stability: Unstable + */ +/* XXX: look for a precedent to see if the stride/offset args should + * have a different order. */ +CoglAttribute * +cogl_attribute_new (CoglAttributeBuffer *attribute_buffer, + const char *name, + size_t stride, + size_t offset, + int components, + CoglAttributeType type); + +/** + * cogl_attribute_new_const_1f: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @value: The constant value for the attribute + * + * Creates a new, single component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constant @value is a single precision floating point scalar + * which should have a corresponding declaration in GLSL code like: + * + * [| + * attribute float name; + * |] + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant @value. + */ +CoglAttribute * +cogl_attribute_new_const_1f (CoglContext *context, + const char *name, + float value); + +/** + * cogl_attribute_new_const_2f: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @component0: The first component of a 2 component vector + * @component1: The second component of a 2 component vector + * + * Creates a new, 2 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (@component0, @component1) represent a 2 component + * float vector which should have a corresponding declaration in GLSL + * code like: + * + * [| + * attribute vec2 name; + * |] + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_2f (CoglContext *context, + const char *name, + float component0, + float component1); + +/** + * cogl_attribute_new_const_3f: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @component0: The first component of a 3 component vector + * @component1: The second component of a 3 component vector + * @component2: The third component of a 3 component vector + * + * Creates a new, 3 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (@component0, @component1, @component2) represent a 3 + * component float vector which should have a corresponding + * declaration in GLSL code like: + * + * [| + * attribute vec3 name; + * |] + * + * unless the built in name "cogl_normal_in" is being used where no + * explicit GLSL declaration need be made. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_3f (CoglContext *context, + const char *name, + float component0, + float component1, + float component2); + +/** + * cogl_attribute_new_const_4f: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @component0: The first component of a 4 component vector + * @component1: The second component of a 4 component vector + * @component2: The third component of a 4 component vector + * @component3: The fourth component of a 4 component vector + * + * Creates a new, 4 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (@component0, @component1, @component2, @constant3) + * represent a 4 component float vector which should have a + * corresponding declaration in GLSL code like: + * + * [| + * attribute vec4 name; + * |] + * + * unless one of the built in names "cogl_color_in", + * "cogl_tex_coord0_in or "cogl_tex_coord1_in" etc is being used where + * no explicit GLSL declaration need be made. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_4f (CoglContext *context, + const char *name, + float component0, + float component1, + float component2, + float component3); + +/** + * cogl_attribute_new_const_2fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @value: A pointer to a 2 component float vector + * + * Creates a new, 2 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (value[0], value[1]) represent a 2 component float + * vector which should have a corresponding declaration in GLSL code + * like: + * + * [| + * attribute vec2 name; + * |] + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_2fv (CoglContext *context, + const char *name, + const float *value); + +/** + * cogl_attribute_new_const_3fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @value: A pointer to a 3 component float vector + * + * Creates a new, 3 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (value[0], value[1], value[2]) represent a 3 + * component float vector which should have a corresponding + * declaration in GLSL code like: + * + * [| + * attribute vec3 name; + * |] + * + * unless the built in name "cogl_normal_in" is being used where no + * explicit GLSL declaration need be made. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_3fv (CoglContext *context, + const char *name, + const float *value); + +/** + * cogl_attribute_new_const_4fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @value: A pointer to a 4 component float vector + * + * Creates a new, 4 component, attribute whose value remains + * constant across all the vertices of a primitive without needing to + * duplicate the value for each vertex. + * + * The constants (value[0], value[1], value[2], value[3]) represent a + * 4 component float vector which should have a corresponding + * declaration in GLSL code like: + * + * [| + * attribute vec4 name; + * |] + * + * unless one of the built in names "cogl_color_in", + * "cogl_tex_coord0_in or "cogl_tex_coord1_in" etc is being used where + * no explicit GLSL declaration need be made. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant vector. + */ +CoglAttribute * +cogl_attribute_new_const_4fv (CoglContext *context, + const char *name, + const float *value); + +/** + * cogl_attribute_new_const_2x2fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @matrix2x2: A pointer to a 2 by 2 matrix + * @transpose: Whether the matrix should be transposed on upload or + * not + * + * Creates a new matrix attribute whose value remains constant + * across all the vertices of a primitive without needing to duplicate + * the value for each vertex. + * + * @matrix2x2 represent a square 2 by 2 matrix specified in + * column-major order (each pair of consecutive numbers represents a + * column) which should have a corresponding declaration in GLSL code + * like: + * + * [| + * attribute mat2 name; + * |] + * + * If @transpose is %TRUE then all matrix components are rotated + * around the diagonal of the matrix such that the first column + * becomes the first row and the second column becomes the second row. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant matrix. + */ +CoglAttribute * +cogl_attribute_new_const_2x2fv (CoglContext *context, + const char *name, + const float *matrix2x2, + CoglBool transpose); + +/** + * cogl_attribute_new_const_3x3fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @matrix3x3: A pointer to a 3 by 3 matrix + * @transpose: Whether the matrix should be transposed on upload or + * not + * + * Creates a new matrix attribute whose value remains constant + * across all the vertices of a primitive without needing to duplicate + * the value for each vertex. + * + * @matrix3x3 represent a square 3 by 3 matrix specified in + * column-major order (each triple of consecutive numbers represents a + * column) which should have a corresponding declaration in GLSL code + * like: + * + * [| + * attribute mat3 name; + * |] + * + * If @transpose is %TRUE then all matrix components are rotated + * around the diagonal of the matrix such that the first column + * becomes the first row and the second column becomes the second row + * etc. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant matrix. + */ +CoglAttribute * +cogl_attribute_new_const_3x3fv (CoglContext *context, + const char *name, + const float *matrix3x3, + CoglBool transpose); + +/** + * cogl_attribute_new_const_4x4fv: + * @context: A #CoglContext + * @name: The name of the attribute (used to reference it from GLSL) + * @matrix4x4: A pointer to a 4 by 4 matrix + * @transpose: Whether the matrix should be transposed on upload or + * not + * + * Creates a new matrix attribute whose value remains constant + * across all the vertices of a primitive without needing to duplicate + * the value for each vertex. + * + * @matrix4x4 represent a square 4 by 4 matrix specified in + * column-major order (each 4-tuple of consecutive numbers represents a + * column) which should have a corresponding declaration in GLSL code + * like: + * + * [| + * attribute mat4 name; + * |] + * + * If @transpose is %TRUE then all matrix components are rotated + * around the diagonal of the matrix such that the first column + * becomes the first row and the second column becomes the second row + * etc. + * + * Return value: (transfer full): A newly allocated #CoglAttribute + * representing the given constant matrix. + */ +CoglAttribute * +cogl_attribute_new_const_4x4fv (CoglContext *context, + const char *name, + const float *matrix4x4, + CoglBool transpose); + +/** + * cogl_attribute_set_normalized: + * @attribute: A #CoglAttribute + * @normalized: The new value for the normalized property. + * + * Sets whether fixed point attribute types are mapped to the range + * 0→1. For example when this property is TRUE and a + * %COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE type is used then the value 255 + * will be mapped to 1.0. + * + * The default value of this property depends on the name of the + * attribute. For the builtin properties cogl_color_in and + * cogl_normal_in it will default to TRUE and for all other names it + * will default to FALSE. + * + * Stability: unstable + * Since: 1.10 + */ +void +cogl_attribute_set_normalized (CoglAttribute *attribute, + CoglBool normalized); + +/** + * cogl_attribute_get_normalized: + * @attribute: A #CoglAttribute + * + * Return value: the value of the normalized property set with + * cogl_attribute_set_normalized(). + * + * Stability: unstable + * Since: 1.10 + */ +CoglBool +cogl_attribute_get_normalized (CoglAttribute *attribute); + +/** + * cogl_attribute_get_buffer: + * @attribute: A #CoglAttribute + * + * Return value: (transfer none): the #CoglAttributeBuffer that was + * set with cogl_attribute_set_buffer() or cogl_attribute_new(). + * + * Stability: unstable + * Since: 1.10 + */ +CoglAttributeBuffer * +cogl_attribute_get_buffer (CoglAttribute *attribute); + +/** + * cogl_attribute_set_buffer: + * @attribute: A #CoglAttribute + * @attribute_buffer: A #CoglAttributeBuffer + * + * Sets a new #CoglAttributeBuffer for the attribute. + * + * Stability: unstable + * Since: 1.10 + */ +void +cogl_attribute_set_buffer (CoglAttribute *attribute, + CoglAttributeBuffer *attribute_buffer); + +/** + * cogl_is_attribute: + * @object: A #CoglObject + * + * Gets whether the given object references a #CoglAttribute. + * + * Return value: %TRUE if the @object references a #CoglAttribute, + * %FALSE otherwise + */ +CoglBool +cogl_is_attribute (void *object); + +COGL_END_DECLS + +#endif /* __COGL_ATTRIBUTE_H__ */ + diff --git a/cogl/cogl/cogl-bitmap-conversion.c b/cogl/cogl/cogl-bitmap-conversion.c new file mode 100644 index 000000000..67168e747 --- /dev/null +++ b/cogl/cogl/cogl-bitmap-conversion.c @@ -0,0 +1,748 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-bitmap-private.h" +#include "cogl-context-private.h" +#include "cogl-texture-private.h" + +#include + +#define component_type uint8_t +#define component_size 8 +/* We want to specially optimise the packing when we are converting + to/from an 8-bit type so that it won't do anything. That way for + example if we are just doing a swizzle conversion then the inner + loop for the conversion will be really simple */ +#define UNPACK_BYTE(b) (b) +#define PACK_BYTE(b) (b) +#include "cogl-bitmap-packing.h" +#undef PACK_BYTE +#undef UNPACK_BYTE +#undef component_type +#undef component_size + +#define component_type uint16_t +#define component_size 16 +#define UNPACK_BYTE(b) (((b) * 65535 + 127) / 255) +#define PACK_BYTE(b) (((b) * 255 + 32767) / 65535) +#include "cogl-bitmap-packing.h" +#undef PACK_BYTE +#undef UNPACK_BYTE +#undef component_type +#undef component_size + +/* (Un)Premultiplication */ + +inline static void +_cogl_unpremult_alpha_0 (uint8_t *dst) +{ + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; +} + +inline static void +_cogl_unpremult_alpha_last (uint8_t *dst) +{ + uint8_t alpha = dst[3]; + + dst[0] = (dst[0] * 255) / alpha; + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; +} + +inline static void +_cogl_unpremult_alpha_first (uint8_t *dst) +{ + uint8_t alpha = dst[0]; + + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; + dst[3] = (dst[3] * 255) / alpha; +} + +/* No division form of floor((c*a + 128)/255) (I first encountered + * this in the RENDER implementation in the X server.) Being exact + * is important for a == 255 - we want to get exactly c. + */ +#define MULT(d,a,t) \ + G_STMT_START { \ + t = d * a + 128; \ + d = ((t >> 8) + t) >> 8; \ + } G_STMT_END + +inline static void +_cogl_premult_alpha_last (uint8_t *dst) +{ + uint8_t alpha = dst[3]; + /* Using a separate temporary per component has given slightly better + * code generation with GCC in the past; it shouldn't do any worse in + * any case. + */ + unsigned int t1, t2, t3; + MULT(dst[0], alpha, t1); + MULT(dst[1], alpha, t2); + MULT(dst[2], alpha, t3); +} + +inline static void +_cogl_premult_alpha_first (uint8_t *dst) +{ + uint8_t alpha = dst[0]; + unsigned int t1, t2, t3; + + MULT(dst[1], alpha, t1); + MULT(dst[2], alpha, t2); + MULT(dst[3], alpha, t3); +} + +#undef MULT + +/* Use the SSE optimized version to premult four pixels at once when + it is available. The same assembler code works for x86 and x86-64 + because it doesn't refer to any non-SSE registers directly */ +#if defined(__SSE2__) && defined(__GNUC__) \ + && (defined(__x86_64) || defined(__i386)) +#define COGL_USE_PREMULT_SSE2 +#endif + +#ifdef COGL_USE_PREMULT_SSE2 + +inline static void +_cogl_premult_alpha_last_four_pixels_sse2 (uint8_t *p) +{ + /* 8 copies of 128 used below */ + static const int16_t eight_halves[8] __attribute__ ((aligned (16))) = + { 128, 128, 128, 128, 128, 128, 128, 128 }; + /* Mask of the rgb components of the four pixels */ + static const int8_t just_rgb[16] __attribute__ ((aligned (16))) = + { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00 }; + /* Each SSE register only holds two pixels because we need to work + with 16-bit intermediate values. We still do four pixels by + interleaving two registers in the hope that it will pipeline + better */ + asm (/* Load eight_halves into xmm5 for later */ + "movdqa (%1), %%xmm5\n" + /* Clear xmm3 */ + "pxor %%xmm3, %%xmm3\n" + /* Load two pixels from p into the low half of xmm0 */ + "movlps (%0), %%xmm0\n" + /* Load the next set of two pixels from p into the low half of xmm1 */ + "movlps 8(%0), %%xmm1\n" + /* Unpack 8 bytes from the low quad-words in each register to 8 + 16-bit values */ + "punpcklbw %%xmm3, %%xmm0\n" + "punpcklbw %%xmm3, %%xmm1\n" + /* Copy alpha values of the first pixel in xmm0 to all + components of the first pixel in xmm2 */ + "pshuflw $255, %%xmm0, %%xmm2\n" + /* same for xmm1 and xmm3 */ + "pshuflw $255, %%xmm1, %%xmm3\n" + /* The above also copies the second pixel directly so we now + want to replace the RGB components with copies of the alpha + components */ + "pshufhw $255, %%xmm2, %%xmm2\n" + "pshufhw $255, %%xmm3, %%xmm3\n" + /* Multiply the rgb components by the alpha */ + "pmullw %%xmm2, %%xmm0\n" + "pmullw %%xmm3, %%xmm1\n" + /* Add 128 to each component */ + "paddw %%xmm5, %%xmm0\n" + "paddw %%xmm5, %%xmm1\n" + /* Copy the results to temporary registers xmm4 and xmm5 */ + "movdqa %%xmm0, %%xmm4\n" + "movdqa %%xmm1, %%xmm5\n" + /* Divide the results by 256 */ + "psrlw $8, %%xmm0\n" + "psrlw $8, %%xmm1\n" + /* Add the temporaries back in */ + "paddw %%xmm4, %%xmm0\n" + "paddw %%xmm5, %%xmm1\n" + /* Divide again */ + "psrlw $8, %%xmm0\n" + "psrlw $8, %%xmm1\n" + /* Pack the results back as bytes */ + "packuswb %%xmm1, %%xmm0\n" + /* Load just_rgb into xmm3 for later */ + "movdqa (%2), %%xmm3\n" + /* Reload all four pixels into xmm2 */ + "movups (%0), %%xmm2\n" + /* Mask out the alpha from the results */ + "andps %%xmm3, %%xmm0\n" + /* Mask out the RGB from the original four pixels */ + "andnps %%xmm2, %%xmm3\n" + /* Combine the two to get the right alpha values */ + "orps %%xmm3, %%xmm0\n" + /* Write to memory */ + "movdqu %%xmm0, (%0)\n" + : /* no outputs */ + : "r" (p), "r" (eight_halves), "r" (just_rgb) + : "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"); +} + +#endif /* COGL_USE_PREMULT_SSE2 */ + +static void +_cogl_bitmap_premult_unpacked_span_8 (uint8_t *data, + int width) +{ +#ifdef COGL_USE_PREMULT_SSE2 + + /* Process 4 pixels at a time */ + while (width >= 4) + { + _cogl_premult_alpha_last_four_pixels_sse2 (data); + data += 4 * 4; + width -= 4; + } + + /* If there are any pixels left we will fall through and + handle them below */ + +#endif /* COGL_USE_PREMULT_SSE2 */ + + while (width-- > 0) + { + _cogl_premult_alpha_last (data); + data += 4; + } +} + +static void +_cogl_bitmap_unpremult_unpacked_span_8 (uint8_t *data, + int width) +{ + int x; + + for (x = 0; x < width; x++) + { + if (data[3] == 0) + _cogl_unpremult_alpha_0 (data); + else + _cogl_unpremult_alpha_last (data); + data += 4; + } +} + +static void +_cogl_bitmap_unpremult_unpacked_span_16 (uint16_t *data, + int width) +{ + while (width-- > 0) + { + uint16_t alpha = data[3]; + + if (alpha == 0) + memset (data, 0, sizeof (uint16_t) * 3); + else + { + data[0] = (data[0] * 65535) / alpha; + data[1] = (data[1] * 65535) / alpha; + data[2] = (data[2] * 65535) / alpha; + } + } +} + +static void +_cogl_bitmap_premult_unpacked_span_16 (uint16_t *data, + int width) +{ + while (width-- > 0) + { + uint16_t alpha = data[3]; + + data[0] = (data[0] * alpha) / 65535; + data[1] = (data[1] * alpha) / 65535; + data[2] = (data[2] * alpha) / 65535; + } +} + +static CoglBool +_cogl_bitmap_can_fast_premult (CoglPixelFormat format) +{ + switch (format & ~COGL_PREMULT_BIT) + { + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ABGR_8888: + return TRUE; + + default: + return FALSE; + } +} + +static CoglBool +_cogl_bitmap_needs_short_temp_buffer (CoglPixelFormat format) +{ + /* If the format is using more than 8 bits per component then we'll + unpack into a 16-bit per component buffer instead of 8-bit so we + won't lose as much precision. If we ever add support for formats + with more than 16 bits for at least one of the components then we + should probably do something else here, maybe convert to + floats */ + switch (format) + { + case COGL_PIXEL_FORMAT_DEPTH_16: + case COGL_PIXEL_FORMAT_DEPTH_32: + case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: + case COGL_PIXEL_FORMAT_ANY: + case COGL_PIXEL_FORMAT_YUV: + g_assert_not_reached (); + + case COGL_PIXEL_FORMAT_A_8: + case COGL_PIXEL_FORMAT_RG_88: + case COGL_PIXEL_FORMAT_RGB_565: + case COGL_PIXEL_FORMAT_RGBA_4444: + case COGL_PIXEL_FORMAT_RGBA_5551: + case COGL_PIXEL_FORMAT_G_8: + case COGL_PIXEL_FORMAT_RGB_888: + case COGL_PIXEL_FORMAT_BGR_888: + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ABGR_8888: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + case COGL_PIXEL_FORMAT_ABGR_8888_PRE: + case COGL_PIXEL_FORMAT_RGBA_4444_PRE: + case COGL_PIXEL_FORMAT_RGBA_5551_PRE: + return FALSE; + + case COGL_PIXEL_FORMAT_RGBA_1010102: + case COGL_PIXEL_FORMAT_BGRA_1010102: + case COGL_PIXEL_FORMAT_ARGB_2101010: + case COGL_PIXEL_FORMAT_ABGR_2101010: + case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: + case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: + case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: + case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: + return TRUE; + } + + g_assert_not_reached (); +} + +CoglBool +_cogl_bitmap_convert_into_bitmap (CoglBitmap *src_bmp, + CoglBitmap *dst_bmp, + CoglError **error) +{ + uint8_t *src_data; + uint8_t *dst_data; + uint8_t *src; + uint8_t *dst; + void *tmp_row; + int src_rowstride; + int dst_rowstride; + int y; + int width, height; + CoglPixelFormat src_format; + CoglPixelFormat dst_format; + CoglBool use_16; + CoglBool need_premult; + + src_format = cogl_bitmap_get_format (src_bmp); + src_rowstride = cogl_bitmap_get_rowstride (src_bmp); + dst_format = cogl_bitmap_get_format (dst_bmp); + dst_rowstride = cogl_bitmap_get_rowstride (dst_bmp); + width = cogl_bitmap_get_width (src_bmp); + height = cogl_bitmap_get_height (src_bmp); + + _COGL_RETURN_VAL_IF_FAIL (width == cogl_bitmap_get_width (dst_bmp), FALSE); + _COGL_RETURN_VAL_IF_FAIL (height == cogl_bitmap_get_height (dst_bmp), FALSE); + + need_premult + = ((src_format & COGL_PREMULT_BIT) != (dst_format & COGL_PREMULT_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8 && + dst_format != COGL_PIXEL_FORMAT_A_8 && + (src_format & dst_format & COGL_A_BIT)); + + /* If the base format is the same then we can just copy the bitmap + instead */ + if ((src_format & ~COGL_PREMULT_BIT) == (dst_format & ~COGL_PREMULT_BIT) && + (!need_premult || _cogl_bitmap_can_fast_premult (dst_format))) + { + if (!_cogl_bitmap_copy_subregion (src_bmp, dst_bmp, + 0, 0, /* src_x / src_y */ + 0, 0, /* dst_x / dst_y */ + width, height, + error)) + return FALSE; + + if (need_premult) + { + if ((dst_format & COGL_PREMULT_BIT)) + { + if (!_cogl_bitmap_premult (dst_bmp, error)) + return FALSE; + } + else + { + if (!_cogl_bitmap_unpremult (dst_bmp, error)) + return FALSE; + } + } + + return TRUE; + } + + src_data = _cogl_bitmap_map (src_bmp, COGL_BUFFER_ACCESS_READ, 0, error); + if (src_data == NULL) + return FALSE; + dst_data = _cogl_bitmap_map (dst_bmp, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + error); + if (dst_data == NULL) + { + _cogl_bitmap_unmap (src_bmp); + return FALSE; + } + + use_16 = _cogl_bitmap_needs_short_temp_buffer (dst_format); + + /* Allocate a buffer to hold a temporary RGBA row */ + tmp_row = g_malloc (width * + (use_16 ? sizeof (uint16_t) : sizeof (uint8_t)) * 4); + + /* FIXME: Optimize */ + for (y = 0; y < height; y++) + { + src = src_data + y * src_rowstride; + dst = dst_data + y * dst_rowstride; + + if (use_16) + _cogl_unpack_16 (src_format, src, tmp_row, width); + else + _cogl_unpack_8 (src_format, src, tmp_row, width); + + /* Handle premultiplication */ + if (need_premult) + { + if (dst_format & COGL_PREMULT_BIT) + { + if (use_16) + _cogl_bitmap_premult_unpacked_span_16 (tmp_row, width); + else + _cogl_bitmap_premult_unpacked_span_8 (tmp_row, width); + } + else + { + if (use_16) + _cogl_bitmap_unpremult_unpacked_span_16 (tmp_row, width); + else + _cogl_bitmap_unpremult_unpacked_span_8 (tmp_row, width); + } + } + + if (use_16) + _cogl_pack_16 (dst_format, tmp_row, dst, width); + else + _cogl_pack_8 (dst_format, tmp_row, dst, width); + } + + _cogl_bitmap_unmap (src_bmp); + _cogl_bitmap_unmap (dst_bmp); + + g_free (tmp_row); + + return TRUE; +} + +CoglBitmap * +_cogl_bitmap_convert (CoglBitmap *src_bmp, + CoglPixelFormat dst_format, + CoglError **error) +{ + CoglBitmap *dst_bmp; + int width, height; + + _COGL_GET_CONTEXT (ctx, NULL); + + width = cogl_bitmap_get_width (src_bmp); + height = cogl_bitmap_get_height (src_bmp); + + dst_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, + width, height, + dst_format, + error); + if (!dst_bmp) + return NULL; + + if (!_cogl_bitmap_convert_into_bitmap (src_bmp, dst_bmp, error)) + { + cogl_object_unref (dst_bmp); + return NULL; + } + + return dst_bmp; +} + +static CoglBool +driver_can_convert (CoglContext *ctx, + CoglPixelFormat src_format, + CoglPixelFormat internal_format) +{ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION)) + return FALSE; + + if (src_format == internal_format) + return TRUE; + + /* If the driver doesn't natively support alpha textures then it + * won't work correctly to convert to/from component-alpha + * textures */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && + (src_format == COGL_PIXEL_FORMAT_A_8 || + internal_format == COGL_PIXEL_FORMAT_A_8)) + return FALSE; + + /* Same for red-green textures. If red-green textures aren't + * supported then the internal format should never be RG_88 but we + * should still be able to convert from an RG source image */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RG) && + src_format == COGL_PIXEL_FORMAT_RG_88) + return FALSE; + + return TRUE; +} + +CoglBitmap * +_cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error) +{ + CoglContext *ctx = _cogl_bitmap_get_context (src_bmp); + CoglPixelFormat src_format = cogl_bitmap_get_format (src_bmp); + CoglBitmap *dst_bmp; + + _COGL_RETURN_VAL_IF_FAIL (internal_format != COGL_PIXEL_FORMAT_ANY, NULL); + + /* OpenGL supports specifying a different format for the internal + format when uploading texture data. We should use this to convert + formats because it is likely to be faster and support more types + than the Cogl bitmap code. However under GLES the internal format + must be the same as the bitmap format and it only supports a + limited number of formats so we must convert using the Cogl + bitmap code instead */ + + if (driver_can_convert (ctx, src_format, internal_format)) + { + /* If the source format does not have the same premult flag as the + internal_format then we need to copy and convert it */ + if (_cogl_texture_needs_premult_conversion (src_format, + internal_format)) + { + if (can_convert_in_place) + { + if (_cogl_bitmap_convert_premult_status (src_bmp, + (src_format ^ + COGL_PREMULT_BIT), + error)) + { + dst_bmp = cogl_object_ref (src_bmp); + } + else + return NULL; + } + else + { + dst_bmp = _cogl_bitmap_convert (src_bmp, + src_format ^ COGL_PREMULT_BIT, + error); + if (dst_bmp == NULL) + return NULL; + } + } + else + dst_bmp = cogl_object_ref (src_bmp); + } + else + { + CoglPixelFormat closest_format; + + closest_format = + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + NULL, /* ignore gl intformat */ + NULL, /* ignore gl format */ + NULL); /* ignore gl type */ + + if (closest_format != src_format) + dst_bmp = _cogl_bitmap_convert (src_bmp, closest_format, error); + else + dst_bmp = cogl_object_ref (src_bmp); + } + + return dst_bmp; +} + +CoglBool +_cogl_bitmap_unpremult (CoglBitmap *bmp, + CoglError **error) +{ + uint8_t *p, *data; + uint16_t *tmp_row; + int x,y; + CoglPixelFormat format; + int width, height; + int rowstride; + + format = cogl_bitmap_get_format (bmp); + width = cogl_bitmap_get_width (bmp); + height = cogl_bitmap_get_height (bmp); + rowstride = cogl_bitmap_get_rowstride (bmp); + + if ((data = _cogl_bitmap_map (bmp, + COGL_BUFFER_ACCESS_READ | + COGL_BUFFER_ACCESS_WRITE, + 0, + error)) == NULL) + return FALSE; + + /* If we can't directly unpremult the data inline then we'll + allocate a temporary row and unpack the data. This assumes if we + can fast premult then we can also fast unpremult */ + if (_cogl_bitmap_can_fast_premult (format)) + tmp_row = NULL; + else + tmp_row = g_malloc (sizeof (uint16_t) * 4 * width); + + for (y = 0; y < height; y++) + { + p = (uint8_t*) data + y * rowstride; + + if (tmp_row) + { + _cogl_unpack_16 (format, p, tmp_row, width); + _cogl_bitmap_unpremult_unpacked_span_16 (tmp_row, width); + _cogl_pack_16 (format, tmp_row, p, width); + } + else + { + if (format & COGL_AFIRST_BIT) + { + for (x = 0; x < width; x++) + { + if (p[0] == 0) + _cogl_unpremult_alpha_0 (p); + else + _cogl_unpremult_alpha_first (p); + p += 4; + } + } + else + _cogl_bitmap_unpremult_unpacked_span_8 (p, width); + } + } + + g_free (tmp_row); + + _cogl_bitmap_unmap (bmp); + + _cogl_bitmap_set_format (bmp, format & ~COGL_PREMULT_BIT); + + return TRUE; +} + +CoglBool +_cogl_bitmap_premult (CoglBitmap *bmp, + CoglError **error) +{ + uint8_t *p, *data; + uint16_t *tmp_row; + int x,y; + CoglPixelFormat format; + int width, height; + int rowstride; + + format = cogl_bitmap_get_format (bmp); + width = cogl_bitmap_get_width (bmp); + height = cogl_bitmap_get_height (bmp); + rowstride = cogl_bitmap_get_rowstride (bmp); + + if ((data = _cogl_bitmap_map (bmp, + COGL_BUFFER_ACCESS_READ | + COGL_BUFFER_ACCESS_WRITE, + 0, + error)) == NULL) + return FALSE; + + /* If we can't directly premult the data inline then we'll allocate + a temporary row and unpack the data. */ + if (_cogl_bitmap_can_fast_premult (format)) + tmp_row = NULL; + else + tmp_row = g_malloc (sizeof (uint16_t) * 4 * width); + + for (y = 0; y < height; y++) + { + p = (uint8_t*) data + y * rowstride; + + if (tmp_row) + { + _cogl_unpack_16 (format, p, tmp_row, width); + _cogl_bitmap_premult_unpacked_span_16 (tmp_row, width); + _cogl_pack_16 (format, tmp_row, p, width); + } + else + { + if (format & COGL_AFIRST_BIT) + { + for (x = 0; x < width; x++) + { + _cogl_premult_alpha_first (p); + p += 4; + } + } + else + _cogl_bitmap_premult_unpacked_span_8 (p, width); + } + } + + g_free (tmp_row); + + _cogl_bitmap_unmap (bmp); + + _cogl_bitmap_set_format (bmp, format | COGL_PREMULT_BIT); + + return TRUE; +} diff --git a/cogl/cogl/cogl-bitmap-packing.h b/cogl/cogl/cogl-bitmap-packing.h new file mode 100644 index 000000000..1b8e140ff --- /dev/null +++ b/cogl/cogl/cogl-bitmap-packing.h @@ -0,0 +1,767 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This file is included multiple times with different definitions for + the component_type type (either uint8_t or uint16_t). The code ends + up exactly the same for both but we only want to end up hitting the + 16-bit path when one of the types in the conversion is > 8 bits per + component. */ + +/* Unpacking to RGBA */ + +#define UNPACK_1(b) ((b) * ((1 << (sizeof (component_type) * 8)) - 1)) +#define UNPACK_2(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ + 1) / 3) +#define UNPACK_4(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ + 7) / 15) +#define UNPACK_5(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ + 15) / 31) +#define UNPACK_6(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ + 31) / 63) +#define UNPACK_10(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ + 511) / 1023) + +inline static void +G_PASTE (_cogl_unpack_a_8_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = UNPACK_BYTE (*src); + dst += 4; + src++; + } +} + +inline static void +G_PASTE (_cogl_unpack_g_8_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + /* FIXME: I'm not sure if this is right. It looks like Nvidia and + Mesa handle luminance textures differently. Maybe we should + consider just removing luminance textures for Cogl 2.0 because + they have been removed in GL 3.0 */ + while (width-- > 0) + { + component_type v = UNPACK_BYTE (src[0]); + dst[0] = v; + dst[1] = v; + dst[2] = v; + dst[3] = UNPACK_BYTE (255); + dst += 4; + src++; + } +} + +inline static void +G_PASTE (_cogl_unpack_rg_88_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[0]); + dst[1] = UNPACK_BYTE (src[1]); + dst[2] = 0; + dst[3] = UNPACK_BYTE (255); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgb_888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[0]); + dst[1] = UNPACK_BYTE (src[1]); + dst[2] = UNPACK_BYTE (src[2]); + dst[3] = UNPACK_BYTE (255); + dst += 4; + src += 3; + } +} + +inline static void +G_PASTE (_cogl_unpack_bgr_888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[2]); + dst[1] = UNPACK_BYTE (src[1]); + dst[2] = UNPACK_BYTE (src[0]); + dst[3] = UNPACK_BYTE (255); + dst += 4; + src += 3; + } +} + +inline static void +G_PASTE (_cogl_unpack_bgra_8888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[2]); + dst[1] = UNPACK_BYTE (src[1]); + dst[2] = UNPACK_BYTE (src[0]); + dst[3] = UNPACK_BYTE (src[3]); + dst += 4; + src += 4; + } +} + +inline static void +G_PASTE (_cogl_unpack_argb_8888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[1]); + dst[1] = UNPACK_BYTE (src[2]); + dst[2] = UNPACK_BYTE (src[3]); + dst[3] = UNPACK_BYTE (src[0]); + dst += 4; + src += 4; + } +} + +inline static void +G_PASTE (_cogl_unpack_abgr_8888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[3]); + dst[1] = UNPACK_BYTE (src[2]); + dst[2] = UNPACK_BYTE (src[1]); + dst[3] = UNPACK_BYTE (src[0]); + dst += 4; + src += 4; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgba_8888_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = UNPACK_BYTE (src[0]); + dst[1] = UNPACK_BYTE (src[1]); + dst[2] = UNPACK_BYTE (src[2]); + dst[3] = UNPACK_BYTE (src[3]); + dst += 4; + src += 4; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgb_565_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint16_t v = *(const uint16_t *) src; + + dst[0] = UNPACK_5 (v >> 11); + dst[1] = UNPACK_6 ((v >> 5) & 63); + dst[2] = UNPACK_5 (v & 31); + dst[3] = UNPACK_BYTE (255); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgba_4444_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint16_t v = *(const uint16_t *) src; + + dst[0] = UNPACK_4 (v >> 12); + dst[1] = UNPACK_4 ((v >> 8) & 15); + dst[2] = UNPACK_4 ((v >> 4) & 15); + dst[3] = UNPACK_4 (v & 15); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgba_5551_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint16_t v = *(const uint16_t *) src; + + dst[0] = UNPACK_5 (v >> 11); + dst[1] = UNPACK_5 ((v >> 6) & 31); + dst[2] = UNPACK_5 ((v >> 1) & 31); + dst[3] = UNPACK_1 (v & 1); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_rgba_1010102_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint32_t v = *(const uint32_t *) src; + + dst[0] = UNPACK_10 (v >> 22); + dst[1] = UNPACK_10 ((v >> 12) & 1023); + dst[2] = UNPACK_10 ((v >> 2) & 1023); + dst[3] = UNPACK_2 (v & 3); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_bgra_1010102_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint32_t v = *(const uint32_t *) src; + + dst[2] = UNPACK_10 (v >> 22); + dst[1] = UNPACK_10 ((v >> 12) & 1023); + dst[0] = UNPACK_10 ((v >> 2) & 1023); + dst[3] = UNPACK_2 (v & 3); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_argb_2101010_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint32_t v = *(const uint32_t *) src; + + dst[3] = UNPACK_2 (v >> 30); + dst[0] = UNPACK_10 ((v >> 20) & 1023); + dst[1] = UNPACK_10 ((v >> 10) & 1023); + dst[2] = UNPACK_10 (v & 1023); + dst += 4; + src += 2; + } +} + +inline static void +G_PASTE (_cogl_unpack_abgr_2101010_, component_size) (const uint8_t *src, + component_type *dst, + int width) +{ + while (width-- > 0) + { + uint32_t v = *(const uint32_t *) src; + + dst[3] = UNPACK_2 (v >> 30); + dst[2] = UNPACK_10 ((v >> 20) & 1023); + dst[1] = UNPACK_10 ((v >> 10) & 1023); + dst[0] = UNPACK_10 (v & 1023); + dst += 4; + src += 2; + } +} + +#undef UNPACK_1 +#undef UNPACK_2 +#undef UNPACK_4 +#undef UNPACK_5 +#undef UNPACK_6 +#undef UNPACK_10 + +inline static void +G_PASTE (_cogl_unpack_, component_size) (CoglPixelFormat format, + const uint8_t *src, + component_type *dst, + int width) +{ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + G_PASTE (_cogl_unpack_a_8_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_G_8: + G_PASTE (_cogl_unpack_g_8_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RG_88: + G_PASTE (_cogl_unpack_rg_88_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGB_888: + G_PASTE (_cogl_unpack_rgb_888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGR_888: + G_PASTE (_cogl_unpack_bgr_888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + G_PASTE (_cogl_unpack_rgba_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + G_PASTE (_cogl_unpack_bgra_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + G_PASTE (_cogl_unpack_argb_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ABGR_8888: + case COGL_PIXEL_FORMAT_ABGR_8888_PRE: + G_PASTE (_cogl_unpack_abgr_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGB_565: + G_PASTE (_cogl_unpack_rgb_565_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + case COGL_PIXEL_FORMAT_RGBA_4444_PRE: + G_PASTE (_cogl_unpack_rgba_4444_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + case COGL_PIXEL_FORMAT_RGBA_5551_PRE: + G_PASTE (_cogl_unpack_rgba_5551_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_1010102: + case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: + G_PASTE (_cogl_unpack_rgba_1010102_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGRA_1010102: + case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: + G_PASTE (_cogl_unpack_bgra_1010102_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ARGB_2101010: + case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: + G_PASTE (_cogl_unpack_argb_2101010_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ABGR_2101010: + case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: + G_PASTE (_cogl_unpack_abgr_2101010_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_DEPTH_16: + case COGL_PIXEL_FORMAT_DEPTH_32: + case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: + case COGL_PIXEL_FORMAT_ANY: + case COGL_PIXEL_FORMAT_YUV: + g_assert_not_reached (); + } +} + +/* Packing from RGBA */ + +/* Pack and round to nearest */ +#define PACK_SIZE(b, max) \ + (((b) * (max) + (1 << (sizeof (component_type) * 8 - 1)) - 1) / \ + ((1 << (sizeof (component_type) * 8)) - 1)) + +#define PACK_1(b) PACK_SIZE (b, 1) +#define PACK_2(b) PACK_SIZE (b, 3) +#define PACK_4(b) PACK_SIZE (b, 15) +#define PACK_5(b) PACK_SIZE (b, 31) +#define PACK_6(b) PACK_SIZE (b, 63) +#define PACK_10(b) PACK_SIZE (b, 1023) + +inline static void +G_PASTE (_cogl_pack_a_8_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + *dst = PACK_BYTE (src[3]); + src += 4; + dst++; + } +} + +inline static void +G_PASTE (_cogl_pack_g_8_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + /* FIXME: I'm not sure if this is right. It looks like Nvidia and + Mesa handle luminance textures differently. Maybe we should + consider just removing luminance textures for Cogl 2.0 because + they have been removed in GL 3.0 */ + while (width-- > 0) + { + component_type v = (src[0] + src[1] + src[2]) / 3; + *dst = PACK_BYTE (v); + src += 4; + dst++; + } +} + +inline static void +G_PASTE (_cogl_pack_rg_88_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = PACK_BYTE (src[0]); + dst[1] = PACK_BYTE (src[1]); + src += 4; + dst += 2; + } +} + +inline static void +G_PASTE (_cogl_pack_rgb_888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = PACK_BYTE (src[0]); + dst[1] = PACK_BYTE (src[1]); + dst[2] = PACK_BYTE (src[2]); + src += 4; + dst += 3; + } +} + +inline static void +G_PASTE (_cogl_pack_bgr_888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[2] = PACK_BYTE (src[0]); + dst[1] = PACK_BYTE (src[1]); + dst[0] = PACK_BYTE (src[2]); + src += 4; + dst += 3; + } +} + +inline static void +G_PASTE (_cogl_pack_bgra_8888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[2] = PACK_BYTE (src[0]); + dst[1] = PACK_BYTE (src[1]); + dst[0] = PACK_BYTE (src[2]); + dst[3] = PACK_BYTE (src[3]); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_argb_8888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[1] = PACK_BYTE (src[0]); + dst[2] = PACK_BYTE (src[1]); + dst[3] = PACK_BYTE (src[2]); + dst[0] = PACK_BYTE (src[3]); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_abgr_8888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[3] = PACK_BYTE (src[0]); + dst[2] = PACK_BYTE (src[1]); + dst[1] = PACK_BYTE (src[2]); + dst[0] = PACK_BYTE (src[3]); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_rgba_8888_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + dst[0] = PACK_BYTE (src[0]); + dst[1] = PACK_BYTE (src[1]); + dst[2] = PACK_BYTE (src[2]); + dst[3] = PACK_BYTE (src[3]); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_rgb_565_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint16_t *v = (uint16_t *) dst; + + *v = ((PACK_5 (src[0]) << 11) | + (PACK_6 (src[1]) << 5) | + PACK_5 (src[2])); + src += 4; + dst += 2; + } +} + +inline static void +G_PASTE (_cogl_pack_rgba_4444_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint16_t *v = (uint16_t *) dst; + + *v = ((PACK_4 (src[0]) << 12) | + (PACK_4 (src[1]) << 8) | + (PACK_4 (src[2]) << 4) | + PACK_4 (src[3])); + src += 4; + dst += 2; + } +} + +inline static void +G_PASTE (_cogl_pack_rgba_5551_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint16_t *v = (uint16_t *) dst; + + *v = ((PACK_5 (src[0]) << 11) | + (PACK_5 (src[1]) << 6) | + (PACK_5 (src[2]) << 1) | + PACK_1 (src[3])); + src += 4; + dst += 2; + } +} + +inline static void +G_PASTE (_cogl_pack_rgba_1010102_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint32_t *v = (uint32_t *) dst; + + *v = ((PACK_10 (src[0]) << 22) | + (PACK_10 (src[1]) << 12) | + (PACK_10 (src[2]) << 2) | + PACK_2 (src[3])); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_bgra_1010102_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint32_t *v = (uint32_t *) dst; + + *v = ((PACK_10 (src[2]) << 22) | + (PACK_10 (src[1]) << 12) | + (PACK_10 (src[0]) << 2) | + PACK_2 (src[3])); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_argb_2101010_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint32_t *v = (uint32_t *) dst; + + *v = ((PACK_2 (src[3]) << 30) | + (PACK_10 (src[0]) << 20) | + (PACK_10 (src[1]) << 10) | + PACK_10 (src[2])); + src += 4; + dst += 4; + } +} + +inline static void +G_PASTE (_cogl_pack_abgr_2101010_, component_size) (const component_type *src, + uint8_t *dst, + int width) +{ + while (width-- > 0) + { + uint32_t *v = (uint32_t *) dst; + + *v = ((PACK_2 (src[3]) << 30) | + (PACK_10 (src[2]) << 20) | + (PACK_10 (src[1]) << 10) | + PACK_10 (src[0])); + src += 4; + dst += 4; + } +} + +#undef PACK_SIZE +#undef PACK_1 +#undef PACK_2 +#undef PACK_4 +#undef PACK_5 +#undef PACK_6 +#undef PACK_10 + +inline static void +G_PASTE (_cogl_pack_, component_size) (CoglPixelFormat format, + const component_type *src, + uint8_t *dst, + int width) +{ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + G_PASTE (_cogl_pack_a_8_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_G_8: + G_PASTE (_cogl_pack_g_8_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RG_88: + G_PASTE (_cogl_pack_rg_88_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGB_888: + G_PASTE (_cogl_pack_rgb_888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGR_888: + G_PASTE (_cogl_pack_bgr_888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + G_PASTE (_cogl_pack_rgba_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + G_PASTE (_cogl_pack_bgra_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + G_PASTE (_cogl_pack_argb_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ABGR_8888: + case COGL_PIXEL_FORMAT_ABGR_8888_PRE: + G_PASTE (_cogl_pack_abgr_8888_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGB_565: + G_PASTE (_cogl_pack_rgb_565_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + case COGL_PIXEL_FORMAT_RGBA_4444_PRE: + G_PASTE (_cogl_pack_rgba_4444_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + case COGL_PIXEL_FORMAT_RGBA_5551_PRE: + G_PASTE (_cogl_pack_rgba_5551_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_RGBA_1010102: + case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: + G_PASTE (_cogl_pack_rgba_1010102_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_BGRA_1010102: + case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: + G_PASTE (_cogl_pack_bgra_1010102_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ARGB_2101010: + case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: + G_PASTE (_cogl_pack_argb_2101010_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_ABGR_2101010: + case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: + G_PASTE (_cogl_pack_abgr_2101010_, component_size) (src, dst, width); + break; + case COGL_PIXEL_FORMAT_DEPTH_16: + case COGL_PIXEL_FORMAT_DEPTH_32: + case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: + case COGL_PIXEL_FORMAT_ANY: + case COGL_PIXEL_FORMAT_YUV: + g_assert_not_reached (); + } +} diff --git a/cogl/cogl/cogl-bitmap-pixbuf.c b/cogl/cogl/cogl-bitmap-pixbuf.c new file mode 100644 index 000000000..9696de6ec --- /dev/null +++ b/cogl/cogl/cogl-bitmap-pixbuf.c @@ -0,0 +1,136 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-bitmap-private.h" +#include "cogl-context-private.h" +#include "cogl-private.h" +#include "cogl-error-private.h" + +#include + +#include + +CoglBool +_cogl_bitmap_get_size_from_file (const char *filename, + int *width, + int *height) +{ + _COGL_RETURN_VAL_IF_FAIL (filename != NULL, FALSE); + + if (gdk_pixbuf_get_file_info (filename, width, height) != NULL) + return TRUE; + + return FALSE; +} + +CoglBitmap * +_cogl_bitmap_from_file (CoglContext *ctx, + const char *filename, + CoglError **error) +{ + static CoglUserDataKey pixbuf_key; + GdkPixbuf *pixbuf; + CoglBool has_alpha; + GdkColorspace color_space; + CoglPixelFormat pixel_format; + int width; + int height; + int rowstride; + int bits_per_sample; + int n_channels; + CoglBitmap *bmp; + GError *glib_error = NULL; + + /* Load from file using GdkPixbuf */ + pixbuf = gdk_pixbuf_new_from_file (filename, &glib_error); + if (pixbuf == NULL) + { + _cogl_propagate_gerror (error, glib_error); + return FALSE; + } + + /* Get pixbuf properties */ + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + color_space = gdk_pixbuf_get_colorspace (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf); + n_channels = gdk_pixbuf_get_n_channels (pixbuf); + + /* According to current docs this should be true and so + * the translation to cogl pixel format below valid */ + g_assert (bits_per_sample == 8); + + if (has_alpha) + g_assert (n_channels == 4); + else + g_assert (n_channels == 3); + + /* Translate to cogl pixel format */ + switch (color_space) + { + case GDK_COLORSPACE_RGB: + /* The only format supported by GdkPixbuf so far */ + pixel_format = has_alpha ? + COGL_PIXEL_FORMAT_RGBA_8888 : + COGL_PIXEL_FORMAT_RGB_888; + break; + + default: + /* Ouch, spec changed! */ + g_object_unref (pixbuf); + return FALSE; + } + + /* We just use the data directly from the pixbuf so that we don't + have to copy to a seperate buffer. Note that Cogl is expected not + to read past the end of bpp*width on the last row even if the + rowstride is much larger so we don't need to worry about + GdkPixbuf's semantics that it may under-allocate the buffer. */ + bmp = cogl_bitmap_new_for_data (ctx, + width, + height, + pixel_format, + rowstride, + gdk_pixbuf_get_pixels (pixbuf)); + + cogl_object_set_user_data (COGL_OBJECT (bmp), + &pixbuf_key, + pixbuf, + g_object_unref); + + return bmp; +} diff --git a/cogl/cogl/cogl-bitmap-private.h b/cogl/cogl/cogl-bitmap-private.h new file mode 100644 index 000000000..676729d55 --- /dev/null +++ b/cogl/cogl/cogl-bitmap-private.h @@ -0,0 +1,201 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007 OpenedHand + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_BITMAP_H +#define __COGL_BITMAP_H + +#include + +#include "cogl-object-private.h" +#include "cogl-buffer.h" +#include "cogl-bitmap.h" + +struct _CoglBitmap +{ + CoglObject _parent; + + /* Pointer back to the context that this bitmap was created with */ + CoglContext *context; + + CoglPixelFormat format; + int width; + int height; + int rowstride; + + uint8_t *data; + + CoglBool mapped; + CoglBool bound; + + /* If this is non-null then 'data' is ignored and instead it is + fetched from this shared bitmap. */ + CoglBitmap *shared_bmp; + + /* If this is non-null then 'data' is treated as an offset into the + buffer and map will divert to mapping the buffer */ + CoglBuffer *buffer; +}; + + +/* + * _cogl_bitmap_new_with_malloc_buffer: + * @context: A #CoglContext + * @width: width of the bitmap in pixels + * @height: height of the bitmap in pixels + * @format: the format of the pixels the array will store + * @error: A #CoglError for catching exceptional errors or %NULL + * + * This is equivalent to cogl_bitmap_new_with_size() except that it + * allocated the buffer using g_malloc() instead of creating a + * #CoglPixelBuffer. The buffer will be automatically destroyed when + * the bitmap is freed. + * + * Return value: a #CoglPixelBuffer representing the newly created array + * + * Since: 1.10 + * Stability: Unstable + */ +CoglBitmap * +_cogl_bitmap_new_with_malloc_buffer (CoglContext *context, + unsigned int width, + unsigned int height, + CoglPixelFormat format, + CoglError **error); + +/* The idea of this function is that it will create a bitmap that + shares the actual data with another bitmap. This is needed for the + atlas texture backend because it needs upload a bitmap to a sub + texture but override the format so that it ignores the premult + flag. */ +CoglBitmap * +_cogl_bitmap_new_shared (CoglBitmap *shared_bmp, + CoglPixelFormat format, + int width, + int height, + int rowstride); + +CoglBitmap * +_cogl_bitmap_convert (CoglBitmap *bmp, + CoglPixelFormat dst_format, + CoglError **error); + +CoglBitmap * +_cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error); + +CoglBool +_cogl_bitmap_convert_into_bitmap (CoglBitmap *src_bmp, + CoglBitmap *dst_bmp, + CoglError **error); + +CoglBitmap * +_cogl_bitmap_from_file (CoglContext *ctx, + const char *filename, + CoglError **error); + +CoglBool +_cogl_bitmap_unpremult (CoglBitmap *dst_bmp, + CoglError **error); + +CoglBool +_cogl_bitmap_premult (CoglBitmap *dst_bmp, + CoglError **error); + +CoglBool +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format, + CoglError **error); + +CoglBool +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + CoglError **error); + +/* Creates a deep copy of the source bitmap */ +CoglBitmap * +_cogl_bitmap_copy (CoglBitmap *src_bmp, + CoglError **error); + +CoglBool +_cogl_bitmap_get_size_from_file (const char *filename, + int *width, + int *height); + +void +_cogl_bitmap_set_format (CoglBitmap *bitmap, + CoglPixelFormat format); + +/* Maps the bitmap so that the pixels can be accessed directly or if + the bitmap is just a memory bitmap then it just returns the pointer + to memory. Note that the bitmap isn't guaranteed to allocated to + the full size of rowstride*height so it is not safe to read up to + the rowstride of the last row. This will be the case if the user + uploads data using gdk_pixbuf_new_subpixbuf with a sub region + containing the last row of the pixbuf because in that case the + rowstride can be much larger than the width of the image */ +uint8_t * +_cogl_bitmap_map (CoglBitmap *bitmap, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + +void +_cogl_bitmap_unmap (CoglBitmap *bitmap); + +/* These two are replacements for map and unmap that should used when + * the pointer is going to be passed to GL for pixel packing or + * unpacking. The address might not be valid for reading if the bitmap + * was created with new_from_buffer but it will however be good to + * pass to glTexImage2D for example. The access should be READ for + * unpacking and WRITE for packing. It can not be both + * + * TODO: split this bind/unbind functions out into a GL specific file + */ +uint8_t * +_cogl_bitmap_gl_bind (CoglBitmap *bitmap, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + +void +_cogl_bitmap_gl_unbind (CoglBitmap *bitmap); + +CoglContext * +_cogl_bitmap_get_context (CoglBitmap *bitmap); + +#endif /* __COGL_BITMAP_H */ diff --git a/cogl/cogl/cogl-bitmap.c b/cogl/cogl/cogl-bitmap.c new file mode 100644 index 000000000..fbeb2d1c6 --- /dev/null +++ b/cogl/cogl/cogl-bitmap.c @@ -0,0 +1,522 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-debug.h" +#include "cogl-private.h" +#include "cogl-bitmap-private.h" +#include "cogl-buffer-private.h" +#include "cogl-pixel-buffer.h" +#include "cogl-context-private.h" +#include "cogl-buffer-gl-private.h" +#include "cogl-error-private.h" +#include "cogl-gtype-private.h" + +#include + +static void _cogl_bitmap_free (CoglBitmap *bmp); + +COGL_OBJECT_DEFINE (Bitmap, bitmap); +COGL_GTYPE_DEFINE_CLASS (Bitmap, bitmap); + +static void +_cogl_bitmap_free (CoglBitmap *bmp) +{ + g_assert (!bmp->mapped); + g_assert (!bmp->bound); + + if (bmp->shared_bmp) + cogl_object_unref (bmp->shared_bmp); + + if (bmp->buffer) + cogl_object_unref (bmp->buffer); + + g_slice_free (CoglBitmap, bmp); +} + +CoglBool +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format, + CoglError **error) +{ + /* Do we need to unpremultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) > 0 && + (dst_format & COGL_PREMULT_BIT) == 0 && + COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (dst_format)) + return _cogl_bitmap_unpremult (bmp, error); + + /* Do we need to premultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) == 0 && + COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (bmp->format) && + (dst_format & COGL_PREMULT_BIT) > 0) + /* Try premultiplying using imaging library */ + return _cogl_bitmap_premult (bmp, error); + + return TRUE; +} + +CoglBitmap * +_cogl_bitmap_copy (CoglBitmap *src_bmp, + CoglError **error) +{ + CoglBitmap *dst_bmp; + CoglPixelFormat src_format = cogl_bitmap_get_format (src_bmp); + int width = cogl_bitmap_get_width (src_bmp); + int height = cogl_bitmap_get_height (src_bmp); + + dst_bmp = + _cogl_bitmap_new_with_malloc_buffer (src_bmp->context, + width, height, + src_format, + error); + if (!dst_bmp) + return NULL; + + if (!_cogl_bitmap_copy_subregion (src_bmp, + dst_bmp, + 0, 0, /* src_x/y */ + 0, 0, /* dst_x/y */ + width, height, + error)) + { + cogl_object_unref (dst_bmp); + return NULL; + } + + return dst_bmp; +} + +CoglBool +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + CoglError **error) +{ + uint8_t *srcdata; + uint8_t *dstdata; + int bpp; + int line; + CoglBool succeeded = FALSE; + + /* Intended only for fast copies when format is equal! */ + _COGL_RETURN_VAL_IF_FAIL ((src->format & ~COGL_PREMULT_BIT) == + (dst->format & ~COGL_PREMULT_BIT), + FALSE); + + bpp = _cogl_pixel_format_get_bytes_per_pixel (src->format); + + if ((srcdata = _cogl_bitmap_map (src, COGL_BUFFER_ACCESS_READ, 0, error))) + { + if ((dstdata = + _cogl_bitmap_map (dst, COGL_BUFFER_ACCESS_WRITE, 0, error))) + { + srcdata += src_y * src->rowstride + src_x * bpp; + dstdata += dst_y * dst->rowstride + dst_x * bpp; + + for (line = 0; line < height; ++line) + { + memcpy (dstdata, srcdata, width * bpp); + srcdata += src->rowstride; + dstdata += dst->rowstride; + } + + succeeded = TRUE; + + _cogl_bitmap_unmap (dst); + } + + _cogl_bitmap_unmap (src); + } + + return succeeded; +} + +CoglBool +cogl_bitmap_get_size_from_file (const char *filename, + int *width, + int *height) +{ + return _cogl_bitmap_get_size_from_file (filename, width, height); +} + +CoglBitmap * +cogl_bitmap_new_for_data (CoglContext *context, + int width, + int height, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + CoglBitmap *bmp; + + g_return_val_if_fail (cogl_is_context (context), NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + bmp = g_slice_new (CoglBitmap); + bmp->context = context; + bmp->format = format; + bmp->width = width; + bmp->height = height; + bmp->rowstride = rowstride; + bmp->data = data; + bmp->mapped = FALSE; + bmp->bound = FALSE; + bmp->shared_bmp = NULL; + bmp->buffer = NULL; + + return _cogl_bitmap_object_new (bmp); +} + +CoglBitmap * +_cogl_bitmap_new_with_malloc_buffer (CoglContext *context, + unsigned int width, + unsigned int height, + CoglPixelFormat format, + CoglError **error) +{ + static CoglUserDataKey bitmap_free_key; + int bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + int rowstride = ((width * bpp) + 3) & ~3; + uint8_t *data = g_try_malloc (rowstride * height); + CoglBitmap *bitmap; + + if (!data) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY, + "Failed to allocate memory for bitmap"); + return NULL; + } + + bitmap = cogl_bitmap_new_for_data (context, + width, height, + format, + rowstride, + data); + cogl_object_set_user_data (COGL_OBJECT (bitmap), + &bitmap_free_key, + data, + g_free); + + return bitmap; +} + +CoglBitmap * +_cogl_bitmap_new_shared (CoglBitmap *shared_bmp, + CoglPixelFormat format, + int width, + int height, + int rowstride) +{ + CoglBitmap *bmp; + + bmp = cogl_bitmap_new_for_data (shared_bmp->context, + width, height, + format, + rowstride, + NULL /* data */); + + bmp->shared_bmp = cogl_object_ref (shared_bmp); + + return bmp; +} + +CoglBitmap * +cogl_bitmap_new_from_file (const char *filename, + CoglError **error) +{ + _COGL_GET_CONTEXT (ctx, NULL); + + _COGL_RETURN_VAL_IF_FAIL (filename != NULL, NULL); + _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL); + + return _cogl_bitmap_from_file (ctx, filename, error); +} + +CoglBitmap * +cogl_bitmap_new_from_buffer (CoglBuffer *buffer, + CoglPixelFormat format, + int width, + int height, + int rowstride, + int offset) +{ + CoglBitmap *bmp; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL); + + bmp = cogl_bitmap_new_for_data (buffer->context, + width, height, + format, + rowstride, + NULL /* data */); + + bmp->buffer = cogl_object_ref (buffer); + bmp->data = GINT_TO_POINTER (offset); + + return bmp; +} + +CoglBitmap * +cogl_bitmap_new_with_size (CoglContext *context, + unsigned int width, + unsigned int height, + CoglPixelFormat format) +{ + CoglPixelBuffer *pixel_buffer; + CoglBitmap *bitmap; + unsigned int rowstride; + + /* creating a buffer to store "any" format does not make sense */ + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + + /* for now we fallback to cogl_pixel_buffer_new, later, we could ask + * libdrm a tiled buffer for instance */ + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + pixel_buffer = + cogl_pixel_buffer_new (context, + height * rowstride, + NULL); /* data */ + + _COGL_RETURN_VAL_IF_FAIL (pixel_buffer != NULL, NULL); + + bitmap = cogl_bitmap_new_from_buffer (COGL_BUFFER (pixel_buffer), + format, + width, height, + rowstride, + 0 /* offset */); + + cogl_object_unref (pixel_buffer); + + return bitmap; +} + +CoglPixelFormat +cogl_bitmap_get_format (CoglBitmap *bitmap) +{ + return bitmap->format; +} + +void +_cogl_bitmap_set_format (CoglBitmap *bitmap, + CoglPixelFormat format) +{ + bitmap->format = format; +} + +int +cogl_bitmap_get_width (CoglBitmap *bitmap) +{ + return bitmap->width; +} + +int +cogl_bitmap_get_height (CoglBitmap *bitmap) +{ + return bitmap->height; +} + +int +cogl_bitmap_get_rowstride (CoglBitmap *bitmap) +{ + return bitmap->rowstride; +} + +CoglPixelBuffer * +cogl_bitmap_get_buffer (CoglBitmap *bitmap) +{ + while (bitmap->shared_bmp) + bitmap = bitmap->shared_bmp; + + return COGL_PIXEL_BUFFER (bitmap->buffer); +} + +uint32_t +cogl_bitmap_error_quark (void) +{ + return g_quark_from_static_string ("cogl-bitmap-error-quark"); +} + +uint8_t * +_cogl_bitmap_map (CoglBitmap *bitmap, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + /* Divert to another bitmap if this data is shared */ + if (bitmap->shared_bmp) + return _cogl_bitmap_map (bitmap->shared_bmp, access, hints, error); + + g_assert (!bitmap->mapped); + + if (bitmap->buffer) + { + uint8_t *data = _cogl_buffer_map (bitmap->buffer, + access, + hints, + error); + + COGL_NOTE (BITMAP, "A pixel array is being mapped from a bitmap. This " + "usually means that some conversion on the pixel array is " + "needed so a sub-optimal format is being used."); + + if (data) + { + bitmap->mapped = TRUE; + + return data + GPOINTER_TO_INT (bitmap->data); + } + else + return NULL; + } + else + { + bitmap->mapped = TRUE; + + return bitmap->data; + } +} + +void +_cogl_bitmap_unmap (CoglBitmap *bitmap) +{ + /* Divert to another bitmap if this data is shared */ + if (bitmap->shared_bmp) + { + _cogl_bitmap_unmap (bitmap->shared_bmp); + return; + } + + g_assert (bitmap->mapped); + bitmap->mapped = FALSE; + + if (bitmap->buffer) + cogl_buffer_unmap (bitmap->buffer); +} + +uint8_t * +_cogl_bitmap_gl_bind (CoglBitmap *bitmap, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + uint8_t *ptr; + CoglError *internal_error = NULL; + + g_return_val_if_fail (access & (COGL_BUFFER_ACCESS_READ | + COGL_BUFFER_ACCESS_WRITE), + NULL); + + /* Divert to another bitmap if this data is shared */ + if (bitmap->shared_bmp) + return _cogl_bitmap_gl_bind (bitmap->shared_bmp, access, hints, error); + + _COGL_RETURN_VAL_IF_FAIL (!bitmap->bound, NULL); + + /* If the bitmap wasn't created from a buffer then the + implementation of bind is the same as map */ + if (bitmap->buffer == NULL) + { + uint8_t *data = _cogl_bitmap_map (bitmap, access, hints, error); + if (data) + bitmap->bound = TRUE; + return data; + } + + if (access == COGL_BUFFER_ACCESS_READ) + ptr = _cogl_buffer_gl_bind (bitmap->buffer, + COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, + &internal_error); + else if (access == COGL_BUFFER_ACCESS_WRITE) + ptr = _cogl_buffer_gl_bind (bitmap->buffer, + COGL_BUFFER_BIND_TARGET_PIXEL_PACK, + &internal_error); + else + { + ptr = NULL; + g_assert_not_reached (); + return NULL; + } + + /* NB: _cogl_buffer_gl_bind() may return NULL in non-error + * conditions so we have to explicitly check internal_error to see + * if an exception was thrown */ + if (internal_error) + { + _cogl_propagate_error (error, internal_error); + return NULL; + } + + bitmap->bound = TRUE; + + /* The data pointer actually stores the offset */ + return ptr + GPOINTER_TO_INT (bitmap->data); +} + +void +_cogl_bitmap_gl_unbind (CoglBitmap *bitmap) +{ + /* Divert to another bitmap if this data is shared */ + if (bitmap->shared_bmp) + { + _cogl_bitmap_gl_unbind (bitmap->shared_bmp); + return; + } + + g_assert (bitmap->bound); + bitmap->bound = FALSE; + + /* If the bitmap wasn't created from a pixel array then the + implementation of unbind is the same as unmap */ + if (bitmap->buffer) + _cogl_buffer_gl_unbind (bitmap->buffer); + else + _cogl_bitmap_unmap (bitmap); +} + +CoglContext * +_cogl_bitmap_get_context (CoglBitmap *bitmap) +{ + return bitmap->context; +} diff --git a/cogl/cogl/cogl-bitmap.h b/cogl/cogl/cogl-bitmap.h new file mode 100644 index 000000000..2ef5299c2 --- /dev/null +++ b/cogl/cogl/cogl-bitmap.h @@ -0,0 +1,310 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_BITMAP_H__ +#define __COGL_BITMAP_H__ + +/* XXX: We forward declare CoglBitmap here to allow for circular + * dependencies between some headers */ +typedef struct _CoglBitmap CoglBitmap; + +#include +#include +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_bitmap_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_bitmap_get_gtype (void); +#endif + +/** + * SECTION:cogl-bitmap + * @short_description: Functions for loading images + * + * Cogl allows loading image data into memory as CoglBitmaps without + * loading them immediately into GPU textures. + * + * #CoglBitmap is available since Cogl 1.0 + */ + + +/** + * cogl_bitmap_new_from_file: + * @filename: the file to load. + * @error: a #CoglError or %NULL. + * + * Loads an image file from disk. This function can be safely called from + * within a thread. + * + * Return value: (transfer full): a #CoglBitmap to the new loaded + * image data, or %NULL if loading the image failed. + * + * Since: 1.0 + */ +CoglBitmap * +cogl_bitmap_new_from_file (const char *filename, + CoglError **error); + +#if defined (COGL_ENABLE_EXPERIMENTAL_API) + +/** + * cogl_bitmap_new_from_buffer: + * @buffer: A #CoglBuffer containing image data + * @format: The #CoglPixelFormat defining the format of the image data + * in the given @buffer. + * @width: The width of the image data in the given @buffer. + * @height: The height of the image data in the given @buffer. + * @rowstride: The rowstride in bytes of the image data in the given @buffer. + * @offset: The offset into the given @buffer to the first pixel that + * should be considered part of the #CoglBitmap. + * + * Wraps some image data that has been uploaded into a #CoglBuffer as + * a #CoglBitmap. The data is not copied in this process. + * + * Return value: (transfer full): a #CoglBitmap encapsulating the given @buffer. + * + * Since: 1.8 + * Stability: unstable + */ +CoglBitmap * +cogl_bitmap_new_from_buffer (CoglBuffer *buffer, + CoglPixelFormat format, + int width, + int height, + int rowstride, + int offset); + +/** + * cogl_bitmap_new_with_size: + * @context: A #CoglContext + * @width: width of the bitmap in pixels + * @height: height of the bitmap in pixels + * @format: the format of the pixels the array will store + * + * Creates a new #CoglBitmap with the given width, height and format. + * The initial contents of the bitmap are undefined. + * + * The data for the bitmap will be stored in a newly created + * #CoglPixelBuffer. You can get a pointer to the pixel buffer using + * cogl_bitmap_get_buffer(). The #CoglBuffer API can then be + * used to fill the bitmap with data. + * + * Cogl will try its best to provide a hardware array you can + * map, write into and effectively do a zero copy upload when creating + * a texture from it with cogl_texture_new_from_bitmap(). For various + * reasons, such arrays are likely to have a stride larger than width + * * bytes_per_pixel. The user must take the stride into account when + * writing into it. The stride can be retrieved with + * cogl_bitmap_get_rowstride(). + * + * Return value: (transfer full): a #CoglPixelBuffer representing the + * newly created array or %NULL on failure + * + * Since: 1.10 + * Stability: Unstable + */ +CoglBitmap * +cogl_bitmap_new_with_size (CoglContext *context, + unsigned int width, + unsigned int height, + CoglPixelFormat format); + +/** + * cogl_bitmap_new_for_data: + * @context: A #CoglContext + * @width: The width of the bitmap. + * @height: The height of the bitmap. + * @format: The format of the pixel data. + * @rowstride: The rowstride of the bitmap (the number of bytes from + * the start of one row of the bitmap to the next). + * @data: A pointer to the data. The bitmap will take ownership of this data. + * + * Creates a bitmap using some existing data. The data is not copied + * so the application must keep the buffer alive for the lifetime of + * the #CoglBitmap. This can be used for example with + * cogl_framebuffer_read_pixels_into_bitmap() to read data directly + * into an application buffer with the specified rowstride. + * + * Return value: (transfer full): A new #CoglBitmap. + * Since: 1.10 + * Stability: unstable + */ +CoglBitmap * +cogl_bitmap_new_for_data (CoglContext *context, + int width, + int height, + CoglPixelFormat format, + int rowstride, + uint8_t *data); + +/** + * cogl_bitmap_get_format: + * @bitmap: A #CoglBitmap + * + * Return value: the #CoglPixelFormat that the data for the bitmap is in. + * Since: 1.10 + * Stability: unstable + */ +CoglPixelFormat +cogl_bitmap_get_format (CoglBitmap *bitmap); + +/** + * cogl_bitmap_get_width: + * @bitmap: A #CoglBitmap + * + * Return value: the width of the bitmap + * Since: 1.10 + * Stability: unstable + */ +int +cogl_bitmap_get_width (CoglBitmap *bitmap); + +/** + * cogl_bitmap_get_height: + * @bitmap: A #CoglBitmap + * + * Return value: the height of the bitmap + * Since: 1.10 + * Stability: unstable + */ +int +cogl_bitmap_get_height (CoglBitmap *bitmap); + +/** + * cogl_bitmap_get_rowstride: + * @bitmap: A #CoglBitmap + * + * Return value: the rowstride of the bitmap. This is the number of + * bytes between the address of start of one row to the address of the + * next row in the image. + * Since: 1.10 + * Stability: unstable + */ +int +cogl_bitmap_get_rowstride (CoglBitmap *bitmap); + +/** + * cogl_bitmap_get_buffer: + * @bitmap: A #CoglBitmap + * + * Return value: (transfer none): the #CoglPixelBuffer that this + * buffer uses for storage. Note that if the bitmap was created with + * cogl_bitmap_new_from_file() then it will not actually be using a + * pixel buffer and this function will return %NULL. + * Stability: unstable + * Since: 1.10 + */ +CoglPixelBuffer * +cogl_bitmap_get_buffer (CoglBitmap *bitmap); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +/** + * cogl_bitmap_get_size_from_file: + * @filename: the file to check + * @width: (out): return location for the bitmap width, or %NULL + * @height: (out): return location for the bitmap height, or %NULL + * + * Parses an image file enough to extract the width and height + * of the bitmap. + * + * Return value: %TRUE if the image was successfully parsed + * + * Since: 1.0 + */ +CoglBool +cogl_bitmap_get_size_from_file (const char *filename, + int *width, + int *height); + +/** + * cogl_is_bitmap: + * @object: a #CoglObject pointer + * + * Checks whether @object is a #CoglBitmap + * + * Return value: %TRUE if the passed @object represents a bitmap, + * and %FALSE otherwise + * + * Since: 1.0 + */ +CoglBool +cogl_is_bitmap (void *object); + +/** + * COGL_BITMAP_ERROR: + * + * #CoglError domain for bitmap errors. + * + * Since: 1.4 + */ +#define COGL_BITMAP_ERROR (cogl_bitmap_error_quark ()) + +/** + * CoglBitmapError: + * @COGL_BITMAP_ERROR_FAILED: Generic failure code, something went + * wrong. + * @COGL_BITMAP_ERROR_UNKNOWN_TYPE: Unknown image type. + * @COGL_BITMAP_ERROR_CORRUPT_IMAGE: An image file was broken somehow. + * + * Error codes that can be thrown when performing bitmap + * operations. Note that gdk_pixbuf_new_from_file() can also throw + * errors directly from the underlying image loading library. For + * example, if GdkPixbuf is used then errors #GdkPixbufErrors + * will be used directly. + * + * Since: 1.4 + */ +typedef enum { + COGL_BITMAP_ERROR_FAILED, + COGL_BITMAP_ERROR_UNKNOWN_TYPE, + COGL_BITMAP_ERROR_CORRUPT_IMAGE +} CoglBitmapError; + +uint32_t cogl_bitmap_error_quark (void); + +COGL_END_DECLS + +#endif /* __COGL_BITMAP_H__ */ diff --git a/cogl/cogl/cogl-bitmask.c b/cogl/cogl/cogl-bitmask.c new file mode 100644 index 000000000..7034bc990 --- /dev/null +++ b/cogl/cogl/cogl-bitmask.c @@ -0,0 +1,489 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#include "config.h" + +#include +#include + +#include + +#include "cogl-bitmask.h" +#include "cogl-util.h" +#include "cogl-flags.h" + +/* This code assumes that we can cast an unsigned long to a pointer + and back without losing any data */ +_COGL_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *), + "This toolchain breaks Cogl's assumption that it can " + "safely cast an unsigned long to a pointer without " + "loosing data"); + +#define ARRAY_INDEX(bit_num) \ + ((bit_num) / (sizeof (unsigned long) * 8)) +#define BIT_INDEX(bit_num) \ + ((bit_num) & (sizeof (unsigned long) * 8 - 1)) +#define BIT_MASK(bit_num) \ + (1UL << BIT_INDEX (bit_num)) + +CoglBool +_cogl_bitmask_get_from_array (const CoglBitmask *bitmask, + unsigned int bit_num) +{ + GArray *array = (GArray *) *bitmask; + + /* If the index is off the end of the array then assume the bit is + not set */ + if (bit_num >= sizeof (unsigned long) * 8 * array->len) + return FALSE; + else + return !!(g_array_index (array, unsigned long, ARRAY_INDEX (bit_num)) & + BIT_MASK (bit_num)); +} + +static void +_cogl_bitmask_convert_to_array (CoglBitmask *bitmask) +{ + GArray *array; + /* Fetch the old values */ + unsigned long old_values = _cogl_bitmask_to_bits (bitmask); + + array = g_array_new (FALSE, /* not zero-terminated */ + TRUE, /* do clear new entries */ + sizeof (unsigned long)); + /* Copy the old values back in */ + g_array_append_val (array, old_values); + + *bitmask = (struct _CoglBitmaskImaginaryType *) array; +} + +void +_cogl_bitmask_set_in_array (CoglBitmask *bitmask, + unsigned int bit_num, + CoglBool value) +{ + GArray *array; + unsigned int array_index; + unsigned long new_value_mask; + + /* If the bitmask is not already an array then we need to allocate one */ + if (!_cogl_bitmask_has_array (bitmask)) + _cogl_bitmask_convert_to_array (bitmask); + + array = (GArray *) *bitmask; + + array_index = ARRAY_INDEX (bit_num); + /* Grow the array if necessary. This will clear the new data */ + if (array_index >= array->len) + g_array_set_size (array, array_index + 1); + + new_value_mask = BIT_MASK (bit_num); + + if (value) + g_array_index (array, unsigned long, array_index) |= new_value_mask; + else + g_array_index (array, unsigned long, array_index) &= ~new_value_mask; +} + +void +_cogl_bitmask_set_bits (CoglBitmask *dst, + const CoglBitmask *src) +{ + if (_cogl_bitmask_has_array (src)) + { + GArray *src_array, *dst_array; + int i; + + if (!_cogl_bitmask_has_array (dst)) + _cogl_bitmask_convert_to_array (dst); + + dst_array = (GArray *) *dst; + src_array = (GArray *) *src; + + if (dst_array->len < src_array->len) + g_array_set_size (dst_array, src_array->len); + + for (i = 0; i < src_array->len; i++) + g_array_index (dst_array, unsigned long, i) |= + g_array_index (src_array, unsigned long, i); + } + else if (_cogl_bitmask_has_array (dst)) + { + GArray *dst_array; + + dst_array = (GArray *) *dst; + + g_array_index (dst_array, unsigned long, 0) |= + _cogl_bitmask_to_bits (src); + } + else + *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) | + _cogl_bitmask_to_bits (src)); +} + +void +_cogl_bitmask_set_range_in_array (CoglBitmask *bitmask, + unsigned int n_bits, + CoglBool value) +{ + GArray *array; + unsigned int array_index, bit_index; + + if (n_bits == 0) + return; + + /* If the bitmask is not already an array then we need to allocate one */ + if (!_cogl_bitmask_has_array (bitmask)) + _cogl_bitmask_convert_to_array (bitmask); + + array = (GArray *) *bitmask; + + /* Get the array index of the top most value that will be touched */ + array_index = ARRAY_INDEX (n_bits - 1); + /* Get the bit index of the top most value */ + bit_index = BIT_INDEX (n_bits - 1); + /* Grow the array if necessary. This will clear the new data */ + if (array_index >= array->len) + g_array_set_size (array, array_index + 1); + + if (value) + { + /* Set the bits that are touching this index */ + g_array_index (array, unsigned long, array_index) |= + ~0UL >> (sizeof (unsigned long) * 8 - 1 - bit_index); + + /* Set all of the bits in any lesser indices */ + memset (array->data, 0xff, sizeof (unsigned long) * array_index); + } + else + { + /* Clear the bits that are touching this index */ + g_array_index (array, unsigned long, array_index) &= ~1UL << bit_index; + + /* Clear all of the bits in any lesser indices */ + memset (array->data, 0x00, sizeof (unsigned long) * array_index); + } +} + +void +_cogl_bitmask_xor_bits (CoglBitmask *dst, + const CoglBitmask *src) +{ + if (_cogl_bitmask_has_array (src)) + { + GArray *src_array, *dst_array; + int i; + + if (!_cogl_bitmask_has_array (dst)) + _cogl_bitmask_convert_to_array (dst); + + dst_array = (GArray *) *dst; + src_array = (GArray *) *src; + + if (dst_array->len < src_array->len) + g_array_set_size (dst_array, src_array->len); + + for (i = 0; i < src_array->len; i++) + g_array_index (dst_array, unsigned long, i) ^= + g_array_index (src_array, unsigned long, i); + } + else if (_cogl_bitmask_has_array (dst)) + { + GArray *dst_array; + + dst_array = (GArray *) *dst; + + g_array_index (dst_array, unsigned long, 0) ^= + _cogl_bitmask_to_bits (src); + } + else + *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) ^ + _cogl_bitmask_to_bits (src)); +} + +void +_cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask) +{ + GArray *array = (GArray *) *bitmask; + + memset (array->data, 0, sizeof (unsigned long) * array->len); +} + +void +_cogl_bitmask_foreach (const CoglBitmask *bitmask, + CoglBitmaskForeachFunc func, + void *user_data) +{ + if (_cogl_bitmask_has_array (bitmask)) + { + GArray *array = (GArray *) *bitmask; + const unsigned long *values = &g_array_index (array, unsigned long, 0); + int bit_num; + + COGL_FLAGS_FOREACH_START (values, array->len, bit_num) + { + if (!func (bit_num, user_data)) + return; + } + COGL_FLAGS_FOREACH_END; + } + else + { + unsigned long mask = _cogl_bitmask_to_bits (bitmask); + int bit_num; + + COGL_FLAGS_FOREACH_START (&mask, 1, bit_num) + { + if (!func (bit_num, user_data)) + return; + } + COGL_FLAGS_FOREACH_END; + } +} + +void +_cogl_bitmask_set_flags_array (const CoglBitmask *bitmask, + unsigned long *flags) +{ + const GArray *array = (const GArray *) *bitmask; + int i; + + for (i = 0; i < array->len; i++) + flags[i] |= g_array_index (array, unsigned long, i); +} + +int +_cogl_bitmask_popcount_in_array (const CoglBitmask *bitmask) +{ + const GArray *array = (const GArray *) *bitmask; + int pop = 0; + int i; + + for (i = 0; i < array->len; i++) + pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i)); + + return pop; +} + +int +_cogl_bitmask_popcount_upto_in_array (const CoglBitmask *bitmask, + int upto) +{ + const GArray *array = (const GArray *) *bitmask; + + if (upto >= array->len * sizeof (unsigned long) * 8) + return _cogl_bitmask_popcount_in_array (bitmask); + else + { + unsigned long top_mask; + int array_index = ARRAY_INDEX (upto); + int bit_index = BIT_INDEX (upto); + int pop = 0; + int i; + + for (i = 0; i < array_index; i++) + pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i)); + + top_mask = g_array_index (array, unsigned long, array_index); + + return pop + _cogl_util_popcountl (top_mask & ((1UL << bit_index) - 1)); + } +} + +typedef struct +{ + int n_bits; + int *bits; +} CheckData; + +static CoglBool +check_bit (int bit_num, void *user_data) +{ + CheckData *data = user_data; + int i; + + for (i = 0; i < data->n_bits; i++) + if (data->bits[i] == bit_num) + { + data->bits[i] = -1; + return TRUE; + } + + g_assert_not_reached (); + + return TRUE; +} + +static void +verify_bits (const CoglBitmask *bitmask, + ...) +{ + CheckData data; + va_list ap, ap_copy; + int i; + + va_start (ap, bitmask); + G_VA_COPY (ap_copy, ap); + + for (data.n_bits = 0; va_arg (ap, int) != -1; data.n_bits++); + + data.bits = alloca (data.n_bits * (sizeof (int))); + + G_VA_COPY (ap, ap_copy); + + for (i = 0; i < data.n_bits; i++) + data.bits[i] = va_arg (ap, int); + + _cogl_bitmask_foreach (bitmask, check_bit, &data); + + for (i = 0; i < data.n_bits; i++) + g_assert_cmpint (data.bits[i], ==, -1); + + g_assert_cmpint (_cogl_bitmask_popcount (bitmask), ==, data.n_bits); + + for (i = 0; i < 1024; i++) + { + int upto_popcount = 0; + int j; + + G_VA_COPY (ap, ap_copy); + + for (j = 0; j < data.n_bits; j++) + if (va_arg (ap, int) < i) + upto_popcount++; + + g_assert_cmpint (_cogl_bitmask_popcount_upto (bitmask, i), + ==, + upto_popcount); + + G_VA_COPY (ap, ap_copy); + + for (j = 0; j < data.n_bits; j++) + if (va_arg (ap, int) == i) + break; + + g_assert_cmpint (_cogl_bitmask_get (bitmask, i), ==, (j < data.n_bits)); + } +} + +UNIT_TEST (check_bitmask_api, + 0 /* no requirements */, + 0 /* no failure cases */) +{ + CoglBitmask bitmask; + CoglBitmask other_bitmask; + /* A dummy bit to make it use arrays sometimes */ + int dummy_bit; + int i; + + for (dummy_bit = -1; dummy_bit < 256; dummy_bit += 40) + { + _cogl_bitmask_init (&bitmask); + _cogl_bitmask_init (&other_bitmask); + + if (dummy_bit != -1) + _cogl_bitmask_set (&bitmask, dummy_bit, TRUE); + + verify_bits (&bitmask, dummy_bit, -1); + + _cogl_bitmask_set (&bitmask, 1, TRUE); + _cogl_bitmask_set (&bitmask, 4, TRUE); + _cogl_bitmask_set (&bitmask, 5, TRUE); + + verify_bits (&bitmask, 1, 4, 5, dummy_bit, -1); + + _cogl_bitmask_set (&bitmask, 4, FALSE); + + verify_bits (&bitmask, 1, 5, dummy_bit, -1); + + _cogl_bitmask_clear_all (&bitmask); + + verify_bits (&bitmask, -1); + + if (dummy_bit != -1) + _cogl_bitmask_set (&bitmask, dummy_bit, TRUE); + + verify_bits (&bitmask, dummy_bit, -1); + + _cogl_bitmask_set (&bitmask, 1, TRUE); + _cogl_bitmask_set (&bitmask, 4, TRUE); + _cogl_bitmask_set (&bitmask, 5, TRUE); + _cogl_bitmask_set (&other_bitmask, 5, TRUE); + _cogl_bitmask_set (&other_bitmask, 6, TRUE); + + _cogl_bitmask_set_bits (&bitmask, &other_bitmask); + + verify_bits (&bitmask, 1, 4, 5, 6, dummy_bit, -1); + verify_bits (&other_bitmask, 5, 6, -1); + + _cogl_bitmask_set (&bitmask, 6, FALSE); + + verify_bits (&bitmask, 1, 4, 5, dummy_bit, -1); + + _cogl_bitmask_xor_bits (&bitmask, &other_bitmask); + + verify_bits (&bitmask, 1, 4, 6, dummy_bit, -1); + verify_bits (&other_bitmask, 5, 6, -1); + + _cogl_bitmask_set_range (&bitmask, 5, TRUE); + + verify_bits (&bitmask, 0, 1, 2, 3, 4, 6, dummy_bit, -1); + + _cogl_bitmask_set_range (&bitmask, 4, FALSE); + + verify_bits (&bitmask, 4, 6, dummy_bit, -1); + + _cogl_bitmask_destroy (&other_bitmask); + _cogl_bitmask_destroy (&bitmask); + } + + /* Extra tests for really long bitmasks */ + _cogl_bitmask_init (&bitmask); + _cogl_bitmask_set_range (&bitmask, 400, TRUE); + _cogl_bitmask_init (&other_bitmask); + _cogl_bitmask_set (&other_bitmask, 5, TRUE); + _cogl_bitmask_xor_bits (&bitmask, &other_bitmask); + + for (i = 0; i < 1024; i++) + g_assert_cmpint (_cogl_bitmask_get (&bitmask, i), + ==, + (i == 5 ? FALSE : + i < 400 ? TRUE : + FALSE)); + + _cogl_bitmask_set_range (&other_bitmask, 500, TRUE); + _cogl_bitmask_set_bits (&bitmask, &other_bitmask); + + for (i = 0; i < 1024; i++) + g_assert_cmpint (_cogl_bitmask_get (&bitmask, i), ==, (i < 500)); +} diff --git a/cogl/cogl/cogl-bitmask.h b/cogl/cogl/cogl-bitmask.h new file mode 100644 index 000000000..e0db3df26 --- /dev/null +++ b/cogl/cogl/cogl-bitmask.h @@ -0,0 +1,312 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_BITMASK_H +#define __COGL_BITMASK_H + +#include +#include "cogl-util.h" + +COGL_BEGIN_DECLS + +/* + * CoglBitmask implements a growable array of bits. A CoglBitmask can + * be allocated on the stack but it must be initialised with + * _cogl_bitmask_init() before use and then destroyed with + * _cogl_bitmask_destroy(). A CoglBitmask will try to avoid allocating + * any memory unless more than the number of bits in a long - 1 bits + * are needed. + * + * Internally a CoglBitmask is a pointer. If the least significant bit + * of the pointer is 1 then the rest of the bits are directly used as + * part of the bitmask, otherwise it is a pointer to a GArray of + * unsigned ints. This relies on the fact the g_malloc will return a + * pointer aligned to at least two bytes (so that the least + * significant bit of the address is always 0). It also assumes that + * the size of a pointer is always greater than or equal to the size + * of a long (although there is a compile time assert to verify this). + * + * If the maximum possible bit number in the set is known at compile + * time, it may make more sense to use the macros in cogl-flags.h + * instead of this type. + */ + +typedef struct _CoglBitmaskImaginaryType *CoglBitmask; + +/* These are internal helper macros */ +#define _cogl_bitmask_to_number(bitmask) \ + ((unsigned long) (*bitmask)) +#define _cogl_bitmask_to_bits(bitmask) \ + (_cogl_bitmask_to_number (bitmask) >> 1UL) +/* The least significant bit is set to mark that no array has been + allocated yet */ +#define _cogl_bitmask_from_bits(bits) \ + ((void *) ((((unsigned long) (bits)) << 1UL) | 1UL)) + +/* Internal helper macro to determine whether this bitmask has a + GArray allocated or whether the pointer is just used directly */ +#define _cogl_bitmask_has_array(bitmask) \ + (!(_cogl_bitmask_to_number (bitmask) & 1UL)) + +/* Number of bits we can use before needing to allocate an array */ +#define COGL_BITMASK_MAX_DIRECT_BITS (sizeof (unsigned long) * 8 - 1) + +/* + * _cogl_bitmask_init: + * @bitmask: A pointer to a bitmask + * + * Initialises the cogl bitmask. This must be called before any other + * bitmask functions are called. Initially all of the values are + * zero + */ +#define _cogl_bitmask_init(bitmask) \ + G_STMT_START { *(bitmask) = _cogl_bitmask_from_bits (0); } G_STMT_END + +CoglBool +_cogl_bitmask_get_from_array (const CoglBitmask *bitmask, + unsigned int bit_num); + +void +_cogl_bitmask_set_in_array (CoglBitmask *bitmask, + unsigned int bit_num, + CoglBool value); + +void +_cogl_bitmask_set_range_in_array (CoglBitmask *bitmask, + unsigned int n_bits, + CoglBool value); + +void +_cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask); + +void +_cogl_bitmask_set_flags_array (const CoglBitmask *bitmask, + unsigned long *flags); + +int +_cogl_bitmask_popcount_in_array (const CoglBitmask *bitmask); + +int +_cogl_bitmask_popcount_upto_in_array (const CoglBitmask *bitmask, + int upto); + +/* + * cogl_bitmask_set_bits: + * @dst: The bitmask to modify + * @src: The bitmask to copy bits from + * + * This makes sure that all of the bits that are set in @src are also + * set in @dst. Any unset bits in @src are left alone in @dst. + */ +void +_cogl_bitmask_set_bits (CoglBitmask *dst, + const CoglBitmask *src); + +/* + * cogl_bitmask_xor_bits: + * @dst: The bitmask to modify + * @src: The bitmask to copy bits from + * + * For every bit that is set in src, the corresponding bit in dst is + * inverted. + */ +void +_cogl_bitmask_xor_bits (CoglBitmask *dst, + const CoglBitmask *src); + +/* The foreach function can return FALSE to stop iteration */ +typedef CoglBool (* CoglBitmaskForeachFunc) (int bit_num, void *user_data); + +/* + * cogl_bitmask_foreach: + * @bitmask: A pointer to a bitmask + * @func: A callback function + * @user_data: A pointer to pass to the callback + * + * This calls @func for each bit that is set in @bitmask. + */ +void +_cogl_bitmask_foreach (const CoglBitmask *bitmask, + CoglBitmaskForeachFunc func, + void *user_data); + +/* + * _cogl_bitmask_get: + * @bitmask: A pointer to a bitmask + * @bit_num: A bit number + * + * Return value: whether bit number @bit_num is set in @bitmask + */ +static inline CoglBool +_cogl_bitmask_get (const CoglBitmask *bitmask, unsigned int bit_num) +{ + if (_cogl_bitmask_has_array (bitmask)) + return _cogl_bitmask_get_from_array (bitmask, bit_num); + else if (bit_num >= COGL_BITMASK_MAX_DIRECT_BITS) + return FALSE; + else + return !!(_cogl_bitmask_to_bits (bitmask) & (1UL << bit_num)); +} + +/* + * _cogl_bitmask_set: + * @bitmask: A pointer to a bitmask + * @bit_num: A bit number + * @value: The new value + * + * Sets or resets a bit number @bit_num in @bitmask according to @value. + */ +static inline void +_cogl_bitmask_set (CoglBitmask *bitmask, unsigned int bit_num, CoglBool value) +{ + if (_cogl_bitmask_has_array (bitmask) || + bit_num >= COGL_BITMASK_MAX_DIRECT_BITS) + _cogl_bitmask_set_in_array (bitmask, bit_num, value); + else if (value) + *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) | + (1UL << bit_num)); + else + *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) & + ~(1UL << bit_num)); +} + +/* + * _cogl_bitmask_set_range: + * @bitmask: A pointer to a bitmask + * @n_bits: The number of bits to set + * @value: The value to set + * + * Sets the first @n_bits in @bitmask to @value. + */ +static inline void +_cogl_bitmask_set_range (CoglBitmask *bitmask, + unsigned int n_bits, + CoglBool value) +{ + if (_cogl_bitmask_has_array (bitmask) || + n_bits > COGL_BITMASK_MAX_DIRECT_BITS) + _cogl_bitmask_set_range_in_array (bitmask, n_bits, value); + else if (value) + *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) | + ~(~0UL << n_bits)); + else + *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) & + (~0UL << n_bits)); +} + +/* + * _cogl_bitmask_destroy: + * @bitmask: A pointer to a bitmask + * + * Destroys any resources allocated by the bitmask + */ +static inline void +_cogl_bitmask_destroy (CoglBitmask *bitmask) +{ + if (_cogl_bitmask_has_array (bitmask)) + g_array_free ((GArray *) *bitmask, TRUE); +} + +/* + * _cogl_bitmask_clear_all: + * @bitmask: A pointer to a bitmask + * + * Clears all the bits in a bitmask without destroying any resources. + */ +static inline void +_cogl_bitmask_clear_all (CoglBitmask *bitmask) +{ + if (_cogl_bitmask_has_array (bitmask)) + _cogl_bitmask_clear_all_in_array (bitmask); + else + *bitmask = _cogl_bitmask_from_bits (0); +} + +/* + * _cogl_bitmask_set_flags: + * @bitmask: A pointer to a bitmask + * @flags: An array of flags + * + * Bitwise or's the bits from @bitmask into the flags array (see + * cogl-flags) pointed to by @flags. + */ +static inline void +_cogl_bitmask_set_flags (const CoglBitmask *bitmask, + unsigned long *flags) +{ + if (_cogl_bitmask_has_array (bitmask)) + _cogl_bitmask_set_flags_array (bitmask, flags); + else + flags[0] |= _cogl_bitmask_to_bits (bitmask); +} + +/* + * _cogl_bitmask_popcount: + * @bitmask: A pointer to a bitmask + * + * Counts the number of bits that are set in the bitmask. + * + * Return value: the number of bits set in @bitmask. + */ +static inline int +_cogl_bitmask_popcount (const CoglBitmask *bitmask) +{ + return (_cogl_bitmask_has_array (bitmask) ? + _cogl_bitmask_popcount_in_array (bitmask) : + _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask))); +} + +/* + * _cogl_bitmask_popcount: + * @Bitmask: A pointer to a bitmask + * @upto: The maximum bit index to consider + * + * Counts the number of bits that are set and have an index which is + * less than @upto. + * + * Return value: the number of bits set in @bitmask that are less than @upto. + */ +static inline int +_cogl_bitmask_popcount_upto (const CoglBitmask *bitmask, + int upto) +{ + if (_cogl_bitmask_has_array (bitmask)) + return _cogl_bitmask_popcount_upto_in_array (bitmask, upto); + else if (upto >= COGL_BITMASK_MAX_DIRECT_BITS) + return _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask)); + else + return _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask) & + ((1UL << upto) - 1)); +} + +COGL_END_DECLS + +#endif /* __COGL_BITMASK_H */ diff --git a/cogl/cogl/cogl-blend-string.c b/cogl/cogl/cogl-blend-string.c new file mode 100644 index 000000000..5d876bfd8 --- /dev/null +++ b/cogl/cogl/cogl-blend-string.c @@ -0,0 +1,1003 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "cogl-context-private.h" +#include "cogl-debug.h" +#include "cogl-blend-string.h" +#include "cogl-error-private.h" + +typedef enum _ParserState +{ + PARSER_STATE_EXPECT_DEST_CHANNELS, + PARSER_STATE_SCRAPING_DEST_CHANNELS, + PARSER_STATE_EXPECT_FUNCTION_NAME, + PARSER_STATE_SCRAPING_FUNCTION_NAME, + PARSER_STATE_EXPECT_ARG_START, + PARSER_STATE_EXPECT_STATEMENT_END +} ParserState; + +typedef enum _ParserArgState +{ + PARSER_ARG_STATE_START, + PARSER_ARG_STATE_EXPECT_MINUS, + PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME, + PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME, + PARSER_ARG_STATE_MAYBE_COLOR_MASK, + PARSER_ARG_STATE_SCRAPING_MASK, + PARSER_ARG_STATE_MAYBE_MULT, + PARSER_ARG_STATE_EXPECT_OPEN_PAREN, + PARSER_ARG_STATE_EXPECT_FACTOR, + PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE, + PARSER_ARG_STATE_MAYBE_MINUS, + PARSER_ARG_STATE_EXPECT_CLOSE_PAREN, + PARSER_ARG_STATE_EXPECT_END +} ParserArgState; + + +#define DEFINE_COLOR_SOURCE(NAME, NAME_LEN) \ + {/*.type = */COGL_BLEND_STRING_COLOR_SOURCE_ ## NAME, \ + /*.name = */#NAME, \ + /*.name_len = */NAME_LEN} + +static CoglBlendStringColorSourceInfo blending_color_sources[] = { + DEFINE_COLOR_SOURCE (SRC_COLOR, 9), + DEFINE_COLOR_SOURCE (DST_COLOR, 9), + DEFINE_COLOR_SOURCE (CONSTANT, 8) +}; + +static CoglBlendStringColorSourceInfo tex_combine_color_sources[] = { + DEFINE_COLOR_SOURCE (TEXTURE, 7), + /* DEFINE_COLOR_SOURCE (TEXTURE_N, *) - handled manually */ + DEFINE_COLOR_SOURCE (PRIMARY, 7), + DEFINE_COLOR_SOURCE (CONSTANT, 8), + DEFINE_COLOR_SOURCE (PREVIOUS, 8) +}; + +static CoglBlendStringColorSourceInfo tex_combine_texture_n_color_source = { + /*.type = */COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, + /*.name = */"TEXTURE_N", + /*.name_len = */0 +}; + +#undef DEFINE_COLOR_SOURCE + +#define DEFINE_FUNCTION(NAME, NAME_LEN, ARGC) \ + { /*.type = */COGL_BLEND_STRING_FUNCTION_ ## NAME, \ + /*.name = */#NAME, \ + /*.name_len = */NAME_LEN, \ + /*.argc = */ARGC } + +/* NB: These must be sorted so any name that's a subset of another + * comes later than the longer name. */ +static CoglBlendStringFunctionInfo tex_combine_functions[] = { + DEFINE_FUNCTION (REPLACE, 7, 1), + DEFINE_FUNCTION (MODULATE, 8, 2), + DEFINE_FUNCTION (ADD_SIGNED, 10, 2), + DEFINE_FUNCTION (ADD, 3, 2), + DEFINE_FUNCTION (INTERPOLATE, 11, 3), + DEFINE_FUNCTION (SUBTRACT, 8, 2), + DEFINE_FUNCTION (DOT3_RGBA, 9, 2), + DEFINE_FUNCTION (DOT3_RGB, 8, 2) +}; + +static CoglBlendStringFunctionInfo blend_functions[] = { + DEFINE_FUNCTION (ADD, 3, 2) +}; + +#undef DEFINE_FUNCTION + +uint32_t +cogl_blend_string_error_quark (void) +{ + return g_quark_from_static_string ("cogl-blend-string-error-quark"); +} + +void +_cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, + CoglBlendStringStatement *rgb, + CoglBlendStringStatement *a) +{ + int i; + + memcpy (rgb, statement, sizeof (CoglBlendStringStatement)); + memcpy (a, statement, sizeof (CoglBlendStringStatement)); + + rgb->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + + for (i = 0; i < statement->function->argc; i++) + { + CoglBlendStringArgument *arg = &statement->args[i]; + CoglBlendStringArgument *rgb_arg = &rgb->args[i]; + CoglBlendStringArgument *a_arg = &a->args[i]; + + if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + rgb_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + } + + if (arg->factor.is_color && + arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + rgb_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + } + } +} + +static CoglBool +validate_tex_combine_statements (CoglBlendStringStatement *statements, + int n_statements, + CoglError **error) +{ + int i, j; + const char *error_string; + CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; + + for (i = 0; i < n_statements; i++) + { + for (j = 0; j < statements[i].function->argc; j++) + { + CoglBlendStringArgument *arg = &statements[i].args[j]; + if (arg->source.is_zero) + { + error_string = "You can't use the constant '0' as a texture " + "combine argument"; + goto error; + } + if (!arg->factor.is_one) + { + error_string = "Argument factors are only relevant to blending " + "not texture combining"; + goto error; + } + } + } + + return TRUE; + +error: + _cogl_set_error (error, + COGL_BLEND_STRING_ERROR, + detail, + "Invalid texture combine string: %s", + error_string); + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + g_debug ("Invalid texture combine string: %s", + error_string); + } + return FALSE; +} + +static CoglBool +validate_blend_statements (CoglBlendStringStatement *statements, + int n_statements, + CoglError **error) +{ + int i, j; + const char *error_string; + CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; + + _COGL_GET_CONTEXT (ctx, 0); + + if (n_statements == 2 && + !ctx->glBlendEquationSeparate && + statements[0].function->type != statements[1].function->type) + { + error_string = "Separate blend functions for the RGB an A " + "channels isn't supported by the driver"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } + + for (i = 0; i < n_statements; i++) + for (j = 0; j < statements[i].function->argc; j++) + { + CoglBlendStringArgument *arg = &statements[i].args[j]; + + if (arg->source.is_zero) + continue; + + if ((j == 0 && + arg->source.info->type != + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) + || (j == 1 && + arg->source.info->type != + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR)) + { + error_string = "For blending you must always use SRC_COLOR " + "for arg0 and DST_COLOR for arg1"; + goto error; + } + + if (!_cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_BLEND_CONSTANT) && + arg->factor.is_color && + (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT)) + { + error_string = "Driver doesn't support constant blend factors"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } + } + + return TRUE; + +error: + _cogl_set_error (error, + COGL_BLEND_STRING_ERROR, + detail, + "Invalid blend string: %s", + error_string); + return FALSE; +} + +static CoglBool +validate_statements_for_context (CoglBlendStringStatement *statements, + int n_statements, + CoglBlendStringContext context, + CoglError **error) +{ + const char *error_string; + + if (n_statements == 1) + { + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + { + error_string = "You need to also give a blend statement for the RGB" + "channels"; + goto error; + } + else if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + error_string = "You need to also give a blend statement for the " + "Alpha channel"; + goto error; + } + } + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + return validate_blend_statements (statements, n_statements, error); + else + return validate_tex_combine_statements (statements, n_statements, error); + +error: + _cogl_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_INVALID_ERROR, + "Invalid %s string: %s", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + error_string); + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + g_debug ("Invalid %s string: %s", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + error_string); + } + + return FALSE; +} + +static void +print_argument (CoglBlendStringArgument *arg) +{ + const char *mask_names[] = { + "RGB", + "A", + "RGBA" + }; + + g_print (" Arg:\n"); + g_print (" is zero = %s\n", arg->source.is_zero ? "yes" : "no"); + if (!arg->source.is_zero) + { + g_print (" color source = %s\n", arg->source.info->name); + g_print (" one minus = %s\n", arg->source.one_minus ? "yes" : "no"); + g_print (" mask = %s\n", mask_names[arg->source.mask]); + g_print (" texture = %d\n", arg->source.texture); + g_print ("\n"); + g_print (" factor is_one = %s\n", arg->factor.is_one ? "yes" : "no"); + g_print (" factor is_src_alpha_saturate = %s\n", + arg->factor.is_src_alpha_saturate ? "yes" : "no"); + g_print (" factor is_color = %s\n", arg->factor.is_color ? "yes" : "no"); + if (arg->factor.is_color) + { + g_print (" factor color:is zero = %s\n", + arg->factor.source.is_zero ? "yes" : "no"); + g_print (" factor color:color source = %s\n", + arg->factor.source.info->name); + g_print (" factor color:one minus = %s\n", + arg->factor.source.one_minus ? "yes" : "no"); + g_print (" factor color:mask = %s\n", + mask_names[arg->factor.source.mask]); + g_print (" factor color:texture = %d\n", + arg->factor.source.texture); + } + } +} + +static void +print_statement (int num, CoglBlendStringStatement *statement) +{ + const char *mask_names[] = { + "RGB", + "A", + "RGBA" + }; + int i; + g_print ("Statement %d:\n", num); + g_print (" Destination channel mask = %s\n", + mask_names[statement->mask]); + g_print (" Function = %s\n", statement->function->name); + for (i = 0; i < statement->function->argc; i++) + print_argument (&statement->args[i]); +} + +static const CoglBlendStringFunctionInfo * +get_function_info (const char *mark, + const char *p, + CoglBlendStringContext context) +{ + size_t len = p - mark; + CoglBlendStringFunctionInfo *functions; + size_t array_len; + int i; + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + { + functions = blend_functions; + array_len = G_N_ELEMENTS (blend_functions); + } + else + { + functions = tex_combine_functions; + array_len = G_N_ELEMENTS (tex_combine_functions); + } + + for (i = 0; i < array_len; i++) + { + if (len >= functions[i].name_len + && strncmp (mark, functions[i].name, functions[i].name_len) == 0) + return &functions[i]; + } + return NULL; +} + +static const CoglBlendStringColorSourceInfo * +get_color_src_info (const char *mark, + const char *p, + CoglBlendStringContext context) +{ + size_t len = p - mark; + CoglBlendStringColorSourceInfo *sources; + size_t array_len; + int i; + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + { + sources = blending_color_sources; + array_len = G_N_ELEMENTS (blending_color_sources); + } + else + { + sources = tex_combine_color_sources; + array_len = G_N_ELEMENTS (tex_combine_color_sources); + } + + if (len >= 8 && + strncmp (mark, "TEXTURE_", 8) == 0 && + g_ascii_isdigit (mark[8])) + { + return &tex_combine_texture_n_color_source; + } + + for (i = 0; i < array_len; i++) + { + if (len >= sources[i].name_len + && strncmp (mark, sources[i].name, sources[i].name_len) == 0) + return &sources[i]; + } + + return NULL; +} + +static CoglBool +is_symbol_char (const char c) +{ + return (g_ascii_isalpha (c) || c == '_') ? TRUE : FALSE; +} + +static CoglBool +is_alphanum_char (const char c) +{ + return (g_ascii_isalnum (c) || c == '_') ? TRUE : FALSE; +} + +static CoglBool +parse_argument (const char *string, /* original user string */ + const char **ret_p, /* start of argument IN:OUT */ + const CoglBlendStringStatement *statement, + int current_arg, + CoglBlendStringArgument *arg, /* OUT */ + CoglBlendStringContext context, + CoglError **error) +{ + const char *p = *ret_p; + const char *mark = NULL; + const char *error_string = NULL; + ParserArgState state = PARSER_ARG_STATE_START; + CoglBool parsing_factor = FALSE; + CoglBool implicit_factor_brace = FALSE; + + arg->source.is_zero = FALSE; + arg->source.info = NULL; + arg->source.texture = 0; + arg->source.one_minus = FALSE; + arg->source.mask = statement->mask; + + arg->factor.is_one = FALSE; + arg->factor.is_color = FALSE; + arg->factor.is_src_alpha_saturate = FALSE; + + arg->factor.source.is_zero = FALSE; + arg->factor.source.info = NULL; + arg->factor.source.texture = 0; + arg->factor.source.one_minus = FALSE; + arg->factor.source.mask = statement->mask; + + do + { + if (g_ascii_isspace (*p)) + continue; + + if (*p == '\0') + { + error_string = "Unexpected end of string while parsing argument"; + goto error; + } + + switch (state) + { + case PARSER_ARG_STATE_START: + if (*p == '1') + state = PARSER_ARG_STATE_EXPECT_MINUS; + else if (*p == '0') + { + arg->source.is_zero = TRUE; + state = PARSER_ARG_STATE_EXPECT_END; + } + else + { + p--; /* backtrack */ + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + } + continue; + + case PARSER_ARG_STATE_EXPECT_MINUS: + if (*p != '-') + { + error_string = "expected a '-' following the 1"; + goto error; + } + arg->source.one_minus = TRUE; + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + continue; + + case PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME: + if (!is_symbol_char (*p)) + { + error_string = "expected a color source name"; + goto error; + } + state = PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME; + mark = p; + if (parsing_factor) + arg->factor.is_color = TRUE; + + /* fall through */ + case PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME: + if (!is_symbol_char (*p)) + { + CoglBlendStringColorSource *source = + parsing_factor ? &arg->factor.source : &arg->source; + source->info = get_color_src_info (mark, p, context); + if (!source->info) + { + error_string = "Unknown color source name"; + goto error; + } + if (source->info->type == + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N) + { + char *endp; + source->texture = + strtoul (&mark[strlen ("TEXTURE_")], &endp, 10); + if (mark == endp) + { + error_string = "invalid texture number given with " + "TEXTURE_N color source"; + goto error; + } + p = endp; + } + state = PARSER_ARG_STATE_MAYBE_COLOR_MASK; + } + else + continue; + + /* fall through */ + case PARSER_ARG_STATE_MAYBE_COLOR_MASK: + if (*p != '[') + { + p--; /* backtrack */ + if (!parsing_factor) + state = PARSER_ARG_STATE_MAYBE_MULT; + else + state = PARSER_ARG_STATE_EXPECT_END; + continue; + } + state = PARSER_ARG_STATE_SCRAPING_MASK; + mark = p; + + /* fall through */ + case PARSER_ARG_STATE_SCRAPING_MASK: + if (*p == ']') + { + size_t len = p - mark; + CoglBlendStringColorSource *source = + parsing_factor ? &arg->factor.source : &arg->source; + + if (len == 5 && strncmp (mark, "[RGBA", len) == 0) + { + if (statement->mask != COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + error_string = "You can't use an RGBA color mask if the " + "statement hasn't also got an RGBA= mask"; + goto error; + } + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; + } + else if (len == 4 && strncmp (mark, "[RGB", len) == 0) + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + else if (len == 2 && strncmp (mark, "[A", len) == 0) + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + else + { + error_string = "Expected a channel mask of [RGBA]" + "[RGB] or [A]"; + goto error; + } + if (parsing_factor) + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + else + state = PARSER_ARG_STATE_MAYBE_MULT; + } + continue; + + case PARSER_ARG_STATE_EXPECT_OPEN_PAREN: + if (*p != '(') + { + if (is_alphanum_char (*p)) + { + p--; /* compensate for implicit brace and ensure this + * char gets considered part of the blend factor */ + implicit_factor_brace = TRUE; + } + else + { + error_string = "Expected '(' around blend factor or alpha " + "numeric character for blend factor name"; + goto error; + } + } + else + implicit_factor_brace = FALSE; + parsing_factor = TRUE; + state = PARSER_ARG_STATE_EXPECT_FACTOR; + continue; + + case PARSER_ARG_STATE_EXPECT_FACTOR: + if (*p == '1') + state = PARSER_ARG_STATE_MAYBE_MINUS; + else if (*p == '0') + { + arg->source.is_zero = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + else + { + state = PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE; + mark = p; + } + continue; + + case PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE: + if (!is_symbol_char (*p)) + { + size_t len = p - mark; + if (len >= strlen ("SRC_ALPHA_SATURATE") && + strncmp (mark, "SRC_ALPHA_SATURATE", len) == 0) + { + arg->factor.is_src_alpha_saturate = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + else + { + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + p = mark - 1; /* backtrack */ + } + } + continue; + + case PARSER_ARG_STATE_MAYBE_MINUS: + if (*p == '-') + { + if (implicit_factor_brace) + { + error_string = "Expected ( ) braces around blend factor with " + "a subtraction"; + goto error; + } + arg->factor.source.one_minus = TRUE; + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + } + else + { + arg->factor.is_one = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + continue; + + case PARSER_ARG_STATE_EXPECT_CLOSE_PAREN: + if (implicit_factor_brace) + { + p--; + state = PARSER_ARG_STATE_EXPECT_END; + continue; + } + if (*p != ')') + { + error_string = "Expected closing parenthesis after blend factor"; + goto error; + } + state = PARSER_ARG_STATE_EXPECT_END; + continue; + + case PARSER_ARG_STATE_MAYBE_MULT: + if (*p == '*') + { + state = PARSER_ARG_STATE_EXPECT_OPEN_PAREN; + continue; + } + arg->factor.is_one = TRUE; + state = PARSER_ARG_STATE_EXPECT_END; + + /* fall through */ + case PARSER_ARG_STATE_EXPECT_END: + if (*p != ',' && *p != ')') + { + error_string = "expected , or )"; + goto error; + } + + *ret_p = p - 1; + return TRUE; + } + } + while (p++); + +error: + { + int offset = p - string; + _cogl_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, + "Syntax error for argument %d at offset %d: %s", + current_arg, + offset, + error_string); + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + g_debug ("Syntax error for argument %d at offset %d: %s", + current_arg, offset, error_string); + } + return FALSE; + } +} + +int +_cogl_blend_string_compile (const char *string, + CoglBlendStringContext context, + CoglBlendStringStatement *statements, + CoglError **error) +{ + const char *p = string; + const char *mark = NULL; + const char *error_string; + ParserState state = PARSER_STATE_EXPECT_DEST_CHANNELS; + CoglBlendStringStatement *statement = statements; + int current_statement = 0; + int current_arg = 0; + int remaining_argc = 0; + +#if 0 + COGL_DEBUG_SET_FLAG (COGL_DEBUG_BLEND_STRINGS); +#endif + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + COGL_NOTE (BLEND_STRINGS, "Compiling %s string:\n%s\n", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + string); + } + + do + { + if (g_ascii_isspace (*p)) + continue; + + if (*p == '\0') + { + switch (state) + { + case PARSER_STATE_EXPECT_DEST_CHANNELS: + if (current_statement != 0) + goto finished; + error_string = "Empty statement"; + goto error; + case PARSER_STATE_SCRAPING_DEST_CHANNELS: + error_string = "Expected an '=' following the destination " + "channel mask"; + goto error; + case PARSER_STATE_EXPECT_FUNCTION_NAME: + error_string = "Expected a function name"; + goto error; + case PARSER_STATE_SCRAPING_FUNCTION_NAME: + error_string = "Expected parenthesis after the function name"; + goto error; + case PARSER_STATE_EXPECT_ARG_START: + error_string = "Expected to find the start of an argument"; + goto error; + case PARSER_STATE_EXPECT_STATEMENT_END: + error_string = "Expected closing parenthesis for statement"; + goto error; + } + } + + switch (state) + { + case PARSER_STATE_EXPECT_DEST_CHANNELS: + mark = p; + state = PARSER_STATE_SCRAPING_DEST_CHANNELS; + + /* fall through */ + case PARSER_STATE_SCRAPING_DEST_CHANNELS: + if (*p != '=') + continue; + if (strncmp (mark, "RGBA", 4) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; + else if (strncmp (mark, "RGB", 3) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + else if (strncmp (mark, "A", 1) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + else + { + error_string = "Unknown destination channel mask; " + "expected RGBA=, RGB= or A="; + goto error; + } + state = PARSER_STATE_EXPECT_FUNCTION_NAME; + continue; + + case PARSER_STATE_EXPECT_FUNCTION_NAME: + mark = p; + state = PARSER_STATE_SCRAPING_FUNCTION_NAME; + + /* fall through */ + case PARSER_STATE_SCRAPING_FUNCTION_NAME: + if (*p != '(') + { + if (!is_alphanum_char (*p)) + { + error_string = "non alpha numeric character in function" + "name"; + goto error; + } + continue; + } + statement->function = get_function_info (mark, p, context); + if (!statement->function) + { + error_string = "Unknown function name"; + goto error; + } + remaining_argc = statement->function->argc; + current_arg = 0; + state = PARSER_STATE_EXPECT_ARG_START; + + /* fall through */ + case PARSER_STATE_EXPECT_ARG_START: + if (*p != '(' && *p != ',') + continue; + if (remaining_argc) + { + p++; /* parse_argument expects to see the first char of the arg */ + if (!parse_argument (string, &p, statement, + current_arg, &statement->args[current_arg], + context, error)) + return 0; + current_arg++; + remaining_argc--; + } + if (!remaining_argc) + state = PARSER_STATE_EXPECT_STATEMENT_END; + continue; + + case PARSER_STATE_EXPECT_STATEMENT_END: + if (*p != ')') + { + error_string = "Expected end of statement"; + goto error; + } + state = PARSER_STATE_EXPECT_DEST_CHANNELS; + if (current_statement++ == 1) + goto finished; + statement = &statements[current_statement]; + } + } + while (p++); + +finished: + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + if (current_statement > 0) + print_statement (0, &statements[0]); + if (current_statement > 1) + print_statement (1, &statements[1]); + } + + if (!validate_statements_for_context (statements, + current_statement, + context, + error)) + return 0; + + return current_statement; + +error: + { + int offset = p - string; + _cogl_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_PARSE_ERROR, + "Syntax error at offset %d: %s", + offset, + error_string); + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) + { + g_debug ("Syntax error at offset %d: %s", + offset, error_string); + } + return 0; + } +} + +/* + * INTERNAL TESTING CODE ... + */ + +struct _TestString +{ + const char *string; + CoglBlendStringContext context; +}; + +/* FIXME: this should probably be moved to a unit test */ +int +_cogl_blend_string_test (void); + +int +_cogl_blend_string_test (void) +{ + struct _TestString strings[] = { + {" A = MODULATE ( TEXTURE[RGB], PREVIOUS[A], PREVIOUS[A] ) ", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {" RGB = MODULATE ( TEXTURE[RGB], PREVIOUS[A] ) ", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + + {"RGBA = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, DST_COLOR*(0))", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, 0)", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD()", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, 0, DST_COLOR)", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {NULL} + }; + int i; + + CoglError *error = NULL; + for (i = 0; strings[i].string; i++) + { + CoglBlendStringStatement statements[2]; + int count = _cogl_blend_string_compile (strings[i].string, + strings[i].context, + statements, + &error); + if (!count) + { + g_print ("Failed to parse string:\n%s\n%s\n", + strings[i].string, + error->message); + cogl_error_free (error); + error = NULL; + continue; + } + g_print ("Original:\n"); + g_print ("%s\n", strings[i].string); + if (count > 0) + print_statement (0, &statements[0]); + if (count > 1) + print_statement (1, &statements[1]); + } + + return 0; +} + diff --git a/cogl/cogl/cogl-blend-string.h b/cogl/cogl/cogl-blend-string.h new file mode 100644 index 000000000..355338c61 --- /dev/null +++ b/cogl/cogl/cogl-blend-string.h @@ -0,0 +1,144 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef COGL_BLEND_STRING_H +#define COGL_BLEND_STRING_H + +#include +#include + +typedef enum _CoglBlendStringContext +{ + COGL_BLEND_STRING_CONTEXT_BLENDING, + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE +} CoglBlendStringContext; + +/* NB: debug stringify code will get upset if these + * are re-ordered */ +typedef enum _CoglBlendStringChannelMask +{ + COGL_BLEND_STRING_CHANNEL_MASK_RGB, + COGL_BLEND_STRING_CHANNEL_MASK_ALPHA, + COGL_BLEND_STRING_CHANNEL_MASK_RGBA +} CoglBlendStringChannelMask; + +typedef enum _CoglBlendStringColorSourceType +{ + /* blending */ + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR, + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR, + + /* shared */ + COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT, + + /* texture combining */ + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE, + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, + COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY, + COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS +} CoglBlendStringColorSourceType; + +typedef struct _CoglBlendStringColorSourceInfo +{ + CoglBlendStringColorSourceType type; + const char *name; + size_t name_len; +} CoglBlendStringColorSourceInfo; + +typedef struct _CoglBlendStringColorSource +{ + CoglBool is_zero; + const CoglBlendStringColorSourceInfo *info; + int texture; /* for the TEXTURE_N color source */ + CoglBool one_minus; + CoglBlendStringChannelMask mask; +} CoglBlendStringColorSource; + +typedef struct _CoglBlendStringFactor +{ + CoglBool is_one; + CoglBool is_src_alpha_saturate; + CoglBool is_color; + CoglBlendStringColorSource source; +} CoglBlendStringFactor; + +typedef struct _CoglBlendStringArgument +{ + CoglBlendStringColorSource source; + CoglBlendStringFactor factor; +} CoglBlendStringArgument; + +typedef enum _CoglBlendStringFunctionType +{ + /* shared */ + COGL_BLEND_STRING_FUNCTION_ADD, + + /* texture combine only */ + COGL_BLEND_STRING_FUNCTION_REPLACE, + COGL_BLEND_STRING_FUNCTION_MODULATE, + COGL_BLEND_STRING_FUNCTION_ADD_SIGNED, + COGL_BLEND_STRING_FUNCTION_INTERPOLATE, + COGL_BLEND_STRING_FUNCTION_SUBTRACT, + COGL_BLEND_STRING_FUNCTION_DOT3_RGB, + COGL_BLEND_STRING_FUNCTION_DOT3_RGBA +} CoglBlendStringFunctionType; + +typedef struct _CoglBlendStringFunctionInfo +{ + enum _CoglBlendStringFunctionType type; + const char *name; + size_t name_len; + int argc; +} CoglBlendStringFunctionInfo; + +typedef struct _CoglBlendStringStatement +{ + CoglBlendStringChannelMask mask; + const CoglBlendStringFunctionInfo *function; + CoglBlendStringArgument args[3]; +} CoglBlendStringStatement; + + +CoglBool +_cogl_blend_string_compile (const char *string, + CoglBlendStringContext context, + CoglBlendStringStatement *statements, + CoglError **error); + +void +_cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, + CoglBlendStringStatement *rgb, + CoglBlendStringStatement *a); + +#endif /* COGL_BLEND_STRING_H */ + diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c new file mode 100644 index 000000000..a6d60efbc --- /dev/null +++ b/cogl/cogl/cogl-blit.c @@ -0,0 +1,438 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-util.h" +#include "cogl-blit.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-private.h" +#include "cogl1-context.h" + +static const CoglBlitMode *_cogl_blit_default_mode = NULL; + +static CoglBool +_cogl_blit_texture_render_begin (CoglBlitData *data) +{ + CoglContext *ctx = data->src_tex->context; + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglPipeline *pipeline; + unsigned int dst_width, dst_height; + CoglError *ignore_error = NULL; + + offscreen = _cogl_offscreen_new_with_texture_full + (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); + + fb = COGL_FRAMEBUFFER (offscreen); + if (!cogl_framebuffer_allocate (fb, &ignore_error)) + { + cogl_error_free (ignore_error); + cogl_object_unref (fb); + return FALSE; + } + + data->dest_fb = fb; + + dst_width = cogl_texture_get_width (data->dst_tex); + dst_height = cogl_texture_get_height (data->dst_tex); + + /* Set up an orthographic projection so we can use pixel + coordinates to render to the texture */ + cogl_framebuffer_orthographic (fb, + 0, 0, dst_width, dst_height, + -1 /* near */, 1 /* far */); + + /* We cache a pipeline used for migrating on to the context so + that it doesn't have to continuously regenerate a shader + program */ + if (ctx->blit_texture_pipeline == NULL) + { + ctx->blit_texture_pipeline = cogl_pipeline_new (ctx); + + cogl_pipeline_set_layer_filters (ctx->blit_texture_pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + /* Disable blending by just directly taking the contents of the + source texture */ + cogl_pipeline_set_blend (ctx->blit_texture_pipeline, + "RGBA = ADD(SRC_COLOR, 0)", + NULL); + } + + pipeline = ctx->blit_texture_pipeline; + + cogl_pipeline_set_layer_texture (pipeline, 0, data->src_tex); + + data->pipeline = pipeline; + + return TRUE; +} + +static void +_cogl_blit_texture_render_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + cogl_framebuffer_draw_textured_rectangle (data->dest_fb, + data->pipeline, + dst_x, dst_y, + dst_x + width, + dst_y + height, + src_x / (float) data->src_width, + src_y / (float) data->src_height, + (src_x + width) / + (float) data->src_width, + (src_y + height) / + (float) data->src_height); +} + +static void +_cogl_blit_texture_render_end (CoglBlitData *data) +{ + CoglContext *ctx = data->src_tex->context; + + /* Attach the target texture to the texture render pipeline so that + we don't keep a reference to the source texture forever. This is + assuming that the destination texture will live for a long time + which is currently the case when cogl_blit_* is used from the + atlas code. It may be better in future to keep around a set of + dummy 1x1 textures for each texture target that we could bind + instead. This would also be useful when using a pipeline as a + hash table key such as for the ARBfp program cache. */ + cogl_pipeline_set_layer_texture (ctx->blit_texture_pipeline, 0, + data->dst_tex); + + cogl_object_unref (data->dest_fb); +} + +static CoglBool +_cogl_blit_framebuffer_begin (CoglBlitData *data) +{ + CoglContext *ctx = data->src_tex->context; + CoglOffscreen *dst_offscreen = NULL, *src_offscreen = NULL; + CoglFramebuffer *dst_fb, *src_fb; + CoglError *ignore_error = NULL; + + /* We can only blit between FBOs if both textures are the same + format and the blit framebuffer extension is supported */ + if ((_cogl_texture_get_format (data->src_tex) & ~COGL_A_BIT) != + (_cogl_texture_get_format (data->dst_tex) & ~COGL_A_BIT) || + !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)) + return FALSE; + + dst_offscreen = _cogl_offscreen_new_with_texture_full + (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); + + dst_fb = COGL_FRAMEBUFFER (dst_offscreen); + if (!cogl_framebuffer_allocate (dst_fb, &ignore_error)) + { + cogl_error_free (ignore_error); + goto error; + } + + src_offscreen= _cogl_offscreen_new_with_texture_full + (data->src_tex, + COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, + 0 /* level */); + + src_fb = COGL_FRAMEBUFFER (src_offscreen); + if (!cogl_framebuffer_allocate (src_fb, &ignore_error)) + { + cogl_error_free (ignore_error); + goto error; + } + + data->src_fb = src_fb; + data->dest_fb = dst_fb; + + return TRUE; + +error: + + if (dst_offscreen) + cogl_object_unref (dst_offscreen); + if (src_offscreen) + cogl_object_unref (src_offscreen); + + return FALSE; +} + +static void +_cogl_blit_framebuffer_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + _cogl_blit_framebuffer (data->src_fb, + data->dest_fb, + src_x, src_y, + dst_x, dst_y, + width, height); +} + +static void +_cogl_blit_framebuffer_end (CoglBlitData *data) +{ + cogl_object_unref (data->src_fb); + cogl_object_unref (data->dest_fb); +} + +static CoglBool +_cogl_blit_copy_tex_sub_image_begin (CoglBlitData *data) +{ + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglError *ignore_error = NULL; + + /* This will only work if the target texture is a CoglTexture2D */ + if (!cogl_is_texture_2d (data->dst_tex)) + return FALSE; + + offscreen = _cogl_offscreen_new_with_texture_full + (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); + + fb = COGL_FRAMEBUFFER (offscreen); + if (!cogl_framebuffer_allocate (fb, &ignore_error)) + { + cogl_error_free (ignore_error); + cogl_object_unref (fb); + return FALSE; + } + + data->src_fb = fb; + + return TRUE; +} + +static void +_cogl_blit_copy_tex_sub_image_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + _cogl_texture_2d_copy_from_framebuffer (COGL_TEXTURE_2D (data->dst_tex), + src_x, src_y, + width, height, + data->src_fb, + dst_x, dst_y, + 0); /* level */ +} + +static void +_cogl_blit_copy_tex_sub_image_end (CoglBlitData *data) +{ + cogl_object_unref (data->src_fb); +} + +static CoglBool +_cogl_blit_get_tex_data_begin (CoglBlitData *data) +{ + data->format = _cogl_texture_get_format (data->src_tex); + data->bpp = _cogl_pixel_format_get_bytes_per_pixel (data->format); + + data->image_data = g_malloc (data->bpp * data->src_width * + data->src_height); + cogl_texture_get_data (data->src_tex, data->format, + data->src_width * data->bpp, data->image_data); + + return TRUE; +} + +static void +_cogl_blit_get_tex_data_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + CoglError *ignore = NULL; + int rowstride = data->src_width * data->bpp; + int offset = rowstride * src_y + src_x * data->bpp; + + _cogl_texture_set_region (data->dst_tex, + width, height, + data->format, + rowstride, + data->image_data + offset, + dst_x, dst_y, + 0, /* level */ + &ignore); + /* TODO: support chaining up errors during the blit */ +} + +static void +_cogl_blit_get_tex_data_end (CoglBlitData *data) +{ + g_free (data->image_data); +} + +/* These should be specified in order of preference */ +static const CoglBlitMode +_cogl_blit_modes[] = + { + { + "texture-render", + _cogl_blit_texture_render_begin, + _cogl_blit_texture_render_blit, + _cogl_blit_texture_render_end + }, + { + "framebuffer", + _cogl_blit_framebuffer_begin, + _cogl_blit_framebuffer_blit, + _cogl_blit_framebuffer_end + }, + { + "copy-tex-sub-image", + _cogl_blit_copy_tex_sub_image_begin, + _cogl_blit_copy_tex_sub_image_blit, + _cogl_blit_copy_tex_sub_image_end + }, + { + "get-tex-data", + _cogl_blit_get_tex_data_begin, + _cogl_blit_get_tex_data_blit, + _cogl_blit_get_tex_data_end + } + }; + +void +_cogl_blit_begin (CoglBlitData *data, + CoglTexture *dst_tex, + CoglTexture *src_tex) +{ + int i; + + if (_cogl_blit_default_mode == NULL) + { + const char *default_mode_string; + + /* Allow the default to be specified with an environment + variable. For the time being these functions are only used + when blitting between atlas textures so the environment + variable is named to be specific to the atlas code. If we + want to use the code in other places we should create another + environment variable for each specific use case */ + if ((default_mode_string = g_getenv ("COGL_ATLAS_DEFAULT_BLIT_MODE"))) + { + for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++) + if (!strcmp (_cogl_blit_modes[i].name, default_mode_string)) + { + _cogl_blit_default_mode = _cogl_blit_modes + i; + break; + } + + if (i >= G_N_ELEMENTS (_cogl_blit_modes)) + { + g_warning ("Unknown blit mode %s", default_mode_string); + _cogl_blit_default_mode = _cogl_blit_modes; + } + } + else + /* Default to the first blit mode */ + _cogl_blit_default_mode = _cogl_blit_modes; + } + + memset (data, 0, sizeof (CoglBlitData)); + + data->dst_tex = dst_tex; + data->src_tex = src_tex; + + data->src_width = cogl_texture_get_width (src_tex); + data->src_height = cogl_texture_get_height (src_tex); + + /* Try the default blit mode first */ + if (!_cogl_blit_default_mode->begin_func (data)) + { + COGL_NOTE (ATLAS, "Failed to set up blit mode %s", + _cogl_blit_default_mode->name); + + /* Try all of the other modes in order */ + for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++) + if (_cogl_blit_modes + i != _cogl_blit_default_mode && + _cogl_blit_modes[i].begin_func (data)) + { + /* Use this mode as the default from now on */ + _cogl_blit_default_mode = _cogl_blit_modes + i; + break; + } + else + COGL_NOTE (ATLAS, + "Failed to set up blit mode %s", + _cogl_blit_modes[i].name); + + /* The last blit mode can't fail so this should never happen */ + _COGL_RETURN_IF_FAIL (i < G_N_ELEMENTS (_cogl_blit_modes)); + } + + data->blit_mode = _cogl_blit_default_mode; + + COGL_NOTE (ATLAS, "Setup blit using %s", _cogl_blit_default_mode->name); +} + +void +_cogl_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + data->blit_mode->blit_func (data, src_x, src_y, dst_x, dst_y, width, height); +} + +void +_cogl_blit_end (CoglBlitData *data) +{ + data->blit_mode->end_func (data); +} diff --git a/cogl/cogl/cogl-blit.h b/cogl/cogl/cogl-blit.h new file mode 100644 index 000000000..f4e25e7fe --- /dev/null +++ b/cogl/cogl/cogl-blit.h @@ -0,0 +1,101 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_BLIT_H +#define __COGL_BLIT_H + +#include +#include "cogl-object-private.h" +#include "cogl-texture.h" +#include "cogl-framebuffer.h" + +/* This structures and functions are used when a series of blits needs + to be performed between two textures. In this case there are + multiple methods we can use, most of which involve transferring + between an FBO bound to the texture. */ + +typedef struct _CoglBlitData CoglBlitData; + +typedef CoglBool (* CoglBlitBeginFunc) (CoglBlitData *data); +typedef void (* CoglBlitEndFunc) (CoglBlitData *data); + +typedef void (* CoglBlitFunc) (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height); + +typedef struct +{ + const char *name; + CoglBlitBeginFunc begin_func; + CoglBlitFunc blit_func; + CoglBlitEndFunc end_func; +} CoglBlitMode; + +struct _CoglBlitData +{ + CoglTexture *src_tex, *dst_tex; + + unsigned int src_width; + unsigned int src_height; + + const CoglBlitMode *blit_mode; + + /* If we're not using an FBO then we g_malloc a buffer and copy the + complete texture data in */ + unsigned char *image_data; + CoglPixelFormat format; + + int bpp; + + CoglFramebuffer *src_fb; + CoglFramebuffer *dest_fb; + CoglPipeline *pipeline; +}; + +void +_cogl_blit_begin (CoglBlitData *data, + CoglTexture *dst_tex, + CoglTexture *src_tex); + +void +_cogl_blit (CoglBlitData *data, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height); + +void +_cogl_blit_end (CoglBlitData *data); + +#endif /* __COGL_BLIT_H */ diff --git a/cogl/cogl/cogl-boxed-value.c b/cogl/cogl/cogl-boxed-value.c new file mode 100644 index 000000000..bf6a97919 --- /dev/null +++ b/cogl/cogl/cogl-boxed-value.c @@ -0,0 +1,377 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-boxed-value.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" + +CoglBool +_cogl_boxed_value_equal (const CoglBoxedValue *bva, + const CoglBoxedValue *bvb) +{ + const void *pa, *pb; + + if (bva->type != bvb->type) + return FALSE; + + switch (bva->type) + { + case COGL_BOXED_NONE: + return TRUE; + + case COGL_BOXED_INT: + if (bva->size != bvb->size || bva->count != bvb->count) + return FALSE; + + if (bva->count == 1) + { + pa = bva->v.int_value; + pb = bvb->v.int_value; + } + else + { + pa = bva->v.int_array; + pb = bvb->v.int_array; + } + + return !memcmp (pa, pb, sizeof (int) * bva->size * bva->count); + + case COGL_BOXED_FLOAT: + if (bva->size != bvb->size || bva->count != bvb->count) + return FALSE; + + if (bva->count == 1) + { + pa = bva->v.float_value; + pb = bvb->v.float_value; + } + else + { + pa = bva->v.float_array; + pb = bvb->v.float_array; + } + + return !memcmp (pa, pb, sizeof (float) * bva->size * bva->count); + + case COGL_BOXED_MATRIX: + if (bva->size != bvb->size || + bva->count != bvb->count) + return FALSE; + + if (bva->count == 1) + { + pa = bva->v.matrix; + pb = bvb->v.matrix; + } + else + { + pa = bva->v.array; + pb = bvb->v.array; + } + + return !memcmp (pa, pb, + sizeof (float) * bva->size * bva->size * bva->count); + } + + g_warn_if_reached (); + + return FALSE; +} + +static void +_cogl_boxed_value_tranpose (float *dst, + int size, + const float *src) +{ + int y, x; + + /* If the value is transposed we'll just transpose it now as it + * is copied into the boxed value instead of passing TRUE to + * glUniformMatrix because that is not supported on GLES and it + * doesn't seem like the GL driver would be able to do anything + * much smarter than this anyway */ + + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + *(dst++) = src[y + x * size]; +} + +static void +_cogl_boxed_value_set_x (CoglBoxedValue *bv, + int size, + int count, + CoglBoxedType type, + size_t value_size, + const void *value, + CoglBool transpose) +{ + if (count == 1) + { + if (bv->count > 1) + g_free (bv->v.array); + + if (transpose) + _cogl_boxed_value_tranpose (bv->v.float_value, + size, + value); + else + memcpy (bv->v.float_value, value, value_size); + } + else + { + if (bv->count > 1) + { + if (bv->count != count || + bv->size != size || + bv->type != type) + { + g_free (bv->v.array); + bv->v.array = g_malloc (count * value_size); + } + } + else + bv->v.array = g_malloc (count * value_size); + + if (transpose) + { + int value_num; + + for (value_num = 0; value_num < count; value_num++) + _cogl_boxed_value_tranpose (bv->v.float_array + + value_num * size * size, + size, + (const float *) value + + value_num * size * size); + } + else + memcpy (bv->v.array, value, count * value_size); + } + + bv->type = type; + bv->size = size; + bv->count = count; +} + +void +_cogl_boxed_value_set_1f (CoglBoxedValue *bv, + float value) +{ + _cogl_boxed_value_set_x (bv, + 1, 1, COGL_BOXED_FLOAT, + sizeof (float), &value, FALSE); +} + +void +_cogl_boxed_value_set_1i (CoglBoxedValue *bv, + int value) +{ + _cogl_boxed_value_set_x (bv, + 1, 1, COGL_BOXED_INT, + sizeof (int), &value, FALSE); +} + +void +_cogl_boxed_value_set_float (CoglBoxedValue *bv, + int n_components, + int count, + const float *value) +{ + _cogl_boxed_value_set_x (bv, + n_components, count, + COGL_BOXED_FLOAT, + sizeof (float) * n_components, value, FALSE); +} + +void +_cogl_boxed_value_set_int (CoglBoxedValue *bv, + int n_components, + int count, + const int *value) +{ + _cogl_boxed_value_set_x (bv, + n_components, count, + COGL_BOXED_INT, + sizeof (int) * n_components, value, FALSE); +} + +void +_cogl_boxed_value_set_matrix (CoglBoxedValue *bv, + int dimensions, + int count, + CoglBool transpose, + const float *value) +{ + _cogl_boxed_value_set_x (bv, + dimensions, count, + COGL_BOXED_MATRIX, + sizeof (float) * dimensions * dimensions, + value, + transpose); +} + +void +_cogl_boxed_value_copy (CoglBoxedValue *dst, + const CoglBoxedValue *src) +{ + *dst = *src; + + if (src->count > 1) + { + switch (src->type) + { + case COGL_BOXED_NONE: + break; + + case COGL_BOXED_INT: + dst->v.int_array = g_memdup (src->v.int_array, + src->size * src->count * sizeof (int)); + break; + + case COGL_BOXED_FLOAT: + dst->v.float_array = g_memdup (src->v.float_array, + src->size * + src->count * + sizeof (float)); + break; + + case COGL_BOXED_MATRIX: + dst->v.float_array = g_memdup (src->v.float_array, + src->size * src->size * + src->count * sizeof (float)); + break; + } + } +} + +void +_cogl_boxed_value_destroy (CoglBoxedValue *bv) +{ + if (bv->count > 1) + g_free (bv->v.array); +} + +void +_cogl_boxed_value_set_uniform (CoglContext *ctx, + GLint location, + const CoglBoxedValue *value) +{ + switch (value->type) + { + case COGL_BOXED_NONE: + break; + + case COGL_BOXED_INT: + { + const int *ptr; + + if (value->count == 1) + ptr = value->v.int_value; + else + ptr = value->v.int_array; + + switch (value->size) + { + case 1: + GE( ctx, glUniform1iv (location, value->count, ptr) ); + break; + case 2: + GE( ctx, glUniform2iv (location, value->count, ptr) ); + break; + case 3: + GE( ctx, glUniform3iv (location, value->count, ptr) ); + break; + case 4: + GE( ctx, glUniform4iv (location, value->count, ptr) ); + break; + } + } + break; + + case COGL_BOXED_FLOAT: + { + const float *ptr; + + if (value->count == 1) + ptr = value->v.float_value; + else + ptr = value->v.float_array; + + switch (value->size) + { + case 1: + GE( ctx, glUniform1fv (location, value->count, ptr) ); + break; + case 2: + GE( ctx, glUniform2fv (location, value->count, ptr) ); + break; + case 3: + GE( ctx, glUniform3fv (location, value->count, ptr) ); + break; + case 4: + GE( ctx, glUniform4fv (location, value->count, ptr) ); + break; + } + } + break; + + case COGL_BOXED_MATRIX: + { + const float *ptr; + + if (value->count == 1) + ptr = value->v.matrix; + else + ptr = value->v.float_array; + + switch (value->size) + { + case 2: + GE( ctx, glUniformMatrix2fv (location, value->count, + FALSE, ptr) ); + break; + case 3: + GE( ctx, glUniformMatrix3fv (location, value->count, + FALSE, ptr) ); + break; + case 4: + GE( ctx, glUniformMatrix4fv (location, value->count, + FALSE, ptr) ); + break; + } + } + break; + } +} diff --git a/cogl/cogl/cogl-boxed-value.h b/cogl/cogl/cogl-boxed-value.h new file mode 100644 index 000000000..c8eda44dd --- /dev/null +++ b/cogl/cogl/cogl-boxed-value.h @@ -0,0 +1,117 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_BOXED_VALUE_H +#define __COGL_BOXED_VALUE_H + +#include + +#include "cogl-context.h" + +typedef enum { + COGL_BOXED_NONE, + COGL_BOXED_INT, + COGL_BOXED_FLOAT, + COGL_BOXED_MATRIX +} CoglBoxedType; + +typedef struct _CoglBoxedValue +{ + CoglBoxedType type; + int size, count; + + union { + float float_value[4]; + int int_value[4]; + float matrix[16]; + float *float_array; + int *int_array; + void *array; + } v; +} CoglBoxedValue; + +#define _cogl_boxed_value_init(bv) \ + G_STMT_START { \ + CoglBoxedValue *_bv = (bv); \ + _bv->type = COGL_BOXED_NONE; \ + _bv->count = 1; \ + } G_STMT_END + +CoglBool +_cogl_boxed_value_equal (const CoglBoxedValue *bva, + const CoglBoxedValue *bvb); + +void +_cogl_boxed_value_set_1f (CoglBoxedValue *bv, + float value); + +void +_cogl_boxed_value_set_1i (CoglBoxedValue *bv, + int value); + +void +_cogl_boxed_value_set_float (CoglBoxedValue *bv, + int n_components, + int count, + const float *value); + +void +_cogl_boxed_value_set_int (CoglBoxedValue *bv, + int n_components, + int count, + const int *value); + +void +_cogl_boxed_value_set_matrix (CoglBoxedValue *bv, + int dimensions, + int count, + CoglBool transpose, + const float *value); + +/* + * _cogl_boxed_value_copy: + * @dst: The destination boxed value + * @src: The source boxed value + * + * This copies @src to @dst. It is assumed that @dst is initialised. + */ +void +_cogl_boxed_value_copy (CoglBoxedValue *dst, + const CoglBoxedValue *src); + +void +_cogl_boxed_value_destroy (CoglBoxedValue *bv); + +void +_cogl_boxed_value_set_uniform (CoglContext *ctx, + int location, + const CoglBoxedValue *value); + +#endif /* __COGL_BOXED_VALUE_H */ diff --git a/cogl/cogl/cogl-buffer-private.h b/cogl/cogl/cogl-buffer-private.h new file mode 100644 index 000000000..eab81fda4 --- /dev/null +++ b/cogl/cogl/cogl-buffer-private.h @@ -0,0 +1,180 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +#ifndef __COGL_BUFFER_PRIVATE_H__ +#define __COGL_BUFFER_PRIVATE_H__ + +#include + +#include "cogl-object-private.h" +#include "cogl-buffer.h" +#include "cogl-context.h" +#include "cogl-gl-header.h" + +COGL_BEGIN_DECLS + +typedef struct _CoglBufferVtable CoglBufferVtable; + +struct _CoglBufferVtable +{ + void * (* map_range) (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + + void (* unmap) (CoglBuffer *buffer); + + CoglBool (* set_data) (CoglBuffer *buffer, + unsigned int offset, + const void *data, + unsigned int size, + CoglError **error); +}; + +typedef enum _CoglBufferFlags +{ + COGL_BUFFER_FLAG_NONE = 0, + COGL_BUFFER_FLAG_BUFFER_OBJECT = 1UL << 0, /* real openGL buffer object */ + COGL_BUFFER_FLAG_MAPPED = 1UL << 1, + COGL_BUFFER_FLAG_MAPPED_FALLBACK = 1UL << 2 +} CoglBufferFlags; + +typedef enum { + COGL_BUFFER_USAGE_HINT_TEXTURE, + COGL_BUFFER_USAGE_HINT_ATTRIBUTE_BUFFER, + COGL_BUFFER_USAGE_HINT_INDEX_BUFFER +} CoglBufferUsageHint; + +typedef enum { + COGL_BUFFER_BIND_TARGET_PIXEL_PACK, + COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, + COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, + COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, + + COGL_BUFFER_BIND_TARGET_COUNT +} CoglBufferBindTarget; + +struct _CoglBuffer +{ + CoglObject _parent; + + CoglContext *context; + + CoglBufferVtable vtable; + + CoglBufferBindTarget last_target; + + CoglBufferFlags flags; + + GLuint gl_handle; /* OpenGL handle */ + unsigned int size; /* size of the buffer, in bytes */ + CoglBufferUsageHint usage_hint; + CoglBufferUpdateHint update_hint; + + /* points to the mapped memory when the CoglBuffer is a VBO, PBO, + * ... or points to allocated memory in the fallback paths */ + uint8_t *data; + + int immutable_ref; + + unsigned int store_created:1; +}; + +/* This is used to register a type to the list of handle types that + will be considered a texture in cogl_is_texture() */ +void +_cogl_buffer_register_buffer_type (const CoglObjectClass *klass); + +#define COGL_BUFFER_DEFINE(TypeName, type_name) \ + COGL_OBJECT_DEFINE_WITH_CODE \ + (TypeName, type_name, \ + _cogl_buffer_register_buffer_type (&_cogl_##type_name##_class)) + +void +_cogl_buffer_initialize (CoglBuffer *buffer, + CoglContext *context, + size_t size, + CoglBufferBindTarget default_target, + CoglBufferUsageHint usage_hint, + CoglBufferUpdateHint update_hint); + +void +_cogl_buffer_fini (CoglBuffer *buffer); + +CoglBufferUsageHint +_cogl_buffer_get_usage_hint (CoglBuffer *buffer); + +GLenum +_cogl_buffer_access_to_gl_enum (CoglBufferAccess access); + +CoglBuffer * +_cogl_buffer_immutable_ref (CoglBuffer *buffer); + +void +_cogl_buffer_immutable_unref (CoglBuffer *buffer); + +CoglBool +_cogl_buffer_set_data (CoglBuffer *buffer, + size_t offset, + const void *data, + size_t size, + CoglError **error); + +void * +_cogl_buffer_map (CoglBuffer *buffer, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + +/* This is a wrapper around cogl_buffer_map_range for internal use + when we want to map the buffer for write only to replace the entire + contents. If the map fails then it will fallback to writing to a + temporary buffer. When _cogl_buffer_unmap_for_fill_or_fallback is + called the temporary buffer will be copied into the array. Note + that these calls share a global array so they can not be nested. */ +void * +_cogl_buffer_map_range_for_fill_or_fallback (CoglBuffer *buffer, + size_t offset, + size_t size); +void * +_cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer); + +void +_cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer); + +COGL_END_DECLS + +#endif /* __COGL_BUFFER_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-buffer.c b/cogl/cogl/cogl-buffer.c new file mode 100644 index 000000000..ede46cce6 --- /dev/null +++ b/cogl/cogl/cogl-buffer.c @@ -0,0 +1,411 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +/* For an overview of the functionality implemented here, please see + * cogl-buffer.h, which contains the gtk-doc section overview for the + * Pixel Buffers API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-pixel-buffer-private.h" + +/* XXX: + * The CoglObject macros don't support any form of inheritance, so for + * now we implement the CoglObject support for the CoglBuffer + * abstract class manually. + */ + +static GSList *_cogl_buffer_types; + +void +_cogl_buffer_register_buffer_type (const CoglObjectClass *klass) +{ + _cogl_buffer_types = g_slist_prepend (_cogl_buffer_types, (void *) klass); +} + +CoglBool +cogl_is_buffer (void *object) +{ + const CoglObject *obj = object; + GSList *l; + + if (object == NULL) + return FALSE; + + for (l = _cogl_buffer_types; l; l = l->next) + if (l->data == obj->klass) + return TRUE; + + return FALSE; +} + +/* + * Fallback path, buffer->data points to a malloc'ed buffer. + */ + +static void * +malloc_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + buffer->flags |= COGL_BUFFER_FLAG_MAPPED; + return buffer->data + offset; +} + +static void +malloc_unmap (CoglBuffer *buffer) +{ + buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED; +} + +static CoglBool +malloc_set_data (CoglBuffer *buffer, + unsigned int offset, + const void *data, + unsigned int size, + CoglError **error) +{ + memcpy (buffer->data + offset, data, size); + return TRUE; +} + +void +_cogl_buffer_initialize (CoglBuffer *buffer, + CoglContext *ctx, + size_t size, + CoglBufferBindTarget default_target, + CoglBufferUsageHint usage_hint, + CoglBufferUpdateHint update_hint) +{ + CoglBool use_malloc = FALSE; + + buffer->context = ctx; + buffer->flags = COGL_BUFFER_FLAG_NONE; + buffer->store_created = FALSE; + buffer->size = size; + buffer->last_target = default_target; + buffer->usage_hint = usage_hint; + buffer->update_hint = update_hint; + buffer->data = NULL; + buffer->immutable_ref = 0; + + if (default_target == COGL_BUFFER_BIND_TARGET_PIXEL_PACK || + default_target == COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK) + { + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_PBOS)) + use_malloc = TRUE; + } + else if (default_target == COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER || + default_target == COGL_BUFFER_BIND_TARGET_INDEX_BUFFER) + { + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_VBOS)) + use_malloc = TRUE; + } + + if (use_malloc) + { + buffer->vtable.map_range = malloc_map_range; + buffer->vtable.unmap = malloc_unmap; + buffer->vtable.set_data = malloc_set_data; + + buffer->data = g_malloc (size); + } + else + { + buffer->vtable.map_range = ctx->driver_vtable->buffer_map_range; + buffer->vtable.unmap = ctx->driver_vtable->buffer_unmap; + buffer->vtable.set_data = ctx->driver_vtable->buffer_set_data; + + ctx->driver_vtable->buffer_create (buffer); + + buffer->flags |= COGL_BUFFER_FLAG_BUFFER_OBJECT; + } +} + +void +_cogl_buffer_fini (CoglBuffer *buffer) +{ + _COGL_RETURN_IF_FAIL (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED)); + _COGL_RETURN_IF_FAIL (buffer->immutable_ref == 0); + + if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) + buffer->context->driver_vtable->buffer_destroy (buffer); + else + g_free (buffer->data); +} + +unsigned int +cogl_buffer_get_size (CoglBuffer *buffer) +{ + if (!cogl_is_buffer (buffer)) + return 0; + + return COGL_BUFFER (buffer)->size; +} + +void +cogl_buffer_set_update_hint (CoglBuffer *buffer, + CoglBufferUpdateHint hint) +{ + if (!cogl_is_buffer (buffer)) + return; + + if (G_UNLIKELY (hint > COGL_BUFFER_UPDATE_HINT_STREAM)) + hint = COGL_BUFFER_UPDATE_HINT_STATIC; + + buffer->update_hint = hint; +} + +CoglBufferUpdateHint +cogl_buffer_get_update_hint (CoglBuffer *buffer) +{ + if (!cogl_is_buffer (buffer)) + return FALSE; + + return buffer->update_hint; +} + +static void +warn_about_midscene_changes (void) +{ + static CoglBool seen = FALSE; + if (!seen) + { + g_warning ("Mid-scene modification of buffers has " + "undefined results\n"); + seen = TRUE; + } +} + +void * +_cogl_buffer_map (CoglBuffer *buffer, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL); + + return cogl_buffer_map_range (buffer, 0, buffer->size, access, hints, error); +} + +void * +cogl_buffer_map (CoglBuffer *buffer, + CoglBufferAccess access, + CoglBufferMapHint hints) +{ + CoglError *ignore_error = NULL; + void *ptr = + cogl_buffer_map_range (buffer, 0, buffer->size, access, hints, + &ignore_error); + if (!ptr) + cogl_error_free (ignore_error); + return ptr; +} + +void * +cogl_buffer_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL); + _COGL_RETURN_VAL_IF_FAIL (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED), NULL); + + if (G_UNLIKELY (buffer->immutable_ref)) + warn_about_midscene_changes (); + + buffer->data = buffer->vtable.map_range (buffer, + offset, + size, + access, + hints, + error); + + return buffer->data; +} + +void +cogl_buffer_unmap (CoglBuffer *buffer) +{ + if (!cogl_is_buffer (buffer)) + return; + + if (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED)) + return; + + buffer->vtable.unmap (buffer); +} + +void * +_cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer) +{ + return _cogl_buffer_map_range_for_fill_or_fallback (buffer, 0, buffer->size); +} + +void * +_cogl_buffer_map_range_for_fill_or_fallback (CoglBuffer *buffer, + size_t offset, + size_t size) +{ + CoglContext *ctx = buffer->context; + void *ret; + CoglError *ignore_error = NULL; + + _COGL_RETURN_VAL_IF_FAIL (!ctx->buffer_map_fallback_in_use, NULL); + + ctx->buffer_map_fallback_in_use = TRUE; + + ret = cogl_buffer_map_range (buffer, + offset, + size, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + &ignore_error); + + if (ret) + return ret; + + cogl_error_free (ignore_error); + + /* If the map fails then we'll use a temporary buffer to fill + the data and then upload it using cogl_buffer_set_data when + the buffer is unmapped. The temporary buffer is shared to + avoid reallocating it every time */ + g_byte_array_set_size (ctx->buffer_map_fallback_array, size); + ctx->buffer_map_fallback_offset = offset; + + buffer->flags |= COGL_BUFFER_FLAG_MAPPED_FALLBACK; + + return ctx->buffer_map_fallback_array->data; +} + +void +_cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer) +{ + CoglContext *ctx = buffer->context; + + _COGL_RETURN_IF_FAIL (ctx->buffer_map_fallback_in_use); + + ctx->buffer_map_fallback_in_use = FALSE; + + if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK)) + { + /* Note: don't try to catch OOM errors here since the use cases + * we currently have for this api (the journal and path stroke + * tesselator) don't have anything particularly sensible they + * can do in response to a failure anyway so it seems better to + * simply abort instead. + * + * If we find this is a problem for real world applications + * then in the path tesselation case we could potentially add an + * explicit cogl_path_tesselate_stroke() api that can throw an + * error for the app to cache. For the journal we could + * potentially flush the journal in smaller batches so we use + * smaller buffers, though that would probably not help for + * deferred renderers. + */ + _cogl_buffer_set_data (buffer, + ctx->buffer_map_fallback_offset, + ctx->buffer_map_fallback_array->data, + ctx->buffer_map_fallback_array->len, + NULL); + buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK; + } + else + cogl_buffer_unmap (buffer); +} + +CoglBool +_cogl_buffer_set_data (CoglBuffer *buffer, + size_t offset, + const void *data, + size_t size, + CoglError **error) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), FALSE); + _COGL_RETURN_VAL_IF_FAIL ((offset + size) <= buffer->size, FALSE); + + if (G_UNLIKELY (buffer->immutable_ref)) + warn_about_midscene_changes (); + + return buffer->vtable.set_data (buffer, offset, data, size, error); +} + +CoglBool +cogl_buffer_set_data (CoglBuffer *buffer, + size_t offset, + const void *data, + size_t size) +{ + CoglError *ignore_error = NULL; + CoglBool status = + _cogl_buffer_set_data (buffer, offset, data, size, &ignore_error); + if (!status) + cogl_error_free (ignore_error); + return status; +} + +CoglBuffer * +_cogl_buffer_immutable_ref (CoglBuffer *buffer) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL); + + buffer->immutable_ref++; + return buffer; +} + +void +_cogl_buffer_immutable_unref (CoglBuffer *buffer) +{ + _COGL_RETURN_IF_FAIL (cogl_is_buffer (buffer)); + _COGL_RETURN_IF_FAIL (buffer->immutable_ref > 0); + + buffer->immutable_ref--; +} + diff --git a/cogl/cogl/cogl-buffer.h b/cogl/cogl/cogl-buffer.h new file mode 100644 index 000000000..adbc51f9c --- /dev/null +++ b/cogl/cogl/cogl-buffer.h @@ -0,0 +1,324 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C)2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_BUFFER_H__ +#define __COGL_BUFFER_H__ + +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-buffer + * @short_description: Common buffer functions, including data upload APIs + * @stability: unstable + * + * The CoglBuffer API provides a common interface to manipulate + * buffers that have been allocated either via cogl_pixel_buffer_new() + * or cogl_attribute_buffer_new(). The API allows you to upload data + * to these buffers and define usage hints that help Cogl manage your + * buffer optimally. + * + * Data can either be uploaded by supplying a pointer and size so Cogl + * can copy your data, or you can mmap() a CoglBuffer and then you can + * copy data to the buffer directly. + * + * One of the most common uses for CoglBuffers is to upload texture + * data asynchronously since the ability to mmap the buffers into + * the CPU makes it possible for another thread to handle the IO + * of loading an image file and unpacking it into the mapped buffer + * without blocking other Cogl operations. + */ + +#ifdef __COGL_H_INSIDE__ +/* For the public C api we typedef interface types as void to avoid needing + * lots of casting in code and instead we will rely on runtime type checking + * for these objects. */ +typedef void CoglBuffer; +#else +typedef struct _CoglBuffer CoglBuffer; +#define COGL_BUFFER(buffer) ((CoglBuffer *)(buffer)) +#endif + +#define COGL_BUFFER_ERROR (_cogl_buffer_error_domain ()) + +/** + * CoglBufferError: + * @COGL_BUFFER_ERROR_MAP: A buffer could not be mapped either + * because the feature isn't supported or because a system + * limitation was hit. + * + * Error enumeration for #CoglBuffer + * + * Stability: unstable + */ +typedef enum { /*< prefix=COGL_BUFFER_ERROR >*/ + COGL_BUFFER_ERROR_MAP +} CoglBufferError; + +uint32_t +_cogl_buffer_error_domain (void); + +/** + * cogl_is_buffer: + * @object: a buffer object + * + * Checks whether @buffer is a buffer object. + * + * Return value: %TRUE if the handle is a CoglBuffer, and %FALSE otherwise + * + * Since: 1.2 + * Stability: unstable + */ +CoglBool +cogl_is_buffer (void *object); + +/** + * cogl_buffer_get_size: + * @buffer: a buffer object + * + * Retrieves the size of buffer + * + * Return value: the size of the buffer in bytes + * + * Since: 1.2 + * Stability: unstable + */ +unsigned int +cogl_buffer_get_size (CoglBuffer *buffer); + +/** + * CoglBufferUpdateHint: + * @COGL_BUFFER_UPDATE_HINT_STATIC: the buffer will not change over time + * @COGL_BUFFER_UPDATE_HINT_DYNAMIC: the buffer will change from time to time + * @COGL_BUFFER_UPDATE_HINT_STREAM: the buffer will be used once or a couple of + * times + * + * The update hint on a buffer allows the user to give some detail on how often + * the buffer data is going to be updated. + * + * Since: 1.2 + * Stability: unstable + */ +typedef enum { /*< prefix=COGL_BUFFER_UPDATE_HINT >*/ + COGL_BUFFER_UPDATE_HINT_STATIC, + COGL_BUFFER_UPDATE_HINT_DYNAMIC, + COGL_BUFFER_UPDATE_HINT_STREAM +} CoglBufferUpdateHint; + +/** + * cogl_buffer_set_update_hint: + * @buffer: a buffer object + * @hint: the new hint + * + * Sets the update hint on a buffer. See #CoglBufferUpdateHint for a description + * of the available hints. + * + * Since: 1.2 + * Stability: unstable + */ +void +cogl_buffer_set_update_hint (CoglBuffer *buffer, + CoglBufferUpdateHint hint); + +/** + * cogl_buffer_get_update_hint: + * @buffer: a buffer object + * + * Retrieves the update hints set using cogl_buffer_set_update_hint() + * + * Return value: the #CoglBufferUpdateHint currently used by the buffer + * + * Since: 1.2 + * Stability: unstable + */ +CoglBufferUpdateHint +cogl_buffer_get_update_hint (CoglBuffer *buffer); + +/** + * CoglBufferAccess: + * @COGL_BUFFER_ACCESS_READ: the buffer will be read + * @COGL_BUFFER_ACCESS_WRITE: the buffer will written to + * @COGL_BUFFER_ACCESS_READ_WRITE: the buffer will be used for both reading and + * writing + * + * The access hints for cogl_buffer_set_update_hint() + * + * Since: 1.2 + * Stability: unstable + */ +typedef enum { /*< prefix=COGL_BUFFER_ACCESS >*/ + COGL_BUFFER_ACCESS_READ = 1 << 0, + COGL_BUFFER_ACCESS_WRITE = 1 << 1, + COGL_BUFFER_ACCESS_READ_WRITE = COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE +} CoglBufferAccess; + + +/** + * CoglBufferMapHint: + * @COGL_BUFFER_MAP_HINT_DISCARD: Tells Cogl that you plan to replace + * all the buffer's contents. When this flag is used to map a + * buffer, the entire contents of the buffer become undefined, even + * if only a subregion of the buffer is mapped. + * @COGL_BUFFER_MAP_HINT_DISCARD_RANGE: Tells Cogl that you plan to + * replace all the contents of the mapped region. The contents of + * the region specified are undefined after this flag is used to + * map a buffer. + * + * Hints to Cogl about how you are planning to modify the data once it + * is mapped. + * + * Since: 1.4 + * Stability: unstable + */ +typedef enum { /*< prefix=COGL_BUFFER_MAP_HINT >*/ + COGL_BUFFER_MAP_HINT_DISCARD = 1 << 0, + COGL_BUFFER_MAP_HINT_DISCARD_RANGE = 1 << 1 +} CoglBufferMapHint; + +/** + * cogl_buffer_map: + * @buffer: a buffer object + * @access: how the mapped buffer will be used by the application + * @hints: A mask of #CoglBufferMapHints that tell Cogl how + * the data will be modified once mapped. + * + * Maps the buffer into the application address space for direct + * access. This is equivalent to calling cogl_buffer_map_range() with + * zero as the offset and the size of the entire buffer as the size. + * + * It is strongly recommended that you pass + * %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace + * all the buffer's data. This way if the buffer is currently being + * used by the GPU then the driver won't have to stall the CPU and + * wait for the hardware to finish because it can instead allocate a + * new buffer to map. + * + * The behaviour is undefined if you access the buffer in a way + * conflicting with the @access mask you pass. It is also an error to + * release your last reference while the buffer is mapped. + * + * Return value: (transfer none): A pointer to the mapped memory or + * %NULL is the call fails + * + * Since: 1.2 + * Stability: unstable + */ +void * +cogl_buffer_map (CoglBuffer *buffer, + CoglBufferAccess access, + CoglBufferMapHint hints); + +/** + * cogl_buffer_map_range: + * @buffer: a buffer object + * @offset: Offset within the buffer to start the mapping + * @size: The size of data to map + * @access: how the mapped buffer will be used by the application + * @hints: A mask of #CoglBufferMapHints that tell Cogl how + * the data will be modified once mapped. + * @error: A #CoglError for catching exceptional errors + * + * Maps a sub-region of the buffer into the application's address space + * for direct access. + * + * It is strongly recommended that you pass + * %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace + * all the buffer's data. This way if the buffer is currently being + * used by the GPU then the driver won't have to stall the CPU and + * wait for the hardware to finish because it can instead allocate a + * new buffer to map. You can pass + * %COGL_BUFFER_MAP_HINT_DISCARD_RANGE instead if you want the + * regions outside of the mapping to be retained. + * + * The behaviour is undefined if you access the buffer in a way + * conflicting with the @access mask you pass. It is also an error to + * release your last reference while the buffer is mapped. + * + * Return value: (transfer none): A pointer to the mapped memory or + * %NULL is the call fails + * + * Since: 2.0 + * Stability: unstable + */ +void * +cogl_buffer_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + +/** + * cogl_buffer_unmap: + * @buffer: a buffer object + * + * Unmaps a buffer previously mapped by cogl_buffer_map(). + * + * Since: 1.2 + * Stability: unstable + */ +void +cogl_buffer_unmap (CoglBuffer *buffer); + +/** + * cogl_buffer_set_data: + * @buffer: a buffer object + * @offset: destination offset (in bytes) in the buffer + * @data: a pointer to the data to be copied into the buffer + * @size: number of bytes to copy + * + * Updates part of the buffer with new data from @data. Where to put this new + * data is controlled by @offset and @offset + @data should be less than the + * buffer size. + * + * Return value: %TRUE is the operation succeeded, %FALSE otherwise + * + * Since: 1.2 + * Stability: unstable + */ +CoglBool +cogl_buffer_set_data (CoglBuffer *buffer, + size_t offset, + const void *data, + size_t size); + +COGL_END_DECLS + +#endif /* __COGL_BUFFER_H__ */ diff --git a/cogl/cogl/cogl-clip-stack.c b/cogl/cogl/cogl-clip-stack.c new file mode 100644 index 000000000..b0e5b0d1b --- /dev/null +++ b/cogl/cogl/cogl-clip-stack.c @@ -0,0 +1,412 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "cogl-clip-stack.h" +#include "cogl-primitives.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-journal-private.h" +#include "cogl-util.h" +#include "cogl-matrix-private.h" +#include "cogl-primitives-private.h" +#include "cogl-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-attribute-private.h" +#include "cogl-primitive-private.h" +#include "cogl1-context.h" +#include "cogl-offscreen.h" +#include "cogl-matrix-stack.h" + + + +static void * +_cogl_clip_stack_push_entry (CoglClipStack *clip_stack, + size_t size, + CoglClipStackType type) +{ + CoglClipStack *entry = g_slice_alloc (size); + + /* The new entry starts with a ref count of 1 because the stack + holds a reference to it as it is the top entry */ + entry->ref_count = 1; + entry->type = type; + entry->parent = clip_stack; + + /* We don't need to take a reference to the parent from the entry + because the we are stealing the ref in the new stack top */ + + return entry; +} + +static void +get_transformed_corners (float x_1, + float y_1, + float x_2, + float y_2, + CoglMatrix *modelview, + CoglMatrix *projection, + const float *viewport, + float *transformed_corners) +{ + int i; + + transformed_corners[0] = x_1; + transformed_corners[1] = y_1; + transformed_corners[2] = x_2; + transformed_corners[3] = y_1; + transformed_corners[4] = x_2; + transformed_corners[5] = y_2; + transformed_corners[6] = x_1; + transformed_corners[7] = y_2; + + + /* Project the coordinates to window space coordinates */ + for (i = 0; i < 4; i++) + { + float *v = transformed_corners + i * 2; + _cogl_transform_point (modelview, projection, viewport, v, v + 1); + } +} + +/* Sets the window-space bounds of the entry based on the projected + coordinates of the given rectangle */ +static void +_cogl_clip_stack_entry_set_bounds (CoglClipStack *entry, + float *transformed_corners) +{ + float min_x = G_MAXFLOAT, min_y = G_MAXFLOAT; + float max_x = -G_MAXFLOAT, max_y = -G_MAXFLOAT; + int i; + + for (i = 0; i < 4; i++) + { + float *v = transformed_corners + i * 2; + + if (v[0] > max_x) + max_x = v[0]; + if (v[0] < min_x) + min_x = v[0]; + if (v[1] > max_y) + max_y = v[1]; + if (v[1] < min_y) + min_y = v[1]; + } + + entry->bounds_x0 = floorf (min_x); + entry->bounds_x1 = ceilf (max_x); + entry->bounds_y0 = floorf (min_y); + entry->bounds_y1 = ceilf (max_y); +} + +CoglClipStack * +_cogl_clip_stack_push_window_rectangle (CoglClipStack *stack, + int x_offset, + int y_offset, + int width, + int height) +{ + CoglClipStack *entry; + + entry = _cogl_clip_stack_push_entry (stack, + sizeof (CoglClipStackWindowRect), + COGL_CLIP_STACK_WINDOW_RECT); + + entry->bounds_x0 = x_offset; + entry->bounds_x1 = x_offset + width; + entry->bounds_y0 = y_offset; + entry->bounds_y1 = y_offset + height; + + return entry; +} + +CoglClipStack * +_cogl_clip_stack_push_rectangle (CoglClipStack *stack, + float x_1, + float y_1, + float x_2, + float y_2, + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) +{ + CoglClipStackRect *entry; + CoglMatrix modelview; + CoglMatrix projection; + CoglMatrix modelview_projection; + + /* Corners of the given rectangle in an clockwise order: + * (0, 1) (2, 3) + * + * + * + * (6, 7) (4, 5) + */ + float rect[] = { + x_1, y_1, + x_2, y_1, + x_2, y_2, + x_1, y_2 + }; + + /* Make a new entry */ + entry = _cogl_clip_stack_push_entry (stack, + sizeof (CoglClipStackRect), + COGL_CLIP_STACK_RECT); + + entry->x0 = x_1; + entry->y0 = y_1; + entry->x1 = x_2; + entry->y1 = y_2; + + entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); + + cogl_matrix_entry_get (modelview_entry, &modelview); + cogl_matrix_entry_get (projection_entry, &projection); + + cogl_matrix_multiply (&modelview_projection, + &projection, + &modelview); + + /* Technically we could avoid the viewport transform at this point + * if we want to make this a bit faster. */ + _cogl_transform_point (&modelview, &projection, viewport, &rect[0], &rect[1]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[2], &rect[3]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[4], &rect[5]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[6], &rect[7]); + + /* If the fully transformed rectangle isn't still axis aligned we + * can't handle it using a scissor. + * + * We don't use an epsilon here since we only really aim to catch + * simple cases where the transform doesn't leave the rectangle screen + * aligned and don't mind some false positives. + */ + if (rect[0] != rect[6] || + rect[1] != rect[3] || + rect[2] != rect[4] || + rect[7] != rect[5]) + { + entry->can_be_scissor = FALSE; + + _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, + rect); + } + else + { + CoglClipStack *base_entry = (CoglClipStack *) entry; + x_1 = rect[0]; + y_1 = rect[1]; + x_2 = rect[4]; + y_2 = rect[5]; + + /* Consider that the modelview matrix may flip the rectangle + * along the x or y axis... */ +#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) + if (x_1 > x_2) + SWAP (x_1, x_2); + if (y_1 > y_2) + SWAP (y_1, y_2); +#undef SWAP + + base_entry->bounds_x0 = COGL_UTIL_NEARBYINT (x_1); + base_entry->bounds_y0 = COGL_UTIL_NEARBYINT (y_1); + base_entry->bounds_x1 = COGL_UTIL_NEARBYINT (x_2); + base_entry->bounds_y1 = COGL_UTIL_NEARBYINT (y_2); + entry->can_be_scissor = TRUE; + } + + return (CoglClipStack *) entry; +} + +CoglClipStack * +_cogl_clip_stack_push_primitive (CoglClipStack *stack, + CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2, + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) +{ + CoglClipStackPrimitive *entry; + CoglMatrix modelview; + CoglMatrix projection; + float transformed_corners[8]; + + entry = _cogl_clip_stack_push_entry (stack, + sizeof (CoglClipStackPrimitive), + COGL_CLIP_STACK_PRIMITIVE); + + entry->primitive = cogl_object_ref (primitive); + + entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); + + entry->bounds_x1 = bounds_x1; + entry->bounds_y1 = bounds_y1; + entry->bounds_x2 = bounds_x2; + entry->bounds_y2 = bounds_y2; + + cogl_matrix_entry_get (modelview_entry, &modelview); + cogl_matrix_entry_get (projection_entry, &projection); + + get_transformed_corners (bounds_x1, bounds_y1, bounds_x2, bounds_y2, + &modelview, + &projection, + viewport, + transformed_corners); + + /* NB: this is referring to the bounds in window coordinates as opposed + * to the bounds above in primitive local coordinates. */ + _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, + transformed_corners); + + return (CoglClipStack *) entry; +} + +CoglClipStack * +_cogl_clip_stack_ref (CoglClipStack *entry) +{ + /* A NULL pointer is considered a valid stack so we should accept + that as an argument */ + if (entry) + entry->ref_count++; + + return entry; +} + +void +_cogl_clip_stack_unref (CoglClipStack *entry) +{ + /* Unref all of the entries until we hit the root of the list or the + entry still has a remaining reference */ + while (entry && --entry->ref_count <= 0) + { + CoglClipStack *parent = entry->parent; + + switch (entry->type) + { + case COGL_CLIP_STACK_RECT: + { + CoglClipStackRect *rect = (CoglClipStackRect *) entry; + cogl_matrix_entry_unref (rect->matrix_entry); + g_slice_free1 (sizeof (CoglClipStackRect), entry); + break; + } + case COGL_CLIP_STACK_WINDOW_RECT: + g_slice_free1 (sizeof (CoglClipStackWindowRect), entry); + break; + case COGL_CLIP_STACK_PRIMITIVE: + { + CoglClipStackPrimitive *primitive_entry = + (CoglClipStackPrimitive *) entry; + cogl_matrix_entry_unref (primitive_entry->matrix_entry); + cogl_object_unref (primitive_entry->primitive); + g_slice_free1 (sizeof (CoglClipStackPrimitive), entry); + break; + } + default: + g_assert_not_reached (); + } + + entry = parent; + } +} + +CoglClipStack * +_cogl_clip_stack_pop (CoglClipStack *stack) +{ + CoglClipStack *new_top; + + _COGL_RETURN_VAL_IF_FAIL (stack != NULL, NULL); + + /* To pop we are moving the top of the stack to the old top's parent + node. The stack always needs to have a reference to the top entry + so we must take a reference to the new top. The stack would have + previously had a reference to the old top so we need to decrease + the ref count on that. We need to ref the new head first in case + this stack was the only thing referencing the old top. In that + case the call to _cogl_clip_stack_entry_unref will unref the + parent. */ + new_top = stack->parent; + + _cogl_clip_stack_ref (new_top); + + _cogl_clip_stack_unref (stack); + + return new_top; +} + +void +_cogl_clip_stack_get_bounds (CoglClipStack *stack, + int *scissor_x0, + int *scissor_y0, + int *scissor_x1, + int *scissor_y1) +{ + CoglClipStack *entry; + + *scissor_x0 = 0; + *scissor_y0 = 0; + *scissor_x1 = G_MAXINT; + *scissor_y1 = G_MAXINT; + + for (entry = stack; entry; entry = entry->parent) + { + /* Get the intersection of the current scissor and the bounding + box of this clip */ + _cogl_util_scissor_intersect (entry->bounds_x0, + entry->bounds_y0, + entry->bounds_x1, + entry->bounds_y1, + scissor_x0, + scissor_y0, + scissor_x1, + scissor_y1); + } +} + +void +_cogl_clip_stack_flush (CoglClipStack *stack, + CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + ctx->driver_vtable->clip_stack_flush (stack, framebuffer); +} diff --git a/cogl/cogl/cogl-clip-stack.h b/cogl/cogl/cogl-clip-stack.h new file mode 100644 index 000000000..56bc3f805 --- /dev/null +++ b/cogl/cogl/cogl-clip-stack.h @@ -0,0 +1,213 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_CLIP_STACK_H +#define __COGL_CLIP_STACK_H + +#include "cogl-matrix.h" +#include "cogl-primitive.h" +#include "cogl-framebuffer.h" +#include "cogl-matrix-stack.h" + +/* The clip stack works like a GSList where only a pointer to the top + of the stack is stored. The empty clip stack is represented simply + by the NULL pointer. When an entry is added to or removed from the + stack the new top of the stack is returned. When an entry is pushed + a new clip stack entry is created which effectively takes ownership + of the reference on the old entry. Therefore unrefing the top entry + effectively loses ownership of all entries in the stack */ + +typedef struct _CoglClipStack CoglClipStack; +typedef struct _CoglClipStackRect CoglClipStackRect; +typedef struct _CoglClipStackWindowRect CoglClipStackWindowRect; +typedef struct _CoglClipStackPrimitive CoglClipStackPrimitive; + +typedef enum + { + COGL_CLIP_STACK_RECT, + COGL_CLIP_STACK_WINDOW_RECT, + COGL_CLIP_STACK_PRIMITIVE + } CoglClipStackType; + +/* A clip stack consists a list of entries. Each entry has a reference + * count and a link to its parent node. The child takes a reference on + * the parent and the CoglClipStack holds a reference to the top of + * the stack. There are no links back from the parent to the + * children. This allows stacks that have common ancestry to share the + * entries. + * + * For example, the following sequence of operations would generate + * the tree below: + * + * CoglClipStack *stack_a = NULL; + * stack_a = _cogl_clip_stack_push_rectangle (stack_a, ...); + * stack_a = _cogl_clip_stack_push_rectangle (stack_a, ...); + * stack_a = _cogl_clip_stack_push_primitive (stack_a, ...); + * CoglClipStack *stack_b = NULL; + * stack_b = cogl_clip_stack_push_window_rectangle (stack_b, ...); + * + * stack_a + * \ holds a ref to + * +-----------+ + * | prim node | + * |ref count 1| + * +-----------+ + * \ + * +-----------+ +-----------+ + * both tops hold | rect node | | rect node | + * a ref to the |ref count 2|--|ref count 1| + * same rect node +-----------+ +-----------+ + * / + * +-----------+ + * | win. rect | + * |ref count 1| + * +-----------+ + * / holds a ref to + * stack_b + * + */ + +struct _CoglClipStack +{ + /* This will be null if there is no parent. If it is not null then + this node must be holding a reference to the parent */ + CoglClipStack *parent; + + CoglClipStackType type; + + /* All clip entries have a window-space bounding box which we can + use to calculate a scissor. The scissor limits the clip so that + we don't need to do a full stencil clear if the stencil buffer is + needed. This is stored in Cogl's coordinate space (ie, 0,0 is the + top left) */ + int bounds_x0; + int bounds_y0; + int bounds_x1; + int bounds_y1; + + unsigned int ref_count; +}; + +struct _CoglClipStackRect +{ + CoglClipStack _parent_data; + + /* The rectangle for this clip */ + float x0; + float y0; + float x1; + float y1; + + /* The matrix that was current when the clip was set */ + CoglMatrixEntry *matrix_entry; + + /* If this is true then the clip for this rectangle is entirely + described by the scissor bounds. This implies that the rectangle + is screen aligned and we don't need to use the stencil buffer to + set the clip. We keep the entry as a rect entry rather than a + window rect entry so that it will be easier to detect if the + modelview matrix is that same as when a rectangle is added to the + journal. In that case we can use the original clip coordinates + and modify the rectangle instead. */ + CoglBool can_be_scissor; +}; + +struct _CoglClipStackWindowRect +{ + CoglClipStack _parent_data; + + /* The window rect clip doesn't need any specific data because it + just adds to the scissor clip */ +}; + +struct _CoglClipStackPrimitive +{ + CoglClipStack _parent_data; + + /* The matrix that was current when the clip was set */ + CoglMatrixEntry *matrix_entry; + + CoglPrimitive *primitive; + + float bounds_x1; + float bounds_y1; + float bounds_x2; + float bounds_y2; +}; + +CoglClipStack * +_cogl_clip_stack_push_window_rectangle (CoglClipStack *stack, + int x_offset, + int y_offset, + int width, + int height); + +CoglClipStack * +_cogl_clip_stack_push_rectangle (CoglClipStack *stack, + float x_1, + float y_1, + float x_2, + float y_2, + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport); + +CoglClipStack * +_cogl_clip_stack_push_primitive (CoglClipStack *stack, + CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2, + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport); + +CoglClipStack * +_cogl_clip_stack_pop (CoglClipStack *stack); + +void +_cogl_clip_stack_get_bounds (CoglClipStack *stack, + int *scissor_x0, + int *scissor_y0, + int *scissor_x1, + int *scissor_y1); + +void +_cogl_clip_stack_flush (CoglClipStack *stack, + CoglFramebuffer *framebuffer); + +CoglClipStack * +_cogl_clip_stack_ref (CoglClipStack *stack); + +void +_cogl_clip_stack_unref (CoglClipStack *stack); + +#endif /* __COGL_CLIP_STACK_H */ diff --git a/cogl/cogl/cogl-closure-list-private.h b/cogl/cogl/cogl-closure-list-private.h new file mode 100644 index 000000000..5446cb207 --- /dev/null +++ b/cogl/cogl/cogl-closure-list-private.h @@ -0,0 +1,118 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _COGL_CLOSURE_LIST_PRIVATE_H_ +#define _COGL_CLOSURE_LIST_PRIVATE_H_ + +#include "cogl-object.h" +#include "cogl-list.h" + +/* + * This implements a list of callbacks that can be used a bit like + * signals in GObject, but that don't have any marshalling overhead. + * + * The idea is that any Cogl code that wants to provide a callback + * point will provide api to add a callback for that particular point. + * The function can take a function pointer with the correct + * signature. Internally the Cogl code can use _cogl_closure_list_add, + * _cogl_closure_disconnect and _cogl_closure_list_disconnect_all + * + * In the future we could consider exposing the CoglClosure type which + * would allow applications to use _cogl_closure_disconnect() directly + * so we don't need to expose new disconnect apis for each callback + * point. + */ + +typedef struct _CoglClosure +{ + CoglList link; + + void *function; + void *user_data; + CoglUserDataDestroyCallback destroy_cb; +} CoglClosure; + +/* + * _cogl_closure_disconnect: + * @closure: A closure connected to a Cogl closure list + * + * Removes the given closure from the callback list it is connected to + * and destroys it. If the closure was created with a destroy function + * then it will be invoked. */ +void +_cogl_closure_disconnect (CoglClosure *closure); + +void +_cogl_closure_list_disconnect_all (CoglList *list); + +CoglClosure * +_cogl_closure_list_add (CoglList *list, + void *function, + void *user_data, + CoglUserDataDestroyCallback destroy_cb); + +/* + * _cogl_closure_list_invoke: + * @list: A pointer to a CoglList containing CoglClosures + * @cb_type: The name of a typedef for the closure callback function signature + * @...: The the arguments to pass to the callback + * + * A convenience macro to invoke a closure list with a variable number + * of arguments that will be passed to the closure callback functions. + * + * Note that the arguments will be evaluated multiple times so it is + * not safe to pass expressions that have side-effects. + * + * Note also that this function ignores the return value from the + * callbacks. If you want to handle the return value you should + * manually iterate the list and invoke the callbacks yourself. + */ +#define _cogl_closure_list_invoke(list, cb_type, ...) \ + G_STMT_START { \ + CoglClosure *_c, *_tmp; \ + \ + _cogl_list_for_each_safe (_c, _tmp, (list), link) \ + { \ + cb_type _cb = _c->function; \ + _cb (__VA_ARGS__, _c->user_data); \ + } \ + } G_STMT_END + +#define _cogl_closure_list_invoke_no_args(list) \ + G_STMT_START { \ + CoglClosure *_c, *_tmp; \ + \ + _cogl_list_for_each_safe (_c, _tmp, (list), link) \ + { \ + void (*_cb)(void *) = _c->function; \ + _cb (_c->user_data); \ + } \ + } G_STMT_END + +#endif /* _COGL_CLOSURE_LIST_PRIVATE_H_ */ diff --git a/cogl/cogl/cogl-closure-list.c b/cogl/cogl/cogl-closure-list.c new file mode 100644 index 000000000..eb8cadd27 --- /dev/null +++ b/cogl/cogl/cogl-closure-list.c @@ -0,0 +1,71 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include + +#include "cogl-closure-list-private.h" + +void +_cogl_closure_disconnect (CoglClosure *closure) +{ + _cogl_list_remove (&closure->link); + + if (closure->destroy_cb) + closure->destroy_cb (closure->user_data); + + g_slice_free (CoglClosure, closure); +} + +void +_cogl_closure_list_disconnect_all (CoglList *list) +{ + CoglClosure *closure, *next; + + _cogl_list_for_each_safe (closure, next, list, link) + _cogl_closure_disconnect (closure); +} + +CoglClosure * +_cogl_closure_list_add (CoglList *list, + void *function, + void *user_data, + CoglUserDataDestroyCallback destroy_cb) +{ + CoglClosure *closure = g_slice_new (CoglClosure); + + closure->function = function; + closure->user_data = user_data; + closure->destroy_cb = destroy_cb; + + _cogl_list_insert (list, &closure->link); + + return closure; +} diff --git a/cogl/cogl/cogl-color-private.h b/cogl/cogl/cogl-color-private.h new file mode 100644 index 000000000..bb1a58e25 --- /dev/null +++ b/cogl/cogl/cogl-color-private.h @@ -0,0 +1,51 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_COLOR_PRIVATE_PRIVATE_H +#define __COGL_COLOR_PRIVATE_PRIVATE_H + +#include "cogl-color.h" + +#include + +/* cogl-pipeline.c wants to be able to hash CoglColor data so it needs + * the exact data size to be able to avoid reading the padding bytes. + */ +#define _COGL_COLOR_DATA_SIZE 4 + +void +_cogl_color_get_rgba_4ubv (const CoglColor *color, + uint8_t *dest); + +#endif /* __COGL_COLOR_PRIVATE_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-color.c b/cogl/cogl/cogl-color.c new file mode 100644 index 000000000..02e501d43 --- /dev/null +++ b/cogl/cogl/cogl-color.c @@ -0,0 +1,449 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-util.h" +#include "cogl-color.h" +#include "cogl-fixed.h" +#include "cogl-color-private.h" +#include "cogl-gtype-private.h" + +COGL_GTYPE_DEFINE_BOXED (Color, color, cogl_color_copy, cogl_color_free); + +CoglColor * +cogl_color_new (void) +{ + return g_slice_new (CoglColor); +} + +CoglColor * +cogl_color_copy (const CoglColor *color) +{ + if (G_LIKELY (color)) + return g_slice_dup (CoglColor, color); + + return NULL; +} + +void +cogl_color_free (CoglColor *color) +{ + if (G_LIKELY (color)) + g_slice_free (CoglColor, color); +} + +void +cogl_color_init_from_4ub (CoglColor *color, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + _COGL_RETURN_IF_FAIL (color != NULL); + + color->red = red; + color->green = green; + color->blue = blue; + color->alpha = alpha; +} + +/* XXX: deprecated, use cogl_color_init_from_4ub */ +void +cogl_color_set_from_4ub (CoglColor *dest, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + cogl_color_init_from_4ub (dest, red, green, blue, alpha); +} + +void +cogl_color_init_from_4f (CoglColor *color, + float red, + float green, + float blue, + float alpha) +{ + _COGL_RETURN_IF_FAIL (color != NULL); + + color->red = (red * 255); + color->green = (green * 255); + color->blue = (blue * 255); + color->alpha = (alpha * 255); +} + +/* XXX: deprecated, use cogl_color_init_from_4f */ +void +cogl_color_set_from_4f (CoglColor *color, + float red, + float green, + float blue, + float alpha) +{ + cogl_color_init_from_4f (color, red, green, blue, alpha); +} + +void +cogl_color_init_from_4fv (CoglColor *color, + const float *color_array) +{ + _COGL_RETURN_IF_FAIL (color != NULL); + + color->red = (color_array[0] * 255); + color->green = (color_array[1] * 255); + color->blue = (color_array[2] * 255); + color->alpha = (color_array[3] * 255); +} + +unsigned char +cogl_color_get_red_byte (const CoglColor *color) +{ + return color->red; +} + +float +cogl_color_get_red_float (const CoglColor *color) +{ + return (float) color->red / 255.0; +} + +float +cogl_color_get_red (const CoglColor *color) +{ + return ((float) color->red / 255.0); +} + +unsigned char +cogl_color_get_green_byte (const CoglColor *color) +{ + return color->green; +} + +float +cogl_color_get_green_float (const CoglColor *color) +{ + return (float) color->green / 255.0; +} + +float +cogl_color_get_green (const CoglColor *color) +{ + return ((float) color->green / 255.0); +} + +unsigned char +cogl_color_get_blue_byte (const CoglColor *color) +{ + return color->blue; +} + +float +cogl_color_get_blue_float (const CoglColor *color) +{ + return (float) color->blue / 255.0; +} + +float +cogl_color_get_blue (const CoglColor *color) +{ + return ((float) color->blue / 255.0); +} + +unsigned char +cogl_color_get_alpha_byte (const CoglColor *color) +{ + return color->alpha; +} + +float +cogl_color_get_alpha_float (const CoglColor *color) +{ + return (float) color->alpha / 255.0; +} + +float +cogl_color_get_alpha (const CoglColor *color) +{ + return ((float) color->alpha / 255.0); +} + +void +cogl_color_set_red_byte (CoglColor *color, + unsigned char red) +{ + color->red = red; +} + +void +cogl_color_set_red_float (CoglColor *color, + float red) +{ + color->red = red * 255.0; +} + +void +cogl_color_set_red (CoglColor *color, + float red) +{ + color->red = red * 255.0; +} + +void +cogl_color_set_green_byte (CoglColor *color, + unsigned char green) +{ + color->green = green; +} + +void +cogl_color_set_green_float (CoglColor *color, + float green) +{ + color->green = green * 255.0; +} + +void +cogl_color_set_green (CoglColor *color, + float green) +{ + color->green = green * 255.0; +} + +void +cogl_color_set_blue_byte (CoglColor *color, + unsigned char blue) +{ + color->blue = blue; +} + +void +cogl_color_set_blue_float (CoglColor *color, + float blue) +{ + color->blue = blue * 255.0; +} + +void +cogl_color_set_blue (CoglColor *color, + float blue) +{ + color->blue = blue * 255.0; +} + +void +cogl_color_set_alpha_byte (CoglColor *color, + unsigned char alpha) +{ + color->alpha = alpha; +} + +void +cogl_color_set_alpha_float (CoglColor *color, + float alpha) +{ + color->alpha = alpha * 255.0; +} + +void +cogl_color_set_alpha (CoglColor *color, + float alpha) +{ + color->alpha = alpha * 255.0; +} + +void +cogl_color_premultiply (CoglColor *color) +{ + color->red = (color->red * color->alpha + 128) / 255; + color->green = (color->green * color->alpha + 128) / 255; + color->blue = (color->blue * color->alpha + 128) / 255; +} + +void +cogl_color_unpremultiply (CoglColor *color) +{ + if (color->alpha != 0) + { + color->red = (color->red * 255) / color->alpha; + color->green = (color->green * 255) / color->alpha; + color->blue = (color->blue * 255) / color->alpha; + } +} + +CoglBool +cogl_color_equal (const void *v1, const void *v2) +{ + const uint32_t *c1 = v1, *c2 = v2; + + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + /* XXX: We don't compare the padding */ + return *c1 == *c2 ? TRUE : FALSE; +} + +void +_cogl_color_get_rgba_4ubv (const CoglColor *color, + uint8_t *dest) +{ + memcpy (dest, color, 4); +} + +void +cogl_color_to_hsl (const CoglColor *color, + float *hue, + float *saturation, + float *luminance) +{ + float red, green, blue; + float min, max, delta; + float h, l, s; + + red = color->red / 255.0; + green = color->green / 255.0; + blue = color->blue / 255.0; + + if (red > green) + { + if (red > blue) + max = red; + else + max = blue; + + if (green < blue) + min = green; + else + min = blue; + } + else + { + if (green > blue) + max = green; + else + max = blue; + + if (red < blue) + min = red; + else + min = blue; + } + + l = (max + min) / 2; + s = 0; + h = 0; + + if (max != min) + { + if (l <= 0.5) + s = (max - min) / (max + min); + else + s = (max - min) / (2.0 - max - min); + + delta = max - min; + + if (red == max) + h = (green - blue) / delta; + else if (green == max) + h = 2.0 + (blue - red) / delta; + else if (blue == max) + h = 4.0 + (red - green) / delta; + + h *= 60; + + if (h < 0) + h += 360.0; + } + + if (hue) + *hue = h; + + if (luminance) + *luminance = l; + + if (saturation) + *saturation = s; +} + +void +cogl_color_init_from_hsl (CoglColor *color, + float hue, + float saturation, + float luminance) +{ + float tmp1, tmp2; + float tmp3[3]; + float clr[3]; + int i; + + hue /= 360.0; + + if (saturation == 0) + { + cogl_color_init_from_4f (color, luminance, luminance, luminance, 1.0f); + return; + } + + if (luminance <= 0.5) + tmp2 = luminance * (1.0 + saturation); + else + tmp2 = luminance + saturation - (luminance * saturation); + + tmp1 = 2.0 * luminance - tmp2; + + tmp3[0] = hue + 1.0 / 3.0; + tmp3[1] = hue; + tmp3[2] = hue - 1.0 / 3.0; + + for (i = 0; i < 3; i++) + { + if (tmp3[i] < 0) + tmp3[i] += 1.0; + + if (tmp3[i] > 1) + tmp3[i] -= 1.0; + + if (6.0 * tmp3[i] < 1.0) + clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0; + else if (2.0 * tmp3[i] < 1.0) + clr[i] = tmp2; + else if (3.0 * tmp3[i] < 2.0) + clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0); + else + clr[i] = tmp1; + } + + cogl_color_init_from_4f (color, clr[0], clr[1], clr[2], 1.0f); +} diff --git a/cogl/cogl/cogl-color.h b/cogl/cogl/cogl-color.h new file mode 100644 index 000000000..bdb0bfaf6 --- /dev/null +++ b/cogl/cogl/cogl-color.h @@ -0,0 +1,604 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_COLOR_H__ +#define __COGL_COLOR_H__ + +/** + * SECTION:cogl-color + * @short_description: A generic color definition + * + * #CoglColor is a simple structure holding the definition of a color such + * that it can be efficiently used by GL + * + * Since: 1.0 + */ + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_color_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_color_get_gtype (void); +#endif + +/** + * cogl_color_new: + * + * Creates a new (empty) color + * + * Return value: a newly-allocated #CoglColor. Use cogl_color_free() + * to free the allocated resources + * + * Since: 1.0 + */ +CoglColor * +cogl_color_new (void); + +/** + * cogl_color_copy: + * @color: the color to copy + * + * Creates a copy of @color + * + * Return value: a newly-allocated #CoglColor. Use cogl_color_free() + * to free the allocate resources + * + * Since: 1.0 + */ +CoglColor * +cogl_color_copy (const CoglColor *color); + +/** + * cogl_color_free: + * @color: the color to free + * + * Frees the resources allocated by cogl_color_new() and cogl_color_copy() + * + * Since: 1.0 + */ +void +cogl_color_free (CoglColor *color); + +/** + * cogl_color_init_from_4ub: + * @color: A pointer to a #CoglColor to initialize + * @red: value of the red channel, between 0 and 255 + * @green: value of the green channel, between 0 and 255 + * @blue: value of the blue channel, between 0 and 255 + * @alpha: value of the alpha channel, between 0 and 255 + * + * Sets the values of the passed channels into a #CoglColor. + * + * Since: 1.4 + */ +void +cogl_color_init_from_4ub (CoglColor *color, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); + +/** + * cogl_color_set_from_4ub: + * @color: A pointer to a #CoglColor to initialize + * @red: value of the red channel, between 0 and 255 + * @green: value of the green channel, between 0 and 255 + * @blue: value of the blue channel, between 0 and 255 + * @alpha: value of the alpha channel, between 0 and 255 + * + * Sets the values of the passed channels into a #CoglColor. + * + * Since: 1.0 + * Deprecated: 1.4: Use cogl_color_init_from_4ub instead. + */ +COGL_DEPRECATED_IN_1_4_FOR (cogl_color_init_from_4ub) +void +cogl_color_set_from_4ub (CoglColor *color, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); + +/** + * cogl_color_init_from_4f: + * @color: A pointer to a #CoglColor to initialize + * @red: value of the red channel, between 0 and 1.0 + * @green: value of the green channel, between 0 and 1.0 + * @blue: value of the blue channel, between 0 and 1.0 + * @alpha: value of the alpha channel, between 0 and 1.0 + * + * Sets the values of the passed channels into a #CoglColor + * + * Since: 1.4 + */ +void +cogl_color_init_from_4f (CoglColor *color, + float red, + float green, + float blue, + float alpha); + +/** + * cogl_color_set_from_4f: + * @color: A pointer to a #CoglColor to initialize + * @red: value of the red channel, between 0 and %1.0 + * @green: value of the green channel, between 0 and %1.0 + * @blue: value of the blue channel, between 0 and %1.0 + * @alpha: value of the alpha channel, between 0 and %1.0 + * + * Sets the values of the passed channels into a #CoglColor + * + * Since: 1.0 + * Deprecated: 1.4: Use cogl_color_init_from_4f instead. + */ +COGL_DEPRECATED_IN_1_4_FOR (cogl_color_init_from_4f) +void +cogl_color_set_from_4f (CoglColor *color, + float red, + float green, + float blue, + float alpha); + +/** + * cogl_color_init_from_4fv: + * @color: A pointer to a #CoglColor to initialize + * @color_array: a pointer to an array of 4 float color components + * + * Sets the values of the passed channels into a #CoglColor + * + * Since: 1.4 + */ +void +cogl_color_init_from_4fv (CoglColor *color, + const float *color_array); + +/** + * cogl_color_get_red_byte: + * @color: a #CoglColor + * + * Retrieves the red channel of @color as a byte value + * between 0 and 255 + * + * Return value: the red channel of the passed color + * + * Since: 1.0 + */ +unsigned char +cogl_color_get_red_byte (const CoglColor *color); + +/** + * cogl_color_get_green_byte: + * @color: a #CoglColor + * + * Retrieves the green channel of @color as a byte value + * between 0 and 255 + * + * Return value: the green channel of the passed color + * + * Since: 1.0 + */ +unsigned char +cogl_color_get_green_byte (const CoglColor *color); + +/** + * cogl_color_get_blue_byte: + * @color: a #CoglColor + * + * Retrieves the blue channel of @color as a byte value + * between 0 and 255 + * + * Return value: the blue channel of the passed color + * + * Since: 1.0 + */ +unsigned char +cogl_color_get_blue_byte (const CoglColor *color); + +/** + * cogl_color_get_alpha_byte: + * @color: a #CoglColor + * + * Retrieves the alpha channel of @color as a byte value + * between 0 and 255 + * + * Return value: the alpha channel of the passed color + * + * Since: 1.0 + */ +unsigned char +cogl_color_get_alpha_byte (const CoglColor *color); + +/** + * cogl_color_get_red_float: + * @color: a #CoglColor + * + * Retrieves the red channel of @color as a floating point + * value between 0.0 and 1.0 + * + * Return value: the red channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_red_float (const CoglColor *color); + +/** + * cogl_color_get_green_float: + * @color: a #CoglColor + * + * Retrieves the green channel of @color as a floating point + * value between 0.0 and 1.0 + * + * Return value: the green channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_green_float (const CoglColor *color); + +/** + * cogl_color_get_blue_float: + * @color: a #CoglColor + * + * Retrieves the blue channel of @color as a floating point + * value between 0.0 and 1.0 + * + * Return value: the blue channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_blue_float (const CoglColor *color); + +/** + * cogl_color_get_alpha_float: + * @color: a #CoglColor + * + * Retrieves the alpha channel of @color as a floating point + * value between 0.0 and 1.0 + * + * Return value: the alpha channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_alpha_float (const CoglColor *color); + +/** + * cogl_color_get_red: + * @color: a #CoglColor + * + * Retrieves the red channel of @color as a fixed point + * value between 0 and 1.0. + * + * Return value: the red channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_red (const CoglColor *color); + +/** + * cogl_color_get_green: + * @color: a #CoglColor + * + * Retrieves the green channel of @color as a fixed point + * value between 0 and 1.0. + * + * Return value: the green channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_green (const CoglColor *color); + +/** + * cogl_color_get_blue: + * @color: a #CoglColor + * + * Retrieves the blue channel of @color as a fixed point + * value between 0 and 1.0. + * + * Return value: the blue channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_blue (const CoglColor *color); + +/** + * cogl_color_get_alpha: + * @color: a #CoglColor + * + * Retrieves the alpha channel of @color as a fixed point + * value between 0 and 1.0. + * + * Return value: the alpha channel of the passed color + * + * Since: 1.0 + */ +float +cogl_color_get_alpha (const CoglColor *color); + +/** + * cogl_color_set_red_byte: + * @color: a #CoglColor + * @red: a byte value between 0 and 255 + * + * Sets the red channel of @color to @red. + * + * Since: 1.4 + */ +void +cogl_color_set_red_byte (CoglColor *color, + unsigned char red); + +/** + * cogl_color_set_green_byte: + * @color: a #CoglColor + * @green: a byte value between 0 and 255 + * + * Sets the green channel of @color to @green. + * + * Since: 1.4 + */ +void +cogl_color_set_green_byte (CoglColor *color, + unsigned char green); + +/** + * cogl_color_set_blue_byte: + * @color: a #CoglColor + * @blue: a byte value between 0 and 255 + * + * Sets the blue channel of @color to @blue. + * + * Since: 1.4 + */ +void +cogl_color_set_blue_byte (CoglColor *color, + unsigned char blue); + +/** + * cogl_color_set_alpha_byte: + * @color: a #CoglColor + * @alpha: a byte value between 0 and 255 + * + * Sets the alpha channel of @color to @alpha. + * + * Since: 1.4 + */ +void +cogl_color_set_alpha_byte (CoglColor *color, + unsigned char alpha); + +/** + * cogl_color_set_red_float: + * @color: a #CoglColor + * @red: a float value between 0.0f and 1.0f + * + * Sets the red channel of @color to @red. + * + * since: 1.4 + */ +void +cogl_color_set_red_float (CoglColor *color, + float red); + +/** + * cogl_color_set_green_float: + * @color: a #CoglColor + * @green: a float value between 0.0f and 1.0f + * + * Sets the green channel of @color to @green. + * + * since: 1.4 + */ +void +cogl_color_set_green_float (CoglColor *color, + float green); + +/** + * cogl_color_set_blue_float: + * @color: a #CoglColor + * @blue: a float value between 0.0f and 1.0f + * + * Sets the blue channel of @color to @blue. + * + * since: 1.4 + */ +void +cogl_color_set_blue_float (CoglColor *color, + float blue); + +/** + * cogl_color_set_alpha_float: + * @color: a #CoglColor + * @alpha: a float value between 0.0f and 1.0f + * + * Sets the alpha channel of @color to @alpha. + * + * since: 1.4 + */ +void +cogl_color_set_alpha_float (CoglColor *color, + float alpha); + +/** + * cogl_color_set_red: + * @color: a #CoglColor + * @red: a float value between 0.0f and 1.0f + * + * Sets the red channel of @color to @red. + * + * Since: 1.4 + */ +void +cogl_color_set_red (CoglColor *color, + float red); + +/** + * cogl_color_set_green: + * @color: a #CoglColor + * @green: a float value between 0.0f and 1.0f + * + * Sets the green channel of @color to @green. + * + * Since: 1.4 + */ +void +cogl_color_set_green (CoglColor *color, + float green); + +/** + * cogl_color_set_blue: + * @color: a #CoglColor + * @blue: a float value between 0.0f and 1.0f + * + * Sets the blue channel of @color to @blue. + * + * Since: 1.4 + */ +void +cogl_color_set_blue (CoglColor *color, + float blue); + +/** + * cogl_color_set_alpha: + * @color: a #CoglColor + * @alpha: a float value between 0.0f and 1.0f + * + * Sets the alpha channel of @color to @alpha. + * + * Since: 1.4 + */ +void +cogl_color_set_alpha (CoglColor *color, + float alpha); + +/** + * cogl_color_premultiply: + * @color: the color to premultiply + * + * Converts a non-premultiplied color to a pre-multiplied color. For + * example, semi-transparent red is (1.0, 0, 0, 0.5) when non-premultiplied + * and (0.5, 0, 0, 0.5) when premultiplied. + * + * Since: 1.0 + */ +void +cogl_color_premultiply (CoglColor *color); + +/** + * cogl_color_unpremultiply: + * @color: the color to unpremultiply + * + * Converts a pre-multiplied color to a non-premultiplied color. For + * example, semi-transparent red is (0.5, 0, 0, 0.5) when premultiplied + * and (1.0, 0, 0, 0.5) when non-premultiplied. + * + * Since: 1.4 + */ +void +cogl_color_unpremultiply (CoglColor *color); + +/** + * cogl_color_equal: + * @v1: a #CoglColor + * @v2: a #CoglColor + * + * Compares two #CoglColors and checks if they are the same. + * + * This function can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using #CoglColors as keys in a #GHashTable. + * + * Return value: %TRUE if the two colors are the same. + * + * Since: 1.0 + */ +CoglBool +cogl_color_equal (const void *v1, const void *v2); + +/** + * cogl_color_to_hsl: + * @color: a #CoglColor + * @hue: (out): return location for the hue value or %NULL + * @saturation: (out): return location for the saturation value or %NULL + * @luminance: (out): return location for the luminance value or %NULL + * + * Converts @color to the HLS format. + * + * The @hue value is in the 0 .. 360 range. The @luminance and + * @saturation values are in the 0 .. 1 range. + * + * Since: 1.16 + */ +void +cogl_color_to_hsl (const CoglColor *color, + float *hue, + float *saturation, + float *luminance); + +/** + * cogl_color_init_from_hsl: + * @color: (out): return location for a #CoglColor + * @hue: hue value, in the 0 .. 360 range + * @saturation: saturation value, in the 0 .. 1 range + * @luminance: luminance value, in the 0 .. 1 range + * + * Converts a color expressed in HLS (hue, luminance and saturation) + * values into a #CoglColor. + * + * Since: 1.16 + */ +void +cogl_color_init_from_hsl (CoglColor *color, + float hue, + float saturation, + float luminance); + +COGL_END_DECLS + +#endif /* __COGL_COLOR_H__ */ diff --git a/cogl/cogl/cogl-config-private.h b/cogl/cogl/cogl-config-private.h new file mode 100644 index 000000000..93ff943b2 --- /dev/null +++ b/cogl/cogl/cogl-config-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_CONFIG_PRIVATE_H +#define __COGL_CONFIG_PRIVATE_H + +void +_cogl_config_read (void); + +extern char *_cogl_config_driver; +extern char *_cogl_config_renderer; +extern char *_cogl_config_disable_gl_extensions; +extern char *_cogl_config_override_gl_version; + +#endif /* __COGL_CONFIG_PRIVATE_H */ diff --git a/cogl/cogl/cogl-config.c b/cogl/cogl/cogl-config.c new file mode 100644 index 000000000..6c960d4b5 --- /dev/null +++ b/cogl/cogl/cogl-config.c @@ -0,0 +1,147 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-config-private.h" + +#include + +char *_cogl_config_driver; +char *_cogl_config_renderer; +char *_cogl_config_disable_gl_extensions; +char *_cogl_config_override_gl_version; + +#ifndef COGL_HAS_GLIB_SUPPORT + +void +_cogl_config_read (void) +{ + +} + +#else /* COGL_HAS_GLIB_SUPPORT */ + +/* Array of config options that just set a global string */ +static const struct +{ + const char *conf_name; + char **variable; +} cogl_config_string_options[] = + { + { "COGL_DRIVER", &_cogl_config_driver }, + { "COGL_RENDERER", &_cogl_config_renderer }, + { "COGL_DISABLE_GL_EXTENSIONS", &_cogl_config_disable_gl_extensions }, + { "COGL_OVERRIDE_GL_VERSION", &_cogl_config_override_gl_version } + }; + +static void +_cogl_config_process (GKeyFile *key_file) +{ + char *value; + int i; + + value = g_key_file_get_string (key_file, "global", "COGL_DEBUG", NULL); + if (value) + { + _cogl_parse_debug_string (value, + TRUE /* enable the flags */, + TRUE /* ignore help option */); + g_free (value); + } + + value = g_key_file_get_string (key_file, "global", "COGL_NO_DEBUG", NULL); + if (value) + { + _cogl_parse_debug_string (value, + FALSE /* disable the flags */, + TRUE /* ignore help option */); + g_free (value); + } + + for (i = 0; i < G_N_ELEMENTS (cogl_config_string_options); i++) + { + const char *conf_name = cogl_config_string_options[i].conf_name; + char **variable = cogl_config_string_options[i].variable; + + value = g_key_file_get_string (key_file, "global", conf_name, NULL); + if (value) + { + g_free (*variable); + *variable = value; + } + } +} + +void +_cogl_config_read (void) +{ + GKeyFile *key_file = g_key_file_new (); + const char * const *system_dirs = g_get_system_config_dirs (); + char *filename; + CoglBool status = FALSE; + int i; + + for (i = 0; system_dirs[i]; i++) + { + filename = g_build_filename (system_dirs[i], "cogl", "cogl.conf", NULL); + status = g_key_file_load_from_file (key_file, + filename, + 0, + NULL); + g_free (filename); + if (status) + { + _cogl_config_process (key_file); + g_key_file_free (key_file); + key_file = g_key_file_new (); + break; + } + } + + filename = g_build_filename (g_get_user_config_dir (), "cogl", "cogl.conf", NULL); + status = g_key_file_load_from_file (key_file, + filename, + 0, + NULL); + g_free (filename); + + if (status) + _cogl_config_process (key_file); + + g_key_file_free (key_file); +} + +#endif /* COGL_HAS_GLIB_SUPPORT */ diff --git a/cogl/cogl/cogl-context-private.h b/cogl/cogl/cogl-context-private.h new file mode 100644 index 000000000..9e6620733 --- /dev/null +++ b/cogl/cogl/cogl-context-private.h @@ -0,0 +1,407 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_CONTEXT_PRIVATE_H +#define __COGL_CONTEXT_PRIVATE_H + +#include "cogl-context.h" +#include "cogl-winsys-private.h" +#include "cogl-flags.h" + +#ifdef COGL_HAS_XLIB_SUPPORT +#include "cogl-xlib-private.h" +#endif + +#include "cogl-display-private.h" +#include "cogl-primitives.h" +#include "cogl-clip-stack.h" +#include "cogl-matrix-stack.h" +#include "cogl-pipeline-private.h" +#include "cogl-buffer-private.h" +#include "cogl-bitmask.h" +#include "cogl-atlas.h" +#include "cogl-driver.h" +#include "cogl-texture-driver.h" +#include "cogl-pipeline-cache.h" +#include "cogl-texture-2d.h" +#include "cogl-texture-3d.h" +#include "cogl-texture-rectangle.h" +#include "cogl-sampler-cache-private.h" +#include "cogl-gpu-info-private.h" +#include "cogl-gl-header.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-fence-private.h" +#include "cogl-poll-private.h" +#include "cogl-path/cogl-path-types.h" +#include "cogl-private.h" + +typedef struct +{ + GLfloat v[3]; + GLfloat t[2]; + GLubyte c[4]; +} CoglTextureGLVertex; + +struct _CoglContext +{ + CoglObject _parent; + + CoglDisplay *display; + + CoglDriver driver; + + /* Information about the GPU and driver which we can use to + determine certain workarounds */ + CoglGpuInfo gpu; + + /* vtables for the driver functions */ + const CoglDriverVtable *driver_vtable; + const CoglTextureDriver *texture_driver; + + int glsl_major; + int glsl_minor; + + /* This is the GLSL version that we will claim that snippets are + * written against using the #version pragma. This will be the + * largest version that is less than or equal to the version + * provided by the driver without massively altering the syntax. Eg, + * we wouldn't use version 1.3 even if it is available because that + * removes the ‘attribute’ and ‘varying’ keywords. */ + int glsl_version_to_use; + + /* Features cache */ + unsigned long features[COGL_FLAGS_N_LONGS_FOR_SIZE (_COGL_N_FEATURE_IDS)]; + CoglFeatureFlags feature_flags; /* legacy/deprecated feature flags */ + unsigned long private_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)]; + + CoglBool needs_viewport_scissor_workaround; + CoglFramebuffer *viewport_scissor_workaround_framebuffer; + + CoglPipeline *default_pipeline; + CoglPipelineLayer *default_layer_0; + CoglPipelineLayer *default_layer_n; + CoglPipelineLayer *dummy_layer_dependant; + + GHashTable *attribute_name_states_hash; + GArray *attribute_name_index_map; + int n_attribute_names; + + CoglBitmask enabled_builtin_attributes; + CoglBitmask enabled_texcoord_attributes; + CoglBitmask enabled_custom_attributes; + + /* These are temporary bitmasks that are used when disabling + * builtin,texcoord and custom attribute arrays. They are here just + * to avoid allocating new ones each time */ + CoglBitmask enable_builtin_attributes_tmp; + CoglBitmask enable_texcoord_attributes_tmp; + CoglBitmask enable_custom_attributes_tmp; + CoglBitmask changed_bits_tmp; + + CoglBool legacy_backface_culling_enabled; + + /* A few handy matrix constants */ + CoglMatrix identity_matrix; + CoglMatrix y_flip_matrix; + + /* Value that was last used when calling glMatrixMode to avoid + calling it multiple times */ + CoglMatrixMode flushed_matrix_mode; + + /* The matrix stack entries that should be flushed during the next + * pipeline state flush */ + CoglMatrixEntry *current_projection_entry; + CoglMatrixEntry *current_modelview_entry; + + CoglMatrixEntry identity_entry; + + /* A cache of the last (immutable) matrix stack entries that were + * flushed to the GL matrix builtins */ + CoglMatrixEntryCache builtin_flushed_projection; + CoglMatrixEntryCache builtin_flushed_modelview; + + GArray *texture_units; + int active_texture_unit; + + CoglPipelineFogState legacy_fog_state; + + /* Pipelines */ + CoglPipeline *opaque_color_pipeline; /* used for set_source_color */ + CoglPipeline *blended_color_pipeline; /* used for set_source_color */ + CoglPipeline *texture_pipeline; /* used for set_source_texture */ + GString *codegen_header_buffer; + GString *codegen_source_buffer; + GString *codegen_boilerplate_buffer; + GList *source_stack; + + int legacy_state_set; + + CoglPipelineCache *pipeline_cache; + + /* Textures */ + CoglTexture2D *default_gl_texture_2d_tex; + CoglTexture3D *default_gl_texture_3d_tex; + CoglTextureRectangle *default_gl_texture_rect_tex; + + /* Central list of all framebuffers so all journals can be flushed + * at any time. */ + GList *framebuffers; + + /* Global journal buffers */ + GArray *journal_flush_attributes_array; + GArray *journal_clip_bounds; + + GArray *polygon_vertices; + + /* Some simple caching, to minimize state changes... */ + CoglPipeline *current_pipeline; + unsigned long current_pipeline_changes_since_flush; + CoglBool current_pipeline_with_color_attrib; + CoglBool current_pipeline_unknown_color_alpha; + unsigned long current_pipeline_age; + + CoglBool gl_blend_enable_cache; + + CoglBool depth_test_enabled_cache; + CoglDepthTestFunction depth_test_function_cache; + CoglBool depth_writing_enabled_cache; + float depth_range_near_cache; + float depth_range_far_cache; + + CoglBool legacy_depth_test_enabled; + + CoglBuffer *current_buffer[COGL_BUFFER_BIND_TARGET_COUNT]; + + /* Framebuffers */ + GSList *framebuffer_stack; + CoglFramebuffer *window_buffer; + unsigned long current_draw_buffer_state_flushed; + unsigned long current_draw_buffer_changes; + CoglFramebuffer *current_draw_buffer; + CoglFramebuffer *current_read_buffer; + + gboolean have_last_offscreen_allocate_flags; + CoglOffscreenAllocateFlags last_offscreen_allocate_flags; + + GHashTable *swap_callback_closures; + int next_swap_callback_id; + + CoglList onscreen_events_queue; + CoglList onscreen_dirty_queue; + CoglClosure *onscreen_dispatch_idle; + + CoglGLES2Context *current_gles2_context; + GQueue gles2_context_stack; + + /* This becomes TRUE the first time the context is bound to an + * onscreen buffer. This is used by cogl-framebuffer-gl to determine + * when to initialise the glDrawBuffer state */ + CoglBool was_bound_to_onscreen; + + /* Primitives */ + CoglPath *current_path; + CoglPipeline *stencil_pipeline; + + /* Pre-generated VBOs containing indices to generate GL_TRIANGLES + out of a vertex array of quads */ + CoglIndices *quad_buffer_indices_byte; + unsigned int quad_buffer_indices_len; + CoglIndices *quad_buffer_indices; + + CoglIndices *rectangle_byte_indices; + CoglIndices *rectangle_short_indices; + int rectangle_short_indices_len; + + CoglBool in_begin_gl_block; + + CoglPipeline *texture_download_pipeline; + CoglPipeline *blit_texture_pipeline; + + GSList *atlases; + GHookList atlas_reorganize_callbacks; + + /* This debugging variable is used to pick a colour for visually + displaying the quad batches. It needs to be global so that it can + be reset by cogl_clear. It needs to be reset to increase the + chances of getting the same colour during an animation */ + uint8_t journal_rectangles_color; + + /* Cached values for GL_MAX_TEXTURE_[IMAGE_]UNITS to avoid calling + glGetInteger too often */ + GLint max_texture_units; + GLint max_texture_image_units; + GLint max_activateable_texture_units; + + /* Fragment processing programs */ + CoglHandle current_program; + + CoglPipelineProgramType current_fragment_program_type; + CoglPipelineProgramType current_vertex_program_type; + GLuint current_gl_program; + + CoglBool current_gl_dither_enabled; + CoglColorMask current_gl_color_mask; + GLenum current_gl_draw_buffer; + + /* Clipping */ + /* TRUE if we have a valid clipping stack flushed. In that case + current_clip_stack will describe what the current state is. If + this is FALSE then the current clip stack is completely unknown + so it will need to be reflushed. In that case current_clip_stack + doesn't need to be a valid pointer. We can't just use NULL in + current_clip_stack to mark a dirty state because NULL is a valid + stack (meaning no clipping) */ + CoglBool current_clip_stack_valid; + /* The clip state that was flushed. This isn't intended to be used + as a stack to push and pop new entries. Instead the current stack + that the user wants is part of the framebuffer state. This is + just used to record the flush state so we can avoid flushing the + same state multiple times. When the clip state is flushed this + will hold a reference */ + CoglClipStack *current_clip_stack; + /* Whether the stencil buffer was used as part of the current clip + state. If TRUE then any further use of the stencil buffer (such + as for drawing paths) would need to be merged with the existing + stencil buffer */ + CoglBool current_clip_stack_uses_stencil; + + /* This is used as a temporary buffer to fill a CoglBuffer when + cogl_buffer_map fails and we only want to map to fill it with new + data */ + GByteArray *buffer_map_fallback_array; + CoglBool buffer_map_fallback_in_use; + size_t buffer_map_fallback_offset; + + CoglWinsysRectangleState rectangle_state; + + CoglSamplerCache *sampler_cache; + + /* FIXME: remove these when we remove the last xlib based clutter + * backend. they should be tracked as part of the renderer but e.g. + * the eglx backend doesn't yet have a corresponding Cogl winsys + * and so we wont have a renderer in that case. */ +#ifdef COGL_HAS_XLIB_SUPPORT + int damage_base; + /* List of callback functions that will be given every Xlib event */ + GSList *event_filters; + /* Current top of the XError trap state stack. The actual memory for + these is expected to be allocated on the stack by the caller */ + CoglXlibTrapState *trap_state; +#endif + + unsigned long winsys_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_WINSYS_FEATURE_N_FEATURES)]; + void *winsys; + + /* Array of names of uniforms. These are used like quarks to give a + unique number to each uniform name except that we ensure that + they increase sequentially so that we can use the id as an index + into a bitfield representing the uniforms that a pipeline + overrides from its parent. */ + GPtrArray *uniform_names; + /* A hash table to quickly get an index given an existing name. The + name strings are owned by the uniform_names array. The values are + the uniform location cast to a pointer. */ + GHashTable *uniform_name_hash; + int n_uniform_names; + + CoglPollSource *fences_poll_source; + CoglList fences; + + /* This defines a list of function pointers that Cogl uses from + either GL or GLES. All functions are accessed indirectly through + these pointers rather than linking to them directly */ +#ifndef APIENTRY +#define APIENTRY +#endif + +#define COGL_EXT_BEGIN(name, \ + min_gl_major, min_gl_minor, \ + gles_availability, \ + extension_suffixes, extension_names) +#define COGL_EXT_FUNCTION(ret, name, args) \ + ret (APIENTRY * name) args; +#define COGL_EXT_END() + +#include "gl-prototypes/cogl-all-functions.h" + +#undef COGL_EXT_BEGIN +#undef COGL_EXT_FUNCTION +#undef COGL_EXT_END +}; + +CoglContext * +_cogl_context_get_default (); + +const CoglWinsysVtable * +_cogl_context_get_winsys (CoglContext *context); + +/* Query the GL extensions and lookup the corresponding function + * pointers. Theoretically the list of extensions can change for + * different GL contexts so it is the winsys backend's responsiblity + * to know when to re-query the GL extensions. The backend should also + * check whether the GL context is supported by Cogl. If not it should + * return FALSE and set @error */ +CoglBool +_cogl_context_update_features (CoglContext *context, + CoglError **error); + +/* Obtains the context and returns retval if NULL */ +#define _COGL_GET_CONTEXT(ctxvar, retval) \ +CoglContext *ctxvar = _cogl_context_get_default (); \ +if (ctxvar == NULL) return retval; + +#define NO_RETVAL + +void +_cogl_context_set_current_projection_entry (CoglContext *context, + CoglMatrixEntry *entry); + +void +_cogl_context_set_current_modelview_entry (CoglContext *context, + CoglMatrixEntry *entry); + +/* + * _cogl_context_get_gl_extensions: + * @context: A CoglContext + * + * Return value: a NULL-terminated array of strings representing the + * supported extensions by the current driver. This array is owned + * by the caller and should be freed with g_strfreev(). + */ +char ** +_cogl_context_get_gl_extensions (CoglContext *context); + +const char * +_cogl_context_get_gl_version (CoglContext *context); + +#endif /* __COGL_CONTEXT_PRIVATE_H */ diff --git a/cogl/cogl/cogl-context.c b/cogl/cogl/cogl-context.c new file mode 100644 index 000000000..a7eed29ab --- /dev/null +++ b/cogl/cogl/cogl-context.c @@ -0,0 +1,786 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-object.h" +#include "cogl-private.h" +#include "cogl-winsys-private.h" +#include "winsys/cogl-winsys-stub-private.h" +#include "cogl-profile.h" +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-display-private.h" +#include "cogl-renderer-private.h" +#include "cogl-journal-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-3d-private.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-attribute-private.h" +#include "cogl1-context.h" +#include "cogl-gpu-info-private.h" +#include "cogl-config-private.h" +#include "cogl-error-private.h" +#include "cogl-gtype-private.h" + +#include "cogl/deprecated/cogl-framebuffer-deprecated.h" + +#include +#include + +#ifdef HAVE_COGL_GL +#include "cogl-pipeline-fragend-arbfp-private.h" +#endif + +/* These aren't defined in the GLES headers */ +#ifndef GL_POINT_SPRITE +#define GL_POINT_SPRITE 0x8861 +#endif + +#ifndef GL_NUM_EXTENSIONS +#define GL_NUM_EXTENSIONS 0x821D +#endif + +static void _cogl_context_free (CoglContext *context); + +COGL_OBJECT_DEFINE (Context, context); +COGL_GTYPE_DEFINE_CLASS (Context, context); + +extern void +_cogl_create_context_driver (CoglContext *context); + +static CoglContext *_cogl_context = NULL; + +static void +_cogl_init_feature_overrides (CoglContext *ctx) +{ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_VBOS))) + COGL_FLAGS_SET (ctx->private_features, COGL_PRIVATE_FEATURE_VBOS, FALSE); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PBOS))) + COGL_FLAGS_SET (ctx->private_features, COGL_PRIVATE_FEATURE_PBOS, FALSE); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ARBFP))) + { + ctx->feature_flags &= ~COGL_FEATURE_SHADERS_ARBFP; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_ARBFP, FALSE); + } + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_GLSL))) + { + ctx->feature_flags &= ~COGL_FEATURE_SHADERS_GLSL; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, FALSE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + FALSE); + } + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_NPOT_TEXTURES))) + { + ctx->feature_flags &= ~(COGL_FEATURE_TEXTURE_NPOT | + COGL_FEATURE_TEXTURE_NPOT_BASIC | + COGL_FEATURE_TEXTURE_NPOT_MIPMAP | + COGL_FEATURE_TEXTURE_NPOT_REPEAT); + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT, FALSE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, FALSE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, FALSE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, FALSE); + } +} + +const CoglWinsysVtable * +_cogl_context_get_winsys (CoglContext *context) +{ + return context->display->renderer->winsys_vtable; +} + +/* For reference: There was some deliberation over whether to have a + * constructor that could throw an exception but looking at standard + * practices with several high level OO languages including python, C++, + * C# Java and Ruby they all support exceptions in constructors and the + * general consensus appears to be that throwing an exception is neater + * than successfully constructing with an internal error status that + * would then have to be explicitly checked via some form of ::is_ok() + * method. + */ +CoglContext * +cogl_context_new (CoglDisplay *display, + CoglError **error) +{ + CoglContext *context; + uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff }; + CoglBitmap *white_pixel_bitmap; + const CoglWinsysVtable *winsys; + int i; + CoglError *internal_error = NULL; + + _cogl_init (); + +#ifdef COGL_ENABLE_PROFILE + /* We need to be absolutely sure that uprof has been initialized + * before calling _cogl_uprof_init. uprof_init (NULL, NULL) + * will be a NOP if it has been initialized but it will also + * mean subsequent parsing of the UProf GOptionGroup will have no + * affect. + * + * Sadly GOptionGroup based library initialization is extremely + * fragile by design because GOptionGroups have no notion of + * dependencies and so the order things are initialized isn't + * currently under tight control. + */ + uprof_init (NULL, NULL); + _cogl_uprof_init (); +#endif + + /* Allocate context memory */ + context = g_malloc0 (sizeof (CoglContext)); + + /* Convert the context into an object immediately in case any of the + code below wants to verify that the context pointer is a valid + object */ + _cogl_context_object_new (context); + + /* XXX: Gross hack! + * Currently everything in Cogl just assumes there is a default + * context which it can access via _COGL_GET_CONTEXT() including + * code used to construct a CoglContext. Until all of that code + * has been updated to take an explicit context argument we have + * to immediately make our pointer the default context. + */ + _cogl_context = context; + + /* Init default values */ + memset (context->features, 0, sizeof (context->features)); + context->feature_flags = 0; + memset (context->private_features, 0, sizeof (context->private_features)); + + context->rectangle_state = COGL_WINSYS_RECTANGLE_STATE_UNKNOWN; + + memset (context->winsys_features, 0, sizeof (context->winsys_features)); + + if (!display) + { + CoglRenderer *renderer = cogl_renderer_new (); + if (!cogl_renderer_connect (renderer, error)) + { + g_free (context); + return NULL; + } + + display = cogl_display_new (renderer, NULL); + cogl_object_unref(renderer); + } + else + cogl_object_ref (display); + + if (!cogl_display_setup (display, error)) + { + cogl_object_unref (display); + g_free (context); + return NULL; + } + + context->display = display; + + /* This is duplicated data, but it's much more convenient to have + the driver attached to the context and the value is accessed a + lot throughout Cogl */ + context->driver = display->renderer->driver; + + /* Again this is duplicated data, but it convenient to be able + * access these from the context. */ + context->driver_vtable = display->renderer->driver_vtable; + context->texture_driver = display->renderer->texture_driver; + + for (i = 0; i < G_N_ELEMENTS (context->private_features); i++) + context->private_features[i] |= display->renderer->private_features[i]; + + winsys = _cogl_context_get_winsys (context); + if (!winsys->context_init (context, error)) + { + cogl_object_unref (display); + g_free (context); + return NULL; + } + + context->attribute_name_states_hash = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + context->attribute_name_index_map = NULL; + context->n_attribute_names = 0; + + /* The "cogl_color_in" attribute needs a deterministic name_index + * so we make sure it's the first attribute name we register */ + _cogl_attribute_register_attribute_name (context, "cogl_color_in"); + + + context->uniform_names = + g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + context->uniform_name_hash = g_hash_table_new (g_str_hash, g_str_equal); + context->n_uniform_names = 0; + + /* Initialise the driver specific state */ + _cogl_init_feature_overrides (context); + + /* XXX: ONGOING BUG: Intel viewport scissor + * + * Intel gen6 drivers don't currently correctly handle offset + * viewports, since primitives aren't clipped within the bounds of + * the viewport. To workaround this we push our own clip for the + * viewport that will use scissoring to ensure we clip as expected. + * + * TODO: file a bug upstream! + */ + if (context->gpu.driver_package == COGL_GPU_INFO_DRIVER_PACKAGE_MESA && + context->gpu.architecture == COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE && + !getenv ("COGL_DISABLE_INTEL_VIEWPORT_SCISSORT_WORKAROUND")) + context->needs_viewport_scissor_workaround = TRUE; + else + context->needs_viewport_scissor_workaround = FALSE; + + context->sampler_cache = _cogl_sampler_cache_new (context); + + _cogl_pipeline_init_default_pipeline (); + _cogl_pipeline_init_default_layers (); + _cogl_pipeline_init_state_hash_functions (); + _cogl_pipeline_init_layer_state_hash_functions (); + + context->current_clip_stack_valid = FALSE; + context->current_clip_stack = NULL; + + context->legacy_backface_culling_enabled = FALSE; + + cogl_matrix_init_identity (&context->identity_matrix); + cogl_matrix_init_identity (&context->y_flip_matrix); + cogl_matrix_scale (&context->y_flip_matrix, 1, -1, 1); + + context->flushed_matrix_mode = COGL_MATRIX_MODELVIEW; + + context->texture_units = + g_array_new (FALSE, FALSE, sizeof (CoglTextureUnit)); + + if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_ANY_GL)) + { + /* See cogl-pipeline.c for more details about why we leave texture unit 1 + * active by default... */ + context->active_texture_unit = 1; + GE (context, glActiveTexture (GL_TEXTURE1)); + } + + context->legacy_fog_state.enabled = FALSE; + + context->opaque_color_pipeline = cogl_pipeline_new (context); + context->blended_color_pipeline = cogl_pipeline_new (context); + context->texture_pipeline = cogl_pipeline_new (context); + context->codegen_header_buffer = g_string_new (""); + context->codegen_source_buffer = g_string_new (""); + context->codegen_boilerplate_buffer = g_string_new (""); + context->source_stack = NULL; + + context->legacy_state_set = 0; + + context->default_gl_texture_2d_tex = NULL; + context->default_gl_texture_3d_tex = NULL; + context->default_gl_texture_rect_tex = NULL; + + context->framebuffers = NULL; + context->current_draw_buffer = NULL; + context->current_read_buffer = NULL; + context->current_draw_buffer_state_flushed = 0; + context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL; + + context->swap_callback_closures = + g_hash_table_new (g_direct_hash, g_direct_equal); + + _cogl_list_init (&context->onscreen_events_queue); + _cogl_list_init (&context->onscreen_dirty_queue); + + g_queue_init (&context->gles2_context_stack); + + context->journal_flush_attributes_array = + g_array_new (TRUE, FALSE, sizeof (CoglAttribute *)); + context->journal_clip_bounds = NULL; + + context->polygon_vertices = g_array_new (FALSE, FALSE, sizeof (float)); + + context->current_pipeline = NULL; + context->current_pipeline_changes_since_flush = 0; + context->current_pipeline_with_color_attrib = FALSE; + + _cogl_bitmask_init (&context->enabled_builtin_attributes); + _cogl_bitmask_init (&context->enable_builtin_attributes_tmp); + _cogl_bitmask_init (&context->enabled_texcoord_attributes); + _cogl_bitmask_init (&context->enable_texcoord_attributes_tmp); + _cogl_bitmask_init (&context->enabled_custom_attributes); + _cogl_bitmask_init (&context->enable_custom_attributes_tmp); + _cogl_bitmask_init (&context->changed_bits_tmp); + + context->max_texture_units = -1; + context->max_activateable_texture_units = -1; + + context->current_fragment_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED; + context->current_vertex_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED; + context->current_gl_program = 0; + + context->current_gl_dither_enabled = TRUE; + context->current_gl_color_mask = COGL_COLOR_MASK_ALL; + + context->gl_blend_enable_cache = FALSE; + + context->depth_test_enabled_cache = FALSE; + context->depth_test_function_cache = COGL_DEPTH_TEST_FUNCTION_LESS; + context->depth_writing_enabled_cache = TRUE; + context->depth_range_near_cache = 0; + context->depth_range_far_cache = 1; + + context->legacy_depth_test_enabled = FALSE; + + context->pipeline_cache = _cogl_pipeline_cache_new (); + + for (i = 0; i < COGL_BUFFER_BIND_TARGET_COUNT; i++) + context->current_buffer[i] = NULL; + + context->window_buffer = NULL; + context->framebuffer_stack = _cogl_create_framebuffer_stack (); + + /* XXX: In this case the Clutter backend is still responsible for + * the OpenGL binding API and for creating onscreen framebuffers and + * so we have to add a dummy framebuffer to represent the backend + * owned window... */ + if (_cogl_context_get_winsys (context) == _cogl_winsys_stub_get_vtable ()) + { + CoglOnscreen *window = _cogl_onscreen_new (); + cogl_set_framebuffer (COGL_FRAMEBUFFER (window)); + cogl_object_unref (COGL_FRAMEBUFFER (window)); + } + + context->current_path = NULL; + context->stencil_pipeline = cogl_pipeline_new (context); + + context->in_begin_gl_block = FALSE; + + context->quad_buffer_indices_byte = NULL; + context->quad_buffer_indices = NULL; + context->quad_buffer_indices_len = 0; + + context->rectangle_byte_indices = NULL; + context->rectangle_short_indices = NULL; + context->rectangle_short_indices_len = 0; + + context->texture_download_pipeline = NULL; + context->blit_texture_pipeline = NULL; + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_ALPHA_TEST)) + /* The default for GL_ALPHA_TEST is to always pass which is equivalent to + * the test being disabled therefore we assume that for all drivers there + * will be no performance impact if we always leave the test enabled which + * makes things a bit simpler for us. Under GLES2 the alpha test is + * implemented in the fragment shader so there is no enable for it + */ + GE (context, glEnable (GL_ALPHA_TEST)); +#endif + +#if defined (HAVE_COGL_GL) + if ((context->driver == COGL_DRIVER_GL3)) + { + GLuint vertex_array; + + /* In a forward compatible context, GL 3 doesn't support rendering + * using the default vertex array object. Cogl doesn't use vertex + * array objects yet so for now we just create a dummy array + * object that we will use as our own default object. Eventually + * it could be good to attach the vertex array objects to + * CoglPrimitives */ + context->glGenVertexArrays (1, &vertex_array); + context->glBindVertexArray (vertex_array); + } +#endif + + context->current_modelview_entry = NULL; + context->current_projection_entry = NULL; + _cogl_matrix_entry_identity_init (&context->identity_entry); + _cogl_matrix_entry_cache_init (&context->builtin_flushed_projection); + _cogl_matrix_entry_cache_init (&context->builtin_flushed_modelview); + + /* Create default textures used for fall backs */ + context->default_gl_texture_2d_tex = + cogl_texture_2d_new_from_data (context, + 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 0, /* rowstride */ + white_pixel, + NULL); /* abort on error */ + + /* If 3D or rectangle textures aren't supported then these will + * return errors that we can simply ignore. */ + internal_error = NULL; + context->default_gl_texture_3d_tex = + cogl_texture_3d_new_from_data (context, + 1, 1, 1, /* width, height, depth */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 0, /* rowstride */ + 0, /* image stride */ + white_pixel, + &internal_error); + if (internal_error) + cogl_error_free (internal_error); + + /* TODO: add cogl_texture_rectangle_new_from_data() */ + white_pixel_bitmap = + cogl_bitmap_new_for_data (context, + 1, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + white_pixel); + + internal_error = NULL; + context->default_gl_texture_rect_tex = + cogl_texture_rectangle_new_from_bitmap (white_pixel_bitmap); + + /* XXX: we need to allocate the texture now because the white_pixel + * data is on the stack */ + cogl_texture_allocate (COGL_TEXTURE (context->default_gl_texture_rect_tex), + &internal_error); + if (internal_error) + cogl_error_free (internal_error); + + cogl_object_unref (white_pixel_bitmap); + + cogl_push_source (context->opaque_color_pipeline); + + context->atlases = NULL; + g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook)); + + context->buffer_map_fallback_array = g_byte_array_new (); + context->buffer_map_fallback_in_use = FALSE; + + /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect + unless GL_COORD_REPLACE is enabled for an individual layer. + Therefore it seems like it should be ok to just leave it enabled + all the time instead of having to have a set property on each + pipeline to track whether any layers have point sprite coords + enabled. We don't need to do this for GL3 or GLES2 because point + sprites are handled using a builtin varying in the shader. */ + if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_GL_FIXED) && + cogl_has_feature (context, COGL_FEATURE_ID_POINT_SPRITE)) + GE (context, glEnable (GL_POINT_SPRITE)); + + _cogl_list_init (&context->fences); + + return context; +} + +static void +_cogl_context_free (CoglContext *context) +{ + const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); + + winsys->context_deinit (context); + + _cogl_free_framebuffer_stack (context->framebuffer_stack); + + if (context->current_path) + cogl_handle_unref (context->current_path); + + if (context->default_gl_texture_2d_tex) + cogl_object_unref (context->default_gl_texture_2d_tex); + if (context->default_gl_texture_3d_tex) + cogl_object_unref (context->default_gl_texture_3d_tex); + if (context->default_gl_texture_rect_tex) + cogl_object_unref (context->default_gl_texture_rect_tex); + + if (context->opaque_color_pipeline) + cogl_object_unref (context->opaque_color_pipeline); + if (context->blended_color_pipeline) + cogl_object_unref (context->blended_color_pipeline); + if (context->texture_pipeline) + cogl_object_unref (context->texture_pipeline); + + if (context->blit_texture_pipeline) + cogl_object_unref (context->blit_texture_pipeline); + + if (context->swap_callback_closures) + g_hash_table_destroy (context->swap_callback_closures); + + g_warn_if_fail (context->gles2_context_stack.length == 0); + + if (context->journal_flush_attributes_array) + g_array_free (context->journal_flush_attributes_array, TRUE); + if (context->journal_clip_bounds) + g_array_free (context->journal_clip_bounds, TRUE); + + if (context->polygon_vertices) + g_array_free (context->polygon_vertices, TRUE); + + if (context->quad_buffer_indices_byte) + cogl_object_unref (context->quad_buffer_indices_byte); + if (context->quad_buffer_indices) + cogl_object_unref (context->quad_buffer_indices); + + if (context->rectangle_byte_indices) + cogl_object_unref (context->rectangle_byte_indices); + if (context->rectangle_short_indices) + cogl_object_unref (context->rectangle_short_indices); + + if (context->default_pipeline) + cogl_object_unref (context->default_pipeline); + + if (context->dummy_layer_dependant) + cogl_object_unref (context->dummy_layer_dependant); + if (context->default_layer_n) + cogl_object_unref (context->default_layer_n); + if (context->default_layer_0) + cogl_object_unref (context->default_layer_0); + + if (context->current_clip_stack_valid) + _cogl_clip_stack_unref (context->current_clip_stack); + + g_slist_free (context->atlases); + g_hook_list_clear (&context->atlas_reorganize_callbacks); + + _cogl_bitmask_destroy (&context->enabled_builtin_attributes); + _cogl_bitmask_destroy (&context->enable_builtin_attributes_tmp); + _cogl_bitmask_destroy (&context->enabled_texcoord_attributes); + _cogl_bitmask_destroy (&context->enable_texcoord_attributes_tmp); + _cogl_bitmask_destroy (&context->enabled_custom_attributes); + _cogl_bitmask_destroy (&context->enable_custom_attributes_tmp); + _cogl_bitmask_destroy (&context->changed_bits_tmp); + + if (context->current_modelview_entry) + cogl_matrix_entry_unref (context->current_modelview_entry); + if (context->current_projection_entry) + cogl_matrix_entry_unref (context->current_projection_entry); + _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_projection); + _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_modelview); + + _cogl_pipeline_cache_free (context->pipeline_cache); + + _cogl_sampler_cache_free (context->sampler_cache); + + _cogl_destroy_texture_units (); + + g_ptr_array_free (context->uniform_names, TRUE); + g_hash_table_destroy (context->uniform_name_hash); + + g_hash_table_destroy (context->attribute_name_states_hash); + g_array_free (context->attribute_name_index_map, TRUE); + + g_byte_array_free (context->buffer_map_fallback_array, TRUE); + + cogl_object_unref (context->display); + + g_free (context); +} + +CoglContext * +_cogl_context_get_default (void) +{ + CoglError *error = NULL; + /* Create if doesn't exist yet */ + if (_cogl_context == NULL) + { + _cogl_context = cogl_context_new (NULL, &error); + if (!_cogl_context) + { + g_warning ("Failed to create default context: %s", + error->message); + cogl_error_free (error); + } + } + + return _cogl_context; +} + +CoglDisplay * +cogl_context_get_display (CoglContext *context) +{ + return context->display; +} + +CoglRenderer * +cogl_context_get_renderer (CoglContext *context) +{ + return context->display->renderer; +} + +CoglBool +_cogl_context_update_features (CoglContext *context, + CoglError **error) +{ + return context->driver_vtable->update_features (context, error); +} + +void +_cogl_context_set_current_projection_entry (CoglContext *context, + CoglMatrixEntry *entry) +{ + cogl_matrix_entry_ref (entry); + if (context->current_projection_entry) + cogl_matrix_entry_unref (context->current_projection_entry); + context->current_projection_entry = entry; +} + +void +_cogl_context_set_current_modelview_entry (CoglContext *context, + CoglMatrixEntry *entry) +{ + cogl_matrix_entry_ref (entry); + if (context->current_modelview_entry) + cogl_matrix_entry_unref (context->current_modelview_entry); + context->current_modelview_entry = entry; +} + +char ** +_cogl_context_get_gl_extensions (CoglContext *context) +{ + const char *env_disabled_extensions; + char **ret; + + /* In GL 3, querying GL_EXTENSIONS is deprecated so we have to build + * the array using glGetStringi instead */ +#ifdef HAVE_COGL_GL + if (context->driver == COGL_DRIVER_GL3) + { + int num_extensions, i; + + context->glGetIntegerv (GL_NUM_EXTENSIONS, &num_extensions); + + ret = g_malloc (sizeof (char *) * (num_extensions + 1)); + + for (i = 0; i < num_extensions; i++) + { + const char *ext = + (const char *) context->glGetStringi (GL_EXTENSIONS, i); + ret[i] = g_strdup (ext); + } + + ret[num_extensions] = NULL; + } + else +#endif + { + const char *all_extensions = + (const char *) context->glGetString (GL_EXTENSIONS); + + ret = g_strsplit (all_extensions, " ", 0 /* max tokens */); + } + + if ((env_disabled_extensions = g_getenv ("COGL_DISABLE_GL_EXTENSIONS")) + || _cogl_config_disable_gl_extensions) + { + char **split_env_disabled_extensions; + char **split_conf_disabled_extensions; + char **src, **dst; + + if (env_disabled_extensions) + split_env_disabled_extensions = + g_strsplit (env_disabled_extensions, + ",", + 0 /* no max tokens */); + else + split_env_disabled_extensions = NULL; + + if (_cogl_config_disable_gl_extensions) + split_conf_disabled_extensions = + g_strsplit (_cogl_config_disable_gl_extensions, + ",", + 0 /* no max tokens */); + else + split_conf_disabled_extensions = NULL; + + for (dst = ret, src = ret; + *src; + src++) + { + char **d; + + if (split_env_disabled_extensions) + for (d = split_env_disabled_extensions; *d; d++) + if (!strcmp (*src, *d)) + goto disabled; + if (split_conf_disabled_extensions) + for (d = split_conf_disabled_extensions; *d; d++) + if (!strcmp (*src, *d)) + goto disabled; + + *(dst++) = *src; + continue; + + disabled: + g_free (*src); + continue; + } + + *dst = NULL; + + if (split_env_disabled_extensions) + g_strfreev (split_env_disabled_extensions); + if (split_conf_disabled_extensions) + g_strfreev (split_conf_disabled_extensions); + } + + return ret; +} + +const char * +_cogl_context_get_gl_version (CoglContext *context) +{ + const char *version_override; + + if ((version_override = g_getenv ("COGL_OVERRIDE_GL_VERSION"))) + return version_override; + else if (_cogl_config_override_gl_version) + return _cogl_config_override_gl_version; + else + return (const char *) context->glGetString (GL_VERSION); + +} + +int64_t +cogl_get_clock_time (CoglContext *context) +{ + const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); + + if (winsys->context_get_clock_time) + return winsys->context_get_clock_time (context); + else + return 0; +} diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h new file mode 100644 index 000000000..529481765 --- /dev/null +++ b/cogl/cogl/cogl-context.h @@ -0,0 +1,381 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_CONTEXT_H__ +#define __COGL_CONTEXT_H__ + +/* We forward declare the CoglContext type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglContext CoglContext; + +#include +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-context + * @short_description: The top level application context. + * + * A #CoglContext is the top most sandbox of Cogl state for an + * application or toolkit. Its main purpose is to act as a sandbox + * for the memory management of state objects. Normally an application + * will only create a single context since there is no way to share + * resources between contexts. + * + * For those familiar with OpenGL or perhaps Cairo it should be + * understood that unlike these APIs a Cogl context isn't a rendering + * context as such. In other words Cogl doesn't aim to provide a state + * machine style model for configuring rendering parameters. Most + * rendering state in Cogl is directly associated with user managed + * objects called pipelines and geometry is drawn with a specific + * pipeline object to a framebuffer object and those 3 things fully + * define the state for drawing. This is an important part of Cogl's + * design since it helps you write orthogonal rendering components + * that can all access the same GPU without having to worry about + * what state other components have left you with. + * + * Cogl does not maintain internal references to the context for + * resources that depend on the context so applications. This is to + * help applications control the lifetime a context without us needing to + * introduce special api to handle the breakup of internal circular + * references due to internal resources and caches associated with the + * context. + * + * One a context has been destroyed then all directly or indirectly + * dependant resources will be in an inconsistent state and should not + * be manipulated or queried in any way. + * + * For applications that rely on the operating system to clean up + * resources this policy shouldn't affect them, but for applications + * that need to carefully destroy and re-create Cogl contexts multiple + * times throughout their lifetime (such as Android applications) they + * should be careful to destroy all context dependant resources, such as + * framebuffers or textures etc before unrefing and destroying the + * context. + */ + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +#define COGL_CONTEXT(OBJECT) ((CoglContext *)OBJECT) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_context_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_context_get_gtype (void); +#endif + +/** + * cogl_context_new: (constructor) + * @display: (allow-none): A #CoglDisplay pointer + * @error: A CoglError return location. + * + * Creates a new #CoglContext which acts as an application sandbox + * for any state objects that are allocated. + * + * Return value: (transfer full): A newly allocated #CoglContext + * Since: 1.8 + * Stability: unstable + */ +CoglContext * +cogl_context_new (CoglDisplay *display, + CoglError **error); + +/** + * cogl_context_get_display: + * @context: A #CoglContext pointer + * + * Retrieves the #CoglDisplay that is internally associated with the + * given @context. This will return the same #CoglDisplay that was + * passed to cogl_context_new() or if %NULL was passed to + * cogl_context_new() then this function returns a pointer to the + * display that was automatically setup internally. + * + * Return value: (transfer none): The #CoglDisplay associated with the + * given @context. + * Since: 1.8 + * Stability: unstable + */ +CoglDisplay * +cogl_context_get_display (CoglContext *context); + +/** + * cogl_context_get_renderer: + * @context: A #CoglContext pointer + * + * Retrieves the #CoglRenderer that is internally associated with the + * given @context. This will return the same #CoglRenderer that was + * passed to cogl_display_new() or if %NULL was passed to + * cogl_display_new() or cogl_context_new() then this function returns + * a pointer to the renderer that was automatically connected + * internally. + * + * Return value: (transfer none): The #CoglRenderer associated with the + * given @context. + * Since: 1.16 + * Stability: unstable + */ +CoglRenderer * +cogl_context_get_renderer (CoglContext *context); + +/** + * cogl_is_context: + * @object: An object or %NULL + * + * Gets whether the given object references an existing context object. + * + * Return value: %TRUE if the @object references a #CoglContext, + * %FALSE otherwise + * + * Since: 1.10 + * Stability: Unstable + */ +CoglBool +cogl_is_context (void *object); + +#endif /* COGL_ENABLE_EXPERIMENTAL_2_0_API */ + +/* XXX: not guarded by the EXPERIMENTAL_API defines to avoid + * upsetting glib-mkenums, but this can still be considered implicitly + * experimental since it's only useable with experimental API... */ +/** + * CoglFeatureID: + * @COGL_FEATURE_ID_TEXTURE_NPOT_BASIC: The hardware supports non power + * of two textures, but you also need to check the + * %COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP and %COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT + * features to know if the hardware supports npot texture mipmaps + * or repeat modes other than + * %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE respectively. + * @COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP: Mipmapping is supported in + * conjuntion with non power of two textures. + * @COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT: Repeat modes other than + * %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE are supported by the + * hardware. + * @COGL_FEATURE_ID_TEXTURE_NPOT: Non power of two textures are supported + * by the hardware. This is a equivalent to the + * %COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, %COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP + * and %COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT features combined. + * @COGL_FEATURE_ID_TEXTURE_RECTANGLE: Support for rectangular + * textures with non-normalized texture coordinates. + * @COGL_FEATURE_ID_TEXTURE_RG: Support for + * %COGL_TEXTURE_COMPONENTS_RG as the internal components of a + * texture. + * @COGL_FEATURE_ID_TEXTURE_3D: 3D texture support + * @COGL_FEATURE_ID_OFFSCREEN: Offscreen rendering support + * @COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE: Multisample support for + * offscreen framebuffers + * @COGL_FEATURE_ID_ONSCREEN_MULTIPLE: Multiple onscreen framebuffers + * supported. + * @COGL_FEATURE_ID_GLSL: GLSL support + * @COGL_FEATURE_ID_ARBFP: ARBFP support + * @COGL_FEATURE_ID_UNSIGNED_INT_INDICES: Set if + * %COGL_INDICES_TYPE_UNSIGNED_INT is supported in + * cogl_indices_new(). + * @COGL_FEATURE_ID_DEPTH_RANGE: cogl_pipeline_set_depth_range() support + * @COGL_FEATURE_ID_POINT_SPRITE: Whether + * cogl_pipeline_set_layer_point_sprite_coords_enabled() is supported. + * @COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE: Whether cogl_point_size_in + * can be used as an attribute to set a per-vertex point size. + * @COGL_FEATURE_ID_MAP_BUFFER_FOR_READ: Whether cogl_buffer_map() is + * supported with CoglBufferAccess including read support. + * @COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE: Whether cogl_buffer_map() is + * supported with CoglBufferAccess including write support. + * @COGL_FEATURE_ID_MIRRORED_REPEAT: Whether + * %COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT is supported. + * @COGL_FEATURE_ID_SWAP_BUFFERS_EVENT: + * Available if the window system supports reporting an event + * for swap buffer completions. + * @COGL_FEATURE_ID_BUFFER_AGE: Available if the age of #CoglOnscreen back + * buffers are tracked and so cogl_onscreen_get_buffer_age() can be + * expected to return age values other than 0. + * @COGL_FEATURE_ID_GLES2_CONTEXT: Whether creating new GLES2 contexts is + * suported. + * @COGL_FEATURE_ID_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering + * the depth buffer to a texture. + * @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation + * time stamps will be recorded in #CoglFrameInfo objects. + * + * All the capabilities that can vary between different GPUs supported + * by Cogl. Applications that depend on any of these features should explicitly + * check for them using cogl_has_feature() or cogl_has_features(). + * + * Since: 1.10 + */ +typedef enum _CoglFeatureID +{ + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC = 1, + COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, + COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, + COGL_FEATURE_ID_TEXTURE_NPOT, + COGL_FEATURE_ID_TEXTURE_RECTANGLE, + COGL_FEATURE_ID_TEXTURE_3D, + COGL_FEATURE_ID_GLSL, + COGL_FEATURE_ID_ARBFP, + COGL_FEATURE_ID_OFFSCREEN, + COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE, + COGL_FEATURE_ID_ONSCREEN_MULTIPLE, + COGL_FEATURE_ID_UNSIGNED_INT_INDICES, + COGL_FEATURE_ID_DEPTH_RANGE, + COGL_FEATURE_ID_POINT_SPRITE, + COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, + COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, + COGL_FEATURE_ID_MIRRORED_REPEAT, + COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, + COGL_FEATURE_ID_GLES2_CONTEXT, + COGL_FEATURE_ID_DEPTH_TEXTURE, + COGL_FEATURE_ID_PRESENTATION_TIME, + COGL_FEATURE_ID_FENCE, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + COGL_FEATURE_ID_TEXTURE_RG, + COGL_FEATURE_ID_BUFFER_AGE, + + /*< private >*/ + _COGL_N_FEATURE_IDS /*< skip >*/ +} CoglFeatureID; + + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * cogl_has_feature: + * @context: A #CoglContext pointer + * @feature: A #CoglFeatureID + * + * Checks if a given @feature is currently available + * + * Cogl does not aim to be a lowest common denominator API, it aims to + * expose all the interesting features of GPUs to application which + * means applications have some responsibility to explicitly check + * that certain features are available before depending on them. + * + * Returns: %TRUE if the @feature is currently supported or %FALSE if + * not. + * + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_has_feature (CoglContext *context, CoglFeatureID feature); + +/** + * cogl_has_features: + * @context: A #CoglContext pointer + * @...: A 0 terminated list of CoglFeatureIDs + * + * Checks if a list of features are all currently available. + * + * This checks all of the listed features using cogl_has_feature() and + * returns %TRUE if all the features are available or %FALSE + * otherwise. + * + * Return value: %TRUE if all the features are available, %FALSE + * otherwise. + * + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_has_features (CoglContext *context, ...); + +/** + * CoglFeatureCallback: + * @feature: A single feature currently supported by Cogl + * @user_data: A private pointer passed to cogl_foreach_feature(). + * + * A callback used with cogl_foreach_feature() for enumerating all + * context level features supported by Cogl. + * + * Since: 0.10 + * Stability: unstable + */ +typedef void (*CoglFeatureCallback) (CoglFeatureID feature, void *user_data); + +/** + * cogl_foreach_feature: + * @context: A #CoglContext pointer + * @callback: (scope call): A #CoglFeatureCallback called for each + * supported feature + * @user_data: (closure): Private data to pass to the callback + * + * Iterates through all the context level features currently supported + * for a given @context and for each feature @callback is called. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_foreach_feature (CoglContext *context, + CoglFeatureCallback callback, + void *user_data); + +/** + * cogl_get_clock_time: + * @context: a #CoglContext pointer + * + * Returns the current time value from Cogl's internal clock. This + * clock is used for measuring times such as the presentation time + * in a #CoglFrameInfo. + * + * This method is meant for converting timestamps retrieved from Cogl + * to other time systems, and is not meant to be used as a standalone + * timing system. For that reason, if this function is called without + * having retrieved a valid (non-zero) timestamp from Cogl first, it + * may return 0 to indicate that Cogl has no active internal clock. + * + * Return value: the time value for the Cogl clock, in nanoseconds + * from an arbitrary point in time, or 0 if Cogl doesn't have an + * active internal clock. + * Since: 1.14 + * Stability: unstable + */ +int64_t +cogl_get_clock_time (CoglContext *context); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +#endif /* __COGL_CONTEXT_H__ */ + diff --git a/cogl/cogl/cogl-debug-options.h b/cogl/cogl/cogl-debug-options.h new file mode 100644 index 000000000..0f3b30731 --- /dev/null +++ b/cogl/cogl/cogl-debug-options.h @@ -0,0 +1,199 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +OPT (OBJECT, + N_("Cogl Tracing"), + "ref-counts", + N_("CoglObject references"), + N_("Debug ref counting issues for CoglObjects")) +OPT (SLICING, + N_("Cogl Tracing"), + "slicing", + N_("Trace Texture Slicing"), + N_("debug the creation of texture slices")) +OPT (ATLAS, + N_("Cogl Tracing"), + "atlas", + N_("Trace Atlas Textures"), + N_("Debug texture atlas management")) +OPT (BLEND_STRINGS, + N_("Cogl Tracing"), + "blend-strings", + N_("Trace Blend Strings"), + N_("Debug CoglBlendString parsing")) +OPT (JOURNAL, + N_("Cogl Tracing"), + "journal", + N_("Trace Journal"), + N_("View all the geometry passing through the journal")) +OPT (BATCHING, + N_("Cogl Tracing"), + "batching", + N_("Trace Batching"), + N_("Show how geometry is being batched in the journal")) +OPT (MATRICES, + N_("Cogl Tracing"), + "matrices", + N_("Trace matrices"), + N_("Trace all matrix manipulation")) +/* XXX we should replace the "draw" option its very hand wavy... */ +OPT (DRAW, + N_("Cogl Tracing"), + "draw", + N_("Trace Misc Drawing"), + N_("Trace some misc drawing operations")) +OPT (PANGO, + N_("Cogl Tracing"), + "pango", + N_("Trace Pango Renderer"), + N_("Trace the Cogl Pango renderer")) +OPT (TEXTURE_PIXMAP, + N_("Cogl Tracing"), + "texture-pixmap", + N_("Trace CoglTexturePixmap backend"), + N_("Trace the Cogl texture pixmap backend")) +OPT (RECTANGLES, + N_("Visualize"), + "rectangles", + N_("Outline rectangles"), + N_("Add wire outlines for all rectangular geometry")) +OPT (WIREFRAME, + N_("Visualize"), + "wireframe", + N_("Show wireframes"), + N_("Add wire outlines for all geometry")) +OPT (DISABLE_BATCHING, + N_("Root Cause"), + "disable-batching", + N_("Disable Journal batching"), + N_("Disable batching of geometry in the Cogl Journal.")) +OPT (DISABLE_VBOS, + N_("Root Cause"), + "disable-vbos", + N_("Disable GL Vertex Buffers"), + N_("Disable use of OpenGL vertex buffer objects")) +OPT (DISABLE_PBOS, + N_("Root Cause"), + "disable-pbos", + N_("Disable GL Pixel Buffers"), + N_("Disable use of OpenGL pixel buffer objects")) +OPT (DISABLE_SOFTWARE_TRANSFORM, + N_("Root Cause"), + "disable-software-transform", + N_("Disable software rect transform"), + N_("Use the GPU to transform rectangular geometry")) +OPT (DUMP_ATLAS_IMAGE, + N_("Cogl Specialist"), + "dump-atlas-image", + N_("Dump atlas images"), + N_("Dump texture atlas changes to an image file")) +OPT (DISABLE_ATLAS, + N_("Root Cause"), + "disable-atlas", + N_("Disable texture atlasing"), + N_("Disable use of texture atlasing")) +OPT (DISABLE_SHARED_ATLAS, + N_("Root Cause"), + "disable-shared-atlas", + N_("Disable sharing the texture atlas between text and images"), + N_("When this is set the glyph cache will always use a separate texture " + "for its atlas. Otherwise it will try to share the atlas with images.")) +OPT (DISABLE_TEXTURING, + N_("Root Cause"), + "disable-texturing", + N_("Disable texturing"), + N_("Disable texturing any primitives")) +OPT (DISABLE_ARBFP, + N_("Root Cause"), + "disable-arbfp", + N_("Disable arbfp"), + N_("Disable use of ARB fragment programs")) +OPT (DISABLE_FIXED, + N_("Root Cause"), + "disable-fixed", + N_("Disable fixed"), + N_("Disable use of the fixed function pipeline backend")) +OPT (DISABLE_GLSL, + N_("Root Cause"), + "disable-glsl", + N_("Disable GLSL"), + N_("Disable use of GLSL")) +OPT (DISABLE_BLENDING, + N_("Root Cause"), + "disable-blending", + N_("Disable blending"), + N_("Disable use of blending")) +OPT (DISABLE_NPOT_TEXTURES, + N_("Root Cause"), + "disable-npot-textures", + N_("Disable non-power-of-two textures"), + N_("Makes Cogl think that the GL driver doesn't support NPOT textures " + "so that it will create sliced textures or textures with waste instead.")) +OPT (DISABLE_SOFTWARE_CLIP, + N_("Root Cause"), + "disable-software-clip", + N_("Disable software clipping"), + N_("Disables Cogl's attempts to clip some rectangles in software.")) +OPT (SHOW_SOURCE, + N_("Cogl Tracing"), + "show-source", + N_("Show source"), + N_("Show generated ARBfp/GLSL source code")) +OPT (OPENGL, + N_("Cogl Tracing"), + "opengl", + N_("Trace some OpenGL"), + N_("Traces some select OpenGL calls")) +OPT (OFFSCREEN, + N_("Cogl Tracing"), + "offscreen", + N_("Trace offscreen support"), + N_("Debug offscreen support")) +OPT (DISABLE_BLENDING, + N_("Root Cause"), + "disable-program-caches", + N_("Disable program caches"), + N_("Disable fallback caches for arbfp and glsl programs")) +OPT (DISABLE_FAST_READ_PIXEL, + N_("Root Cause"), + "disable-fast-read-pixel", + N_("Disable read pixel optimization"), + N_("Disable optimization for reading 1px for simple " + "scenes of opaque rectangles")) +OPT (CLIPPING, + N_("Cogl Tracing"), + "clipping", + N_("Trace clipping"), + N_("Logs information about how Cogl is implementing clipping")) +OPT (PERFORMANCE, + N_("Cogl Tracing"), + "performance", + N_("Trace performance concerns"), + N_("Tries to highlight sub-optimal Cogl usage.")) diff --git a/cogl/cogl/cogl-debug.c b/cogl/cogl/cogl-debug.c new file mode 100644 index 000000000..345406c91 --- /dev/null +++ b/cogl/cogl/cogl-debug.c @@ -0,0 +1,302 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-i18n-private.h" +#include "cogl-private.h" +#include "cogl-debug.h" +#include "cogl1-context.h" + +/* XXX: If you add a debug option, please also add an option + * definition to cogl-debug-options.h. This will enable us - for + * example - to emit a "help" description for the option. + */ + +/* NB: Only these options get enabled if COGL_DEBUG=all is + * used since they don't affect the behaviour of Cogl they + * simply print out verbose information */ +static const GDebugKey cogl_log_debug_keys[] = { + { "object", COGL_DEBUG_OBJECT }, + { "slicing", COGL_DEBUG_SLICING }, + { "atlas", COGL_DEBUG_ATLAS }, + { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, + { "journal", COGL_DEBUG_JOURNAL }, + { "batching", COGL_DEBUG_BATCHING }, + { "matrices", COGL_DEBUG_MATRICES }, + { "draw", COGL_DEBUG_DRAW }, + { "opengl", COGL_DEBUG_OPENGL }, + { "pango", COGL_DEBUG_PANGO }, + { "show-source", COGL_DEBUG_SHOW_SOURCE}, + { "offscreen", COGL_DEBUG_OFFSCREEN }, + { "texture-pixmap", COGL_DEBUG_TEXTURE_PIXMAP }, + { "bitmap", COGL_DEBUG_BITMAP }, + { "clipping", COGL_DEBUG_CLIPPING }, + { "winsys", COGL_DEBUG_WINSYS }, + { "performance", COGL_DEBUG_PERFORMANCE } +}; +static const int n_cogl_log_debug_keys = + G_N_ELEMENTS (cogl_log_debug_keys); + +static const GDebugKey cogl_behavioural_debug_keys[] = { + { "rectangles", COGL_DEBUG_RECTANGLES }, + { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, + { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, + { "disable-pbos", COGL_DEBUG_DISABLE_PBOS }, + { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, + { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE }, + { "disable-atlas", COGL_DEBUG_DISABLE_ATLAS }, + { "disable-shared-atlas", COGL_DEBUG_DISABLE_SHARED_ATLAS }, + { "disable-texturing", COGL_DEBUG_DISABLE_TEXTURING}, + { "disable-arbfp", COGL_DEBUG_DISABLE_ARBFP}, + { "disable-fixed", COGL_DEBUG_DISABLE_FIXED}, + { "disable-glsl", COGL_DEBUG_DISABLE_GLSL}, + { "disable-blending", COGL_DEBUG_DISABLE_BLENDING}, + { "disable-npot-textures", COGL_DEBUG_DISABLE_NPOT_TEXTURES}, + { "wireframe", COGL_DEBUG_WIREFRAME}, + { "disable-software-clip", COGL_DEBUG_DISABLE_SOFTWARE_CLIP}, + { "disable-program-caches", COGL_DEBUG_DISABLE_PROGRAM_CACHES}, + { "disable-fast-read-pixel", COGL_DEBUG_DISABLE_FAST_READ_PIXEL} +}; +static const int n_cogl_behavioural_debug_keys = + G_N_ELEMENTS (cogl_behavioural_debug_keys); + +unsigned long _cogl_debug_flags[COGL_DEBUG_N_LONGS]; +GHashTable *_cogl_debug_instances; + +static void +_cogl_parse_debug_string_for_keys (const char *value, + CoglBool enable, + const GDebugKey *keys, + unsigned int nkeys) +{ + int long_num, key_num; + + /* g_parse_debug_string expects the value field in GDebugKey to be a + mask in an unsigned int but the flags are stored in an array of + multiple longs so we need to build a separate array for each + possible unsigned int */ + + for (long_num = 0; long_num < COGL_DEBUG_N_LONGS; long_num++) + { + int int_num; + + for (int_num = 0; + int_num < sizeof (unsigned long) / sizeof (unsigned int); + int_num++) + { + GDebugKey keys_for_int[sizeof (unsigned int) * 8]; + int nkeys_for_int = 0; + + for (key_num = 0; key_num < nkeys; key_num++) + { + int long_index = COGL_FLAGS_GET_INDEX (keys[key_num].value); + int int_index = (keys[key_num].value % + (sizeof (unsigned long) * 8) / + (sizeof (unsigned int) * 8)); + + if (long_index == long_num && int_index == int_num) + { + keys_for_int[nkeys_for_int] = keys[key_num]; + keys_for_int[nkeys_for_int].value = + COGL_FLAGS_GET_MASK (keys[key_num].value) >> + (int_num * sizeof (unsigned int) * 8); + nkeys_for_int++; + } + } + + if (nkeys_for_int > 0) + { + unsigned long mask = + ((unsigned long) g_parse_debug_string (value, + keys_for_int, + nkeys_for_int)) << + (int_num * sizeof (unsigned int) * 8); + + if (enable) + _cogl_debug_flags[long_num] |= mask; + else + _cogl_debug_flags[long_num] &= ~mask; + } + } + } +} + +void +_cogl_parse_debug_string (const char *value, + CoglBool enable, + CoglBool ignore_help) +{ + if (ignore_help && strcmp (value, "help") == 0) + return; + + /* We don't want to let g_parse_debug_string handle "all" because + * literally enabling all the debug options wouldn't be useful to + * anyone; instead the all option enables all non behavioural + * options. + */ + if (strcmp (value, "all") == 0 || + strcmp (value, "verbose") == 0) + { + int i; + for (i = 0; i < n_cogl_log_debug_keys; i++) + if (enable) + COGL_DEBUG_SET_FLAG (cogl_log_debug_keys[i].value); + else + COGL_DEBUG_CLEAR_FLAG (cogl_log_debug_keys[i].value); + } + else if (g_ascii_strcasecmp (value, "help") == 0) + { + g_printerr ("\n\n%28s\n", _("Supported debug values:")); +#define OPT(MASK_NAME, GROUP, NAME, NAME_FORMATTED, DESCRIPTION) \ + g_printerr ("%28s %s\n", NAME ":", DESCRIPTION); +#include "cogl-debug-options.h" + g_printerr ("\n%28s\n", _("Special debug values:")); + OPT (IGNORED, "ignored", "all", "ignored", \ + N_("Enables all non-behavioural debug options")); + OPT (IGNORED, "ignored", "verbose", "ignored", \ + N_("Enables all non-behavioural debug options")); +#undef OPT + + g_printerr ("\n" + "%28s\n" + " COGL_DISABLE_GL_EXTENSIONS: %s\n" + " COGL_OVERRIDE_GL_VERSION: %s\n", + _("Additional environment variables:"), + _("Comma-separated list of GL extensions to pretend are " + "disabled"), + _("Override the GL version that Cogl will assume the driver " + "supports")); + exit (1); + } + else + { + _cogl_parse_debug_string_for_keys (value, + enable, + cogl_log_debug_keys, + n_cogl_log_debug_keys); + _cogl_parse_debug_string_for_keys (value, + enable, + cogl_behavioural_debug_keys, + n_cogl_behavioural_debug_keys); + } +} + +#ifdef COGL_ENABLE_DEBUG +static CoglBool +cogl_arg_debug_cb (const char *key, + const char *value, + void *user_data) +{ + _cogl_parse_debug_string (value, + TRUE /* enable the flags */, + FALSE /* don't ignore help */); + return TRUE; +} + +static CoglBool +cogl_arg_no_debug_cb (const char *key, + const char *value, + void *user_data) +{ + _cogl_parse_debug_string (value, + FALSE, /* disable the flags */ + TRUE /* ignore help */); + return TRUE; +} +#endif /* COGL_ENABLE_DEBUG */ + +static GOptionEntry cogl_args[] = { +#ifdef COGL_ENABLE_DEBUG + { "cogl-debug", 0, 0, G_OPTION_ARG_CALLBACK, cogl_arg_debug_cb, + N_("Cogl debugging flags to set"), "FLAGS" }, + { "cogl-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, cogl_arg_no_debug_cb, + N_("Cogl debugging flags to unset"), "FLAGS" }, +#endif /* COGL_ENABLE_DEBUG */ + { NULL, }, +}; + +void +_cogl_debug_check_environment (void) +{ + const char *env_string; + + env_string = g_getenv ("COGL_DEBUG"); + if (env_string != NULL) + { + _cogl_parse_debug_string (env_string, + TRUE /* enable the flags */, + FALSE /* don't ignore help */); + env_string = NULL; + } + + env_string = g_getenv ("COGL_NO_DEBUG"); + if (env_string != NULL) + { + _cogl_parse_debug_string (env_string, + FALSE /* disable the flags */, + FALSE /* don't ignore help */); + env_string = NULL; + } +} + +static CoglBool +pre_parse_hook (GOptionContext *context, + GOptionGroup *group, + void *data, + GError **error) +{ + _cogl_init (); + + return TRUE; +} + +/* XXX: GOption based library initialization is not reliable because the + * GOption API has no way to represent dependencies between libraries. + */ +GOptionGroup * +cogl_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("cogl", + _("Cogl Options"), + _("Show Cogl options"), + NULL, NULL); + + g_option_group_set_parse_hooks (group, pre_parse_hook, NULL); + g_option_group_add_entries (group, cogl_args); + + return group; +} diff --git a/cogl/cogl/cogl-debug.h b/cogl/cogl/cogl-debug.h new file mode 100644 index 000000000..91a49e163 --- /dev/null +++ b/cogl/cogl/cogl-debug.h @@ -0,0 +1,126 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_DEBUG_H__ +#define __COGL_DEBUG_H__ + +#include "cogl-profile.h" +#include "cogl-flags.h" +#include "cogl-util.h" + +#include + +COGL_BEGIN_DECLS + +typedef enum { + COGL_DEBUG_SLICING, + COGL_DEBUG_OFFSCREEN, + COGL_DEBUG_DRAW, + COGL_DEBUG_PANGO, + COGL_DEBUG_RECTANGLES, + COGL_DEBUG_OBJECT, + COGL_DEBUG_BLEND_STRINGS, + COGL_DEBUG_DISABLE_BATCHING, + COGL_DEBUG_DISABLE_VBOS, + COGL_DEBUG_DISABLE_PBOS, + COGL_DEBUG_JOURNAL, + COGL_DEBUG_BATCHING, + COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM, + COGL_DEBUG_MATRICES, + COGL_DEBUG_ATLAS, + COGL_DEBUG_DUMP_ATLAS_IMAGE, + COGL_DEBUG_DISABLE_ATLAS, + COGL_DEBUG_DISABLE_SHARED_ATLAS, + COGL_DEBUG_OPENGL, + COGL_DEBUG_DISABLE_TEXTURING, + COGL_DEBUG_DISABLE_ARBFP, + COGL_DEBUG_DISABLE_FIXED, + COGL_DEBUG_DISABLE_GLSL, + COGL_DEBUG_SHOW_SOURCE, + COGL_DEBUG_DISABLE_BLENDING, + COGL_DEBUG_TEXTURE_PIXMAP, + COGL_DEBUG_BITMAP, + COGL_DEBUG_DISABLE_NPOT_TEXTURES, + COGL_DEBUG_WIREFRAME, + COGL_DEBUG_DISABLE_SOFTWARE_CLIP, + COGL_DEBUG_DISABLE_PROGRAM_CACHES, + COGL_DEBUG_DISABLE_FAST_READ_PIXEL, + COGL_DEBUG_CLIPPING, + COGL_DEBUG_WINSYS, + COGL_DEBUG_PERFORMANCE, + + COGL_DEBUG_N_FLAGS +} CoglDebugFlags; + +extern GHashTable *_cogl_debug_instances; +#define COGL_DEBUG_N_LONGS COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_DEBUG_N_FLAGS) + +/* _cogl_debug_flags currently needs to exported outside of the shared + library for cogl-pango. The special COGL_EXPORT macro is needed to + get this to work when building with MSVC */ +COGL_EXPORT extern unsigned long _cogl_debug_flags[COGL_DEBUG_N_LONGS]; + +#define COGL_DEBUG_ENABLED(flag) \ + COGL_FLAGS_GET (_cogl_debug_flags, flag) + +#define COGL_DEBUG_SET_FLAG(flag) \ + COGL_FLAGS_SET (_cogl_debug_flags, flag, TRUE) + +#define COGL_DEBUG_CLEAR_FLAG(flag) \ + COGL_FLAGS_SET (_cogl_debug_flags, flag, FALSE) + +#ifdef __GNUC__ +#define COGL_NOTE(type,x,a...) G_STMT_START { \ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_##type))) { \ + _cogl_profile_trace_message ("[" #type "] " G_STRLOC " & " x, ##a); \ + } } G_STMT_END + +#else +#define COGL_NOTE(type,...) G_STMT_START { \ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_##type))) { \ + char *_fmt = g_strdup_printf (__VA_ARGS__); \ + _cogl_profile_trace_message ("[" #type "] " G_STRLOC " & %s", _fmt);\ + g_free (_fmt); \ + } } G_STMT_END + +#endif /* __GNUC__ */ + +void +_cogl_debug_check_environment (void); + +void +_cogl_parse_debug_string (const char *value, + CoglBool enable, + CoglBool ignore_help); + +COGL_END_DECLS + +#endif /* __COGL_DEBUG_H__ */ + diff --git a/cogl/cogl/cogl-defines.h.in b/cogl/cogl/cogl-defines.h.in new file mode 100644 index 000000000..eb3ad9268 --- /dev/null +++ b/cogl/cogl/cogl-defines.h.in @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_DEFINES_H__ +#define __COGL_DEFINES_H__ + +@COGL_DEFINES@ + +#define COGL_VERSION_MAJOR_INTERNAL 1 +#define COGL_VERSION_MINOR_INTERNAL @COGL_1_MINOR_VERSION@ +#define COGL_VERSION_MICRO_INTERNAL @COGL_1_MICRO_VERSION@ +#define COGL_VERSION_STRING_INTERNAL "@COGL_1_VERSION@" + +#endif diff --git a/cogl/cogl/cogl-deprecated.h b/cogl/cogl/cogl-deprecated.h new file mode 100644 index 000000000..ca56fa18a --- /dev/null +++ b/cogl/cogl/cogl-deprecated.h @@ -0,0 +1,43 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef COGL_DEPRECATED_H + +#define cogl_color cogl_color_REPLACED_BY_cogl_set_source_color +#define cogl_enable_depth_test cogl_enable_depth_test_RENAMED_TO_cogl_set_depth_test_enabled +#define cogl_enable_backface_culling cogl_enable_backface_culling_RENAMED_TO_cogl_set_backface_culling_enabled + +#define cogl_texture_rectangle cogl_texture_rectangle_REPLACE_BY_cogl_set_source_texture_AND_cogl_rectangle_with_texture_coords + +#define cogl_texture_multiple_rectangles cogl_texture_multiple_rectangles_REPLACED_BY_cogl_set_source_texture_AND_cogl_rectangles_with_texture_coords + +#define cogl_texture_polygon cogl_texture_polygon_REPLACED_BY_cogl_set_source_texture_AND_cogl_polygon + +#endif diff --git a/cogl/cogl/cogl-depth-state-private.h b/cogl/cogl/cogl-depth-state-private.h new file mode 100644 index 000000000..6c14bb9f2 --- /dev/null +++ b/cogl/cogl/cogl-depth-state-private.h @@ -0,0 +1,38 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_DEPTH_STATE_PRIVATE_H +#define __COGL_DEPTH_STATE_PRIVATE_H + + +#define COGL_DEPTH_STATE_MAGIC 0xDEADBEEF + +#endif /* __COGL_DEPTH_STATE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-depth-state.c b/cogl/cogl/cogl-depth-state.c new file mode 100644 index 000000000..5ea0aab49 --- /dev/null +++ b/cogl/cogl/cogl-depth-state.c @@ -0,0 +1,116 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-depth-state-private.h" +#include "cogl-depth-state.h" + +void +cogl_depth_state_init (CoglDepthState *state) +{ + state->magic = COGL_DEPTH_STATE_MAGIC; + + /* The same as the GL defaults */ + state->test_enabled = FALSE; + state->write_enabled = TRUE; + state->test_function = COGL_DEPTH_TEST_FUNCTION_LESS; + state->range_near = 0; + state->range_far = 1; +} + +void +cogl_depth_state_set_test_enabled (CoglDepthState *state, + CoglBool enabled) +{ + _COGL_RETURN_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC); + state->test_enabled = enabled; +} + +CoglBool +cogl_depth_state_get_test_enabled (CoglDepthState *state) +{ + _COGL_RETURN_VAL_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); + return state->test_enabled; +} + +void +cogl_depth_state_set_write_enabled (CoglDepthState *state, + CoglBool enabled) +{ + _COGL_RETURN_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC); + state->write_enabled = enabled; +} + +CoglBool +cogl_depth_state_get_write_enabled (CoglDepthState *state) +{ + _COGL_RETURN_VAL_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); + return state->write_enabled; +} + +void +cogl_depth_state_set_test_function (CoglDepthState *state, + CoglDepthTestFunction function) +{ + _COGL_RETURN_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC); + state->test_function = function; +} + +CoglDepthTestFunction +cogl_depth_state_get_test_function (CoglDepthState *state) +{ + _COGL_RETURN_VAL_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); + return state->test_function; +} + +void +cogl_depth_state_set_range (CoglDepthState *state, + float near, + float far) +{ + _COGL_RETURN_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC); + state->range_near = near; + state->range_far = far; +} + +void +cogl_depth_state_get_range (CoglDepthState *state, + float *near_out, + float *far_out) +{ + _COGL_RETURN_IF_FAIL (state->magic == COGL_DEPTH_STATE_MAGIC); + *near_out = state->range_near; + *far_out = state->range_far; +} diff --git a/cogl/cogl/cogl-depth-state.h b/cogl/cogl/cogl-depth-state.h new file mode 100644 index 000000000..581b96110 --- /dev/null +++ b/cogl/cogl/cogl-depth-state.h @@ -0,0 +1,270 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_DEPTH_STATE_H__ +#define __COGL_DEPTH_STATE_H__ + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-depth-state + * @short_description: Functions for describing the depth testing + * state of your GPU. + */ + +/** + * CoglDepthState: + * + * Since: 2.0 + */ +typedef struct { + /*< private >*/ + uint32_t COGL_PRIVATE (magic); + + CoglBool COGL_PRIVATE (test_enabled); + CoglDepthTestFunction COGL_PRIVATE (test_function); + CoglBool COGL_PRIVATE (write_enabled); + float COGL_PRIVATE (range_near); + float COGL_PRIVATE (range_far); + + uint32_t COGL_PRIVATE (padding0); + uint32_t COGL_PRIVATE (padding1); + uint32_t COGL_PRIVATE (padding2); + uint32_t COGL_PRIVATE (padding3); + uint32_t COGL_PRIVATE (padding4); + uint32_t COGL_PRIVATE (padding5); + uint32_t COGL_PRIVATE (padding6); + uint32_t COGL_PRIVATE (padding7); + uint32_t COGL_PRIVATE (padding8); + uint32_t COGL_PRIVATE (padding9); +} CoglDepthState; + +/** + * cogl_depth_state_init: + * @state: A #CoglDepthState struct + * + * Initializes the members of @state to their default values. + * + * You should never pass an un initialized #CoglDepthState structure + * to cogl_pipeline_set_depth_state(). + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_init (CoglDepthState *state); + +/** + * cogl_depth_state_set_test_enabled: + * @state: A #CoglDepthState struct + * @enable: The enable state you want + * + * Enables or disables depth testing according to the value of + * @enable. + * + * If depth testing is enable then the #CoglDepthTestFunction set + * using cogl_depth_state_set_test_function() us used to evaluate + * the depth value of incoming fragments against the corresponding + * value stored in the current depth buffer, and if the test passes + * then the fragments depth value is used to update the depth buffer. + * (unless you have disabled depth writing via + * cogl_depth_state_set_write_enabled()) + * + * By default depth testing is disabled. + * + * NB: this won't directly affect the state of the GPU. You have + * to then set the state on a #CoglPipeline using + * cogl_pipeline_set_depth_state() + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_set_test_enabled (CoglDepthState *state, + CoglBool enable); + +/** + * cogl_depth_state_get_test_enabled: + * @state: A #CoglDepthState struct + * + * Gets the current depth test enabled state as previously set by + * cogl_depth_state_set_test_enabled(). + * + * Returns: The pipeline's current depth test enabled state. + * Since: 2.0 + * Stability: Unstable + */ +CoglBool +cogl_depth_state_get_test_enabled (CoglDepthState *state); + +/** + * cogl_depth_state_set_write_enabled: + * @state: A #CoglDepthState struct + * @enable: The enable state you want + * + * Enables or disables depth buffer writing according to the value of + * @enable. Normally when depth testing is enabled and the comparison + * between a fragment's depth value and the corresponding depth buffer + * value passes then the fragment's depth is written to the depth + * buffer unless writing is disabled here. + * + * By default depth writing is enabled + * + * NB: this won't directly affect the state of the GPU. You have + * to then set the state on a #CoglPipeline using + * cogl_pipeline_set_depth_state() + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_set_write_enabled (CoglDepthState *state, + CoglBool enable); + +/** + * cogl_depth_state_get_write_enabled: + * @state: A #CoglDepthState struct + * + * Gets the depth writing enable state as set by the corresponding + * cogl_depth_state_set_write_enabled(). + * + * Returns: The current depth writing enable state + * Since: 2.0 + * Stability: Unstable + */ +CoglBool +cogl_depth_state_get_write_enabled (CoglDepthState *state); + +/** + * cogl_depth_state_set_test_function: + * @state: A #CoglDepthState struct + * @function: The #CoglDepthTestFunction to set + * + * Sets the #CoglDepthTestFunction used to compare the depth value of + * an incoming fragment against the corresponding value in the current + * depth buffer. + * + * By default the depth test function is %COGL_DEPTH_TEST_FUNCTION_LESS + * + * NB: this won't directly affect the state of the GPU. You have + * to then set the state on a #CoglPipeline using + * cogl_pipeline_set_depth_state() + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_set_test_function (CoglDepthState *state, + CoglDepthTestFunction function); + +/** + * cogl_depth_state_get_test_function: + * @state: A #CoglDepthState struct + * + * Gets the current depth test enable state as previously set via + * cogl_depth_state_set_test_enabled(). + * + * Returns: The current depth test enable state. + * Since: 2.0 + * Stability: Unstable + */ +CoglDepthTestFunction +cogl_depth_state_get_test_function (CoglDepthState *state); + +/** + * cogl_depth_state_set_range: + * @state: A #CoglDepthState object + * @near_val: The near component of the desired depth range which will be + * clamped to the range [0, 1] + * @far_val: The far component of the desired depth range which will be + * clamped to the range [0, 1] + * + * Sets the range to map depth values in normalized device coordinates + * to before writing out to a depth buffer. + * + * After your geometry has be transformed, clipped and had perspective + * division applied placing it in normalized device + * coordinates all depth values between the near and far z clipping + * planes are in the range -1 to 1. Before writing any depth value to + * the depth buffer though the value is mapped into the range [0, 1]. + * + * With this function you can change the range which depth values are + * mapped too although the range must still lye within the range [0, + * 1]. + * + * If your driver does not support this feature (for example you are + * using GLES 1 drivers) then if you don't use the default range + * values you will get an error reported when calling + * cogl_pipeline_set_depth_state (). You can check ahead of time for + * the %COGL_FEATURE_ID_DEPTH_RANGE feature with + * cogl_has_feature() to know if this function will succeed. + * + * By default normalized device coordinate depth values are mapped to + * the full range of depth buffer values, [0, 1]. + * + * NB: this won't directly affect the state of the GPU. You have + * to then set the state on a #CoglPipeline using + * cogl_pipeline_set_depth_state(). + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_set_range (CoglDepthState *state, + float near_val, + float far_val); + +/** + * cogl_depth_state_get_range: + * @state: A #CoglDepthState object + * @near_val: A pointer to store the near component of the depth range + * @far_val: A pointer to store the far component of the depth range + * + * Gets the current range to which normalized depth values are mapped + * before writing to the depth buffer. This corresponds to the range + * set with cogl_depth_state_set_range(). + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_depth_state_get_range (CoglDepthState *state, + float *near_val, + float *far_val); + +COGL_END_DECLS + +#endif /* __COGL_DEPTH_STATE_H__ */ diff --git a/cogl/cogl/cogl-display-private.h b/cogl/cogl/cogl-display-private.h new file mode 100644 index 000000000..9788435bb --- /dev/null +++ b/cogl/cogl/cogl-display-private.h @@ -0,0 +1,54 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_DISPLAY_PRIVATE_H +#define __COGL_DISPLAY_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-display.h" +#include "cogl-renderer.h" +#include "cogl-onscreen-template.h" + +struct _CoglDisplay +{ + CoglObject _parent; + + CoglBool setup; + CoglRenderer *renderer; + CoglOnscreenTemplate *onscreen_template; + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT + struct wl_display *wayland_compositor_display; +#endif + + void *winsys; +}; + +#endif /* __COGL_DISPLAY_PRIVATE_H */ diff --git a/cogl/cogl/cogl-display.c b/cogl/cogl/cogl-display.c new file mode 100644 index 000000000..039e88199 --- /dev/null +++ b/cogl/cogl/cogl-display.c @@ -0,0 +1,167 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-private.h" +#include "cogl-object.h" + +#include "cogl-display-private.h" +#include "cogl-renderer-private.h" +#include "cogl-winsys-private.h" +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT +#include "cogl-wayland-server.h" +#endif +#include "cogl-gtype-private.h" + +static void _cogl_display_free (CoglDisplay *display); + +COGL_OBJECT_DEFINE (Display, display); +COGL_GTYPE_DEFINE_CLASS (Display, display); + +static const CoglWinsysVtable * +_cogl_display_get_winsys (CoglDisplay *display) +{ + return display->renderer->winsys_vtable; +} + +static void +_cogl_display_free (CoglDisplay *display) +{ + const CoglWinsysVtable *winsys; + + if (display->setup) + { + winsys = _cogl_display_get_winsys (display); + winsys->display_destroy (display); + display->setup = FALSE; + } + + if (display->renderer) + { + cogl_object_unref (display->renderer); + display->renderer = NULL; + } + + if (display->onscreen_template) + { + cogl_object_unref (display->onscreen_template); + display->onscreen_template = NULL; + } + + g_slice_free (CoglDisplay, display); +} + +CoglDisplay * +cogl_display_new (CoglRenderer *renderer, + CoglOnscreenTemplate *onscreen_template) +{ + CoglDisplay *display = g_slice_new0 (CoglDisplay); + CoglError *error = NULL; + + _cogl_init (); + + display->renderer = renderer; + if (renderer) + cogl_object_ref (renderer); + else + display->renderer = cogl_renderer_new (); + + if (!cogl_renderer_connect (display->renderer, &error)) + g_error ("Failed to connect to renderer: %s\n", error->message); + + display->setup = FALSE; + + display = _cogl_display_object_new (display); + + cogl_display_set_onscreen_template (display, onscreen_template); + + return display; +} + +CoglRenderer * +cogl_display_get_renderer (CoglDisplay *display) +{ + return display->renderer; +} + +void +cogl_display_set_onscreen_template (CoglDisplay *display, + CoglOnscreenTemplate *onscreen_template) +{ + _COGL_RETURN_IF_FAIL (display->setup == FALSE); + + if (onscreen_template) + cogl_object_ref (onscreen_template); + + if (display->onscreen_template) + cogl_object_unref (display->onscreen_template); + + display->onscreen_template = onscreen_template; + + /* NB: we want to maintain the invariable that there is always an + * onscreen template associated with a CoglDisplay... */ + if (!onscreen_template) + display->onscreen_template = cogl_onscreen_template_new (NULL); +} + +CoglBool +cogl_display_setup (CoglDisplay *display, + CoglError **error) +{ + const CoglWinsysVtable *winsys; + + if (display->setup) + return TRUE; + + winsys = _cogl_display_get_winsys (display); + if (!winsys->display_setup (display, error)) + return FALSE; + + display->setup = TRUE; + + return TRUE; +} + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT +void +cogl_wayland_display_set_compositor_display (CoglDisplay *display, + struct wl_display *wayland_display) +{ + _COGL_RETURN_IF_FAIL (display->setup == FALSE); + + display->wayland_compositor_display = wayland_display; +} +#endif diff --git a/cogl/cogl/cogl-display.h b/cogl/cogl/cogl-display.h new file mode 100644 index 000000000..47720a3fb --- /dev/null +++ b/cogl/cogl/cogl-display.h @@ -0,0 +1,214 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_DISPLAY_H__ +#define __COGL_DISPLAY_H__ + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-display + * @short_description: Common aspects of a display pipeline + * + * The basic intention for this object is to let the application + * configure common display preferences before creating a context, and + * there are a few different aspects to this... + * + * Firstly there are options directly relating to the physical display + * pipeline that is currently being used including the digital to + * analogue conversion hardware and the screens the user sees. + * + * Another aspect is that display options may constrain or affect how + * onscreen framebuffers should later be configured. The original + * rationale for the display object in fact was to let us handle GLX + * and EGLs requirements that framebuffers must be "compatible" with + * the config associated with the current context meaning we have to + * force the user to describe how they would like to create their + * onscreen windows before we can choose a suitable fbconfig and + * create a GLContext. + */ + +typedef struct _CoglDisplay CoglDisplay; + +#define COGL_DISPLAY(OBJECT) ((CoglDisplay *)OBJECT) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_display_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_display_get_gtype (void); +#endif + +/** + * cogl_display_new: + * @renderer: A #CoglRenderer + * @onscreen_template: A #CoglOnscreenTemplate + * + * Explicitly allocates a new #CoglDisplay object to encapsulate the + * common state of the display pipeline that applies to the whole + * application. + * + * Many applications don't need to explicitly use + * cogl_display_new() and can just jump straight to cogl_context_new() + * and pass a %NULL display argument so Cogl will automatically + * connect and setup a renderer and display. + * + * A @display can only be made for a specific choice of renderer which + * is why this takes the @renderer argument. + * + * A common use for explicitly allocating a display object is to + * define a template for allocating onscreen framebuffers which is + * what the @onscreen_template argument is for, or alternatively + * you can use cogl_display_set_onscreen_template(). + * + * When a display is first allocated via cogl_display_new() it is in a + * mutable configuration mode. It's designed this way so we can + * extend the apis available for configuring a display without + * requiring huge numbers of constructor arguments. + * + * When you have finished configuring a display object you can + * optionally call cogl_display_setup() to explicitly apply the + * configuration and check for errors. Alternaitvely you can pass the + * display to cogl_context_new() and Cogl will implicitly apply your + * configuration but if there are errors then the application will + * abort with a message. For simple applications with no fallback + * options then relying on the implicit setup can be fine. + * + * Return value: (transfer full): A newly allocated #CoglDisplay + * object in a mutable configuration mode. + * Since: 1.10 + * Stability: unstable + */ +CoglDisplay * +cogl_display_new (CoglRenderer *renderer, + CoglOnscreenTemplate *onscreen_template); + +/** + * cogl_display_get_renderer: + * @display: a #CoglDisplay + * + * Queries the #CoglRenderer associated with the given @display. + * + * Return value: (transfer none): The associated #CoglRenderer + * + * Since: 1.10 + * Stability: unstable + */ +CoglRenderer * +cogl_display_get_renderer (CoglDisplay *display); + +/** + * cogl_display_set_onscreen_template: + * @display: a #CoglDisplay + * @onscreen_template: A template for creating #CoglOnscreen framebuffers + * + * Specifies a template for creating #CoglOnscreen framebuffers. + * + * Depending on the system, the constraints for creating #CoglOnscreen + * framebuffers need to be known before setting up a #CoglDisplay because the + * final setup of the display may constrain how onscreen framebuffers may be + * allocated. If Cogl knows how an application wants to allocate onscreen + * framebuffers then it can try to make sure to setup the display accordingly. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_display_set_onscreen_template (CoglDisplay *display, + CoglOnscreenTemplate *onscreen_template); + +/** + * cogl_display_setup: + * @display: a #CoglDisplay + * @error: return location for a #CoglError + * + * Explicitly sets up the given @display object. Use of this api is + * optional since Cogl will internally setup the display if not done + * explicitly. + * + * When a display is first allocated via cogl_display_new() it is in a + * mutable configuration mode. This allows us to extend the apis + * available for configuring a display without requiring huge numbers + * of constructor arguments. + * + * Its possible to request a configuration that might not be + * supportable on the current system and so this api provides a means + * to apply the configuration explicitly but if it fails then an + * exception will be returned so you can handle the error gracefully + * and perhaps fall back to an alternative configuration. + * + * If you instead rely on Cogl implicitly calling cogl_display_setup() + * for you then if there is an error with the configuration you won't + * get an opportunity to handle that and the application may abort + * with a message. For simple applications that don't have any + * fallback options this behaviour may be fine. + * + * Return value: Returns %TRUE if there was no error, else it returns + * %FALSE and returns an exception via @error. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_display_setup (CoglDisplay *display, + CoglError **error); + +/** + * cogl_is_display: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglDisplay. + * + * Return value: %TRUE if the object references a #CoglDisplay + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_display (void *object); + +COGL_END_DECLS + +#endif /* __COGL_DISPLAY_H__ */ + diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h new file mode 100644 index 000000000..648228c6f --- /dev/null +++ b/cogl/cogl/cogl-driver.h @@ -0,0 +1,268 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_DRIVER_H +#define __COGL_DRIVER_H + +#include "cogl-context.h" +#include "cogl-offscreen.h" +#include "cogl-framebuffer-private.h" +#include "cogl-attribute-private.h" + +typedef struct _CoglDriverVtable CoglDriverVtable; + +struct _CoglDriverVtable +{ + /* TODO: factor this out since this is OpenGL specific and + * so can be ignored by non-OpenGL drivers. */ + CoglBool + (* pixel_format_from_gl_internal) (CoglContext *context, + GLenum gl_int_format, + CoglPixelFormat *out_format); + + /* TODO: factor this out since this is OpenGL specific and + * so can be ignored by non-OpenGL drivers. */ + CoglPixelFormat + (* pixel_format_to_gl) (CoglContext *context, + CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype); + + CoglBool + (* update_features) (CoglContext *context, + CoglError **error); + + CoglBool + (* offscreen_allocate) (CoglOffscreen *offscreen, + CoglError **error); + + void + (* offscreen_free) (CoglOffscreen *offscreen); + + void + (* framebuffer_flush_state) (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state); + + void + (* framebuffer_clear) (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha); + + void + (* framebuffer_query_bits) (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits); + + void + (* framebuffer_finish) (CoglFramebuffer *framebuffer); + + void + (* framebuffer_discard_buffers) (CoglFramebuffer *framebuffer, + unsigned long buffers); + + void + (* framebuffer_draw_attributes) (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + + void + (* framebuffer_draw_indexed_attributes) (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + + CoglBool + (* framebuffer_read_pixels_into_bitmap) (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error); + + /* Destroys any driver specific resources associated with the given + * 2D texture. */ + void + (* texture_2d_free) (CoglTexture2D *tex_2d); + + /* Returns TRUE if the driver can support creating a 2D texture with + * the given geometry and specified internal format. + */ + CoglBool + (* texture_2d_can_create) (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format); + + /* Initializes driver private state before allocating any specific + * storage for a 2D texture, where base texture and texture 2D + * members will already be initialized before passing control to + * the driver. + */ + void + (* texture_2d_init) (CoglTexture2D *tex_2d); + + /* Allocates (uninitialized) storage for the given texture according + * to the configured size and format of the texture */ + CoglBool + (* texture_2d_allocate) (CoglTexture *tex, + CoglError **error); + + /* Initialize the specified region of storage of the given texture + * with the contents of the specified framebuffer region + */ + void + (* texture_2d_copy_from_framebuffer) (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level); + + /* If the given texture has a corresponding OpenGL texture handle + * then return that. + * + * This is optional + */ + unsigned int + (* texture_2d_get_gl_handle) (CoglTexture2D *tex_2d); + + /* Update all mipmap levels > 0 */ + void + (* texture_2d_generate_mipmap) (CoglTexture2D *tex_2d); + + /* Initialize the specified region of storage of the given texture + * with the contents of the specified bitmap region + * + * Since this may need to create the underlying storage first + * it may throw a NO_MEMORY error. + */ + CoglBool + (* texture_2d_copy_from_bitmap) (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bitmap, + int dst_x, + int dst_y, + int level, + CoglError **error); + + /* Reads back the full contents of the given texture and write it to + * @data in the given @format and with the given @rowstride. + * + * This is optional + */ + void + (* texture_2d_get_data) (CoglTexture2D *tex_2d, + CoglPixelFormat format, + int rowstride, + uint8_t *data); + + /* Prepares for drawing by flushing the journal, framebuffer state, + * pipeline state and attribute state. + */ + void + (* flush_attributes_state) (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglFlushLayerState *layer_state, + 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); + + /* Enables the driver to create some meta data to represent a buffer + * but with no corresponding storage allocated yet. + */ + void + (* buffer_create) (CoglBuffer *buffer); + + void + (* buffer_destroy) (CoglBuffer *buffer); + + /* Maps a buffer into the CPU */ + void * + (* buffer_map_range) (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + + /* Unmaps a buffer */ + void + (* buffer_unmap) (CoglBuffer *buffer); + + /* Uploads data to the buffer without needing to map it necessarily + */ + CoglBool + (* buffer_set_data) (CoglBuffer *buffer, + unsigned int offset, + const void *data, + unsigned int size, + CoglError **error); +}; + +#define COGL_DRIVER_ERROR (_cogl_driver_error_quark ()) + +typedef enum { /*< prefix=COGL_DRIVER_ERROR >*/ + COGL_DRIVER_ERROR_UNKNOWN_VERSION, + COGL_DRIVER_ERROR_INVALID_VERSION, + COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, + COGL_DRIVER_ERROR_FAILED_TO_LOAD_LIBRARY +} CoglDriverError; + +uint32_t +_cogl_driver_error_quark (void); + +#endif /* __COGL_DRIVER_H */ + diff --git a/cogl/cogl/cogl-egl-defines.h.in b/cogl/cogl/cogl-egl-defines.h.in new file mode 100644 index 000000000..648a62ddf --- /dev/null +++ b/cogl/cogl/cogl-egl-defines.h.in @@ -0,0 +1,40 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_EGL_DEFINES_H__ +#define __COGL_EGL_DEFINES_H__ + +#ifdef COGL_HAS_EGL_SUPPORT + +@COGL_EGL_INCLUDES@ + +#endif /* COGL_HAS_EGL_SUPPORT */ + +#endif diff --git a/cogl/cogl/cogl-egl-private.h b/cogl/cogl/cogl-egl-private.h new file mode 100644 index 000000000..c4b0dfe1b --- /dev/null +++ b/cogl/cogl/cogl-egl-private.h @@ -0,0 +1,40 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_EGL_PRIVATE_H__ +#define __COGL_EGL_PRIVATE_H__ + +#include "cogl-egl-defines.h" + +#ifndef GL_OES_EGL_image +#define GLeglImageOES void * +#endif + +#endif /* __COGL_EGL_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-egl.h b/cogl/cogl/cogl-egl.h new file mode 100644 index 000000000..cea7b1038 --- /dev/null +++ b/cogl/cogl/cogl-egl.h @@ -0,0 +1,118 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_EGL_H__ +#define __COGL_EGL_H__ + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_EGL_H_INSIDE__ */ +#ifndef __COGL_EGL_H_INSIDE__ +#define __COGL_EGL_H_INSIDE__ +#endif + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + + +#include +#include + +COGL_BEGIN_DECLS + +/** + * cogl_egl_context_get_egl_display: + * @context: A #CoglContext pointer + * + * If you have done a runtime check to determine that Cogl is using + * EGL internally then this API can be used to retrieve the EGLDisplay + * handle that was setup internally. The result is undefined if Cogl + * is not using EGL. + * + * Note: The current window system backend can be checked using + * cogl_renderer_get_winsys_id(). + * + * Return value: The internally setup EGLDisplay handle. + * Since: 1.8 + * Stability: unstable + */ +EGLDisplay +cogl_egl_context_get_egl_display (CoglContext *context); + +/** + * cogl_egl_context_get_egl_context: + * @context: A #CoglContext pointer + * + * If you have done a runtime check to determine that Cogl is using + * EGL internally then this API can be used to retrieve the EGLContext + * handle that was setup internally. The result is undefined if Cogl + * is not using EGL. + * + * Note: The current window system backend can be checked using + * cogl_renderer_get_winsys_id(). + * + * Return value: The internally setup EGLDisplay handle. + * Since: 1.18 + * Stability: unstable + */ +EGLContext +cogl_egl_context_get_egl_context (CoglContext *context); + + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_EGL_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_EGL_H__ */ diff --git a/cogl/cogl/cogl-enum-types.c.in b/cogl/cogl/cogl-enum-types.c.in new file mode 100644 index 000000000..a08711ba2 --- /dev/null +++ b/cogl/cogl/cogl-enum-types.c.in @@ -0,0 +1,50 @@ +/*** BEGIN file-header ***/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* We need to undefine this so that we will be sure to include + * cogl-path.h instead of cogl2-path.h when we include the framebuffer + * header. Otherwise it will include both headers and it won't + * compile. */ +#undef COGL_ENABLE_EXPERIMENTAL_2_0_API + +#include "cogl-enum-types.h" +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} +/*** END value-tail ***/ diff --git a/cogl/cogl/cogl-enum-types.h.in b/cogl/cogl/cogl-enum-types.h.in new file mode 100644 index 000000000..14bfccc7d --- /dev/null +++ b/cogl/cogl/cogl-enum-types.h.in @@ -0,0 +1,25 @@ +/*** BEGIN file-header ***/ +#ifndef __COGL_ENUM_TYPES_H__ +#define __COGL_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __COGL_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define COGL_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) + +/*** END value-header ***/ diff --git a/cogl/cogl/cogl-error-private.h b/cogl/cogl/cogl-error-private.h new file mode 100644 index 000000000..d96779fad --- /dev/null +++ b/cogl/cogl/cogl-error-private.h @@ -0,0 +1,59 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_ERROR_PRIVATE_H__ +#define __COGL_ERROR_PRIVATE_H__ + +#include "cogl-error.h" + +void +_cogl_set_error (CoglError **error, + uint32_t domain, + int code, + const char *format, + ...) G_GNUC_PRINTF (4, 5); + +void +_cogl_set_error_literal (CoglError **error, + uint32_t domain, + int code, + const char *message); + +void +_cogl_propagate_error (CoglError **dest, + CoglError *src); + +#ifdef COGL_HAS_GLIB_SUPPORT +void +_cogl_propagate_gerror (CoglError **dest, + GError *src); +#endif /* COGL_HAS_GLIB_SUPPORT */ + +#define _cogl_clear_error(X) g_clear_error ((GError **)X) + +#endif /* __COGL_ERROR_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-error.c b/cogl/cogl/cogl-error.c new file mode 100644 index 000000000..d83afc3a4 --- /dev/null +++ b/cogl/cogl/cogl-error.c @@ -0,0 +1,135 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-types.h" +#include "cogl-util.h" +#include "cogl-error-private.h" + +#include + +void +cogl_error_free (CoglError *error) +{ + g_error_free ((GError *)error); +} + +CoglError * +cogl_error_copy (CoglError *error) +{ + return (CoglError *)g_error_copy ((GError *)error); +} + +CoglBool +cogl_error_matches (CoglError *error, + uint32_t domain, + int code) +{ + return g_error_matches ((GError *)error, domain, code); +} + +#define ERROR_OVERWRITTEN_WARNING \ + "CoglError set over the top of a previous CoglError or " \ + "uninitialized memory.\nThis indicates a bug in someone's " \ + "code. You must ensure an error is NULL before it's set.\n" \ + "The overwriting error message was: %s" + +void +_cogl_set_error (CoglError **error, + uint32_t domain, + int code, + const char *format, + ...) +{ + GError *new; + + va_list args; + + va_start (args, format); + + if (error == NULL) + { + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, format, args); + va_end (args); + return; + } + + new = g_error_new_valist (domain, code, format, args); + va_end (args); + + if (*error == NULL) + *error = (CoglError *)new; + else + g_warning (ERROR_OVERWRITTEN_WARNING, new->message); +} + +void +_cogl_set_error_literal (CoglError **error, + uint32_t domain, + int code, + const char *message) +{ + _cogl_set_error (error, domain, code, "%s", message); +} + +void +_cogl_propagate_error (CoglError **dest, + CoglError *src) +{ + _COGL_RETURN_IF_FAIL (src != NULL); + + if (dest == NULL) + { + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "%s", src->message); + cogl_error_free (src); + } + else if (*dest) + g_warning (ERROR_OVERWRITTEN_WARNING, src->message); + else + *dest = src; +} + +/* This function is only used from the gdk-pixbuf image backend so it + * should only be called if we are using the system GLib. It would be + * difficult to get this to work without the system glib because we + * would need to somehow call the same g_error_free function that + * gdk-pixbuf is using */ +#ifdef COGL_HAS_GLIB_SUPPORT +void +_cogl_propagate_gerror (CoglError **dest, + GError *src) +{ + _cogl_propagate_error (dest, (CoglError *) src); +} +#endif /* COGL_HAS_GLIB_SUPPORT */ diff --git a/cogl/cogl/cogl-error.h b/cogl/cogl/cogl-error.h new file mode 100644 index 000000000..8682d0e0f --- /dev/null +++ b/cogl/cogl/cogl-error.h @@ -0,0 +1,185 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_ERROR_H__ +#define __COGL_ERROR_H__ + +#include "cogl-types.h" + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-error + * @short_description: A way for Cogl to throw exceptions + * + * As a general rule Cogl shields non-recoverable errors from + * developers, such as most heap allocation failures (unless for + * exceptionally large resources which we might reasonably expect to + * fail) and this reduces the burden on developers. + * + * There are some Cogl apis though that can fail for exceptional + * reasons that can also potentially be recovered from at runtime + * and for these apis we use a standard convention for reporting + * runtime recoverable errors. + * + * As an example if we look at the cogl_context_new() api which + * takes an error argument: + * |[ + * CoglContext * + * cogl_context_new (CoglDisplay *display, CoglError **error); + * ]| + * + * A caller interested in catching any runtime error when creating a + * new #CoglContext would pass the address of a #CoglError pointer + * that has first been initialized to %NULL as follows: + * + * |[ + * CoglError *error = NULL; + * CoglContext *context; + * + * context = cogl_context_new (NULL, &error); + * ]| + * + * The return status should usually be enough to determine if there + * was an error set (in this example we can check if context == %NULL) + * but if it's not possible to tell from the function's return status + * you can instead look directly at the error pointer which you + * initialized to %NULL. In this example we now check the error, + * report any error to the user, free the error and then simply + * abort without attempting to recover. + * + * |[ + * if (context == NULL) + * { + * fprintf (stderr, "Failed to create a Cogl context: %s\n", + * error->message); + * cogl_error_free (error); + * abort (); + * } + * ]| + * + * All Cogl APIs that accept an error argument can also be passed a + * %NULL pointer. In this case if an exceptional error condition is hit + * then Cogl will simply log the error message and abort the + * application. This can be compared to language execeptions where the + * developer has not attempted to catch the exception. This means the + * above example is essentially redundant because it's what Cogl would + * have done automatically and so, similarly, if your application has + * no way to recover from a particular error you might just as well + * pass a %NULL #CoglError pointer to save a bit of typing. + * + * If you are used to using the GLib API you will probably + * recognize that #CoglError is just like a #GError. In fact if Cogl + * has been built with --enable-glib then it is safe to cast a + * #CoglError to a #GError. + * + * An important detail to be aware of if you are used to using + * GLib's GError API is that Cogl deviates from the GLib GError + * conventions in one noteable way which is that a %NULL error pointer + * does not mean you want to ignore the details of an error, it means + * you are not trying to catch any exceptional errors the function might + * throw which will result in the program aborting with a log message + * if an error is thrown. + */ + +#ifdef COGL_HAS_GLIB_SUPPORT +#define CoglError GError +#else +/** + * CoglError: + * @domain: A high-level domain identifier for the error + * @code: A specific error code within a specified domain + * @message: A human readable error message + */ +typedef struct _CoglError { + uint32_t domain; + int code; + char *message; +} CoglError; +#endif /* COGL_HAS_GLIB_SUPPORT */ + +/** + * cogl_error_free: + * @error: A #CoglError thrown by the Cogl api + * + * Frees a #CoglError and associated resources. + */ +void +cogl_error_free (CoglError *error); + +/** + * cogl_error_copy: + * @error: A #CoglError thrown by the Cogl api + * + * Makes a copy of @error which can later be freed using + * cogl_error_free(). + * + * Return value: A newly allocated #CoglError initialized to match the + * contents of @error. + */ +CoglError * +cogl_error_copy (CoglError *error); + +/** + * cogl_error_matches: + * @error: A #CoglError thrown by the Cogl api or %NULL + * @domain: The error domain + * @code: The error code + * + * Returns %TRUE if error matches @domain and @code, %FALSE otherwise. + * In particular, when error is %NULL, FALSE will be returned. + * + * Return value: whether the @error corresponds to the given @domain + * and @code. + */ +CoglBool +cogl_error_matches (CoglError *error, + uint32_t domain, + int code); + +/** + * COGL_GLIB_ERROR: + * @COGL_ERROR: A #CoglError thrown by the Cogl api or %NULL + * + * Simply casts a #CoglError to a #CoglError + * + * If Cogl is built with GLib support then it can safely be assumed + * that a CoglError is a GError and can be used directly with the + * GError api. + */ +#ifdef COGL_HAS_GLIB_SUPPORT +#define COGL_GLIB_ERROR(COGL_ERROR) ((CoglError *)COGL_ERROR) +#endif + +COGL_END_DECLS + +#endif /* __COGL_ERROR_H__ */ diff --git a/cogl/cogl/cogl-euler.c b/cogl/cogl/cogl-euler.c new file mode 100644 index 000000000..f877bbc6e --- /dev/null +++ b/cogl/cogl/cogl-euler.c @@ -0,0 +1,198 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "cogl-gtype-private.h" + +#include +#include + +COGL_GTYPE_DEFINE_BOXED (Euler, euler, + cogl_euler_copy, + cogl_euler_free); + +void +cogl_euler_init (CoglEuler *euler, + float heading, + float pitch, + float roll) +{ + euler->heading = heading; + euler->pitch = pitch; + euler->roll = roll; +} + +void +cogl_euler_init_from_matrix (CoglEuler *euler, + const CoglMatrix *matrix) +{ + /* + * Extracting a canonical Euler angle from a matrix: + * (where it is assumed the matrix contains no scaling, mirroring or + * skewing) + * + * A Euler angle is a combination of three rotations around mutually + * perpendicular axis. For this algorithm they are: + * + * Heading: A rotation about the Y axis by an angle H: + * | cosH 0 sinH| + * | 0 1 0| + * |-sinH 0 cosH| + * + * Pitch: A rotation around the X axis by an angle P: + * |1 0 0| + * |0 cosP -sinP| + * |0 sinP cosP| + * + * Roll: A rotation about the Z axis by an angle R: + * |cosR -sinR 0| + * |sinR cosR 0| + * | 0 0 1| + * + * When multiplied as matrices this gives: + * | cosHcosR+sinHsinPsinR sinRcosP -sinHcosR+cosHsinPsinR| + * M = |-cosHsinR+sinHsinPcosR cosRcosP sinRsinH+cosHsinPcosB| + * | sinHcosP -sinP cosHcosP | + * + * Given that there are an infinite number of ways to represent + * a given orientation, the "canonical" Euler angle is any such that: + * -180 < H < 180, + * -180 < R < 180 and + * -90 < P < 90 + * + * M[3][2] = -sinP lets us immediately solve for P = asin(-M[3][2]) + * (Note: asin has a range of +-90) + * This gives cosP + * This means we can use M[3][1] to calculate sinH: + * sinH = M[3][1]/cosP + * And use M[3][3] to calculate cosH: + * cosH = M[3][3]/cosP + * This lets us calculate H = atan2(sinH,cosH), but we optimise this: + * 1st note: atan2(x, y) does: atan(x/y) and uses the sign of x and y to + * determine the quadrant of the final angle. + * 2nd note: we know cosP is > 0 (ignoring cosP == 0) + * Therefore H = atan2((M[3][1]/cosP) / (M[3][3]/cosP)) can be simplified + * by skipping the division by cosP since it won't change the x/y ratio + * nor will it change their sign. This gives: + * H = atan2(M[3][1], M[3][3]) + * R is computed in the same way as H from M[1][2] and M[2][2] so: + * R = atan2(M[1][2], M[2][2]) + * Note: If cosP were == 0 then H and R could not be calculated as above + * because all the necessary matrix values would == 0. In other words we are + * pitched vertically and so H and R would now effectively rotate around the + * same axis - known as "Gimbal lock". In this situation we will set all the + * rotation on H and set R = 0. + * So with P = R = 0 we have cosP = 0, sinR = 0 and cosR = 1 + * We can substitute those into the above equation for M giving: + * | cosH 0 -sinH| + * |sinHsinP 0 cosHsinP| + * | 0 -sinP 0| + * And calculate H as atan2 (-M[3][2], M[1][1]) + */ + + float sinP; + float H; /* heading */ + float P; /* pitch */ + float R; /* roll */ + + /* NB: CoglMatrix provides struct members named according to the + * [row][column] indexed. So matrix->zx is row 3 column 1. */ + sinP = -matrix->zy; + + /* Determine the Pitch, avoiding domain errors with asin () which + * might occur due to previous imprecision in manipulating the + * matrix. */ + if (sinP <= -1.0f) + P = -G_PI_2; + else if (sinP >= 1.0f) + P = G_PI_2; + else + P = asinf (sinP); + + /* If P is too close to 0 then we have hit Gimbal lock */ + if (sinP > 0.999f) + { + H = atan2f (-matrix->zy, matrix->xx); + R = 0; + } + else + { + H = atan2f (matrix->zx, matrix->zz); + R = atan2f (matrix->xy, matrix->yy); + } + + euler->heading = H; + euler->pitch = P; + euler->roll = R; +} + +CoglBool +cogl_euler_equal (const void *v1, const void *v2) +{ + const CoglEuler *a = v1; + const CoglEuler *b = v2; + + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + if (v1 == v2) + return TRUE; + + return (a->heading == b->heading && + a->pitch == b->pitch && + a->roll == b->roll); +} + +CoglEuler * +cogl_euler_copy (const CoglEuler *src) +{ + if (G_LIKELY (src)) + { + CoglEuler *new = g_slice_new (CoglEuler); + memcpy (new, src, sizeof (float) * 3); + return new; + } + else + return NULL; +} + +void +cogl_euler_free (CoglEuler *euler) +{ + g_slice_free (CoglEuler, euler); +} + diff --git a/cogl/cogl/cogl-euler.h b/cogl/cogl/cogl-euler.h new file mode 100644 index 000000000..4ccbb2a66 --- /dev/null +++ b/cogl/cogl/cogl-euler.h @@ -0,0 +1,269 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_EULER_H +#define __COGL_EULER_H + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-euler + * @short_description: Functions for initializing and manipulating + * euler angles. + * + * Euler angles are a simple representation of a 3 dimensional + * rotation; comprised of 3 ordered heading, pitch and roll rotations. + * An important thing to understand is that the axis of rotation + * belong to the object being rotated and so they also rotate as each + * of the heading, pitch and roll rotations are applied. + * + * One way to consider euler angles is to imagine controlling an + * aeroplane, where you first choose a heading (Such as flying south + * east), then you set the pitch (such as 30 degrees to take off) and + * then you might set a roll, by dipping the left, wing as you prepare + * to turn. + * + * They have some advantages and limitations that it helps to be + * aware of: + * + * Advantages: + * + * + * Easy to understand and use, compared to quaternions and matrices, + * so may be a good choice for a user interface. + * + * + * Efficient storage, needing only 3 components any rotation can be + * represented. + * Actually the #CoglEuler type isn't optimized for size because + * we may cache the equivalent #CoglQuaternion along with a euler + * rotation, but it would be trivial for an application to track the + * components of euler rotations in a packed float array if optimizing + * for size was important. The values could be passed to Cogl only when + * manipulation is necessary. + * + * + * + * Disadvantages: + * + * + * Aliasing: it's possible to represent some rotations with multiple + * different heading, pitch and roll rotations. + * + * + * They can suffer from a problem called Gimbal Lock. A good + * explanation of this can be seen on wikipedia here: + * http://en.wikipedia.org/wiki/Gimbal_lock but basically two + * of the axis of rotation may become aligned and so you loose a + * degree of freedom. For example a pitch of +-90° would mean that + * heading and bank rotate around the same axis. + * + * + * If you use euler angles to orient something in 3D space and try to + * transition between orientations by interpolating the component + * angles you probably wont get the transitions you expect as they may + * not follow the shortest path between the two orientations. + * + * + * There's no standard to what order the component axis rotations are + * applied. The most common convention seems to be what we do in Cogl + * with heading (y-axis), pitch (x-axis) and then roll (z-axis), but + * other software might apply x-axis, y-axis then z-axis or any other + * order so you need to consider this if you are accepting euler + * rotations from some other software. Other software may also use + * slightly different aeronautical terms, such as "yaw" instead of + * "heading" or "bank" instead of "roll". + * + * + * + * To minimize the aliasing issue we may refer to "Canonical Euler" + * angles where heading and roll are restricted to +- 180° and pitch is + * restricted to +- 90°. If pitch is +- 90° bank is set to 0°. + * + * Quaternions don't suffer from Gimbal Lock and they can be nicely + * interpolated between, their disadvantage is that they don't have an + * intuitive representation. + * + * A common practice is to accept angles in the intuitive Euler form + * and convert them to quaternions internally to avoid Gimbal Lock and + * handle interpolations. See cogl_quaternion_init_from_euler(). + */ + +/** + * CoglEuler: + * @heading: Angle to rotate around an object's y axis + * @pitch: Angle to rotate around an object's x axis + * @roll: Angle to rotate around an object's z axis + * + * Represents an ordered rotation first of @heading degrees around an + * object's y axis, then @pitch degrees around an object's x axis and + * finally @roll degrees around an object's z axis. + * + * It's important to understand the that axis are associated + * with the object being rotated, so the axis also rotate in sequence + * with the rotations being applied. + * + * The members of a #CoglEuler can be initialized, for example, with + * cogl_euler_init() and cogl_euler_init_from_quaternion (). + * + * You may also want to look at cogl_quaternion_init_from_euler() if + * you want to do interpolation between 3d rotations. + * + * Since: 2.0 + */ +struct _CoglEuler +{ + /*< public > */ + float heading; + float pitch; + float roll; + + /*< private > */ + /* May cached a quaternion here in the future */ + float padding0; + float padding1; + float padding2; + float padding3; + float padding4; +}; +COGL_STRUCT_SIZE_ASSERT (CoglEuler, 32); + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_euler_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_euler_get_gtype (void); +#endif + +/** + * cogl_euler_init: + * @euler: The #CoglEuler angle to initialize + * @heading: Angle to rotate around an object's y axis + * @pitch: Angle to rotate around an object's x axis + * @roll: Angle to rotate around an object's z axis + * + * Initializes @euler to represent a rotation of @x_angle degrees + * around the x axis, then @y_angle degrees around the y_axis and + * @z_angle degrees around the z axis. + * + * Since: 2.0 + */ +void +cogl_euler_init (CoglEuler *euler, + float heading, + float pitch, + float roll); + +/** + * cogl_euler_init_from_matrix: + * @euler: The #CoglEuler angle to initialize + * @matrix: A #CoglMatrix containing a rotation, but no scaling, + * mirroring or skewing. + * + * Extracts a euler rotation from the given @matrix and + * initializses @euler with the component x, y and z rotation angles. + */ +void +cogl_euler_init_from_matrix (CoglEuler *euler, + const CoglMatrix *matrix); + +/** + * cogl_euler_init_from_quaternion: + * @euler: The #CoglEuler angle to initialize + * @quaternion: A #CoglEuler with the rotation to initialize with + * + * Initializes a @euler rotation with the equivalent rotation + * represented by the given @quaternion. + */ +void +cogl_euler_init_from_quaternion (CoglEuler *euler, + const CoglQuaternion *quaternion); + +/** + * cogl_euler_equal: + * @v1: The first euler angle to compare + * @v2: The second euler angle to compare + * + * Compares the two given euler angles @v1 and @v1 and it they are + * equal returns %TRUE else %FALSE. + * + * This function only checks that all three components rotations + * are numerically equal, it does not consider that some rotations + * can be represented with different component rotations + * + * Returns: %TRUE if @v1 and @v2 are equal else %FALSE. + * Since: 2.0 + */ +CoglBool +cogl_euler_equal (const void *v1, const void *v2); + +/** + * cogl_euler_copy: + * @src: A #CoglEuler to copy + * + * Allocates a new #CoglEuler and initilizes it with the component + * angles of @src. The newly allocated euler should be freed using + * cogl_euler_free(). + * + * Returns: A newly allocated #CoglEuler + * Since: 2.0 + */ +CoglEuler * +cogl_euler_copy (const CoglEuler *src); + +/** + * cogl_euler_free: + * @euler: A #CoglEuler allocated via cogl_euler_copy() + * + * Frees a #CoglEuler that was previously allocated using + * cogl_euler_copy(). + * + * Since: 2.0 + */ +void +cogl_euler_free (CoglEuler *euler); + +COGL_END_DECLS + +#endif /* __COGL_EULER_H */ + diff --git a/cogl/cogl/cogl-feature-private.c b/cogl/cogl/cogl-feature-private.c new file mode 100644 index 000000000..7c160c3c1 --- /dev/null +++ b/cogl/cogl/cogl-feature-private.c @@ -0,0 +1,234 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-context-private.h" + +#include "cogl-feature-private.h" +#include "cogl-renderer-private.h" +#include "cogl-private.h" + +CoglBool +_cogl_feature_check (CoglRenderer *renderer, + const char *driver_prefix, + const CoglFeatureData *data, + int gl_major, + int gl_minor, + CoglDriver driver, + char * const *extensions, + void *function_table) + +{ + const char *suffix = NULL; + int func_num; + CoglExtGlesAvailability gles_availability = 0; + CoglBool in_core; + + switch (driver) + { + case COGL_DRIVER_GLES1: + gles_availability = COGL_EXT_IN_GLES; + break; + case COGL_DRIVER_GLES2: + gles_availability = COGL_EXT_IN_GLES2; + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0)) + gles_availability |= COGL_EXT_IN_GLES3; + break; + case COGL_DRIVER_ANY: + g_assert_not_reached (); + case COGL_DRIVER_WEBGL: + /* FIXME: WebGL should probably have its own COGL_EXT_IN_WEBGL flag */ + break; + case COGL_DRIVER_NOP: + case COGL_DRIVER_GL: + case COGL_DRIVER_GL3: + break; + } + + /* First check whether the functions should be directly provided by + GL */ + if (((driver == COGL_DRIVER_GL || + driver == COGL_DRIVER_GL3) && + COGL_CHECK_GL_VERSION (gl_major, gl_minor, + data->min_gl_major, data->min_gl_minor)) || + (data->gles_availability & gles_availability)) + { + suffix = ""; + in_core = TRUE; + } + else + { + /* Otherwise try all of the extensions */ + const char *namespace, *namespace_suffix; + unsigned int namespace_len; + + for (namespace = data->namespaces; + *namespace; + namespace += strlen (namespace) + 1) + { + const char *extension; + GString *full_extension_name = g_string_new (""); + + /* If the namespace part contains a ':' then the suffix for + the function names is different from the name space */ + if ((namespace_suffix = strchr (namespace, ':'))) + { + namespace_len = namespace_suffix - namespace; + namespace_suffix++; + } + else + { + namespace_len = strlen (namespace); + namespace_suffix = namespace; + } + + for (extension = data->extension_names; + *extension; + extension += strlen (extension) + 1) + { + g_string_assign (full_extension_name, driver_prefix); + g_string_append_c (full_extension_name, '_'); + g_string_append_len (full_extension_name, + namespace, namespace_len); + g_string_append_c (full_extension_name, '_'); + g_string_append (full_extension_name, extension); + if (_cogl_check_extension (full_extension_name->str, + extensions)) + break; + } + + g_string_free (full_extension_name, TRUE); + + /* If we found an extension with this namespace then use it + as the suffix */ + if (*extension) + { + suffix = namespace_suffix; + break; + } + } + + in_core = FALSE; + } + + /* If we couldn't find anything that provides the functions then + give up */ + if (suffix == NULL) + goto error; + + /* Try to get all of the entry points */ + for (func_num = 0; data->functions[func_num].name; func_num++) + { + void *func; + char *full_function_name; + + full_function_name = g_strconcat (data->functions[func_num].name, + suffix, NULL); + func = _cogl_renderer_get_proc_address (renderer, + full_function_name, + in_core); + g_free (full_function_name); + + if (func == NULL) + goto error; + + /* Set the function pointer in the context */ + *(void **) ((uint8_t *) function_table + + data->functions[func_num].pointer_offset) = func; + } + + return TRUE; + + /* If the extension isn't found or one of the functions wasn't found + * then set all of the functions pointers to NULL so Cogl can safely + * do feature testing by just looking at the function pointers */ +error: + for (func_num = 0; data->functions[func_num].name; func_num++) + *(void **) ((uint8_t *) function_table + + data->functions[func_num].pointer_offset) = NULL; + + return FALSE; +} + +/* Define a set of arrays containing the functions required from GL + for each feature */ +#define COGL_EXT_BEGIN(name, \ + min_gl_major, min_gl_minor, \ + gles_availability, \ + namespaces, extension_names) \ + static const CoglFeatureFunction cogl_ext_ ## name ## _funcs[] = { +#define COGL_EXT_FUNCTION(ret, name, args) \ + { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglContext, name) }, +#define COGL_EXT_END() \ + { NULL, 0 }, \ + }; +#include "gl-prototypes/cogl-all-functions.h" + +/* Define an array of features */ +#undef COGL_EXT_BEGIN +#define COGL_EXT_BEGIN(name, \ + min_gl_major, min_gl_minor, \ + gles_availability, \ + namespaces, extension_names) \ + { min_gl_major, min_gl_minor, gles_availability, namespaces, \ + extension_names, 0, 0, 0, \ + cogl_ext_ ## name ## _funcs }, +#undef COGL_EXT_FUNCTION +#define COGL_EXT_FUNCTION(ret, name, args) +#undef COGL_EXT_END +#define COGL_EXT_END() + +static const CoglFeatureData +cogl_feature_ext_functions_data[] = + { +#include "gl-prototypes/cogl-all-functions.h" + }; + +void +_cogl_feature_check_ext_functions (CoglContext *context, + int gl_major, + int gl_minor, + char * const *gl_extensions) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (cogl_feature_ext_functions_data); i++) + _cogl_feature_check (context->display->renderer, + "GL", cogl_feature_ext_functions_data + i, + gl_major, gl_minor, context->driver, + gl_extensions, + context); +} diff --git a/cogl/cogl/cogl-feature-private.h b/cogl/cogl/cogl-feature-private.h new file mode 100644 index 000000000..a342d2333 --- /dev/null +++ b/cogl/cogl/cogl-feature-private.h @@ -0,0 +1,106 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_FEATURE_PRIVATE_H +#define __COGL_FEATURE_PRIVATE_H + +#include + + +#define COGL_CHECK_GL_VERSION(driver_major, driver_minor, \ + target_major, target_minor) \ + ((driver_major) > (target_major) || \ + ((driver_major) == (target_major) && (driver_minor) >= (target_minor))) + +typedef enum +{ + COGL_EXT_IN_GLES = (1 << 0), + COGL_EXT_IN_GLES2 = (1 << 1), + COGL_EXT_IN_GLES3 = (1 << 2) +} CoglExtGlesAvailability; + +typedef struct _CoglFeatureFunction CoglFeatureFunction; + +struct _CoglFeatureFunction +{ + /* The name of the function without the "EXT" or "ARB" suffix */ + const char *name; + /* The offset in the context of where to store the function pointer */ + unsigned int pointer_offset; +}; + +typedef struct _CoglFeatureData CoglFeatureData; + +struct _CoglFeatureData +{ + /* A minimum GL version which the functions should be defined in + without needing an extension. Set to 255,255 if it's only + provided in an extension */ + int min_gl_major, min_gl_minor; + /* Flags specifying which versions of GLES the feature is available + in core in */ + CoglExtGlesAvailability gles_availability; + /* \0 separated list of namespaces to try. Eg "EXT\0ARB\0" */ + const char *namespaces; + /* \0 separated list of required extension names without the GL_EXT + or GL_ARB prefix. Any of the extensions must be available for the + feature to be considered available. If the suffix for an + extension is different from the namespace, you can specify it + with a ':' after the namespace */ + const char *extension_names; + /* A set of feature flags to enable if the extension is available */ + CoglFeatureFlags feature_flags; + /* A set of private feature flags to enable if the extension is + * available */ + int feature_flags_private; + /* An optional corresponding winsys feature. */ + CoglWinsysFeature winsys_feature; + /* A list of functions required for this feature. Terminated with a + NULL name */ + const CoglFeatureFunction *functions; +}; + +CoglBool +_cogl_feature_check (CoglRenderer *renderer, + const char *driver_prefix, + const CoglFeatureData *data, + int gl_major, + int gl_minor, + CoglDriver driver, + char * const *extensions, + void *function_table); + +void +_cogl_feature_check_ext_functions (CoglContext *context, + int gl_major, + int gl_minor, + char * const *gl_extensions); + +#endif /* __COGL_FEATURE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-fence-private.h b/cogl/cogl/cogl-fence-private.h new file mode 100644 index 000000000..abbf51ffb --- /dev/null +++ b/cogl/cogl/cogl-fence-private.h @@ -0,0 +1,66 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_FENCE_PRIVATE_H__ +#define __COGL_FENCE_PRIVATE_H__ + +#include "cogl-fence.h" +#include "cogl-list.h" +#include "cogl-winsys-private.h" + +typedef enum +{ + FENCE_TYPE_PENDING, +#ifdef GL_ARB_sync + FENCE_TYPE_GL_ARB, +#endif + FENCE_TYPE_WINSYS, + FENCE_TYPE_ERROR +} CoglFenceType; + +struct _CoglFenceClosure +{ + CoglList link; + CoglFramebuffer *framebuffer; + + CoglFenceType type; + void *fence_obj; + + CoglFenceCallback callback; + void *user_data; +}; + +void +_cogl_fence_submit (CoglFenceClosure *fence); + +void +_cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer); + +#endif /* __COGL_FENCE_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-fence.c b/cogl/cogl/cogl-fence.c new file mode 100644 index 000000000..05c91ffd0 --- /dev/null +++ b/cogl/cogl/cogl-fence.c @@ -0,0 +1,236 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-fence.h" +#include "cogl-fence-private.h" +#include "cogl-context-private.h" +#include "cogl-winsys-private.h" + +#define FENCE_CHECK_TIMEOUT 5000 /* microseconds */ + +void * +cogl_fence_closure_get_user_data (CoglFenceClosure *closure) +{ + return closure->user_data; +} + +static void +_cogl_fence_check (CoglFenceClosure *fence) +{ + CoglContext *context = fence->framebuffer->context; + + if (fence->type == FENCE_TYPE_WINSYS) + { + const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); + CoglBool ret; + + ret = winsys->fence_is_complete (context, fence->fence_obj); + if (!ret) + return; + } +#ifdef GL_ARB_sync + else if (fence->type == FENCE_TYPE_GL_ARB) + { + GLenum arb; + + arb = context->glClientWaitSync (fence->fence_obj, + GL_SYNC_FLUSH_COMMANDS_BIT, + 0); + if (arb != GL_ALREADY_SIGNALED && arb != GL_CONDITION_SATISFIED) + return; + } +#endif + + fence->callback (NULL, /* dummy CoglFence object */ + fence->user_data); + cogl_framebuffer_cancel_fence_callback (fence->framebuffer, fence); +} + +static void +_cogl_fence_poll_dispatch (void *source, int revents) +{ + CoglContext *context = source; + CoglFenceClosure *fence, *tmp; + + _cogl_list_for_each_safe (fence, tmp, &context->fences, link) + _cogl_fence_check (fence); +} + +static int64_t +_cogl_fence_poll_prepare (void *source) +{ + CoglContext *context = source; + GList *l; + + /* If there are any pending fences in any of the journals then we + * need to flush the journal otherwise the fence will never be + * hit and the main loop might block forever */ + for (l = context->framebuffers; l; l = l->next) + { + CoglFramebuffer *fb = l->data; + + if (!_cogl_list_empty (&fb->journal->pending_fences)) + _cogl_framebuffer_flush_journal (fb); + } + + if (!_cogl_list_empty (&context->fences)) + return FENCE_CHECK_TIMEOUT; + else + return -1; +} + +void +_cogl_fence_submit (CoglFenceClosure *fence) +{ + CoglContext *context = fence->framebuffer->context; + const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); + + fence->type = FENCE_TYPE_ERROR; + + if (winsys->fence_add) + { + fence->fence_obj = winsys->fence_add (context); + if (fence->fence_obj) + { + fence->type = FENCE_TYPE_WINSYS; + goto done; + } + } + +#ifdef GL_ARB_sync + if (context->glFenceSync) + { + fence->fence_obj = context->glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, + 0); + if (fence->fence_obj) + { + fence->type = FENCE_TYPE_GL_ARB; + goto done; + } + } +#endif + + done: + _cogl_list_insert (context->fences.prev, &fence->link); + + if (!context->fences_poll_source) + { + context->fences_poll_source = + _cogl_poll_renderer_add_source (context->display->renderer, + _cogl_fence_poll_prepare, + _cogl_fence_poll_dispatch, + context); + } +} + +CoglFenceClosure * +cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer, + CoglFenceCallback callback, + void *user_data) +{ + CoglContext *context = framebuffer->context; + CoglJournal *journal = framebuffer->journal; + CoglFenceClosure *fence; + + if (!COGL_FLAGS_GET (context->features, COGL_FEATURE_ID_FENCE)) + return NULL; + + fence = g_slice_new (CoglFenceClosure); + fence->framebuffer = framebuffer; + fence->callback = callback; + fence->user_data = user_data; + fence->fence_obj = NULL; + + if (journal->entries->len) + { + _cogl_list_insert (journal->pending_fences.prev, &fence->link); + fence->type = FENCE_TYPE_PENDING; + } + else + _cogl_fence_submit (fence); + + return fence; +} + +void +cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer, + CoglFenceClosure *fence) +{ + CoglContext *context = framebuffer->context; + + if (fence->type == FENCE_TYPE_PENDING) + { + _cogl_list_remove (&fence->link); + } + else + { + _cogl_list_remove (&fence->link); + + if (fence->type == FENCE_TYPE_WINSYS) + { + const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); + + winsys->fence_destroy (context, fence->fence_obj); + } +#ifdef GL_ARB_sync + else if (fence->type == FENCE_TYPE_GL_ARB) + { + context->glDeleteSync (fence->fence_obj); + } +#endif + } + + g_slice_free (CoglFenceClosure, fence); +} + +void +_cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer) +{ + CoglJournal *journal = framebuffer->journal; + CoglContext *context = framebuffer->context; + CoglFenceClosure *fence, *tmp; + + while (!_cogl_list_empty (&journal->pending_fences)) + { + fence = _cogl_container_of (journal->pending_fences.next, + CoglFenceClosure, + link); + cogl_framebuffer_cancel_fence_callback (framebuffer, fence); + } + + _cogl_list_for_each_safe (fence, tmp, &context->fences, link) + { + if (fence->framebuffer == framebuffer) + cogl_framebuffer_cancel_fence_callback (framebuffer, fence); + } +} diff --git a/cogl/cogl/cogl-fence.h b/cogl/cogl/cogl-fence.h new file mode 100644 index 000000000..e268f8f5f --- /dev/null +++ b/cogl/cogl/cogl-fence.h @@ -0,0 +1,143 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_FENCE_H__ +#define __COGL_FENCE_H__ + +#include +#include + +/** + * SECTION:cogl-fence + * @short_description: Functions for notification of command completion + * + * Cogl allows notification of GPU command completion; users may mark + * points in the GPU command stream and receive notification when the GPU + * has executed to that point. + */ + +/** + * CoglFence: + * + * An opaque object representing a fence. This type is currently + * unused but in the future may be used to pass extra information + * about the fence completion. + * + * Since: 2.0 + * Stability: Unstable + */ +typedef struct _CoglFence CoglFence; + +/** + * CoglFenceCallback: + * @fence: Unused. In the future this parameter may be used to pass + * extra information about the fence completion but for now it + * should be ignored. + * @user_data: The private data passed to cogl_framebuffer_add_fence_callback() + * + * The callback prototype used with + * cogl_framebuffer_add_fence_callback() for notification of GPU + * command completion. + * + * Since: 2.0 + * Stability: Unstable + */ +typedef void (* CoglFenceCallback) (CoglFence *fence, + void *user_data); + +/** + * CoglFenceClosure: + * + * An opaque type representing one future callback to be made when the + * GPU command stream has passed a certain point. + * + * Since: 2.0 + * Stability: Unstable + */ +typedef struct _CoglFenceClosure CoglFenceClosure; + +/** + * cogl_frame_closure_get_user_data: + * @closure: A #CoglFenceClosure returned from cogl_framebuffer_add_fence() + * + * Returns the user_data submitted to cogl_framebuffer_add_fence() which + * returned a given #CoglFenceClosure. + * + * Since: 2.0 + * Stability: Unstable + */ +void * +cogl_fence_closure_get_user_data (CoglFenceClosure *closure); + +/** + * cogl_framebuffer_add_fence_callback: + * @framebuffer: The #CoglFramebuffer the commands have been submitted to + * @callback: (scope notified): A #CoglFenceCallback to be called when + * all commands submitted to Cogl have been executed + * @user_data: (closure): Private data that will be passed to the callback + * + * Calls the provided callback when all previously-submitted commands have + * been executed by the GPU. + * + * Returns non-NULL if the fence succeeded, or %NULL if it was unable to + * be inserted and the callback will never be called. The user does not + * need to free the closure; it will be freed automatically when the + * callback is called, or cancelled. + * + * Since: 2.0 + * Stability: Unstable + */ +CoglFenceClosure * +cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer, + CoglFenceCallback callback, + void *user_data); + +/** + * cogl_framebuffer_cancel_fence_callback: + * @framebuffer: The #CoglFramebuffer the commands were submitted to + * @closure: The #CoglFenceClosure returned from + * cogl_framebuffer_add_fence_callback() + * + * Removes a fence previously submitted with + * cogl_framebuffer_add_fence_callback(); the callback will not be + * called. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer, + CoglFenceClosure *closure); + +#endif /* __COGL_FENCE_H__ */ diff --git a/cogl/cogl/cogl-flags.h b/cogl/cogl/cogl-flags.h new file mode 100644 index 000000000..33633f0d8 --- /dev/null +++ b/cogl/cogl/cogl-flags.h @@ -0,0 +1,130 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_FLAGS_H +#define __COGL_FLAGS_H + +#include + +#include "cogl-util.h" + +COGL_BEGIN_DECLS + +/* These are macros used to implement a fixed-size array of bits. This + should be used instead of CoglBitmask when the maximum bit number + that will be set is known at compile time, for example when setting + for recording a set of known available features */ + +/* The bits are stored in an array of unsigned longs. To use these + macros, you would typically have an enum defining the available + bits with an extra last enum to define the maximum value. Then to + store the flags you would declare an array of unsigned longs sized + using COGL_FLAGS_N_LONGS_FOR_SIZE, eg: + + typedef enum { FEATURE_A, FEATURE_B, FEATURE_C, N_FEATURES } Features; + + unsigned long feature_flags[COGL_FLAGS_N_LONGS_FOR_SIZE (N_FEATURES)]; +*/ + +#define COGL_FLAGS_N_LONGS_FOR_SIZE(size) \ + (((size) + \ + (sizeof (unsigned long) * 8 - 1)) \ + / (sizeof (unsigned long) * 8)) + +/* @flag is expected to be constant so these should result in a + constant expression. This means that setting a flag is equivalent + to just setting in a bit in a global variable at a known + location */ +#define COGL_FLAGS_GET_INDEX(flag) \ + ((flag) / (sizeof (unsigned long) * 8)) +#define COGL_FLAGS_GET_MASK(flag) \ + (1UL << ((unsigned long) (flag) & \ + (sizeof (unsigned long) * 8 - 1))) + +#define COGL_FLAGS_GET(array, flag) \ + (!!((array)[COGL_FLAGS_GET_INDEX (flag)] & \ + COGL_FLAGS_GET_MASK (flag))) + +/* The expectation here is that @value will be constant so the if + statement will be optimised out */ +#define COGL_FLAGS_SET(array, flag, value) \ + G_STMT_START { \ + if (value) \ + ((array)[COGL_FLAGS_GET_INDEX (flag)] |= \ + COGL_FLAGS_GET_MASK (flag)); \ + else \ + ((array)[COGL_FLAGS_GET_INDEX (flag)] &= \ + ~COGL_FLAGS_GET_MASK (flag)); \ + } G_STMT_END + +/* Macros to help iterate an array of flags. It should be used like + * this: + * + * int n_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (...); + * unsigned long flags[n_longs]; + * int bit_num; + * + * COGL_FLAGS_FOREACH_START (flags, n_longs, bit_num) + * { + * do_something_with_the_bit (bit_num); + * } + * COGL_FLAGS_FOREACH_END; + */ +#define COGL_FLAGS_FOREACH_START(array, n_longs, bit) \ + G_STMT_START { \ + const unsigned long *_p = (array); \ + int _n_longs = (n_longs); \ + int _i; \ + \ + for (_i = 0; _i < _n_longs; _i++) \ + { \ + unsigned long _mask = *(_p++); \ + \ + (bit) = _i * sizeof (unsigned long) * 8 - 1; \ + \ + while (_mask) \ + { \ + int _next_bit = _cogl_util_ffsl (_mask); \ + (bit) += _next_bit; \ + /* This odd two-part shift is to avoid */ \ + /* shifting by sizeof (long)*8 which has */ \ + /* undefined results according to the */ \ + /* C spec (and seems to be a no-op in */ \ + /* practice) */ \ + _mask = (_mask >> (_next_bit - 1)) >> 1; \ + +#define COGL_FLAGS_FOREACH_END \ + } } } G_STMT_END + +COGL_END_DECLS + +#endif /* __COGL_FLAGS_H */ + diff --git a/cogl/cogl/cogl-frame-info-private.h b/cogl/cogl/cogl-frame-info-private.h new file mode 100644 index 000000000..32f5ba8e0 --- /dev/null +++ b/cogl/cogl/cogl-frame-info-private.h @@ -0,0 +1,50 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_FRAME_INFO_PRIVATE_H +#define __COGL_FRAME_INFO_PRIVATE_H + +#include "cogl-frame-info.h" +#include "cogl-object-private.h" + +struct _CoglFrameInfo +{ + CoglObject _parent; + + int64_t frame_counter; + int64_t presentation_time; + float refresh_rate; + + CoglOutput *output; +}; + +CoglFrameInfo *_cogl_frame_info_new (void); + +#endif /* __COGL_FRAME_INFO_PRIVATE_H */ diff --git a/cogl/cogl/cogl-frame-info.c b/cogl/cogl/cogl-frame-info.c new file mode 100644 index 000000000..2114a64b5 --- /dev/null +++ b/cogl/cogl/cogl-frame-info.c @@ -0,0 +1,81 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-frame-info-private.h" +#include "cogl-gtype-private.h" + +static void _cogl_frame_info_free (CoglFrameInfo *info); + +COGL_OBJECT_DEFINE (FrameInfo, frame_info); +COGL_GTYPE_DEFINE_CLASS (FrameInfo, frame_info); + +CoglFrameInfo * +_cogl_frame_info_new (void) +{ + CoglFrameInfo *info; + + info = g_slice_new0 (CoglFrameInfo); + + return _cogl_frame_info_object_new (info); +} + +static void +_cogl_frame_info_free (CoglFrameInfo *info) +{ + g_slice_free (CoglFrameInfo, info); +} + +int64_t +cogl_frame_info_get_frame_counter (CoglFrameInfo *info) +{ + return info->frame_counter; +} + +int64_t +cogl_frame_info_get_presentation_time (CoglFrameInfo *info) +{ + return info->presentation_time; +} + +float +cogl_frame_info_get_refresh_rate (CoglFrameInfo *info) +{ + return info->refresh_rate; +} + +CoglOutput * +cogl_frame_info_get_output (CoglFrameInfo *info) +{ + return info->output; +} diff --git a/cogl/cogl/cogl-frame-info.h b/cogl/cogl/cogl-frame-info.h new file mode 100644 index 000000000..7304c530f --- /dev/null +++ b/cogl/cogl/cogl-frame-info.h @@ -0,0 +1,148 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Owen Taylor + */ +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_FRAME_INFO_H +#define __COGL_FRAME_INFO_H + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif +#include + +G_BEGIN_DECLS + +typedef struct _CoglFrameInfo CoglFrameInfo; +#define COGL_FRAME_INFO(X) ((CoglFrameInfo *)(X)) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_frame_info_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_frame_info_get_gtype (void); +#endif + +/** + * cogl_is_frame_info: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglFrameInfo. + * + * Return value: %TRUE if the object references a #CoglFrameInfo + * and %FALSE otherwise. + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_is_frame_info (void *object); + +/** + * cogl_frame_info_get_frame_counter: + * @info: a #CoglFrameInfo object + * + * Gets the frame counter for the #CoglOnscreen that corresponds + * to this frame. + * + * Return value: The frame counter value + * Since: 1.14 + * Stability: unstable + */ +int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info); + +/** + * cogl_frame_info_get_presentation_time: + * @info: a #CoglFrameInfo object + * + * Gets the presentation time for the frame. This is the time at which + * the frame became visible to the user. + * + * The presentation time measured in nanoseconds is based on a + * monotonic time source. The time source is not necessarily + * correlated with system/wall clock time and may represent the time + * elapsed since some undefined system event such as when the system + * last booted. + * + * Linux kernel version less that 3.8 can result in + * non-monotonic timestamps being reported when using a drm based + * OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also + * incorrectly report non-monotonic timestamps. + * + * Return value: the presentation time for the frame + * Since: 1.14 + * Stability: unstable + */ +int64_t cogl_frame_info_get_presentation_time (CoglFrameInfo *info); + +/** + * cogl_frame_info_get_refresh_rate: + * @info: a #CoglFrameInfo object + * + * Gets the refresh rate in Hertz for the output that the frame was on + * at the time the frame was presented. + * + * Some platforms can't associate a #CoglOutput with a + * #CoglFrameInfo object but are able to report a refresh rate via + * this api. Therefore if you need this information then this api is + * more reliable than using cogl_frame_info_get_output() followed by + * cogl_output_get_refresh_rate(). + * + * Return value: the refresh rate in Hertz + * Since: 1.14 + * Stability: unstable + */ +float cogl_frame_info_get_refresh_rate (CoglFrameInfo *info); + +/** + * cogl_frame_info_get_output: + * @info: a #CoglFrameInfo object + * + * Gets the #CoglOutput that the swapped frame was presented to. + * + * Return value: (transfer none): The #CoglOutput that the frame was + * presented to, or %NULL if this could not be determined. + * Since: 1.14 + * Stability: unstable + */ +CoglOutput * +cogl_frame_info_get_output (CoglFrameInfo *info); + +G_END_DECLS + +#endif /* __COGL_FRAME_INFO_H */ diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h new file mode 100644 index 000000000..99ac2fba9 --- /dev/null +++ b/cogl/cogl/cogl-framebuffer-private.h @@ -0,0 +1,514 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_FRAMEBUFFER_PRIVATE_H +#define __COGL_FRAMEBUFFER_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-matrix-stack-private.h" +#include "cogl-journal-private.h" +#include "cogl-winsys-private.h" +#include "cogl-attribute-private.h" +#include "cogl-offscreen.h" +#include "cogl-gl-header.h" +#include "cogl-clip-stack.h" + +#ifdef COGL_HAS_XLIB_SUPPORT +#include +#endif + +#ifdef COGL_HAS_GLX_SUPPORT +#include +#include +#endif + +typedef enum _CoglFramebufferType { + COGL_FRAMEBUFFER_TYPE_ONSCREEN, + COGL_FRAMEBUFFER_TYPE_OFFSCREEN +} CoglFramebufferType; + +typedef struct +{ + CoglSwapChain *swap_chain; + CoglBool need_stencil; + int samples_per_pixel; + CoglBool swap_throttled; + CoglBool depth_texture_enabled; + CoglBool stereo_enabled; +} CoglFramebufferConfig; + +/* Flags to pass to _cogl_offscreen_new_with_texture_full */ +typedef enum +{ + COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1 +} CoglOffscreenFlags; + +/* XXX: The order of these indices determines the order they are + * flushed. + * + * Flushing clip state may trash the modelview and projection matrices + * so we must do it before flushing the matrices. + */ +typedef enum _CoglFramebufferStateIndex +{ + COGL_FRAMEBUFFER_STATE_INDEX_BIND = 0, + COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT = 1, + COGL_FRAMEBUFFER_STATE_INDEX_CLIP = 2, + COGL_FRAMEBUFFER_STATE_INDEX_DITHER = 3, + COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW = 4, + COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION = 5, + COGL_FRAMEBUFFER_STATE_INDEX_COLOR_MASK = 6, + COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING = 7, + COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE = 8, + COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE = 9, + COGL_FRAMEBUFFER_STATE_INDEX_MAX = 10 +} CoglFramebufferStateIndex; + +typedef enum _CoglFramebufferState +{ + COGL_FRAMEBUFFER_STATE_BIND = 1<<0, + COGL_FRAMEBUFFER_STATE_VIEWPORT = 1<<1, + COGL_FRAMEBUFFER_STATE_CLIP = 1<<2, + COGL_FRAMEBUFFER_STATE_DITHER = 1<<3, + COGL_FRAMEBUFFER_STATE_MODELVIEW = 1<<4, + COGL_FRAMEBUFFER_STATE_PROJECTION = 1<<5, + COGL_FRAMEBUFFER_STATE_COLOR_MASK = 1<<6, + COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING = 1<<7, + COGL_FRAMEBUFFER_STATE_DEPTH_WRITE = 1<<8, + COGL_FRAMEBUFFER_STATE_STEREO_MODE = 1<<9 +} CoglFramebufferState; + +#define COGL_FRAMEBUFFER_STATE_ALL ((1<config to configure if we want a depth or stencil buffer so + * we can get rid of these flags */ + CoglOffscreenFlags create_flags; +}; + +void +_cogl_framebuffer_init (CoglFramebuffer *framebuffer, + CoglContext *ctx, + CoglFramebufferType type, + int width, + int height); + +/* XXX: For a public api we might instead want a way to explicitly + * set the _premult status of a framebuffer or what components we + * care about instead of exposing the CoglPixelFormat + * internal_format. + * + * The current use case for this api is where we create an offscreen + * framebuffer for a shared atlas texture that has a format of + * RGBA_8888 disregarding the premultiplied alpha status for + * individual atlased textures or whether the alpha component is being + * discarded. We want to overried the internal_format that will be + * derived from the texture. + */ +void +_cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer, + CoglPixelFormat internal_format); + +void _cogl_framebuffer_free (CoglFramebuffer *framebuffer); + +const CoglWinsysVtable * +_cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha); + +void +_cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_mark_mid_scene (CoglFramebuffer *framebuffer); + +/* + * _cogl_framebuffer_get_clip_stack: + * @framebuffer: A #CoglFramebuffer + * + * Gets a pointer to the current clip stack. This can be used to later + * return to the same clip stack state with + * _cogl_framebuffer_set_clip_stack(). A reference is not taken on the + * stack so if you want to keep it you should call + * _cogl_clip_stack_ref(). + * + * Return value: a pointer to the @framebuffer clip stack. + */ +CoglClipStack * +_cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer); + +/* + * _cogl_framebuffer_set_clip_stack: + * @framebuffer: A #CoglFramebuffer + * @stack: a pointer to the replacement clip stack + * + * Replaces the @framebuffer clip stack with @stack. + */ +void +_cogl_framebuffer_set_clip_stack (CoglFramebuffer *framebuffer, + CoglClipStack *stack); + +CoglMatrixStack * +_cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer); + +CoglMatrixStack * +_cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer, + CoglFramebuffer *dependency); + +void +_cogl_framebuffer_remove_all_dependencies (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state); + +CoglFramebuffer * +_cogl_get_read_framebuffer (void); + +GSList * +_cogl_create_framebuffer_stack (void); + +void +_cogl_free_framebuffer_stack (GSList *stack); + +/* + * _cogl_offscreen_new_with_texture_full: + * @texture: A #CoglTexture pointer + * @create_flags: Flags specifying how to create the FBO + * @level: The mipmap level within the texture to target + * + * Creates a new offscreen buffer which will target the given + * texture. By default the buffer will have a depth and stencil + * buffer. This can be disabled by passing + * %COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL in @create_flags. + * + * Return value: the new CoglOffscreen object. + */ +CoglOffscreen * +_cogl_offscreen_new_with_texture_full (CoglTexture *texture, + CoglOffscreenFlags create_flags, + int level); + +/* + * _cogl_push_framebuffers: + * @draw_buffer: A pointer to the buffer used for drawing + * @read_buffer: A pointer to the buffer used for reading back pixels + * + * Redirects drawing and reading to the specified framebuffers as in + * cogl_push_framebuffer() except that it allows the draw and read + * buffer to be different. The buffers are pushed as a pair so that + * they can later both be restored with a single call to + * cogl_pop_framebuffer(). + */ +void +_cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer); + +/* + * _cogl_blit_framebuffer: + * @src: The source #CoglFramebuffer + * @dest: The destination #CoglFramebuffer + * @src_x: Source x position + * @src_y: Source y position + * @dst_x: Destination x position + * @dst_y: Destination y position + * @width: Width of region to copy + * @height: Height of region to copy + * + * This blits a region of the color buffer of the current draw buffer + * to the current read buffer. The draw and read buffers can be set up + * using _cogl_push_framebuffers(). This function should only be + * called if the COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT feature is + * advertised. The two buffers must both be offscreen and have the + * same format. + * + * Note that this function differs a lot from the glBlitFramebuffer + * function provided by the GL_EXT_framebuffer_blit extension. Notably + * it doesn't support having different sizes for the source and + * destination rectangle. This isn't supported by the corresponding + * GL_ANGLE_framebuffer_blit extension on GLES2.0 and it doesn't seem + * like a particularly useful feature. If the application wanted to + * scale the results it may make more sense to draw a primitive + * instead. + * + * We can only really support blitting between two offscreen buffers + * for this function on GLES2.0. This is because we effectively render + * upside down to offscreen buffers to maintain Cogl's representation + * of the texture coordinate system where 0,0 is the top left of the + * texture. If we were to blit from an offscreen to an onscreen buffer + * then we would need to mirror the blit along the x-axis but the GLES + * extension does not support this. + * + * The GL function is documented to be affected by the scissor. This + * function therefore ensure that an empty clip stack is flushed + * before performing the blit which means the scissor is effectively + * ignored. + * + * The function also doesn't support specifying the buffers to copy + * and instead only the color buffer is copied. When copying the depth + * or stencil buffers the extension on GLES2.0 only supports copying + * the full buffer which would be awkward to document with this + * API. If we wanted to support that feature it may be better to have + * a separate function to copy the entire buffer for a given mask. + */ +void +_cogl_blit_framebuffer (CoglFramebuffer *src, + CoglFramebuffer *dest, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height); + +void +_cogl_framebuffer_push_projection (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_pop_projection (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_save_clip_stack (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_restore_clip_stack (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_unref (CoglFramebuffer *framebuffer); + +/* This can be called directly by the CoglJournal to draw attributes + * skipping the implicit journal flush, the framebuffer flush and + * pipeline validation. */ +void +_cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +void +_cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +gboolean +_cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx, + CoglTexture *texture, + int texture_level, + int texture_level_width, + int texture_level_height, + CoglTexture *depth_texture, + CoglFramebufferConfig *config, + CoglOffscreenAllocateFlags flags, + CoglGLFramebuffer *gl_framebuffer); + +unsigned long +_cogl_framebuffer_compare (CoglFramebuffer *a, + CoglFramebuffer *b, + unsigned long state); + +static inline CoglMatrixEntry * +_cogl_framebuffer_get_modelview_entry (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + return modelview_stack->last_entry; +} + +static inline CoglMatrixEntry * +_cogl_framebuffer_get_projection_entry (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + return projection_stack->last_entry; +} + +CoglBool +_cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error); + +/* + * _cogl_framebuffer_get_stencil_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of stencil bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 2.0 + * Stability: unstable + */ +int +_cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer); + +#endif /* __COGL_FRAMEBUFFER_PRIVATE_H */ diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c new file mode 100644 index 000000000..1f70d3dc2 --- /dev/null +++ b/cogl/cogl/cogl-framebuffer.c @@ -0,0 +1,2544 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-debug.h" +#include "cogl-context-private.h" +#include "cogl-display-private.h" +#include "cogl-renderer-private.h" +#include "cogl-object-private.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-clip-stack.h" +#include "cogl-journal-private.h" +#include "cogl-winsys-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-matrix-private.h" +#include "cogl-primitive-private.h" +#include "cogl-offscreen.h" +#include "cogl1-context.h" +#include "cogl-private.h" +#include "cogl-primitives-private.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-gtype-private.h" + +extern CoglObjectClass _cogl_onscreen_class; + +#ifdef COGL_ENABLE_DEBUG +static CoglUserDataKey wire_pipeline_key; +#endif + +static void _cogl_offscreen_free (CoglOffscreen *offscreen); + +COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (Offscreen, offscreen, + _cogl_offscreen_class.virt_unref = + _cogl_framebuffer_unref); +COGL_GTYPE_DEFINE_CLASS (Offscreen, offscreen); +COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (offscreen); +COGL_GTYPE_DEFINE_INTERFACE (Framebuffer, framebuffer); + +/* XXX: + * The CoglObject macros don't support any form of inheritance, so for + * now we implement the CoglObject support for the CoglFramebuffer + * abstract class manually. + */ + +uint32_t +cogl_framebuffer_error_quark (void) +{ + return g_quark_from_static_string ("cogl-framebuffer-error-quark"); +} + +CoglBool +cogl_is_framebuffer (void *object) +{ + CoglObject *obj = object; + + if (obj == NULL) + return FALSE; + + return (obj->klass == &_cogl_onscreen_class || + obj->klass == &_cogl_offscreen_class); +} + +void +_cogl_framebuffer_init (CoglFramebuffer *framebuffer, + CoglContext *ctx, + CoglFramebufferType type, + int width, + int height) +{ + framebuffer->context = ctx; + + framebuffer->type = type; + framebuffer->width = width; + framebuffer->height = height; + framebuffer->internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + framebuffer->viewport_x = 0; + framebuffer->viewport_y = 0; + framebuffer->viewport_width = width; + framebuffer->viewport_height = height; + framebuffer->viewport_age = 0; + framebuffer->viewport_age_for_scissor_workaround = -1; + framebuffer->dither_enabled = TRUE; + framebuffer->depth_writing_enabled = TRUE; + + framebuffer->modelview_stack = cogl_matrix_stack_new (ctx); + framebuffer->projection_stack = cogl_matrix_stack_new (ctx); + + framebuffer->dirty_bitmasks = TRUE; + + framebuffer->color_mask = COGL_COLOR_MASK_ALL; + + framebuffer->samples_per_pixel = 0; + + framebuffer->clip_stack = NULL; + + framebuffer->journal = _cogl_journal_new (framebuffer); + + /* Ensure we know the framebuffer->clear_color* members can't be + * referenced for our fast-path read-pixel optimization (see + * _cogl_journal_try_read_pixel()) until some region of the + * framebuffer is initialized. + */ + framebuffer->clear_clip_dirty = TRUE; + + /* XXX: We have to maintain a central list of all framebuffers + * because at times we need to be able to flush all known journals. + * + * Examples where we need to flush all journals are: + * - because journal entries can reference OpenGL texture + * coordinates that may not survive texture-atlas reorganization + * so we need the ability to flush those entries. + * - because although we generally advise against modifying + * pipelines after construction we have to handle that possibility + * and since pipelines may be referenced in journal entries we + * need to be able to flush them before allowing the pipelines to + * be changed. + * + * Note we don't maintain a list of journals and associate + * framebuffers with journals by e.g. having a journal->framebuffer + * reference since that would introduce a circular reference. + * + * Note: As a future change to try and remove the need to index all + * journals it might be possible to defer resolving of OpenGL + * texture coordinates for rectangle primitives until we come to + * flush a journal. This would mean for instance that a single + * rectangle entry in a journal could later be expanded into + * multiple quad primitives to handle sliced textures but would mean + * we don't have to worry about retaining references to OpenGL + * texture coordinates that may later become invalid. + */ + ctx->framebuffers = g_list_prepend (ctx->framebuffers, framebuffer); +} + +void +_cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer, + CoglPixelFormat internal_format) +{ + framebuffer->internal_format = internal_format; +} + +void +_cogl_framebuffer_free (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + _cogl_fence_cancel_fences_for_framebuffer (framebuffer); + + _cogl_clip_stack_unref (framebuffer->clip_stack); + + cogl_object_unref (framebuffer->modelview_stack); + framebuffer->modelview_stack = NULL; + + cogl_object_unref (framebuffer->projection_stack); + framebuffer->projection_stack = NULL; + + cogl_object_unref (framebuffer->journal); + + if (ctx->viewport_scissor_workaround_framebuffer == framebuffer) + ctx->viewport_scissor_workaround_framebuffer = NULL; + + ctx->framebuffers = g_list_remove (ctx->framebuffers, framebuffer); + + if (ctx->current_draw_buffer == framebuffer) + ctx->current_draw_buffer = NULL; + if (ctx->current_read_buffer == framebuffer) + ctx->current_read_buffer = NULL; +} + +const CoglWinsysVtable * +_cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer) +{ + return framebuffer->context->display->renderer->winsys_vtable; +} + +/* This version of cogl_clear can be used internally as an alternative + * to avoid flushing the journal or the framebuffer state. This is + * needed when doing operations that may be called whiling flushing + * the journal */ +void +_cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha) +{ + CoglContext *ctx = framebuffer->context; + + if (!buffers) + { + static CoglBool shown = FALSE; + + if (!shown) + { + g_warning ("You should specify at least one auxiliary buffer " + "when calling cogl_framebuffer_clear"); + } + + return; + } + + ctx->driver_vtable->framebuffer_clear (framebuffer, + buffers, + red, green, blue, alpha); +} + +void +_cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer) +{ + framebuffer->clear_clip_dirty = TRUE; +} + +void +_cogl_framebuffer_mark_mid_scene (CoglFramebuffer *framebuffer) +{ + framebuffer->mid_scene = TRUE; +} + +void +cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha) +{ + CoglContext *ctx = framebuffer->context; + CoglClipStack *clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer); + int scissor_x0; + int scissor_y0; + int scissor_x1; + int scissor_y1; + CoglBool saved_viewport_scissor_workaround; + + _cogl_clip_stack_get_bounds (clip_stack, + &scissor_x0, &scissor_y0, + &scissor_x1, &scissor_y1); + + /* NB: the previous clear could have had an arbitrary clip. + * NB: everything for the last frame might still be in the journal + * but we can't assume anything about how each entry was + * clipped. + * NB: Clutter will scissor its pick renders which would mean all + * journal entries have a common ClipStack entry, but without + * a layering violation Cogl has to explicitly walk the journal + * entries to determine if this is the case. + * NB: We have a software only read-pixel optimization in the + * journal that determines the color at a given framebuffer + * coordinate for simple scenes without rendering with the GPU. + * When Clutter is hitting this fast-path we can expect to + * receive calls to clear the framebuffer with an un-flushed + * journal. + * NB: To fully support software based picking for Clutter we + * need to be able to reliably detect when the contents of a + * journal can be discarded and when we can skip the call to + * glClear because it matches the previous clear request. + */ + + /* Note: we don't check for the stencil buffer being cleared here + * since there isn't any public cogl api to manipulate the stencil + * buffer. + * + * Note: we check for an exact clip match here because + * 1) a smaller clip could mean existing journal entries may + * need to contribute to regions outside the new clear-clip + * 2) a larger clip would mean we need to issue a real + * glClear and we only care about cases avoiding a + * glClear. + * + * Note: Comparing without an epsilon is considered + * appropriate here. + */ + if (buffers & COGL_BUFFER_BIT_COLOR && + buffers & COGL_BUFFER_BIT_DEPTH && + !framebuffer->clear_clip_dirty && + framebuffer->clear_color_red == red && + framebuffer->clear_color_green == green && + framebuffer->clear_color_blue == blue && + framebuffer->clear_color_alpha == alpha && + scissor_x0 == framebuffer->clear_clip_x0 && + scissor_y0 == framebuffer->clear_clip_y0 && + scissor_x1 == framebuffer->clear_clip_x1 && + scissor_y1 == framebuffer->clear_clip_y1) + { + /* NB: We only have to consider the clip state of journal + * entries if the current clear is clipped since otherwise we + * know every pixel of the framebuffer is affected by the clear + * and so all journal entries become redundant and can simply be + * discarded. + */ + if (clip_stack) + { + /* + * Note: the function for checking the journal entries is + * quite strict. It avoids detailed checking of all entry + * clip_stacks by only checking the details of the first + * entry and then it only verifies that the remaining + * entries share the same clip_stack ancestry. This means + * it's possible for some false negatives here but that will + * just result in us falling back to a real clear. + */ + if (_cogl_journal_all_entries_within_bounds (framebuffer->journal, + scissor_x0, scissor_y0, + scissor_x1, scissor_y1)) + { + _cogl_journal_discard (framebuffer->journal); + goto cleared; + } + } + else + { + _cogl_journal_discard (framebuffer->journal); + goto cleared; + } + } + + COGL_NOTE (DRAW, "Clear begin"); + + _cogl_framebuffer_flush_journal (framebuffer); + + /* XXX: ONGOING BUG: Intel viewport scissor + * + * The semantics of cogl_framebuffer_clear() are that it should not + * be affected by the current viewport and so if we are currently + * applying a workaround for viewport scissoring we need to + * temporarily disable the workaround before clearing so any + * special scissoring for the workaround will be removed first. + * + * Note: we only need to disable the workaround if the current + * viewport doesn't match the framebuffer's size since otherwise + * the workaround wont affect clearing anyway. + */ + if (ctx->needs_viewport_scissor_workaround && + (framebuffer->viewport_x != 0 || + framebuffer->viewport_y != 0 || + framebuffer->viewport_width != framebuffer->width || + framebuffer->viewport_height != framebuffer->height)) + { + saved_viewport_scissor_workaround = TRUE; + ctx->needs_viewport_scissor_workaround = FALSE; + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + } + else + saved_viewport_scissor_workaround = FALSE; + + /* NB: _cogl_framebuffer_flush_state may disrupt various state (such + * as the pipeline state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_framebuffer_flush_state (framebuffer, framebuffer, + COGL_FRAMEBUFFER_STATE_ALL); + + _cogl_framebuffer_clear_without_flush4f (framebuffer, buffers, + red, green, blue, alpha); + + /* XXX: ONGOING BUG: Intel viewport scissor + * + * See comment about temporarily disabling this workaround above + */ + if (saved_viewport_scissor_workaround) + { + ctx->needs_viewport_scissor_workaround = TRUE; + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + } + + /* This is a debugging variable used to visually display the quad + * batches from the journal. It is reset here to increase the + * chances of getting the same colours for each frame during an + * animation */ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES)) && + buffers & COGL_BUFFER_BIT_COLOR) + { + framebuffer->context->journal_rectangles_color = 1; + } + + COGL_NOTE (DRAW, "Clear end"); + +cleared: + + _cogl_framebuffer_mark_mid_scene (framebuffer); + _cogl_framebuffer_mark_clear_clip_dirty (framebuffer); + + if (buffers & COGL_BUFFER_BIT_COLOR && buffers & COGL_BUFFER_BIT_DEPTH) + { + /* For our fast-path for reading back a single pixel of simple + * scenes where the whole frame is in the journal we need to + * track the cleared color of the framebuffer in case the point + * read doesn't intersect any of the journal rectangles. */ + framebuffer->clear_clip_dirty = FALSE; + framebuffer->clear_color_red = red; + framebuffer->clear_color_green = green; + framebuffer->clear_color_blue = blue; + framebuffer->clear_color_alpha = alpha; + + /* NB: A clear may be scissored so we need to track the extents + * that the clear is applicable too... */ + if (clip_stack) + { + _cogl_clip_stack_get_bounds (clip_stack, + &framebuffer->clear_clip_x0, + &framebuffer->clear_clip_y0, + &framebuffer->clear_clip_x1, + &framebuffer->clear_clip_y1); + } + else + { + /* FIXME: set degenerate clip */ + } + } +} + +/* Note: the 'buffers' and 'color' arguments were switched around on + * purpose compared to the original cogl_clear API since it was odd + * that you would be expected to specify a color before even + * necessarily choosing to clear the color buffer. + */ +void +cogl_framebuffer_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + const CoglColor *color) +{ + cogl_framebuffer_clear4f (framebuffer, buffers, + cogl_color_get_red_float (color), + cogl_color_get_green_float (color), + cogl_color_get_blue_float (color), + cogl_color_get_alpha_float (color)); +} + +/* We will lazily allocate framebuffers if necessary when querying + * their size/viewport but note we need to be careful in the case of + * onscreen framebuffers that are instantiated with an initial request + * size that we don't trigger an allocation when this is queried since + * that would lead to a recursion when the winsys backend queries this + * requested size during allocation. */ +static void +ensure_size_initialized (CoglFramebuffer *framebuffer) +{ + /* In the case of offscreen framebuffers backed by a texture then + * until that texture has been allocated we might not know the size + * of the framebuffer */ + if (framebuffer->width < 0) + { + /* Currently we assume the size is always initialized for + * onscreen framebuffers. */ + _COGL_RETURN_IF_FAIL (cogl_is_offscreen (framebuffer)); + + /* We also assume the size would have been initialized if the + * framebuffer were allocated. */ + _COGL_RETURN_IF_FAIL (!framebuffer->allocated); + + cogl_framebuffer_allocate (framebuffer, NULL); + } +} + +int +cogl_framebuffer_get_width (CoglFramebuffer *framebuffer) +{ + ensure_size_initialized (framebuffer); + return framebuffer->width; +} + +int +cogl_framebuffer_get_height (CoglFramebuffer *framebuffer) +{ + ensure_size_initialized (framebuffer); + return framebuffer->height; +} + +CoglClipStack * +_cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer) +{ + return framebuffer->clip_stack; +} + +void +_cogl_framebuffer_set_clip_stack (CoglFramebuffer *framebuffer, + CoglClipStack *stack) +{ + _cogl_clip_stack_ref (stack); + _cogl_clip_stack_unref (framebuffer->clip_stack); + framebuffer->clip_stack = stack; +} + +void +cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer, + float x, + float y, + float width, + float height) +{ + CoglContext *context = framebuffer->context; + + _COGL_RETURN_IF_FAIL (width > 0 && height > 0); + + if (framebuffer->viewport_x == x && + framebuffer->viewport_y == y && + framebuffer->viewport_width == width && + framebuffer->viewport_height == height) + return; + + _cogl_framebuffer_flush_journal (framebuffer); + + framebuffer->viewport_x = x; + framebuffer->viewport_y = y; + framebuffer->viewport_width = width; + framebuffer->viewport_height = height; + framebuffer->viewport_age++; + + if (context->current_draw_buffer == framebuffer) + { + context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_VIEWPORT; + + if (context->needs_viewport_scissor_workaround) + context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + } +} + +float +cogl_framebuffer_get_viewport_x (CoglFramebuffer *framebuffer) +{ + return framebuffer->viewport_x; +} + +float +cogl_framebuffer_get_viewport_y (CoglFramebuffer *framebuffer) +{ + return framebuffer->viewport_y; +} + +float +cogl_framebuffer_get_viewport_width (CoglFramebuffer *framebuffer) +{ + ensure_size_initialized (framebuffer); + return framebuffer->viewport_width; +} + +float +cogl_framebuffer_get_viewport_height (CoglFramebuffer *framebuffer) +{ + ensure_size_initialized (framebuffer); + return framebuffer->viewport_height; +} + +void +cogl_framebuffer_get_viewport4fv (CoglFramebuffer *framebuffer, + float *viewport) +{ + ensure_size_initialized (framebuffer); + + viewport[0] = framebuffer->viewport_x; + viewport[1] = framebuffer->viewport_y; + viewport[2] = framebuffer->viewport_width; + viewport[3] = framebuffer->viewport_height; +} + +CoglMatrixStack * +_cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer) +{ + return framebuffer->modelview_stack; +} + +CoglMatrixStack * +_cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer) +{ + return framebuffer->projection_stack; +} + +void +_cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer, + CoglFramebuffer *dependency) +{ + GList *l; + + for (l = framebuffer->deps; l; l = l->next) + { + CoglFramebuffer *existing_dep = l->data; + if (existing_dep == dependency) + return; + } + + /* TODO: generalize the primed-array type structure we e.g. use for + * cogl_object_set_user_data or for pipeline children as a way to + * avoid quite a lot of mid-scene micro allocations here... */ + framebuffer->deps = + g_list_prepend (framebuffer->deps, cogl_object_ref (dependency)); +} + +void +_cogl_framebuffer_remove_all_dependencies (CoglFramebuffer *framebuffer) +{ + GList *l; + for (l = framebuffer->deps; l; l = l->next) + cogl_object_unref (l->data); + g_list_free (framebuffer->deps); + framebuffer->deps = NULL; +} + +void +_cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer) +{ + _cogl_journal_flush (framebuffer->journal); +} + +void +_cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer) +{ + GList *l; + for (l = framebuffer->deps; l; l = l->next) + _cogl_framebuffer_flush_journal (l->data); + _cogl_framebuffer_remove_all_dependencies (framebuffer); +} + +CoglOffscreen * +_cogl_offscreen_new_with_texture_full (CoglTexture *texture, + CoglOffscreenFlags create_flags, + int level) +{ + CoglContext *ctx = texture->context; + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglOffscreen *ret; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_texture (texture), NULL); + + offscreen = g_new0 (CoglOffscreen, 1); + offscreen->texture = cogl_object_ref (texture); + offscreen->texture_level = level; + offscreen->create_flags = create_flags; + + fb = COGL_FRAMEBUFFER (offscreen); + + /* NB: we can't assume we can query the texture's width yet, since + * it might not have been allocated yet and for example if the + * texture is being loaded from a file then the file might not + * have been read yet. */ + + _cogl_framebuffer_init (fb, + ctx, + COGL_FRAMEBUFFER_TYPE_OFFSCREEN, + -1, /* unknown width, until allocation */ + -1); /* unknown height until allocation */ + + ret = _cogl_offscreen_object_new (offscreen); + + _cogl_texture_associate_framebuffer (texture, fb); + + return ret; +} + +/* XXX: deprecated api */ +CoglOffscreen * +cogl_offscreen_new_to_texture (CoglTexture *texture) +{ + CoglOffscreen *ret = _cogl_offscreen_new_with_texture_full (texture, 0, 0); + CoglError *error = NULL; + + if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (ret), &error)) + { + cogl_object_unref (ret); + cogl_error_free (error); + ret = NULL; + } + + return ret; +} + +CoglOffscreen * +cogl_offscreen_new_with_texture (CoglTexture *texture) +{ + return _cogl_offscreen_new_with_texture_full (texture, 0, 0); +} + +static void +_cogl_offscreen_free (CoglOffscreen *offscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen); + CoglContext *ctx = framebuffer->context; + + ctx->driver_vtable->offscreen_free (offscreen); + + /* Chain up to parent */ + _cogl_framebuffer_free (framebuffer); + + if (offscreen->texture != NULL) + cogl_object_unref (offscreen->texture); + + if (offscreen->depth_texture != NULL) + cogl_object_unref (offscreen->depth_texture); + + g_free (offscreen); +} + +CoglBool +cogl_framebuffer_allocate (CoglFramebuffer *framebuffer, + CoglError **error) +{ + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); + CoglContext *ctx = framebuffer->context; + + if (framebuffer->allocated) + return TRUE; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + { + if (framebuffer->config.depth_texture_enabled) + { + _cogl_set_error (error, COGL_FRAMEBUFFER_ERROR, + COGL_FRAMEBUFFER_ERROR_ALLOCATE, + "Can't allocate onscreen framebuffer with a " + "texture based depth buffer"); + return FALSE; + } + + if (!winsys->onscreen_init (onscreen, error)) + return FALSE; + + /* If the winsys doesn't support dirty events then we'll report + * one on allocation so that if the application only paints in + * response to dirty events then it will at least paint once to + * start */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_DIRTY_EVENTS)) + _cogl_onscreen_queue_full_dirty (onscreen); + } + else + { + CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer); + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + _cogl_set_error (error, COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Offscreen framebuffers not supported by system"); + return FALSE; + } + + if (!cogl_texture_allocate (offscreen->texture, error)) + return FALSE; + + /* NB: it's only after allocating the texture that we will + * determine whether a texture needs slicing... */ + if (cogl_texture_is_sliced (offscreen->texture)) + { + _cogl_set_error (error, COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Can't create offscreen framebuffer from " + "sliced texture"); + return FALSE; + } + + /* Now that the texture has been allocated we can determine a + * size for the framebuffer... */ + framebuffer->width = cogl_texture_get_width (offscreen->texture); + framebuffer->height = cogl_texture_get_height (offscreen->texture); + framebuffer->viewport_width = framebuffer->width; + framebuffer->viewport_height = framebuffer->height; + + /* Forward the texture format as the internal format of the + * framebuffer */ + framebuffer->internal_format = + _cogl_texture_get_format (offscreen->texture); + + if (!ctx->driver_vtable->offscreen_allocate (offscreen, error)) + return FALSE; + } + + framebuffer->allocated = TRUE; + + return TRUE; +} + +static unsigned long +_cogl_framebuffer_compare_viewport_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + if (a->viewport_x != b->viewport_x || + a->viewport_y != b->viewport_y || + a->viewport_width != b->viewport_width || + a->viewport_height != b->viewport_height || + /* NB: we render upside down to offscreen framebuffers and that + * can affect how we setup the GL viewport... */ + a->type != b->type) + { + unsigned long differences = COGL_FRAMEBUFFER_STATE_VIEWPORT; + CoglContext *context = a->context; + + /* XXX: ONGOING BUG: Intel viewport scissor + * + * Intel gen6 drivers don't currently correctly handle offset + * viewports, since primitives aren't clipped within the bounds of + * the viewport. To workaround this we push our own clip for the + * viewport that will use scissoring to ensure we clip as expected. + * + * This workaround implies that a change in viewport state is + * effectively also a change in the clipping state. + * + * TODO: file a bug upstream! + */ + if (G_UNLIKELY (context->needs_viewport_scissor_workaround)) + differences |= COGL_FRAMEBUFFER_STATE_CLIP; + + return differences; + } + else + return 0; +} + +static unsigned long +_cogl_framebuffer_compare_clip_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + if (a->clip_stack != b->clip_stack) + return COGL_FRAMEBUFFER_STATE_CLIP; + else + return 0; +} + +static unsigned long +_cogl_framebuffer_compare_dither_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + return a->dither_enabled != b->dither_enabled ? + COGL_FRAMEBUFFER_STATE_DITHER : 0; +} + +static unsigned long +_cogl_framebuffer_compare_modelview_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + /* We always want to flush the modelview state. All this does is set + the current modelview stack on the context to the framebuffer's + stack. */ + return COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +static unsigned long +_cogl_framebuffer_compare_projection_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + /* We always want to flush the projection state. All this does is + set the current projection stack on the context to the + framebuffer's stack. */ + return COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +static unsigned long +_cogl_framebuffer_compare_color_mask_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + if (cogl_framebuffer_get_color_mask (a) != + cogl_framebuffer_get_color_mask (b)) + return COGL_FRAMEBUFFER_STATE_COLOR_MASK; + else + return 0; +} + +static unsigned long +_cogl_framebuffer_compare_front_face_winding_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + if (a->type != b->type) + return COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING; + else + return 0; +} + +static unsigned long +_cogl_framebuffer_compare_depth_write_state (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + return a->depth_writing_enabled != b->depth_writing_enabled ? + COGL_FRAMEBUFFER_STATE_DEPTH_WRITE : 0; +} + +static unsigned long +_cogl_framebuffer_compare_stereo_mode (CoglFramebuffer *a, + CoglFramebuffer *b) +{ + return a->stereo_mode != b->stereo_mode ? + COGL_FRAMEBUFFER_STATE_STEREO_MODE : 0; +} + +unsigned long +_cogl_framebuffer_compare (CoglFramebuffer *a, + CoglFramebuffer *b, + unsigned long state) +{ + unsigned long differences = 0; + int bit; + + if (state & COGL_FRAMEBUFFER_STATE_BIND) + { + differences |= COGL_FRAMEBUFFER_STATE_BIND; + state &= ~COGL_FRAMEBUFFER_STATE_BIND; + } + + COGL_FLAGS_FOREACH_START (&state, 1, bit) + { + /* XXX: We considered having an array of callbacks for each state index + * that we'd call here but decided that this way the compiler is more + * likely going to be able to in-line the comparison functions and use + * the index to jump straight to the required code. */ + switch (bit) + { + case COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT: + differences |= + _cogl_framebuffer_compare_viewport_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_CLIP: + differences |= _cogl_framebuffer_compare_clip_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_DITHER: + differences |= _cogl_framebuffer_compare_dither_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW: + differences |= + _cogl_framebuffer_compare_modelview_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION: + differences |= + _cogl_framebuffer_compare_projection_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_COLOR_MASK: + differences |= + _cogl_framebuffer_compare_color_mask_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING: + differences |= + _cogl_framebuffer_compare_front_face_winding_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE: + differences |= + _cogl_framebuffer_compare_depth_write_state (a, b); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE: + differences |= + _cogl_framebuffer_compare_stereo_mode (a, b); + break; + default: + g_warn_if_reached (); + } + } + COGL_FLAGS_FOREACH_END; + + return differences; +} + +void +_cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state) +{ + CoglContext *ctx = draw_buffer->context; + + ctx->driver_vtable->framebuffer_flush_state (draw_buffer, + read_buffer, + state); +} + +int +cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.red; +} + +int +cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.green; +} + +int +cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.blue; +} + +int +cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.alpha; +} + +int +cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.depth; +} + +int +_cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + CoglFramebufferBits bits; + + ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + + return bits.stencil; +} + +gboolean +cogl_framebuffer_get_is_stereo (CoglFramebuffer *framebuffer) +{ + return framebuffer->config.stereo_enabled; +} + +CoglColorMask +cogl_framebuffer_get_color_mask (CoglFramebuffer *framebuffer) +{ + return framebuffer->color_mask; +} + +void +cogl_framebuffer_set_color_mask (CoglFramebuffer *framebuffer, + CoglColorMask color_mask) +{ + if (framebuffer->color_mask == color_mask) + return; + + /* XXX: Currently color mask changes don't go through the journal */ + _cogl_framebuffer_flush_journal (framebuffer); + + framebuffer->color_mask = color_mask; + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_COLOR_MASK; +} + +CoglStereoMode +cogl_framebuffer_get_stereo_mode (CoglFramebuffer *framebuffer) +{ + return framebuffer->stereo_mode; +} + +void +cogl_framebuffer_set_stereo_mode (CoglFramebuffer *framebuffer, + CoglStereoMode stereo_mode) +{ + if (framebuffer->stereo_mode == stereo_mode) + return; + + /* Stereo mode changes don't go through the journal */ + _cogl_framebuffer_flush_journal (framebuffer); + + framebuffer->stereo_mode = stereo_mode; + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_STEREO_MODE; +} + +CoglBool +cogl_framebuffer_get_depth_write_enabled (CoglFramebuffer *framebuffer) +{ + return framebuffer->depth_writing_enabled; +} + +void +cogl_framebuffer_set_depth_write_enabled (CoglFramebuffer *framebuffer, + CoglBool depth_write_enabled) +{ + if (framebuffer->depth_writing_enabled == depth_write_enabled) + return; + + /* XXX: Currently depth write changes don't go through the journal */ + _cogl_framebuffer_flush_journal (framebuffer); + + framebuffer->depth_writing_enabled = depth_write_enabled; + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_DEPTH_WRITE; +} + +CoglBool +cogl_framebuffer_get_dither_enabled (CoglFramebuffer *framebuffer) +{ + return framebuffer->dither_enabled; +} + +void +cogl_framebuffer_set_dither_enabled (CoglFramebuffer *framebuffer, + CoglBool dither_enabled) +{ + if (framebuffer->dither_enabled == dither_enabled) + return; + + cogl_flush (); /* Currently dithering changes aren't tracked in the journal */ + framebuffer->dither_enabled = dither_enabled; + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_DITHER; +} + +void +cogl_framebuffer_set_depth_texture_enabled (CoglFramebuffer *framebuffer, + CoglBool enabled) +{ + _COGL_RETURN_IF_FAIL (!framebuffer->allocated); + + framebuffer->config.depth_texture_enabled = enabled; +} + +CoglBool +cogl_framebuffer_get_depth_texture_enabled (CoglFramebuffer *framebuffer) +{ + return framebuffer->config.depth_texture_enabled; +} + +CoglTexture * +cogl_framebuffer_get_depth_texture (CoglFramebuffer *framebuffer) +{ + /* lazily allocate the framebuffer... */ + if (!cogl_framebuffer_allocate (framebuffer, NULL)) + return NULL; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_offscreen (framebuffer), NULL); + return COGL_OFFSCREEN(framebuffer)->depth_texture; +} + +int +cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer) +{ + if (framebuffer->allocated) + return framebuffer->samples_per_pixel; + else + return framebuffer->config.samples_per_pixel; +} + +void +cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer, + int samples_per_pixel) +{ + _COGL_RETURN_IF_FAIL (!framebuffer->allocated); + + framebuffer->config.samples_per_pixel = samples_per_pixel; +} + +void +cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer) +{ + cogl_framebuffer_resolve_samples_region (framebuffer, + 0, 0, + framebuffer->width, + framebuffer->height); + + /* TODO: Make this happen implicitly when the resolve texture next gets used + * as a source, either via cogl_texture_get_data(), via cogl_read_pixels() or + * if used as a source for rendering. We would also implicitly resolve if + * necessary before freeing a CoglFramebuffer. + * + * This API should still be kept but it is optional, only necessary + * if the user wants to explicitly control when the resolve happens e.g. + * to ensure it's done in advance of it being used as a source. + * + * Every texture should have a CoglFramebuffer *needs_resolve member + * internally. When the texture gets validated before being used as a source + * we should first check the needs_resolve pointer and if set we'll + * automatically call cogl_framebuffer_resolve_samples (). + * + * Calling cogl_framebuffer_resolve_samples() or + * cogl_framebuffer_resolve_samples_region() should reset the textures + * needs_resolve pointer to NULL. + * + * Rendering anything to a framebuffer will cause the corresponding + * texture's ->needs_resolve pointer to be set. + * + * XXX: Note: we only need to address this TODO item when adding support for + * EXT_framebuffer_multisample because currently we only support hardware + * that resolves implicitly anyway. + */ +} + +void +cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height) +{ + /* NOP for now since we don't support EXT_framebuffer_multisample yet which + * requires an explicit resolve. */ +} + +CoglContext * +cogl_framebuffer_get_context (CoglFramebuffer *framebuffer) +{ + _COGL_RETURN_VAL_IF_FAIL (framebuffer != NULL, NULL); + + return framebuffer->context; +} + +static CoglBool +_cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap) +{ + CoglBool found_intersection; + CoglPixelFormat format; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FAST_READ_PIXEL))) + return FALSE; + + if (source != COGL_READ_PIXELS_COLOR_BUFFER) + return FALSE; + + format = cogl_bitmap_get_format (bitmap); + + if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE && + format != COGL_PIXEL_FORMAT_RGBA_8888) + return FALSE; + + if (!_cogl_journal_try_read_pixel (framebuffer->journal, + x, y, bitmap, + &found_intersection)) + return FALSE; + + /* If we can't determine the color from the primitives in the + * journal then see if we can use the last recorded clear color + */ + + /* If _cogl_journal_try_read_pixel() failed even though there was an + * intersection of the given point with a primitive in the journal + * then we can't fallback to the framebuffer's last clear color... + * */ + if (found_intersection) + return TRUE; + + /* If the framebuffer has been rendered too since it was last + * cleared then we can't return the last known clear color. */ + if (framebuffer->clear_clip_dirty) + return FALSE; + + if (x >= framebuffer->clear_clip_x0 && + x < framebuffer->clear_clip_x1 && + y >= framebuffer->clear_clip_y0 && + y < framebuffer->clear_clip_y1) + { + uint8_t *pixel; + CoglError *ignore_error = NULL; + + /* we currently only care about cases where the premultiplied or + * unpremultipled colors are equivalent... */ + if (framebuffer->clear_color_alpha != 1.0) + return FALSE; + + pixel = _cogl_bitmap_map (bitmap, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + &ignore_error); + if (pixel == NULL) + { + cogl_error_free (ignore_error); + return FALSE; + } + + pixel[0] = framebuffer->clear_color_red * 255.0; + pixel[1] = framebuffer->clear_color_green * 255.0; + pixel[2] = framebuffer->clear_color_blue * 255.0; + pixel[3] = framebuffer->clear_color_alpha * 255.0; + + _cogl_bitmap_unmap (bitmap); + + return TRUE; + } + + return FALSE; +} + +CoglBool +_cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error) +{ + CoglContext *ctx; + int width; + int height; + + _COGL_RETURN_VAL_IF_FAIL (source & COGL_READ_PIXELS_COLOR_BUFFER, FALSE); + _COGL_RETURN_VAL_IF_FAIL (cogl_is_framebuffer (framebuffer), FALSE); + + if (!cogl_framebuffer_allocate (framebuffer, error)) + return FALSE; + + width = cogl_bitmap_get_width (bitmap); + height = cogl_bitmap_get_height (bitmap); + + if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty) + { + /* If everything drawn so far for this frame is still in the + * Journal then if all of the rectangles only have a flat + * opaque color we have a fast-path for reading a single pixel + * that avoids the relatively high cost of flushing primitives + * to be drawn on the GPU (considering how simple the geometry + * is in this case) and then blocking on the long GPU pipelines + * for the result. + */ + if (_cogl_framebuffer_try_fast_read_pixel (framebuffer, + x, y, source, bitmap)) + return TRUE; + } + + ctx = cogl_framebuffer_get_context (framebuffer); + + /* make sure any batched primitives get emitted to the driver + * before issuing our read pixels... + */ + _cogl_framebuffer_flush_journal (framebuffer); + + return ctx->driver_vtable->framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, + source, + bitmap, + error); +} + +CoglBool +cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap) +{ + CoglError *ignore_error = NULL; + CoglBool status = + _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, source, bitmap, + &ignore_error); + if (!status) + cogl_error_free (ignore_error); + return status; +} + +CoglBool +cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height, + CoglPixelFormat format, + uint8_t *pixels) +{ + int bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + CoglBitmap *bitmap; + CoglBool ret; + + bitmap = cogl_bitmap_new_for_data (framebuffer->context, + width, height, + format, + bpp * width, /* rowstride */ + pixels); + + /* Note: we don't try and catch errors here since we created the + * bitmap storage up-front and can assume we wont hit an + * out-of-memory error which should be the only exception + * this api throws. + */ + ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, + COGL_READ_PIXELS_COLOR_BUFFER, + bitmap, + NULL); + cogl_object_unref (bitmap); + + return ret; +} + +void +_cogl_blit_framebuffer (CoglFramebuffer *src, + CoglFramebuffer *dest, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + CoglContext *ctx = src->context; + + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); + + /* We can only support blitting between offscreen buffers because + otherwise we would need to mirror the image and GLES2.0 doesn't + support this */ + _COGL_RETURN_IF_FAIL (cogl_is_offscreen (src)); + _COGL_RETURN_IF_FAIL (cogl_is_offscreen (dest)); + /* The buffers must be the same format */ + _COGL_RETURN_IF_FAIL (src->internal_format == dest->internal_format); + + /* Make sure the current framebuffers are bound. We explicitly avoid + flushing the clip state so we can bind our own empty state */ + _cogl_framebuffer_flush_state (dest, + src, + COGL_FRAMEBUFFER_STATE_ALL & + ~COGL_FRAMEBUFFER_STATE_CLIP); + + /* Flush any empty clip stack because glBlitFramebuffer is affected + by the scissor and we want to hide this feature for the Cogl API + because it's not obvious to an app how the clip state will affect + the scissor */ + _cogl_clip_stack_flush (NULL, dest); + + /* XXX: Because we are manually flushing clip state here we need to + * make sure that the clip state gets updated the next time we flush + * framebuffer state by marking the current framebuffer's clip state + * as changed */ + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + + ctx->glBlitFramebuffer (src_x, src_y, + src_x + width, src_y + height, + dst_x, dst_y, + dst_x + width, dst_y + height, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); +} + +void +cogl_framebuffer_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers) +{ + CoglContext *ctx = framebuffer->context; + + _COGL_RETURN_IF_FAIL (buffers & COGL_BUFFER_BIT_COLOR); + + ctx->driver_vtable->framebuffer_discard_buffers (framebuffer, buffers); +} + +void +cogl_framebuffer_finish (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + _cogl_framebuffer_flush_journal (framebuffer); + + ctx->driver_vtable->framebuffer_finish (framebuffer); +} + +void +cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_push (modelview_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_pop_matrix (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_pop (modelview_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_identity_matrix (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_load_identity (modelview_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_scale (CoglFramebuffer *framebuffer, + float x, + float y, + float z) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_scale (modelview_stack, x, y, z); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_translate (CoglFramebuffer *framebuffer, + float x, + float y, + float z) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_translate (modelview_stack, x, y, z); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_rotate (CoglFramebuffer *framebuffer, + float angle, + float x, + float y, + float z) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_rotate_quaternion (CoglFramebuffer *framebuffer, + const CoglQuaternion *quaternion) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_rotate_quaternion (modelview_stack, quaternion); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_rotate_euler (CoglFramebuffer *framebuffer, + const CoglEuler *euler) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_rotate_euler (modelview_stack, euler); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_transform (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_multiply (modelview_stack, matrix); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; +} + +void +cogl_framebuffer_perspective (CoglFramebuffer *framebuffer, + float fov_y, + float aspect, + float z_near, + float z_far) +{ + float ymax = z_near * tanf (fov_y * G_PI / 360.0); + + cogl_framebuffer_frustum (framebuffer, + -ymax * aspect, /* left */ + ymax * aspect, /* right */ + -ymax, /* bottom */ + ymax, /* top */ + z_near, + z_far); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +void +cogl_framebuffer_frustum (CoglFramebuffer *framebuffer, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + + /* XXX: The projection matrix isn't currently tracked in the journal + * so we need to flush all journaled primitives first... */ + _cogl_framebuffer_flush_journal (framebuffer); + + cogl_matrix_stack_load_identity (projection_stack); + + cogl_matrix_stack_frustum (projection_stack, + left, + right, + bottom, + top, + z_near, + z_far); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +void +cogl_framebuffer_orthographic (CoglFramebuffer *framebuffer, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far) +{ + CoglMatrix ortho; + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + + /* XXX: The projection matrix isn't currently tracked in the journal + * so we need to flush all journaled primitives first... */ + _cogl_framebuffer_flush_journal (framebuffer); + + cogl_matrix_init_identity (&ortho); + cogl_matrix_orthographic (&ortho, x_1, y_1, x_2, y_2, near, far); + cogl_matrix_stack_set (projection_stack, &ortho); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +void +_cogl_framebuffer_push_projection (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + cogl_matrix_stack_push (projection_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +void +_cogl_framebuffer_pop_projection (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + cogl_matrix_stack_pop (projection_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; +} + +void +cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer, + CoglMatrix *matrix) +{ + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + cogl_matrix_entry_get (modelview_entry, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_framebuffer_set_modelview_matrix (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + cogl_matrix_stack_set (modelview_stack, matrix); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_MODELVIEW; + + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer, + CoglMatrix *matrix) +{ + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + cogl_matrix_entry_get (projection_entry, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_framebuffer_set_projection_matrix (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + + /* XXX: The projection matrix isn't currently tracked in the journal + * so we need to flush all journaled primitives first... */ + _cogl_framebuffer_flush_journal (framebuffer); + + cogl_matrix_stack_set (projection_stack, matrix); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_PROJECTION; + + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_framebuffer_push_scissor_clip (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height) +{ + framebuffer->clip_stack = + _cogl_clip_stack_push_window_rectangle (framebuffer->clip_stack, + x, y, width, height); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + +void +cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer, + float x_1, + float y_1, + float x_2, + float y_2) +{ + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; + + framebuffer->clip_stack = + _cogl_clip_stack_push_rectangle (framebuffer->clip_stack, + x_1, y_1, x_2, y_2, + modelview_entry, + projection_entry, + viewport); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + +void +cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, + CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2) +{ + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; + + framebuffer->clip_stack = + _cogl_clip_stack_push_primitive (framebuffer->clip_stack, + primitive, + bounds_x1, bounds_y1, + bounds_x2, bounds_y2, + modelview_entry, + projection_entry, + viewport); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + +void +cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer) +{ + framebuffer->clip_stack = _cogl_clip_stack_pop (framebuffer->clip_stack); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + +void +_cogl_framebuffer_unref (CoglFramebuffer *framebuffer) +{ + /* The journal holds a reference to the framebuffer whenever it is + non-empty. Therefore if the journal is non-empty and we will have + exactly one reference then we know the journal is the only thing + keeping the framebuffer alive. In that case we want to flush the + journal and let the framebuffer die. It is fine at this point if + flushing the journal causes something else to take a reference to + it and it comes back to life */ + if (framebuffer->journal->entries->len > 0) + { + unsigned int ref_count = ((CoglObject *) framebuffer)->ref_count; + + /* There should be at least two references - the one we are + about to drop and the one held by the journal */ + if (ref_count < 2) + g_warning ("Inconsistent ref count on a framebuffer with journal " + "entries."); + + if (ref_count == 2) + _cogl_framebuffer_flush_journal (framebuffer); + } + + /* Chain-up */ + _cogl_object_default_unref (framebuffer); +} + +#ifdef COGL_ENABLE_DEBUG +static int +get_index (void *indices, + CoglIndicesType type, + int _index) +{ + if (!indices) + return _index; + + switch (type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + return ((uint8_t *)indices)[_index]; + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + return ((uint16_t *)indices)[_index]; + case COGL_INDICES_TYPE_UNSIGNED_INT: + return ((uint32_t *)indices)[_index]; + } + + g_return_val_if_reached (0); +} + +static void +add_line (uint32_t *line_indices, + int base, + void *user_indices, + CoglIndicesType user_indices_type, + int index0, + int index1, + int *pos) +{ + index0 = get_index (user_indices, user_indices_type, index0); + index1 = get_index (user_indices, user_indices_type, index1); + + line_indices[(*pos)++] = base + index0; + line_indices[(*pos)++] = base + index1; +} + +static int +get_line_count (CoglVerticesMode mode, int n_vertices) +{ + if (mode == COGL_VERTICES_MODE_TRIANGLES && + (n_vertices % 3) == 0) + { + return n_vertices; + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && + n_vertices >= 3) + { + return 2 * n_vertices - 3; + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && + n_vertices >= 3) + { + return 2 * n_vertices - 3; + } + /* In the journal we are a bit sneaky and actually use GL_QUADS + * which isn't actually a valid CoglVerticesMode! */ +#ifdef HAVE_COGL_GL + else if (mode == GL_QUADS && (n_vertices % 4) == 0) + { + return n_vertices; + } +#endif + + g_return_val_if_reached (0); +} + +static CoglIndices * +get_wire_line_indices (CoglContext *ctx, + CoglVerticesMode mode, + int first_vertex, + int n_vertices_in, + CoglIndices *user_indices, + int *n_indices) +{ + int n_lines; + uint32_t *line_indices; + CoglIndexBuffer *index_buffer; + void *indices; + CoglIndicesType indices_type; + int base = first_vertex; + int pos; + int i; + CoglIndices *ret; + + if (user_indices) + { + index_buffer = cogl_indices_get_buffer (user_indices); + indices = _cogl_buffer_map (COGL_BUFFER (index_buffer), + COGL_BUFFER_ACCESS_READ, 0, + NULL); + indices_type = cogl_indices_get_type (user_indices); + } + else + { + index_buffer = NULL; + indices = NULL; + indices_type = COGL_INDICES_TYPE_UNSIGNED_BYTE; + } + + n_lines = get_line_count (mode, n_vertices_in); + + /* Note: we are using COGL_INDICES_TYPE_UNSIGNED_INT so 4 bytes per index. */ + line_indices = g_malloc (4 * n_lines * 2); + + pos = 0; + + if (mode == COGL_VERTICES_MODE_TRIANGLES && + (n_vertices_in % 3) == 0) + { + for (i = 0; i < n_vertices_in; i += 3) + { + add_line (line_indices, base, indices, indices_type, i, i+1, &pos); + add_line (line_indices, base, indices, indices_type, i+1, i+2, &pos); + add_line (line_indices, base, indices, indices_type, i+2, i, &pos); + } + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && + n_vertices_in >= 3) + { + add_line (line_indices, base, indices, indices_type, 0, 1, &pos); + add_line (line_indices, base, indices, indices_type, 1, 2, &pos); + add_line (line_indices, base, indices, indices_type, 0, 2, &pos); + + for (i = 3; i < n_vertices_in; i++) + { + add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); + add_line (line_indices, base, indices, indices_type, 0, i, &pos); + } + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && + n_vertices_in >= 3) + { + add_line (line_indices, base, indices, indices_type, 0, 1, &pos); + add_line (line_indices, base, indices, indices_type, 1, 2, &pos); + add_line (line_indices, base, indices, indices_type, 0, 2, &pos); + + for (i = 3; i < n_vertices_in; i++) + { + add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); + add_line (line_indices, base, indices, indices_type, i - 2, i, &pos); + } + } + /* In the journal we are a bit sneaky and actually use GL_QUADS + * which isn't actually a valid CoglVerticesMode! */ +#ifdef HAVE_COGL_GL + else if (mode == GL_QUADS && (n_vertices_in % 4) == 0) + { + for (i = 0; i < n_vertices_in; i += 4) + { + add_line (line_indices, + base, indices, indices_type, i, i + 1, &pos); + add_line (line_indices, + base, indices, indices_type, i + 1, i + 2, &pos); + add_line (line_indices, + base, indices, indices_type, i + 2, i + 3, &pos); + add_line (line_indices, + base, indices, indices_type, i + 3, i, &pos); + } + } +#endif + + if (user_indices) + cogl_buffer_unmap (COGL_BUFFER (index_buffer)); + + *n_indices = n_lines * 2; + + ret = cogl_indices_new (ctx, + COGL_INDICES_TYPE_UNSIGNED_INT, + line_indices, + *n_indices); + + g_free (line_indices); + + return ret; +} + +static CoglBool +remove_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + cogl_pipeline_remove_layer (pipeline, layer_index); + return TRUE; +} + +static void +pipeline_destroyed_cb (CoglPipeline *weak_pipeline, void *user_data) +{ + CoglPipeline *original_pipeline = user_data; + + /* XXX: I think we probably need to provide a custom unref function for + * CoglPipeline because it's possible that we will reach this callback + * because original_pipeline is being freed which means cogl_object_unref + * will have already freed any associated user data. + * + * Setting more user data here will *probably* succeed but that may allocate + * a new user-data array which could be leaked. + * + * Potentially we could have a _cogl_object_free_user_data function so + * that a custom unref function could be written that can destroy weak + * pipeline children before removing user data. + */ + cogl_object_set_user_data (COGL_OBJECT (original_pipeline), + &wire_pipeline_key, NULL, NULL); + + cogl_object_unref (weak_pipeline); +} + +static void +draw_wireframe (CoglContext *ctx, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglIndices *indices, + CoglDrawFlags flags) +{ + CoglIndices *wire_indices; + CoglPipeline *wire_pipeline; + int n_indices; + + wire_indices = get_wire_line_indices (ctx, + mode, + first_vertex, + n_vertices, + indices, + &n_indices); + + wire_pipeline = cogl_object_get_user_data (COGL_OBJECT (pipeline), + &wire_pipeline_key); + + if (!wire_pipeline) + { + wire_pipeline = + _cogl_pipeline_weak_copy (pipeline, pipeline_destroyed_cb, NULL); + + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &wire_pipeline_key, wire_pipeline, + NULL); + + /* If we have glsl then the pipeline may have an associated + * vertex program and since we'd like to see the results of the + * vertex program in the wireframe we just add a final clobber + * of the wire color leaving the rest of the state untouched. */ + if (cogl_has_feature (framebuffer->context, COGL_FEATURE_ID_GLSL)) + { + static CoglSnippet *snippet = NULL; + + /* The snippet is cached so that it will reuse the program + * from the pipeline cache if possible */ + if (snippet == NULL) + { + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + NULL); + cogl_snippet_set_replace (snippet, + "cogl_color_out = " + "vec4 (0.0, 1.0, 0.0, 1.0);\n"); + } + + cogl_pipeline_add_snippet (wire_pipeline, snippet); + } + else + { + cogl_pipeline_foreach_layer (wire_pipeline, remove_layer_cb, NULL); + cogl_pipeline_set_color4f (wire_pipeline, 0, 1, 0, 1); + } + } + + /* temporarily disable the wireframe to avoid recursion! */ + flags |= COGL_DRAW_SKIP_DEBUG_WIREFRAME; + _cogl_framebuffer_draw_indexed_attributes ( + framebuffer, + wire_pipeline, + COGL_VERTICES_MODE_LINES, + 0, + n_indices, + wire_indices, + attributes, + n_attributes, + flags); + COGL_DEBUG_SET_FLAG (COGL_DEBUG_WIREFRAME); + + cogl_object_unref (wire_indices); +} +#endif + +/* This can be called directly by the CoglJournal to draw attributes + * skipping the implicit journal flush, the framebuffer flush and + * pipeline validation. */ +void +_cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) && + (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) && + mode != COGL_VERTICES_MODE_LINES && + mode != COGL_VERTICES_MODE_LINE_LOOP && + mode != COGL_VERTICES_MODE_LINE_STRIP) + draw_wireframe (framebuffer->context, + framebuffer, pipeline, + mode, first_vertex, n_vertices, + attributes, n_attributes, NULL, + flags); + else +#endif + { + CoglContext *ctx = framebuffer->context; + + ctx->driver_vtable->framebuffer_draw_attributes (framebuffer, + pipeline, + mode, + first_vertex, + n_vertices, + attributes, + n_attributes, + flags); + } +} + +/* XXX: deprecated */ +void +cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes) +{ + _cogl_framebuffer_draw_attributes (framebuffer, + pipeline, + mode, + first_vertex, + n_vertices, + attributes, n_attributes, + COGL_DRAW_SKIP_LEGACY_STATE); +} + +/* XXX: deprecated */ +void +cogl_framebuffer_vdraw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + ...) +{ + va_list ap; + int n_attributes; + CoglAttribute *attribute; + CoglAttribute **attributes; + int i; + + va_start (ap, n_vertices); + for (n_attributes = 0; va_arg (ap, CoglAttribute *); n_attributes++) + ; + va_end (ap); + + attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); + + va_start (ap, n_vertices); + for (i = 0; (attribute = va_arg (ap, CoglAttribute *)); i++) + attributes[i] = attribute; + va_end (ap); + + _cogl_framebuffer_draw_attributes (framebuffer, + pipeline, + mode, first_vertex, n_vertices, + attributes, n_attributes, + COGL_DRAW_SKIP_LEGACY_STATE); +} + +void +_cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) && + (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) && + mode != COGL_VERTICES_MODE_LINES && + mode != COGL_VERTICES_MODE_LINE_LOOP && + mode != COGL_VERTICES_MODE_LINE_STRIP) + draw_wireframe (framebuffer->context, + framebuffer, pipeline, + mode, first_vertex, n_vertices, + attributes, n_attributes, indices, + flags); + else +#endif + { + CoglContext *ctx = framebuffer->context; + + ctx->driver_vtable->framebuffer_draw_indexed_attributes (framebuffer, + pipeline, + mode, + first_vertex, + n_vertices, + indices, + attributes, + n_attributes, + flags); + } +} + +/* XXX: deprecated */ +void +cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes) +{ + _cogl_framebuffer_draw_indexed_attributes (framebuffer, + pipeline, + mode, first_vertex, + n_vertices, indices, + attributes, n_attributes, + COGL_DRAW_SKIP_LEGACY_STATE); +} + +/* XXX: deprecated */ +void +cogl_framebuffer_vdraw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + ...) + +{ + va_list ap; + int n_attributes; + CoglAttribute **attributes; + int i; + CoglAttribute *attribute; + + va_start (ap, indices); + for (n_attributes = 0; va_arg (ap, CoglAttribute *); n_attributes++) + ; + va_end (ap); + + attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); + + va_start (ap, indices); + for (i = 0; (attribute = va_arg (ap, CoglAttribute *)); i++) + attributes[i] = attribute; + va_end (ap); + + _cogl_framebuffer_draw_indexed_attributes (framebuffer, + pipeline, + mode, + first_vertex, + n_vertices, + indices, + attributes, + n_attributes, + COGL_DRAW_SKIP_LEGACY_STATE); +} + +void +cogl_framebuffer_draw_primitive (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPrimitive *primitive) +{ + _cogl_primitive_draw (primitive, framebuffer, pipeline, + COGL_DRAW_SKIP_LEGACY_STATE); +} + +void +cogl_framebuffer_draw_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the _*_rectangle* APIs normalize their input into an array of + * _CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_framebuffer_draw_multitextured_rectangles. + */ + + rect.position = position; + rect.tex_coords = NULL; + rect.tex_coords_len = 0; + + _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, + pipeline, + &rect, + 1, + TRUE); +} + +void +cogl_framebuffer_draw_textured_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2, + float s_1, + float t_1, + float s_2, + float t_2) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + const float tex_coords[4] = {s_1, t_1, s_2, t_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the _*_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_framebuffer_draw_multitextured_rectangles. + */ + + rect.position = position; + rect.tex_coords = tex_coords; + rect.tex_coords_len = 4; + + _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, + pipeline, + &rect, + 1, + TRUE); +} + +void +cogl_framebuffer_draw_multitextured_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2, + const float *tex_coords, + int tex_coords_len) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the _*_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_framebuffer_draw_multitextured_rectangles. + */ + + rect.position = position; + rect.tex_coords = tex_coords; + rect.tex_coords_len = tex_coords_len; + + _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, + pipeline, + &rect, + 1, + TRUE); +} + +void +cogl_framebuffer_draw_rectangles (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + const float *coordinates, + unsigned int n_rectangles) +{ + CoglMultiTexturedRect *rects; + int i; + + /* XXX: All the _*_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_framebuffer_draw_multitextured_rectangles. + */ + + rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect)); + + for (i = 0; i < n_rectangles; i++) + { + rects[i].position = &coordinates[i * 4]; + rects[i].tex_coords = NULL; + rects[i].tex_coords_len = 0; + } + + _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, + pipeline, + rects, + n_rectangles, + TRUE); +} + +void +cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + const float *coordinates, + unsigned int n_rectangles) +{ + CoglMultiTexturedRect *rects; + int i; + + /* XXX: All the _*_rectangle* APIs normalize their input into an array of + * _CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_framebuffer_draw_multitextured_rectangles. + */ + + rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect)); + + for (i = 0; i < n_rectangles; i++) + { + rects[i].position = &coordinates[i * 8]; + rects[i].tex_coords = &coordinates[i * 8 + 4]; + rects[i].tex_coords_len = 4; + } + + _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, + pipeline, + rects, + n_rectangles, + TRUE); +} diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h new file mode 100644 index 000000000..56ba4aa25 --- /dev/null +++ b/cogl/cogl/cogl-framebuffer.h @@ -0,0 +1,1866 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_FRAMEBUFFER_H +#define __COGL_FRAMEBUFFER_H + +/* We forward declare the CoglFramebuffer type here to avoid some circular + * dependency issues with the following headers. + */ +#ifdef __COGL_H_INSIDE__ +/* For the public C api we typedef interface types as void to avoid needing + * lots of casting in code and instead we will rely on runtime type checking + * for these objects. */ +typedef void CoglFramebuffer; +#else +typedef struct _CoglFramebuffer CoglFramebuffer; +#define COGL_FRAMEBUFFER(X) ((CoglFramebuffer *)(X)) +#endif + +#include +#include +#include +#ifdef COGL_ENABLE_EXPERIMENTAL_API +#include +#include +#include +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-framebuffer + * @short_description: A common interface for manipulating framebuffers + * + * Framebuffers are a collection of buffers that can be rendered too. + * A framebuffer may be comprised of one or more color buffers, an + * optional depth buffer and an optional stencil buffer. Other + * configuration parameters are associated with framebuffers too such + * as whether the framebuffer supports multi-sampling (an anti-aliasing + * technique) or dithering. + * + * There are two kinds of framebuffer in Cogl, #CoglOnscreen + * framebuffers and #CoglOffscreen framebuffers. As the names imply + * offscreen framebuffers are for rendering something offscreen + * (perhaps to a texture which is bound as one of the color buffers). + * The exact semantics of onscreen framebuffers depends on the window + * system backend that you are using, but typically you can expect + * rendering to a #CoglOnscreen framebuffer will be immediately + * visible to the user. + * + * If you want to create a new framebuffer then you should start by + * looking at the #CoglOnscreen and #CoglOffscreen constructor + * functions, such as cogl_offscreen_new_with_texture() or + * cogl_onscreen_new(). The #CoglFramebuffer interface deals with + * all aspects that are common between those two types of framebuffer. + * + * Setup of a new CoglFramebuffer happens in two stages. There is a + * configuration stage where you specify all the options and ancillary + * buffers you want associated with your framebuffer and then when you + * are happy with the configuration you can "allocate" the framebuffer + * using cogl_framebuffer_allocate(). Technically explicitly calling + * cogl_framebuffer_allocate() is optional for convenience and the + * framebuffer will automatically be allocated when you first try to + * draw to it, but if you do the allocation manually then you can + * also catch any possible errors that may arise from your + * configuration. + */ + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_framebuffer_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_framebuffer_get_gtype (void); +#endif + +/** + * cogl_framebuffer_allocate: + * @framebuffer: A #CoglFramebuffer + * @error: A pointer to a #CoglError for returning exceptions. + * + * Explicitly allocates a configured #CoglFramebuffer allowing developers to + * check and handle any errors that might arise from an unsupported + * configuration so that fallback configurations may be tried. + * + * Many applications don't support any fallback options at least when + * they are initially developed and in that case the don't need to use this API + * since Cogl will automatically allocate a framebuffer when it first gets + * used. The disadvantage of relying on automatic allocation is that the + * program will abort with an error message if there is an error during + * automatic allocation. + * + * Return value: %TRUE if there were no error allocating the framebuffer, else %FALSE. + * Since: 1.8 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_allocate (CoglFramebuffer *framebuffer, + CoglError **error); + +/** + * cogl_framebuffer_get_width: + * @framebuffer: A #CoglFramebuffer + * + * Queries the current width of the given @framebuffer. + * + * Return value: The width of @framebuffer. + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_width (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_height: + * @framebuffer: A #CoglFramebuffer + * + * Queries the current height of the given @framebuffer. + * + * Return value: The height of @framebuffer. + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_height (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_viewport: + * @framebuffer: A #CoglFramebuffer + * @x: The top-left x coordinate of the viewport origin (only integers + * supported currently) + * @y: The top-left y coordinate of the viewport origin (only integers + * supported currently) + * @width: The width of the viewport (only integers supported currently) + * @height: The height of the viewport (only integers supported currently) + * + * Defines a scale and offset for everything rendered relative to the + * top-left of the destination framebuffer. + * + * By default the viewport has an origin of (0,0) and width and height + * that match the framebuffer's size. Assuming a default projection and + * modelview matrix then you could translate the contents of a window + * down and right by leaving the viewport size unchanged by moving the + * offset to (10,10). The viewport coordinates are measured in pixels. + * If you left the x and y origin as (0,0) you could scale the windows + * contents down by specify and width and height that's half the real + * size of the framebuffer. + * + * Although the function takes floating point arguments, existing + * drivers only allow the use of integer values. In the future floating + * point values will be exposed via a checkable feature. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer, + float x, + float y, + float width, + float height); + +/** + * cogl_framebuffer_get_viewport_x: + * @framebuffer: A #CoglFramebuffer + * + * Queries the x coordinate of the viewport origin as set using cogl_framebuffer_set_viewport() + * or the default value which is 0. + * + * Return value: The x coordinate of the viewport origin. + * Since: 1.8 + * Stability: unstable + */ +float +cogl_framebuffer_get_viewport_x (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_viewport_y: + * @framebuffer: A #CoglFramebuffer + * + * Queries the y coordinate of the viewport origin as set using cogl_framebuffer_set_viewport() + * or the default value which is 0. + * + * Return value: The y coordinate of the viewport origin. + * Since: 1.8 + * Stability: unstable + */ +float +cogl_framebuffer_get_viewport_y (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_viewport_width: + * @framebuffer: A #CoglFramebuffer + * + * Queries the width of the viewport as set using cogl_framebuffer_set_viewport() + * or the default value which is the width of the framebuffer. + * + * Return value: The width of the viewport. + * Since: 1.8 + * Stability: unstable + */ +float +cogl_framebuffer_get_viewport_width (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_viewport_height: + * @framebuffer: A #CoglFramebuffer + * + * Queries the height of the viewport as set using cogl_framebuffer_set_viewport() + * or the default value which is the height of the framebuffer. + * + * Return value: The height of the viewport. + * Since: 1.8 + * Stability: unstable + */ +float +cogl_framebuffer_get_viewport_height (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_viewport4fv: + * @framebuffer: A #CoglFramebuffer + * @viewport: (out caller-allocates) (array fixed-size=4): A pointer to an + * array of 4 floats to receive the (x, y, width, height) + * components of the current viewport. + * + * Queries the x, y, width and height components of the current viewport as set + * using cogl_framebuffer_set_viewport() or the default values which are 0, 0, + * framebuffer_width and framebuffer_height. The values are written into the + * given @viewport array. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_get_viewport4fv (CoglFramebuffer *framebuffer, + float *viewport); + +/** + * cogl_framebuffer_push_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * + * Copies the current model-view matrix onto the matrix stack. The matrix + * can later be restored with cogl_framebuffer_pop_matrix(). + * + * Since: 1.10 + */ +void +cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_pop_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * + * Restores the model-view matrix on the top of the matrix stack. + * + * Since: 1.10 + */ +void +cogl_framebuffer_pop_matrix (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_identity_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * + * Resets the current model-view matrix to the identity matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_identity_matrix (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_scale: + * @framebuffer: A #CoglFramebuffer pointer + * @x: Amount to scale along the x-axis + * @y: Amount to scale along the y-axis + * @z: Amount to scale along the z-axis + * + * Multiplies the current model-view matrix by one that scales the x, + * y and z axes by the given values. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_scale (CoglFramebuffer *framebuffer, + float x, + float y, + float z); + +/** + * cogl_framebuffer_translate: + * @framebuffer: A #CoglFramebuffer pointer + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Multiplies the current model-view matrix by one that translates the + * model along all three axes according to the given values. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_translate (CoglFramebuffer *framebuffer, + float x, + float y, + float z); + +/** + * cogl_framebuffer_rotate: + * @framebuffer: A #CoglFramebuffer pointer + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Multiplies the current model-view matrix by one that rotates the + * model around the axis-vector specified by @x, @y and @z. The + * rotation follows the right-hand thumb rule so for example rotating + * by 10 degrees about the axis-vector (0, 0, 1) causes a small + * counter-clockwise rotation. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_rotate (CoglFramebuffer *framebuffer, + float angle, + float x, + float y, + float z); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * cogl_framebuffer_rotate_quaternion: + * @framebuffer: A #CoglFramebuffer pointer + * @quaternion: A #CoglQuaternion + * + * Multiplies the current model-view matrix by one that rotates + * according to the rotation described by @quaternion. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_framebuffer_rotate_quaternion (CoglFramebuffer *framebuffer, + const CoglQuaternion *quaternion); + +/** + * cogl_framebuffer_rotate_euler: + * @framebuffer: A #CoglFramebuffer pointer + * @euler: A #CoglEuler + * + * Multiplies the current model-view matrix by one that rotates + * according to the rotation described by @euler. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_framebuffer_rotate_euler (CoglFramebuffer *framebuffer, + const CoglEuler *euler); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +/** + * cogl_framebuffer_transform: + * @framebuffer: A #CoglFramebuffer pointer + * @matrix: the matrix to multiply with the current model-view + * + * Multiplies the current model-view matrix by the given matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_transform (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix); + +/** + * cogl_framebuffer_get_modelview_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * @matrix: (out): return location for the model-view matrix + * + * Stores the current model-view matrix in @matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer, + CoglMatrix *matrix); + +/** + * cogl_framebuffer_set_modelview_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * @matrix: the new model-view matrix + * + * Sets @matrix as the new model-view matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_set_modelview_matrix (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix); + +/** + * cogl_framebuffer_perspective: + * @framebuffer: A #CoglFramebuffer pointer + * @fov_y: Vertical field of view angle in degrees. + * @aspect: The (width over height) aspect ratio for display + * @z_near: The distance to the near clipping plane (Must be positive, + * and must not be 0) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current projection matrix with a perspective matrix + * based on the provided values. + * + * You should be careful not to have to great a @z_far / @z_near + * ratio since that will reduce the effectiveness of depth testing + * since there wont be enough precision to identify the depth of + * objects near to each other. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_perspective (CoglFramebuffer *framebuffer, + float fov_y, + float aspect, + float z_near, + float z_far); + +/** + * cogl_framebuffer_frustum: + * @framebuffer: A #CoglFramebuffer pointer + * @left: X position of the left clipping plane where it + * intersects the near clipping plane + * @right: X position of the right clipping plane where it + * intersects the near clipping plane + * @bottom: Y position of the bottom clipping plane where it + * intersects the near clipping plane + * @top: Y position of the top clipping plane where it intersects + * the near clipping plane + * @z_near: The distance to the near clipping plane (Must be positive) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current projection matrix with a perspective matrix + * for a given viewing frustum defined by 4 side clip planes that + * all cross through the origin and 2 near and far clip planes. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_frustum (CoglFramebuffer *framebuffer, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far); + +/** + * cogl_framebuffer_orthographic: + * @framebuffer: A #CoglFramebuffer pointer + * @x_1: The x coordinate for the first vertical clipping plane + * @y_1: The y coordinate for the first horizontal clipping plane + * @x_2: The x coordinate for the second vertical clipping plane + * @y_2: The y coordinate for the second horizontal clipping plane + * @near: The distance to the near clipping + * plane (will be negative if the plane is + * behind the viewer) + * @far: The distance to the far clipping + * plane (will be negative if the plane is + * behind the viewer) + * + * Replaces the current projection matrix with an orthographic projection + * matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_orthographic (CoglFramebuffer *framebuffer, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far); + +/** + * cogl_framebuffer_get_projection_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * @matrix: (out): return location for the projection matrix + * + * Stores the current projection matrix in @matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer, + CoglMatrix *matrix); + +/** + * cogl_framebuffer_set_projection_matrix: + * @framebuffer: A #CoglFramebuffer pointer + * @matrix: the new projection matrix + * + * Sets @matrix as the new projection matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_set_projection_matrix (CoglFramebuffer *framebuffer, + const CoglMatrix *matrix); + +/** + * cogl_framebuffer_push_scissor_clip: + * @framebuffer: A #CoglFramebuffer pointer + * @x: left edge of the clip rectangle in window coordinates + * @y: top edge of the clip rectangle in window coordinates + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are not transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_framebuffer_pop_clip(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_push_scissor_clip (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height); + +/** + * cogl_framebuffer_push_rectangle_clip: + * @framebuffer: A #CoglFramebuffer pointer + * @x_1: x coordinate for top left corner of the clip rectangle + * @y_1: y coordinate for top left corner of the clip rectangle + * @x_2: x coordinate for bottom right corner of the clip rectangle + * @y_2: y coordinate for bottom right corner of the clip rectangle + * + * Specifies a modelview transformed rectangular clipping area for all + * subsequent drawing operations. Any drawing commands that extend + * outside the rectangle will be clipped so that only the portion + * inside the rectangle will be displayed. The rectangle dimensions + * are transformed by the current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_framebuffer_pop_clip(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer, + float x_1, + float y_1, + float x_2, + float y_2); + +/** + * cogl_framebuffer_push_primitive_clip: + * @framebuffer: A #CoglFramebuffer pointer + * @primitive: A #CoglPrimitive describing a flat 2D shape + * @bounds_x1: x coordinate for the top-left corner of the primitives + * bounds + * @bounds_y1: y coordinate for the top-left corner of the primitives + * bounds + * @bounds_x2: x coordinate for the bottom-right corner of the + * primitives bounds. + * @bounds_y2: y coordinate for the bottom-right corner of the + * primitives bounds. + * + * Sets a new clipping area using a 2D shaped described with a + * #CoglPrimitive. The shape must not contain self overlapping + * geometry and must lie on a single 2D plane. A bounding box of the + * 2D shape in local coordinates (the same coordinates used to + * describe the shape) must be given. It is acceptable for the bounds + * to be larger than the true bounds but behaviour is undefined if the + * bounds are smaller than the true bounds. + * + * The primitive is transformed by the current model-view matrix and + * the silhouette is intersected with the previous clipping area. To + * restore the previous clipping area, call + * cogl_framebuffer_pop_clip(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, + CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2); + +/** + * cogl_framebuffer_pop_clip: + * @framebuffer: A #CoglFramebuffer pointer + * + * Reverts the clipping region to the state before the last call to + * cogl_framebuffer_push_scissor_clip(), cogl_framebuffer_push_rectangle_clip() + * cogl_framebuffer_push_path_clip(), or cogl_framebuffer_push_primitive_clip(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_red_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of red bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_green_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of green bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_blue_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of blue bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_alpha_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of alpha bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 1.8 + * Stability: unstable + */ +int +cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_depth_bits: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves the number of depth bits of @framebuffer + * + * Return value: the number of bits + * + * Since: 2.0 + * Stability: unstable + */ +int +cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer); + +/* + * cogl_framebuffer_get_is_stereo: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Retrieves whether @framebuffer has separate left and right + * buffers for use with stereo drawing. See + * cogl_framebuffer_set_stereo_mode(). + * + * Return value: %TRUE if @framebuffer has separate left and + * right buffers. + * + * Since: 1.20 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_get_is_stereo (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_dither_enabled: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Returns whether dithering has been requested for the given @framebuffer. + * See cogl_framebuffer_set_dither_enabled() for more details about dithering. + * + * This may return %TRUE even when the underlying @framebuffer + * display pipeline does not support dithering. This value only represents + * the user's request for dithering. + * + * Return value: %TRUE if dithering has been requested or %FALSE if not. + * Since: 1.8 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_get_dither_enabled (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_dither_enabled: + * @framebuffer: a pointer to a #CoglFramebuffer + * @dither_enabled: %TRUE to enable dithering or %FALSE to disable + * + * Enables or disabled dithering if supported by the hardware. + * + * Dithering is a hardware dependent technique to increase the visible + * color resolution beyond what the underlying hardware supports by playing + * tricks with the colors placed into the framebuffer to give the illusion + * of other colors. (For example this can be compared to half-toning used + * by some news papers to show varying levels of grey even though their may + * only be black and white are available). + * + * If the current display pipeline for @framebuffer does not support dithering + * then this has no affect. + * + * Dithering is enabled by default. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_set_dither_enabled (CoglFramebuffer *framebuffer, + CoglBool dither_enabled); + +/** + * cogl_framebuffer_get_depth_write_enabled: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Queries whether depth buffer writing is enabled for @framebuffer. This + * can be controlled via cogl_framebuffer_set_depth_write_enabled(). + * + * Return value: %TRUE if depth writing is enabled or %FALSE if not. + * Since: 1.18 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_get_depth_write_enabled (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_depth_write_enabled: + * @framebuffer: a pointer to a #CoglFramebuffer + * @depth_write_enabled: %TRUE to enable depth writing or %FALSE to disable + * + * Enables or disables depth buffer writing when rendering to @framebuffer. + * If depth writing is enabled for both the framebuffer and the rendering + * pipeline, and the framebuffer has an associated depth buffer, depth + * information will be written to this buffer during rendering. + * + * Depth buffer writing is enabled by default. + * + * Since: 1.18 + * Stability: unstable + */ +void +cogl_framebuffer_set_depth_write_enabled (CoglFramebuffer *framebuffer, + CoglBool depth_write_enabled); + +/** + * cogl_framebuffer_get_color_mask: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Gets the current #CoglColorMask of which channels would be written to the + * current framebuffer. Each bit set in the mask means that the + * corresponding color would be written. + * + * Returns: A #CoglColorMask + * Since: 1.8 + * Stability: unstable + */ +CoglColorMask +cogl_framebuffer_get_color_mask (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_color_mask: + * @framebuffer: a pointer to a #CoglFramebuffer + * @color_mask: A #CoglColorMask of which color channels to write to + * the current framebuffer. + * + * Defines a bit mask of which color channels should be written to the + * given @framebuffer. If a bit is set in @color_mask that means that + * color will be written. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_set_color_mask (CoglFramebuffer *framebuffer, + CoglColorMask color_mask); + +/** + * cogl_framebuffer_get_stereo_mode: + * @framebuffer: a pointer to a #CoglFramebuffer + * + * Gets the current #CoglStereoMode, which defines which stereo buffers + * should be drawn to. See cogl_framebuffer_set_stereo_mode(). + * + * Returns: A #CoglStereoMode + * Since: 1.20 + * Stability: unstable + */ +CoglStereoMode +cogl_framebuffer_get_stereo_mode (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_stereo_mode: + * @framebuffer: a pointer to a #CoglFramebuffer + * @stereo_mode: A #CoglStereoMode specifying which stereo buffers + * should be drawn tow. + * + * Sets which stereo buffers should be drawn to. The default + * is %COGL_STEREO_BOTH, which means that both the left and + * right buffers will be affected by drawing. For this to have + * an effect, the display system must support stereo drawables, + * and the framebuffer must have been created with stereo + * enabled. (See cogl_onscreen_template_set_stereo_enabled(), + * cogl_framebuffer_get_is_stereo().) + * + * Since: 1.20 + * Stability: unstable + */ +void +cogl_framebuffer_set_stereo_mode (CoglFramebuffer *framebuffer, + CoglStereoMode stereo_mode); + +/** + * cogl_framebuffer_set_depth_texture_enabled: + * @framebuffer: A #CoglFramebuffer + * @enabled: TRUE or FALSE + * + * If @enabled is #TRUE, the depth buffer used when rendering to @framebuffer + * is available as a texture. You can retrieve the texture with + * cogl_framebuffer_get_depth_texture(). + * + * It's possible that your GPU does not support depth textures. You + * should check the %COGL_FEATURE_ID_DEPTH_TEXTURE feature before using this + * function. + * It's not valid to call this function after the framebuffer has been + * allocated as the creation of the depth texture is done at allocation time. + * + * + * Since: 1.14 + * Stability: unstable + */ +void +cogl_framebuffer_set_depth_texture_enabled (CoglFramebuffer *framebuffer, + CoglBool enabled); + +/** + * cogl_framebuffer_get_depth_texture_enabled: + * @framebuffer: A #CoglFramebuffer + * + * Queries whether texture based depth buffer has been enabled via + * cogl_framebuffer_set_depth_texture_enabled(). + * + * Return value: %TRUE if a depth texture has been enabled, else + * %FALSE. + * + * Since: 1.14 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_get_depth_texture_enabled (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_get_depth_texture: + * @framebuffer: A #CoglFramebuffer + * + * Retrieves the depth buffer of @framebuffer as a #CoglTexture. You need to + * call cogl_framebuffer_get_depth_texture(fb, TRUE); before using this + * function. + * + * Calling this function implicitely allocates the framebuffer. + * The texture returned stays valid as long as the framebuffer stays + * valid. + * + * Returns: (transfer none): the depth texture + * + * Since: 1.14 + * Stability: unstable + */ +CoglTexture * +cogl_framebuffer_get_depth_texture (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_set_samples_per_pixel: + * @framebuffer: A #CoglFramebuffer framebuffer + * @samples_per_pixel: The minimum number of samples per pixel + * + * Requires that when rendering to @framebuffer then @n point samples + * should be made per pixel which will all contribute to the final + * resolved color for that pixel. The idea is that the hardware aims + * to get quality similar to what you would get if you rendered + * everything twice as big (for 4 samples per pixel) and then scaled + * that image back down with filtering. It can effectively remove the + * jagged edges of polygons and should be more efficient than if you + * were to manually render at a higher resolution and downscale + * because the hardware is often able to take some shortcuts. For + * example the GPU may only calculate a single texture sample for all + * points of a single pixel, and for tile based architectures all the + * extra sample data (such as depth and stencil samples) may be + * handled on-chip and so avoid increased demand on system memory + * bandwidth. + * + * By default this value is usually set to 0 and that is referred to + * as "single-sample" rendering. A value of 1 or greater is referred + * to as "multisample" rendering. + * + * There are some semantic differences between single-sample + * rendering and multisampling with just 1 point sample such as it + * being redundant to use the cogl_framebuffer_resolve_samples() and + * cogl_framebuffer_resolve_samples_region() apis with single-sample + * rendering. + * + * It's recommended that + * cogl_framebuffer_resolve_samples_region() be explicitly used at the + * end of rendering to a point sample buffer to minimize the number of + * samples that get resolved. By default Cogl will implicitly resolve + * all framebuffer samples but if only a small region of a + * framebuffer has changed this can lead to redundant work being + * done. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer, + int samples_per_pixel); + +/** + * cogl_framebuffer_get_samples_per_pixel: + * @framebuffer: A #CoglFramebuffer framebuffer + * + * Gets the number of points that are sampled per-pixel when + * rasterizing geometry. Usually by default this will return 0 which + * means that single-sample not multisample rendering has been chosen. + * When using a GPU supporting multisample rendering it's possible to + * increase the number of samples per pixel using + * cogl_framebuffer_set_samples_per_pixel(). + * + * Calling cogl_framebuffer_get_samples_per_pixel() before the + * framebuffer has been allocated will simply return the value set + * using cogl_framebuffer_set_samples_per_pixel(). After the + * framebuffer has been allocated the value will reflect the actual + * number of samples that will be made by the GPU. + * + * Returns: The number of point samples made per pixel when + * rasterizing geometry or 0 if single-sample rendering + * has been chosen. + * + * Since: 1.10 + * Stability: unstable + */ +int +cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer); + + +/** + * cogl_framebuffer_resolve_samples: + * @framebuffer: A #CoglFramebuffer framebuffer + * + * When point sample rendering (also known as multisample rendering) + * has been enabled via cogl_framebuffer_set_samples_per_pixel() + * then you can optionally call this function (or + * cogl_framebuffer_resolve_samples_region()) to explicitly resolve + * the point samples into values for the final color buffer. + * + * Some GPUs will implicitly resolve the point samples during + * rendering and so this function is effectively a nop, but with other + * architectures it is desirable to defer the resolve step until the + * end of the frame. + * + * Since Cogl will automatically ensure samples are resolved if the + * target color buffer is used as a source this API only needs to be + * used if explicit control is desired - perhaps because you want to + * ensure that the resolve is completed in advance to avoid later + * having to wait for the resolve to complete. + * + * If you are performing incremental updates to a framebuffer you + * should consider using cogl_framebuffer_resolve_samples_region() + * instead to avoid resolving redundant pixels. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_resolve_samples_region: + * @framebuffer: A #CoglFramebuffer framebuffer + * @x: top-left x coordinate of region to resolve + * @y: top-left y coordinate of region to resolve + * @width: width of region to resolve + * @height: height of region to resolve + * + * When point sample rendering (also known as multisample rendering) + * has been enabled via cogl_framebuffer_set_samples_per_pixel() + * then you can optionally call this function (or + * cogl_framebuffer_resolve_samples()) to explicitly resolve the point + * samples into values for the final color buffer. + * + * Some GPUs will implicitly resolve the point samples during + * rendering and so this function is effectively a nop, but with other + * architectures it is desirable to defer the resolve step until the + * end of the frame. + * + * Use of this API is recommended if incremental, small updates to + * a framebuffer are being made because by default Cogl will + * implicitly resolve all the point samples of the framebuffer which + * can result in redundant work if only a small number of samples have + * changed. + * + * Because some GPUs implicitly resolve point samples this function + * only guarantees that at-least the region specified will be resolved + * and if you have rendered to a larger region then it's possible that + * other samples may be implicitly resolved. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height); + +/** + * cogl_framebuffer_get_context: + * @framebuffer: A #CoglFramebuffer + * + * Can be used to query the #CoglContext a given @framebuffer was + * instantiated within. This is the #CoglContext that was passed to + * cogl_onscreen_new() for example. + * + * Return value: (transfer none): The #CoglContext that the given + * @framebuffer was instantiated within. + * Since: 1.8 + * Stability: unstable + */ +CoglContext * +cogl_framebuffer_get_context (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_clear: + * @framebuffer: A #CoglFramebuffer + * @buffers: A mask of #CoglBufferBit's identifying which auxiliary + * buffers to clear + * @color: The color to clear the color buffer too if specified in + * @buffers. + * + * Clears all the auxiliary buffers identified in the @buffers mask, and if + * that includes the color buffer then the specified @color is used. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + const CoglColor *color); + +/** + * cogl_framebuffer_clear4f: + * @framebuffer: A #CoglFramebuffer + * @buffers: A mask of #CoglBufferBit's identifying which auxiliary + * buffers to clear + * @red: The red component of color to clear the color buffer too if + * specified in @buffers. + * @green: The green component of color to clear the color buffer too if + * specified in @buffers. + * @blue: The blue component of color to clear the color buffer too if + * specified in @buffers. + * @alpha: The alpha component of color to clear the color buffer too if + * specified in @buffers. + * + * Clears all the auxiliary buffers identified in the @buffers mask, and if + * that includes the color buffer then the specified @color is used. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha); + +/** + * cogl_framebuffer_draw_primitive: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @primitive: A #CoglPrimitive geometry object + * + * Draws the given @primitive geometry to the specified destination + * @framebuffer using the graphics processing state described by @pipeline. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or #CoglTexture3D + * are associated with layers of the given @pipeline. + * + * This api doesn't support any of the legacy global state options such + * as cogl_set_depth_test_enabled(), cogl_set_backface_culling_enabled() or + * cogl_program_use() + * + * Stability: unstable + * Since: 1.10 + * Deprecated: 1.16: Use #CoglPrimitives and + * cogl_primitive_draw() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_draw) +void +cogl_framebuffer_draw_primitive (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPrimitive *primitive); + +/** + * cogl_framebuffer_vdraw_attributes: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @mode: The #CoglVerticesMode defining the topology of vertices + * @first_vertex: The vertex offset within the given attributes to draw from + * @n_vertices: The number of vertices to draw from the given attributes + * @...: A set of vertex #CoglAttributes defining vertex geometry + * + * First defines a geometry primitive by grouping a set of vertex attributes; + * specifying a @first_vertex; a number of vertices (@n_vertices) and + * specifying what kind of topology the vertices have via @mode. + * + * Then the function draws the given @primitive geometry to the specified + * destination @framebuffer using the graphics processing pipeline described by + * @pipeline. + * + * The list of #CoglAttributes define the attributes of the vertices to + * be drawn, such as positions, colors and normals and should be %NULL + * terminated. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or #CoglTexture3D + * are associated with layers of the given @pipeline. + * + * Stability: unstable + * Since: 1.10 + * Deprecated: 1.16: Use #CoglPrimitives and + * cogl_primitive_draw() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_draw) +void +cogl_framebuffer_vdraw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + ...) COGL_GNUC_NULL_TERMINATED; + +/** + * cogl_framebuffer_draw_attributes: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @mode: The #CoglVerticesMode defining the topology of vertices + * @first_vertex: The vertex offset within the given attributes to draw from + * @n_vertices: The number of vertices to draw from the given attributes + * @attributes: An array of pointers to #CoglAttribute<-- -->s defining vertex + * geometry + * @n_attributes: The number of attributes in the @attributes array. + * + * First defines a geometry primitive by grouping a set of vertex @attributes; + * specifying a @first_vertex; a number of vertices (@n_vertices) and + * specifying what kind of topology the vertices have via @mode. + * + * Then the function draws the given @primitive geometry to the specified + * destination @framebuffer using the graphics processing pipeline described by + * @pipeline. + * + * The list of #CoglAttributes define the attributes of the vertices to + * be drawn, such as positions, colors and normals and the number of attributes + * is given as @n_attributes. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or #CoglTexture3D + * are associated with layers of the given @pipeline. + * + * This api doesn't support any of the legacy global state options such + * as cogl_set_depth_test_enabled(), cogl_set_backface_culling_enabled() or + * cogl_program_use() + * + * Stability: unstable + * Since: 1.10 + * Deprecated: 1.16: Use #CoglPrimitives and + * cogl_primitive_draw() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_draw) +void +cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes); + +/** + * cogl_framebuffer_vdraw_indexed_attributes: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @mode: The #CoglVerticesMode defining the topology of vertices + * @first_vertex: The vertex offset within the given attributes to draw from + * @n_vertices: The number of vertices to draw from the given attributes + * @indices: The array of indices used by the GPU to lookup attribute + * data for each vertex. + * @...: A set of vertex #CoglAttributes defining vertex geometry + * + * Behaves the same as cogl_framebuffer_vdraw_attributes() except that + * instead of reading vertex data sequentially from the specified + * attributes the @indices provide an indirection for how the data + * should be indexed allowing a random access order to be + * specified. + * + * For example an indices array of [0, 1, 2, 0, 2, 3] could be used + * used to draw two triangles (@mode = %COGL_VERTICES_MODE_TRIANGLES + + * @n_vertices = 6) but only provide attribute data for the 4 corners + * of a rectangle. When the GPU needs to read in each of the 6 + * vertices it will read the @indices array for each vertex in + * sequence and use the index to look up the vertex attribute data. So + * here you can see that first and fourth vertex will point to the + * same data and third and fifth vertex will also point to shared + * data. + * + * Drawing with indices can be a good way of minimizing the size of a + * mesh by allowing you to avoid data for duplicate vertices because + * multiple entries in the index array can refer back to a single + * shared vertex. + * + * The @indices array must be at least as long as @first_vertex + * + @n_vertices otherwise the GPU will overrun the indices array when + * looking up vertex data. + * + * Since it's very common to want to draw a run of rectangles using + * indices to avoid duplicating vertex data you can use + * cogl_get_rectangle_indices() to get a set of indices that can be + * shared. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or + * #CoglTexture3D are associated with layers of the given @pipeline. + * + * This api doesn't support any of the legacy global state + * options such as cogl_set_depth_test_enabled(), + * cogl_set_backface_culling_enabled() or cogl_program_use() + * + * Stability: unstable + * Since: 1.10 + * Deprecated: 1.16: Use #CoglPrimitives and + * cogl_primitive_draw() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_draw) +void +cogl_framebuffer_vdraw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + ...) COGL_GNUC_NULL_TERMINATED; + +/** + * cogl_framebuffer_draw_indexed_attributes: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @mode: The #CoglVerticesMode defining the topology of vertices + * @first_vertex: The vertex offset within the given attributes to draw from + * @n_vertices: The number of vertices to draw from the given attributes + * @indices: The array of indices used by the GPU to lookup attribute + * data for each vertex. + * @attributes: An array of pointers to #CoglAttribute<-- -->s defining vertex + * geometry + * @n_attributes: The number of attributes in the @attributes array. + * + * Behaves the same as cogl_framebuffer_draw_attributes() except that + * instead of reading vertex data sequentially from the specified + * @attributes the @indices provide an indirection for how the data + * should be indexed allowing a random access order to be + * specified. + * + * For example an indices array of [0, 1, 2, 0, 2, 3] could be used + * used to draw two triangles (@mode = %COGL_VERTICES_MODE_TRIANGLES + + * @n_vertices = 6) but only provide attribute data for the 4 corners + * of a rectangle. When the GPU needs to read in each of the 6 + * vertices it will read the @indices array for each vertex in + * sequence and use the index to look up the vertex attribute data. So + * here you can see that first and fourth vertex will point to the + * same data and third and fifth vertex will also point to shared + * data. + * + * Drawing with indices can be a good way of minimizing the size of a + * mesh by allowing you to avoid data for duplicate vertices because + * multiple entries in the index array can refer back to a single + * shared vertex. + * + * The @indices array must be at least as long as @first_vertex + * + @n_vertices otherwise the GPU will overrun the indices array when + * looking up vertex data. + * + * Since it's very common to want to draw a run of rectangles using + * indices to avoid duplicating vertex data you can use + * cogl_get_rectangle_indices() to get a set of indices that can be + * shared. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or + * #CoglTexture3D are associated with layers of the given @pipeline. + * + * This api doesn't support any of the legacy global state + * options such as cogl_set_depth_test_enabled(), + * cogl_set_backface_culling_enabled() or cogl_program_use() + * + * Stability: unstable + * Since: 1.10 + * Deprecated: 1.16: Use #CoglPrimitives and + * cogl_primitive_draw() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_draw) +void +cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes); + +/** + * cogl_framebuffer_draw_rectangle: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @x_1: X coordinate of the top-left corner + * @y_1: Y coordinate of the top-left corner + * @x_2: X coordinate of the bottom-right corner + * @y_2: Y coordinate of the bottom-right corner + * + * Draws a rectangle to @framebuffer with the given @pipeline state + * and with the top left corner positioned at (@x_1, @y_1) and the + * bottom right corner positioned at (@x_2, @y_2). + * + * The position is the position before the rectangle has been + * transformed by the model-view matrix and the projection + * matrix. + * + * If you want to describe a rectangle with a texture mapped on + * it then you can use + * cogl_framebuffer_draw_textured_rectangle(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_draw_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2); + +/** + * cogl_framebuffer_draw_textured_rectangle: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @x_1: x coordinate upper left on screen. + * @y_1: y coordinate upper left on screen. + * @x_2: x coordinate lower right on screen. + * @y_2: y coordinate lower right on screen. + * @s_1: S texture coordinate of the top-left coorner + * @t_1: T texture coordinate of the top-left coorner + * @s_2: S texture coordinate of the bottom-right coorner + * @t_2: T texture coordinate of the bottom-right coorner + * + * Draws a textured rectangle to @framebuffer using the given + * @pipeline state with the top left corner positioned at (@x_1, @y_1) + * and the bottom right corner positioned at (@x_2, @y_2). The top + * left corner will have texture coordinates of (@s_1, @t_1) and the + * bottom right corner will have texture coordinates of (@s_2, @t_2). + * + * The position is the position before the rectangle has been + * transformed by the model-view matrix and the projection + * matrix. + * + * This is a high level drawing api that can handle any kind of + * #CoglMetaTexture texture such as #CoglTexture2DSliced textures + * which may internally be comprised of multiple low-level textures. + * This is unlike low-level drawing apis such as cogl_primitive_draw() + * which only support low level texture types that are directly + * supported by GPUs such as #CoglTexture2D. + * + * The given texture coordinates will only be used for the first + * texture layer of the pipeline and if your pipeline has more than + * one layer then all other layers will have default texture + * coordinates of @s_1=0.0 @t_1=0.0 @s_2=1.0 @t_2=1.0 + * + * The given texture coordinates should always be normalized such that + * (0, 0) corresponds to the top left and (1, 1) corresponds to the + * bottom right. To map an entire texture across the rectangle pass + * in @s_1=0, @t_1=0, @s_2=1, @t_2=1. + * + * Even if you have associated a #CoglTextureRectangle texture + * with one of your @pipeline layers which normally implies working + * with non-normalized texture coordinates this api should still be + * passed normalized texture coordinates. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_draw_textured_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2, + float s_1, + float t_1, + float s_2, + float t_2); + +/** + * cogl_framebuffer_draw_multitextured_rectangle: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @x_1: x coordinate upper left on screen. + * @y_1: y coordinate upper left on screen. + * @x_2: x coordinate lower right on screen. + * @y_2: y coordinate lower right on screen. + * @tex_coords: (in) (array) (transfer none): An array containing groups of + * 4 float values: [s_1, t_1, s_2, t_2] that are interpreted as two texture + * coordinates; one for the top left texel, and one for the bottom right + * texel. Each value should be between 0.0 and 1.0, where the coordinate + * (0.0, 0.0) represents the top left of the texture, and (1.0, 1.0) the + * bottom right. + * @tex_coords_len: The length of the @tex_coords array. (For one layer + * and one group of texture coordinates, this would be 4) + * + * Draws a textured rectangle to @framebuffer with the given @pipeline + * state with the top left corner positioned at (@x_1, @y_1) and the + * bottom right corner positioned at (@x_2, @y_2). As a pipeline may + * contain multiple texture layers this interface lets you supply + * texture coordinates for each layer of the pipeline. + * + * The position is the position before the rectangle has been + * transformed by the model-view matrix and the projection + * matrix. + * + * This is a high level drawing api that can handle any kind of + * #CoglMetaTexture texture for the first layer such as + * #CoglTexture2DSliced textures which may internally be comprised of + * multiple low-level textures. This is unlike low-level drawing apis + * such as cogl_primitive_draw() which only support low level texture + * types that are directly supported by GPUs such as #CoglTexture2D. + * + * This api can not currently handle multiple high-level meta + * texture layers. The first layer may be a high level meta texture + * such as #CoglTexture2DSliced but all other layers much be low + * level textures such as #CoglTexture2D and additionally they + * should be textures that can be sampled using normalized coordinates + * (so not #CoglTextureRectangle textures). + * + * The top left texture coordinate for layer 0 of any pipeline will be + * (tex_coords[0], tex_coords[1]) and the bottom right coordinate will + * be (tex_coords[2], tex_coords[3]). The coordinates for layer 1 + * would be (tex_coords[4], tex_coords[5]) (tex_coords[6], + * tex_coords[7]) and so on... + * + * The given texture coordinates should always be normalized such that + * (0, 0) corresponds to the top left and (1, 1) corresponds to the + * bottom right. To map an entire texture across the rectangle pass + * in tex_coords[0]=0, tex_coords[1]=0, tex_coords[2]=1, + * tex_coords[3]=1. + * + * Even if you have associated a #CoglTextureRectangle texture + * which normally implies working with non-normalized texture + * coordinates this api should still be passed normalized texture + * coordinates. + * + * The first pair of coordinates are for the first layer (with the + * smallest layer index) and if you supply less texture coordinates + * than there are layers in the current source material then default + * texture coordinates (0.0, 0.0, 1.0, 1.0) are generated. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_draw_multitextured_rectangle (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2, + const float *tex_coords, + int tex_coords_len); + +/** + * cogl_framebuffer_draw_rectangles: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @coordinates: (in) (array) (transfer none): an array of coordinates + * containing groups of 4 float values: [x_1, y_1, x_2, y_2] that are + * interpreted as two position coordinates; one for the top left of + * the rectangle (x1, y1), and one for the bottom right of the + * rectangle (x2, y2). + * @n_rectangles: number of rectangles defined in @coordinates. + * + * Draws a series of rectangles to @framebuffer with the given + * @pipeline state in the same way that + * cogl_framebuffer_draw_rectangle() does. + * + * The top left corner of the first rectangle is positioned at + * (coordinates[0], coordinates[1]) and the bottom right corner is + * positioned at (coordinates[2], coordinates[3]). The positions for + * the second rectangle are (coordinates[4], coordinates[5]) and + * (coordinates[6], coordinates[7]) and so on... + * + * The position is the position before the rectangle has been + * transformed by the model-view matrix and the projection + * matrix. + * + * As a general rule for better performance its recommended to use + * this this API instead of calling + * cogl_framebuffer_draw_textured_rectangle() separately for multiple + * rectangles if all of the rectangles will be drawn together with the + * same @pipeline state. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_draw_rectangles (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + const float *coordinates, + unsigned int n_rectangles); + +/** + * cogl_framebuffer_draw_textured_rectangles: + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * @coordinates: (in) (array) (transfer none): an array containing + * groups of 8 float values: [x_1, y_1, x_2, y_2, s_1, t_1, s_2, t_2] + * that have the same meaning as the arguments for + * cogl_framebuffer_draw_textured_rectangle(). + * @n_rectangles: number of rectangles to @coordinates to draw + * + * Draws a series of rectangles to @framebuffer with the given + * @pipeline state in the same way that + * cogl_framebuffer_draw_textured_rectangle() does. + * + * The position is the position before the rectangle has been + * transformed by the model-view matrix and the projection + * matrix. + * + * This is a high level drawing api that can handle any kind of + * #CoglMetaTexture texture such as #CoglTexture2DSliced textures + * which may internally be comprised of multiple low-level textures. + * This is unlike low-level drawing apis such as cogl_primitive_draw() + * which only support low level texture types that are directly + * supported by GPUs such as #CoglTexture2D. + * + * The top left corner of the first rectangle is positioned at + * (coordinates[0], coordinates[1]) and the bottom right corner is + * positioned at (coordinates[2], coordinates[3]). The top left + * texture coordinate is (coordinates[4], coordinates[5]) and the + * bottom right texture coordinate is (coordinates[6], + * coordinates[7]). The coordinates for subsequent rectangles + * are defined similarly by the subsequent coordinates. + * + * As a general rule for better performance its recommended to use + * this this API instead of calling + * cogl_framebuffer_draw_textured_rectangle() separately for multiple + * rectangles if all of the rectangles will be drawn together with the + * same @pipeline state. + * + * The given texture coordinates should always be normalized such that + * (0, 0) corresponds to the top left and (1, 1) corresponds to the + * bottom right. To map an entire texture across the rectangle pass + * in tex_coords[0]=0, tex_coords[1]=0, tex_coords[2]=1, + * tex_coords[3]=1. + * + * Even if you have associated a #CoglTextureRectangle texture + * which normally implies working with non-normalized texture + * coordinates this api should still be passed normalized texture + * coordinates. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + const float *coordinates, + unsigned int n_rectangles); + +/* XXX: Should we take an n_buffers + buffer id array instead of using + * the CoglBufferBits type which doesn't seem future proof? */ +/** + * cogl_framebuffer_discard_buffers: + * @framebuffer: A #CoglFramebuffer + * @buffers: A #CoglBufferBit mask of which ancillary buffers you want + * to discard. + * + * Declares that the specified @buffers no longer need to be referenced + * by any further rendering commands. This can be an important + * optimization to avoid subsequent frames of rendering depending on + * the results of a previous frame. + * + * For example; some tile-based rendering GPUs are able to avoid allocating and + * accessing system memory for the depth and stencil buffer so long as these + * buffers are not required as input for subsequent frames and that can save a + * significant amount of memory bandwidth used to save and restore their + * contents to system memory between frames. + * + * It is currently considered an error to try and explicitly discard the color + * buffer by passing %COGL_BUFFER_BIT_COLOR. This is because the color buffer is + * already implicitly discard when you finish rendering to a #CoglOnscreen + * framebuffer, and it's not meaningful to try and discard the color buffer of + * a #CoglOffscreen framebuffer since they are single-buffered. + * + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers); + +/** + * cogl_framebuffer_finish: + * @framebuffer: A #CoglFramebuffer pointer + * + * This blocks the CPU until all pending rendering associated with the + * specified framebuffer has completed. It's very rare that developers should + * ever need this level of synchronization with the GPU and should never be + * used unless you clearly understand why you need to explicitly force + * synchronization. + * + * One example might be for benchmarking purposes to be sure timing + * measurements reflect the time that the GPU is busy for not just the time it + * takes to queue rendering commands. + * + * Stability: unstable + * Since: 1.10 + */ +void +cogl_framebuffer_finish (CoglFramebuffer *framebuffer); + +/** + * cogl_framebuffer_read_pixels_into_bitmap: + * @framebuffer: A #CoglFramebuffer + * @x: The x position to read from + * @y: The y position to read from + * @source: Identifies which auxillary buffer you want to read + * (only COGL_READ_PIXELS_COLOR_BUFFER supported currently) + * @bitmap: The bitmap to store the results in. + * + * This reads a rectangle of pixels from the given framebuffer where + * position (0, 0) is the top left. The pixel at (x, y) is the first + * read, and a rectangle of pixels with the same size as the bitmap is + * read right and downwards from that point. + * + * Currently Cogl assumes that the framebuffer is in a premultiplied + * format so if the format of @bitmap is non-premultiplied it will + * convert it. To read the pixel values without any conversion you + * should either specify a format that doesn't use an alpha channel or + * use one of the formats ending in PRE. + * + * Return value: %TRUE if the read succeeded or %FALSE otherwise. The + * function is only likely to fail if the bitmap points to a pixel + * buffer and it could not be mapped. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap); + +/** + * cogl_framebuffer_read_pixels: + * @framebuffer: A #CoglFramebuffer + * @x: The x position to read from + * @y: The y position to read from + * @width: The width of the region of rectangles to read + * @height: The height of the region of rectangles to read + * @format: The pixel format to store the data in + * @pixels: The address of the buffer to store the data in + * + * This is a convenience wrapper around + * cogl_framebuffer_read_pixels_into_bitmap() which allocates a + * temporary #CoglBitmap to read pixel data directly into the given + * buffer. The rowstride of the buffer is assumed to be the width of + * the region times the bytes per pixel of the format. The source for + * the data is always taken from the color buffer. If you want to use + * any other rowstride or source, please use the + * cogl_framebuffer_read_pixels_into_bitmap() function directly. + * + * The implementation of the function looks like this: + * + * |[ + * bitmap = cogl_bitmap_new_for_data (context, + * width, height, + * format, + * /* rowstride */ + * bpp * width, + * pixels); + * cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + * x, y, + * COGL_READ_PIXELS_COLOR_BUFFER, + * bitmap); + * cogl_object_unref (bitmap); + * ]| + * + * Return value: %TRUE if the read succeeded or %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height, + CoglPixelFormat format, + uint8_t *pixels); + +/** + * cogl_get_draw_framebuffer: + * + * Gets the current #CoglFramebuffer as set using + * cogl_push_framebuffer() + * + * Return value: (transfer none): The current #CoglFramebuffer + * Stability: unstable + * Since: 1.8 + */ +CoglFramebuffer * +cogl_get_draw_framebuffer (void); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +/* XXX: Note these are defined outside the COGL_ENABLE_EXPERIMENTAL_API guard since + * otherwise the glib-mkenums stuff will get upset. */ + +uint32_t +cogl_framebuffer_error_quark (void); + +/** + * COGL_FRAMEBUFFER_ERROR: + * + * An error domain for reporting #CoglFramebuffer exceptions + */ +#define COGL_FRAMEBUFFER_ERROR (cogl_framebuffer_error_quark ()) + +typedef enum { /*< prefix=COGL_FRAMEBUFFER_ERROR >*/ + COGL_FRAMEBUFFER_ERROR_ALLOCATE +} CoglFramebufferError; + +/** + * cogl_is_framebuffer: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglFramebuffer. + * + * Return value: %TRUE if the object references a #CoglFramebuffer + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_framebuffer (void *object); + +COGL_END_DECLS + +#endif /* __COGL_FRAMEBUFFER_H */ diff --git a/cogl/cogl/cogl-gl-header.h.in b/cogl/cogl/cogl-gl-header.h.in new file mode 100644 index 000000000..0696dcf72 --- /dev/null +++ b/cogl/cogl/cogl-gl-header.h.in @@ -0,0 +1,46 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(COGL_COMPILATION) +#error "cogl-gl-header.h should only be included when compiling Cogl" +#endif + +#ifndef __COGL_GL_HEADER_H__ +#define __COGL_GL_HEADER_H__ + +#include "cogl-defines.h" + +@COGL_GL_HEADER_INCLUDES@ + +#ifndef GL_OES_EGL_image +#define GLeglImageOES void * +#endif + +#endif /* __COGL_GL_HEADER_H__ */ diff --git a/cogl/cogl/cogl-gles2-context-private.h b/cogl/cogl/cogl-gles2-context-private.h new file mode 100644 index 000000000..805b06468 --- /dev/null +++ b/cogl/cogl/cogl-gles2-context-private.h @@ -0,0 +1,201 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Collabora Ltd. + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Tomeu Vizoso + * Robert Bragg + * + */ + +#ifndef __COGL_GLES2_CONTEXT_PRIVATE_H +#define __COGL_GLES2_CONTEXT_PRIVATE_H + +#include + +#include "cogl-object-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-list.h" + +typedef struct _CoglGLES2Offscreen +{ + CoglList link; + CoglOffscreen *original_offscreen; + CoglGLFramebuffer gl_framebuffer; +} CoglGLES2Offscreen; + +typedef struct +{ + /* GL's ID for the shader */ + GLuint object_id; + /* Shader type */ + GLenum type; + + /* Number of references to this shader. The shader will have one + * reference when it is created. This reference will be removed when + * glDeleteShader is called. An additional reference will be taken + * whenever the shader is attached to a program. This is necessary + * to correctly detect when a shader is destroyed because + * glDeleteShader doesn't actually delete the object if it is + * attached to a program */ + int ref_count; + + /* Set once this object has had glDeleteShader called on it. We need + * to keep track of this so we don't deref the data twice if the + * application calls glDeleteShader multiple times */ + CoglBool deleted; +} CoglGLES2ShaderData; + +typedef enum +{ + COGL_GLES2_FLIP_STATE_UNKNOWN, + COGL_GLES2_FLIP_STATE_NORMAL, + COGL_GLES2_FLIP_STATE_FLIPPED +} CoglGLES2FlipState; + +typedef struct +{ + /* GL's ID for the program */ + GLuint object_id; + + /* List of shaders attached to this program */ + GList *attached_shaders; + + /* Reference count. There can be up to two references. One of these + * will exist between glCreateProgram and glDeleteShader, the other + * will exist while the program is made current. This is necessary + * to correctly detect when the program is deleted because + * glDeleteShader will delay the deletion if the program is + * current */ + int ref_count; + + /* Set once this object has had glDeleteProgram called on it. We need + * to keep track of this so we don't deref the data twice if the + * application calls glDeleteProgram multiple times */ + 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; +} CoglGLES2ProgramData; + +/* State tracked for each texture unit */ +typedef struct +{ + /* The currently bound texture for the GL_TEXTURE_2D */ + GLuint current_texture_2d; +} CoglGLES2TextureUnitData; + +/* State tracked for each texture object */ +typedef struct +{ + /* GL's ID for this object */ + GLuint object_id; + + GLenum target; + + /* The details for texture when it has a 2D target */ + int width, height; + GLenum format; +} CoglGLES2TextureObjectData; + +struct _CoglGLES2Context +{ + CoglObject _parent; + + CoglContext *context; + + /* This is set to FALSE until the first time the GLES2 context is + * bound to something. We need to keep track of this so we can set + * the viewport and scissor the first time it is bound. */ + CoglBool has_been_bound; + + CoglFramebuffer *read_buffer; + CoglGLES2Offscreen *gles2_read_buffer; + CoglFramebuffer *write_buffer; + CoglGLES2Offscreen *gles2_write_buffer; + + GLuint current_fbo_handle; + + CoglList foreign_offscreens; + + CoglGLES2Vtable *vtable; + + /* Hash table mapping GL's IDs for shaders and objects to ShaderData + * and ProgramData so that we can maintain extra data for these + * objects. Although technically the IDs will end up global across + * all GLES2 contexts because they will all be in the same share + * list, we don't really want to expose this outside of the Cogl API + * so we will assume it is undefined behaviour if an application + * relies on this. */ + GHashTable *shader_map; + GHashTable *program_map; + + /* Currently in use program. We need to keep track of this so that + * we can keep a reference to the data for the program while it is + * current */ + CoglGLES2ProgramData *current_program; + + /* 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; + + /* A hash table of CoglGLES2TextureObjects indexed by the texture + * object ID so that we can track some state */ + GHashTable *texture_object_map; + + /* Array of CoglGLES2TextureUnits to keep track of state for each + * texture unit */ + GArray *texture_units; + + /* The currently active texture unit indexed from 0 (not from + * GL_TEXTURE0) */ + int current_texture_unit; + + void *winsys; +}; + +#endif /* __COGL_GLES2_CONTEXT_PRIVATE_H */ diff --git a/cogl/cogl/cogl-gles2-context.c b/cogl/cogl/cogl-gles2-context.c new file mode 100644 index 000000000..33c438798 --- /dev/null +++ b/cogl/cogl/cogl-gles2-context.c @@ -0,0 +1,1966 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Collabora Ltd. + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Tomeu Vizoso + * Robert Bragg + * Neil Roberts + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-gles2.h" +#include "cogl-gles2-context-private.h" + +#include "cogl-context-private.h" +#include "cogl-display-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-framebuffer-gl-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-renderer-private.h" +#include "cogl-swap-chain-private.h" +#include "cogl-texture-2d-gl.h" +#include "cogl-texture-2d-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-error-private.h" +#include "cogl-gtype-private.h" + +static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context); + +COGL_OBJECT_DEFINE (GLES2Context, gles2_context); +COGL_GTYPE_DEFINE_CLASS (GLES2Context, gles2_context); + +static CoglGLES2Context *current_gles2_context; + +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" +/* These comments are used to delimit the added wrapper snippet so + * that we can remove it again when the shader source is requested via + * glGetShaderSource */ +#define MAIN_WRAPPER_BEGIN "/*_COGL_WRAPPER_BEGIN*/" +#define MAIN_WRAPPER_END "/*_COGL_WRAPPER_END*/" + +/* This wrapper function around 'main' is appended to every vertex 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[] = + MAIN_WRAPPER_BEGIN "\n" + "uniform vec4 " MAIN_WRAPPER_FLIP_UNIFORM ";\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " " MAIN_WRAPPER_REPLACEMENT_NAME " ();\n" + " gl_Position *= " MAIN_WRAPPER_FLIP_UNIFORM ";\n" + "}\n" + MAIN_WRAPPER_END; + +enum { + RESTORE_FB_NONE, + RESTORE_FB_FROM_OFFSCREEN, + RESTORE_FB_FROM_ONSCREEN, +}; + +uint32_t +_cogl_gles2_context_error_quark (void) +{ + return g_quark_from_static_string ("cogl-gles2-context-error-quark"); +} + +static void +shader_data_unref (CoglGLES2Context *context, + CoglGLES2ShaderData *shader_data) +{ + if (--shader_data->ref_count < 1) + /* Removing the hash table entry should also destroy the data */ + g_hash_table_remove (context->shader_map, + GINT_TO_POINTER (shader_data->object_id)); +} + +static void +program_data_unref (CoglGLES2ProgramData *program_data) +{ + if (--program_data->ref_count < 1) + /* Removing the hash table entry should also destroy the data */ + g_hash_table_remove (program_data->context->program_map, + GINT_TO_POINTER (program_data->object_id)); +} + +static void +detach_shader (CoglGLES2ProgramData *program_data, + CoglGLES2ShaderData *shader_data) +{ + GList *l; + + for (l = program_data->attached_shaders; l; l = l->next) + { + if (l->data == shader_data) + { + shader_data_unref (program_data->context, shader_data); + program_data->attached_shaders = + g_list_delete_link (program_data->attached_shaders, l); + break; + } + } +} + +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; + } +} + +static GLuint +get_current_texture_2d_object (CoglGLES2Context *gles2_ctx) +{ + return g_array_index (gles2_ctx->texture_units, + CoglGLES2TextureUnitData, + gles2_ctx->current_texture_unit).current_texture_2d; +} + +static void +set_texture_object_data (CoglGLES2Context *gles2_ctx, + GLenum target, + GLint level, + GLenum internal_format, + GLsizei width, + GLsizei height) +{ + GLuint texture_id = get_current_texture_2d_object (gles2_ctx); + CoglGLES2TextureObjectData *texture_object; + + /* We want to keep track of all texture objects where the data is + * created by this context so that we can delete them later */ + texture_object = g_hash_table_lookup (gles2_ctx->texture_object_map, + GUINT_TO_POINTER (texture_id)); + if (texture_object == NULL) + { + texture_object = g_slice_new0 (CoglGLES2TextureObjectData); + texture_object->object_id = texture_id; + + g_hash_table_insert (gles2_ctx->texture_object_map, + GUINT_TO_POINTER (texture_id), + texture_object); + } + + switch (target) + { + case GL_TEXTURE_2D: + texture_object->target = GL_TEXTURE_2D; + + /* We want to keep track of the dimensions of any texture object + * setting the GL_TEXTURE_2D target */ + if (level == 0) + { + texture_object->width = width; + texture_object->height = height; + texture_object->format = internal_format; + } + break; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + texture_object->target = GL_TEXTURE_CUBE_MAP; + break; + } +} + +static void +copy_flipped_texture (CoglGLES2Context *gles2_ctx, + int level, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + GLuint tex_id = get_current_texture_2d_object (gles2_ctx); + CoglGLES2TextureObjectData *tex_object_data; + CoglContext *ctx; + const CoglWinsysVtable *winsys; + CoglTexture2D *dst_texture; + CoglPixelFormat internal_format; + + tex_object_data = g_hash_table_lookup (gles2_ctx->texture_object_map, + GUINT_TO_POINTER (tex_id)); + + /* We can't do anything if the application hasn't set a level 0 + * image on this texture object */ + if (tex_object_data == NULL || + tex_object_data->target != GL_TEXTURE_2D || + tex_object_data->width <= 0 || + tex_object_data->height <= 0) + return; + + switch (tex_object_data->format) + { + case GL_RGB: + internal_format = COGL_PIXEL_FORMAT_RGB_888; + break; + + case GL_RGBA: + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + break; + + case GL_ALPHA: + internal_format = COGL_PIXEL_FORMAT_A_8; + break; + + case GL_LUMINANCE: + internal_format = COGL_PIXEL_FORMAT_G_8; + break; + + default: + /* We can't handle this format so just give up */ + return; + } + + ctx = gles2_ctx->context; + winsys = ctx->display->renderer->winsys_vtable; + + /* We need to make sure the rendering on the GLES2 context is + * complete before the blit will be ready in the GLES2 context */ + ctx->glFinish (); + /* We need to force Cogl to rebind the texture because according to + * the GL spec a shared texture isn't guaranteed to be updated until + * is rebound */ + _cogl_get_texture_unit (0)->dirty_gl_texture = TRUE; + + /* Temporarily switch back to the Cogl context */ + winsys->restore_context (ctx); + + dst_texture = + cogl_gles2_texture_2d_new_from_handle (gles2_ctx->context, + gles2_ctx, + tex_id, + tex_object_data->width, + tex_object_data->height, + internal_format); + + if (dst_texture) + { + CoglTexture *src_texture = + COGL_OFFSCREEN (gles2_ctx->read_buffer)->texture; + CoglPipeline *pipeline = cogl_pipeline_new (ctx); + const CoglOffscreenFlags flags = COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL; + CoglOffscreen *offscreen = + _cogl_offscreen_new_with_texture_full (COGL_TEXTURE (dst_texture), + flags, level); + int src_width = cogl_texture_get_width (src_texture); + int src_height = cogl_texture_get_height (src_texture); + /* The framebuffer size might be different from the texture size + * if a level > 0 is used */ + int dst_width = + cogl_framebuffer_get_width (COGL_FRAMEBUFFER (offscreen)); + int dst_height = + cogl_framebuffer_get_height (COGL_FRAMEBUFFER (offscreen)); + float x_1, y_1, x_2, y_2, s_1, t_1, s_2, t_2; + + cogl_pipeline_set_layer_texture (pipeline, 0, src_texture); + cogl_pipeline_set_blend (pipeline, + "RGBA = ADD(SRC_COLOR, 0)", + NULL); + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer_num */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + x_1 = dst_x * 2.0f / dst_width - 1.0f; + y_1 = dst_y * 2.0f / dst_height - 1.0f; + x_2 = x_1 + width * 2.0f / dst_width; + y_2 = y_1 + height * 2.0f / dst_height; + + s_1 = src_x / (float) src_width; + t_1 = 1.0f - src_y / (float) src_height; + s_2 = (src_x + width) / (float) src_width; + t_2 = 1.0f - (src_y + height) / (float) src_height; + + cogl_framebuffer_draw_textured_rectangle (COGL_FRAMEBUFFER (offscreen), + pipeline, + x_1, y_1, + x_2, y_2, + s_1, t_1, + s_2, t_2); + + _cogl_framebuffer_flush_journal (COGL_FRAMEBUFFER (offscreen)); + + /* We need to make sure the rendering is complete before the + * blit will be ready in the GLES2 context */ + ctx->glFinish (); + + cogl_object_unref (pipeline); + cogl_object_unref (dst_texture); + cogl_object_unref (offscreen); + } + + winsys->set_gles2_context (gles2_ctx, NULL); + + /* From what I understand of the GL spec, changes to a shared object + * are not guaranteed to be propagated to another context until that + * object is rebound in that context so we can just rebind it + * here */ + gles2_ctx->vtable->glBindTexture (GL_TEXTURE_2D, tex_id); +} + +/* We wrap glBindFramebuffer so that when framebuffer 0 is bound + * we can instead bind the write_framebuffer passed to + * cogl_push_gles2_context(). + */ +static void +gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + + gles2_ctx->current_fbo_handle = framebuffer; + + if (framebuffer == 0 && cogl_is_offscreen (gles2_ctx->write_buffer)) + { + CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer; + framebuffer = write->gl_framebuffer.fbo_handle; + } + + gles2_ctx->context->glBindFramebuffer (target, framebuffer); + + update_current_flip_state (gles2_ctx); +} + +static int +transient_bind_read_buffer (CoglGLES2Context *gles2_ctx) +{ + if (gles2_ctx->current_fbo_handle == 0) + { + if (cogl_is_offscreen (gles2_ctx->read_buffer)) + { + CoglGLES2Offscreen *read = gles2_ctx->gles2_read_buffer; + GLuint read_fbo_handle = read->gl_framebuffer.fbo_handle; + + gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER, + read_fbo_handle); + + return RESTORE_FB_FROM_OFFSCREEN; + } + else + { + _cogl_framebuffer_gl_bind (gles2_ctx->read_buffer, + 0 /* target ignored */); + + return RESTORE_FB_FROM_ONSCREEN; + } + } + else + return RESTORE_FB_NONE; +} + +static void +restore_write_buffer (CoglGLES2Context *gles2_ctx, + int restore_mode) +{ + switch (restore_mode) + { + case RESTORE_FB_FROM_OFFSCREEN: + + gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0); + + break; + case RESTORE_FB_FROM_ONSCREEN: + + /* Note: we can't restore the original write buffer using + * _cogl_framebuffer_gl_bind() if it's an offscreen + * framebuffer because _cogl_framebuffer_gl_bind() doesn't + * know about the fbo handle owned by the gles2 context. + */ + if (cogl_is_offscreen (gles2_ctx->write_buffer)) + gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0); + else + _cogl_framebuffer_gl_bind (gles2_ctx->write_buffer, GL_FRAMEBUFFER); + + break; + case RESTORE_FB_NONE: + break; + } +} + +/* We wrap glReadPixels so when framebuffer 0 is bound then we can + * read from the read_framebuffer passed to cogl_push_gles2_context(). + */ +static void +gl_read_pixels_wrapper (GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + int restore_mode = transient_bind_read_buffer (gles2_ctx); + + gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels); + + 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 +gl_copy_tex_image_2d_wrapper (GLenum target, + GLint level, + GLenum internal_format, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + + /* If we are reading from a CoglOffscreen buffer then the image will + * be upside down with respect to what GL expects so we can't use + * glCopyTexImage2D. Instead we we'll try to use the Cogl API to + * flip it */ + if (gles2_ctx->current_fbo_handle == 0 && + cogl_is_offscreen (gles2_ctx->read_buffer)) + { + /* This will only work with the GL_TEXTURE_2D target. FIXME: + * GLES2 also supports setting cube map textures with + * glTexImage2D so we need to handle that too */ + if (target != GL_TEXTURE_2D) + return; + + /* Create an empty texture to hold the data */ + gles2_ctx->vtable->glTexImage2D (target, + level, + internal_format, + width, height, + border, + internal_format, /* format */ + GL_UNSIGNED_BYTE, /* type */ + NULL /* data */); + + copy_flipped_texture (gles2_ctx, + level, + x, y, /* src_x/src_y */ + 0, 0, /* dst_x/dst_y */ + width, height); + } + else + { + int restore_mode = transient_bind_read_buffer (gles2_ctx); + + gles2_ctx->context->glCopyTexImage2D (target, level, internal_format, + x, y, width, height, border); + + restore_write_buffer (gles2_ctx, restore_mode); + + set_texture_object_data (gles2_ctx, + target, + level, + internal_format, + width, height); + } +} + +static void +gl_copy_tex_sub_image_2d_wrapper (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + + /* If we are reading from a CoglOffscreen buffer then the image will + * be upside down with respect to what GL expects so we can't use + * glCopyTexSubImage2D. Instead we we'll try to use the Cogl API to + * flip it */ + if (gles2_ctx->current_fbo_handle == 0 && + cogl_is_offscreen (gles2_ctx->read_buffer)) + { + /* This will only work with the GL_TEXTURE_2D target. FIXME: + * GLES2 also supports setting cube map textures with + * glTexImage2D so we need to handle that too */ + if (target != GL_TEXTURE_2D) + return; + + copy_flipped_texture (gles2_ctx, + level, + x, y, /* src_x/src_y */ + xoffset, yoffset, /* dst_x/dst_y */ + width, height); + } + else + { + int restore_mode = transient_bind_read_buffer (gles2_ctx); + + gles2_ctx->context->glCopyTexSubImage2D (target, level, + xoffset, yoffset, + x, y, width, height); + + restore_write_buffer (gles2_ctx, restore_mode); + } +} + +static GLuint +gl_create_shader_wrapper (GLenum type) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + GLuint id; + + id = gles2_ctx->context->glCreateShader (type); + + if (id != 0) + { + CoglGLES2ShaderData *data = g_slice_new (CoglGLES2ShaderData); + + data->object_id = id; + data->type = type; + data->ref_count = 1; + data->deleted = FALSE; + + g_hash_table_insert (gles2_ctx->shader_map, + GINT_TO_POINTER (id), + data); + } + + return id; +} + +static void +gl_delete_shader_wrapper (GLuint shader) +{ + 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->deleted) + { + shader_data->deleted = TRUE; + shader_data_unref (gles2_ctx, shader_data); + } + + gles2_ctx->context->glDeleteShader (shader); +} + +static GLuint +gl_create_program_wrapper (void) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + GLuint id; + + id = gles2_ctx->context->glCreateProgram (); + + if (id != 0) + { + CoglGLES2ProgramData *data = g_slice_new (CoglGLES2ProgramData); + + data->object_id = id; + data->attached_shaders = NULL; + data->ref_count = 1; + data->deleted = FALSE; + 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, + GINT_TO_POINTER (id), + data); + } + + return id; +} + +static void +gl_delete_program_wrapper (GLuint program) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + CoglGLES2ProgramData *program_data; + + if ((program_data = g_hash_table_lookup (gles2_ctx->program_map, + GINT_TO_POINTER (program))) && + !program_data->deleted) + { + program_data->deleted = TRUE; + program_data_unref (program_data); + } + + gles2_ctx->context->glDeleteProgram (program); +} + +static void +gl_use_program_wrapper (GLuint program) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + CoglGLES2ProgramData *program_data; + + program_data = g_hash_table_lookup (gles2_ctx->program_map, + GINT_TO_POINTER (program)); + + if (program_data) + program_data->ref_count++; + if (gles2_ctx->current_program) + program_data_unref (gles2_ctx->current_program); + + gles2_ctx->current_program = program_data; + + gles2_ctx->context->glUseProgram (program); +} + +static void +gl_attach_shader_wrapper (GLuint program, + GLuint shader) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + CoglGLES2ProgramData *program_data; + CoglGLES2ShaderData *shader_data; + + if ((program_data = g_hash_table_lookup (gles2_ctx->program_map, + GINT_TO_POINTER (program))) && + (shader_data = g_hash_table_lookup (gles2_ctx->shader_map, + GINT_TO_POINTER (shader))) && + /* Ignore attempts to attach a shader that is already attached */ + g_list_find (program_data->attached_shaders, shader_data) == NULL) + { + shader_data->ref_count++; + program_data->attached_shaders = + g_list_prepend (program_data->attached_shaders, shader_data); + } + + gles2_ctx->context->glAttachShader (program, shader); +} + +static void +gl_detach_shader_wrapper (GLuint program, + GLuint shader) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + CoglGLES2ProgramData *program_data; + CoglGLES2ShaderData *shader_data; + + if ((program_data = g_hash_table_lookup (gles2_ctx->program_map, + GINT_TO_POINTER (program))) && + (shader_data = g_hash_table_lookup (gles2_ctx->shader_map, + GINT_TO_POINTER (shader)))) + detach_shader (program_data, shader_data); + + 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 + 1) * sizeof (char *)); + int *length_copy = g_alloca ((count + 1) * sizeof (int)); + 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; + + if (length == NULL || length[i] < 0) + string_length = strlen (string[i]); + else + string_length = length[i]; + + string_copy[i] = g_memdup (string[i], string_length); + + replace_token (string_copy[i], + "main", + MAIN_WRAPPER_REPLACEMENT_NAME, + string_length); + + length_copy[i] = string_length; + } + + string_copy[count] = (char *) main_wrapper_function; + length_copy[count] = sizeof (main_wrapper_function) - 1; + + gles2_ctx->context->glShaderSource (shader, + count + 1, + (const char *const *) string_copy, + length_copy); + + /* Note: we don't need to free the last entry in string_copy[] + * because it is our static wrapper string... */ + 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); + static const char wrapper_marker[] = MAIN_WRAPPER_BEGIN; + char *wrapper_start; + + /* Strip out the wrapper snippet we added when the source was + * specified */ + wrapper_start = _cogl_util_memmem (source, + copy_length, + wrapper_marker, + sizeof (wrapper_marker) - 1); + if (wrapper_start) + { + length = wrapper_start - source; + copy_length = length; + *wrapper_start = '\0'; + } + + /* Correct the name of the main function back to its original */ + 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 +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 +gl_active_texture_wrapper (GLenum texture) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + int texture_unit; + + gles2_ctx->context->glActiveTexture (texture); + + texture_unit = texture - GL_TEXTURE0; + + /* If the application is binding some odd looking texture unit + * numbers then we'll just ignore it and hope that GL has generated + * an error */ + if (texture_unit >= 0 && texture_unit < 512) + { + gles2_ctx->current_texture_unit = texture_unit; + g_array_set_size (gles2_ctx->texture_units, + MAX (texture_unit, gles2_ctx->texture_units->len)); + } +} + +static void +gl_delete_textures_wrapper (GLsizei n, + const GLuint *textures) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + int texture_index; + int texture_unit; + + gles2_ctx->context->glDeleteTextures (n, textures); + + for (texture_index = 0; texture_index < n; texture_index++) + { + /* Reset any texture units that have any of these textures bound */ + for (texture_unit = 0; + texture_unit < gles2_ctx->texture_units->len; + texture_unit++) + { + CoglGLES2TextureUnitData *unit = + &g_array_index (gles2_ctx->texture_units, + CoglGLES2TextureUnitData, + texture_unit); + + if (unit->current_texture_2d == textures[texture_index]) + unit->current_texture_2d = 0; + } + + /* Remove the binding. We can do this immediately because unlike + * shader objects the deletion isn't delayed until the object is + * unbound */ + g_hash_table_remove (gles2_ctx->texture_object_map, + GUINT_TO_POINTER (textures[texture_index])); + } +} + +static void +gl_bind_texture_wrapper (GLenum target, + GLuint texture) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + + gles2_ctx->context->glBindTexture (target, texture); + + if (target == GL_TEXTURE_2D) + { + CoglGLES2TextureUnitData *unit = + &g_array_index (gles2_ctx->texture_units, + CoglGLES2TextureUnitData, + gles2_ctx->current_texture_unit); + unit->current_texture_2d = texture; + } +} + +static void +gl_tex_image_2d_wrapper (GLenum target, + GLint level, + GLint internal_format, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels) +{ + CoglGLES2Context *gles2_ctx = current_gles2_context; + + gles2_ctx->context->glTexImage2D (target, + level, + internal_format, + width, height, + border, + format, + type, + pixels); + + set_texture_object_data (gles2_ctx, + target, + level, + internal_format, + width, height); +} + +static void +_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen) +{ + _cogl_list_remove (&gles2_offscreen->link); + g_slice_free (CoglGLES2Offscreen, gles2_offscreen); +} + +static void +force_delete_program_object (CoglGLES2Context *context, + CoglGLES2ProgramData *program_data) +{ + if (!program_data->deleted) + { + context->context->glDeleteProgram (program_data->object_id); + program_data->deleted = TRUE; + program_data_unref (program_data); + } +} + +static void +force_delete_shader_object (CoglGLES2Context *context, + CoglGLES2ShaderData *shader_data) +{ + if (!shader_data->deleted) + { + context->context->glDeleteShader (shader_data->object_id); + shader_data->deleted = TRUE; + shader_data_unref (context, shader_data); + } +} + +static void +force_delete_texture_object (CoglGLES2Context *context, + CoglGLES2TextureObjectData *texture_data) +{ + context->context->glDeleteTextures (1, &texture_data->object_id); +} + +static void +_cogl_gles2_context_free (CoglGLES2Context *gles2_context) +{ + CoglContext *ctx = gles2_context->context; + const CoglWinsysVtable *winsys; + GList *objects, *l; + + if (gles2_context->current_program) + program_data_unref (gles2_context->current_program); + + /* Try to forcibly delete any shaders, programs and textures so that + * they won't get leaked. Because all GLES2 contexts are in the same + * share list as Cogl's context these won't get deleted by default. + * FIXME: we should do this for all of the other resources too, like + * textures */ + objects = g_hash_table_get_values (gles2_context->program_map); + for (l = objects; l; l = l->next) + force_delete_program_object (gles2_context, l->data); + g_list_free (objects); + objects = g_hash_table_get_values (gles2_context->shader_map); + for (l = objects; l; l = l->next) + force_delete_shader_object (gles2_context, l->data); + g_list_free (objects); + objects = g_hash_table_get_values (gles2_context->texture_object_map); + for (l = objects; l; l = l->next) + force_delete_texture_object (gles2_context, l->data); + g_list_free (objects); + + /* All of the program and shader objects should now be destroyed */ + if (g_hash_table_size (gles2_context->program_map) > 0) + g_warning ("Program objects have been leaked from a CoglGLES2Context"); + if (g_hash_table_size (gles2_context->shader_map) > 0) + g_warning ("Shader objects have been leaked from a CoglGLES2Context"); + + g_hash_table_destroy (gles2_context->program_map); + g_hash_table_destroy (gles2_context->shader_map); + + g_hash_table_destroy (gles2_context->texture_object_map); + g_array_free (gles2_context->texture_units, TRUE); + + winsys = ctx->display->renderer->winsys_vtable; + winsys->destroy_gles2_context (gles2_context); + + while (!_cogl_list_empty (&gles2_context->foreign_offscreens)) + { + CoglGLES2Offscreen *gles2_offscreen = + _cogl_container_of (gles2_context->foreign_offscreens.next, + CoglGLES2Offscreen, + link); + + /* Note: this will also indirectly free the gles2_offscreen by + * calling the destroy notify for the _user_data */ + cogl_object_set_user_data (COGL_OBJECT (gles2_offscreen->original_offscreen), + &offscreen_wrapper_key, + NULL, + NULL); + } + + g_free (gles2_context->vtable); + + g_free (gles2_context); +} + +static void +free_shader_data (CoglGLES2ShaderData *data) +{ + g_slice_free (CoglGLES2ShaderData, data); +} + +static void +free_program_data (CoglGLES2ProgramData *data) +{ + while (data->attached_shaders) + detach_shader (data, + data->attached_shaders->data); + + g_slice_free (CoglGLES2ProgramData, data); +} + +static void +free_texture_object_data (CoglGLES2TextureObjectData *data) +{ + g_slice_free (CoglGLES2TextureObjectData, data); +} + +CoglGLES2Context * +cogl_gles2_context_new (CoglContext *ctx, CoglError **error) +{ + CoglGLES2Context *gles2_ctx; + const CoglWinsysVtable *winsys; + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLES2_CONTEXT)) + { + _cogl_set_error (error, COGL_GLES2_CONTEXT_ERROR, + COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED, + "Backend doesn't support creating GLES2 contexts"); + + return NULL; + } + + gles2_ctx = g_malloc0 (sizeof (CoglGLES2Context)); + + gles2_ctx->context = ctx; + + _cogl_list_init (&gles2_ctx->foreign_offscreens); + + winsys = ctx->display->renderer->winsys_vtable; + gles2_ctx->winsys = winsys->context_create_gles2_context (ctx, error); + if (gles2_ctx->winsys == NULL) + { + g_free (gles2_ctx); + 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)); +#define COGL_EXT_BEGIN(name, \ + min_gl_major, min_gl_minor, \ + gles_availability, \ + extension_suffixes, extension_names) + +#define COGL_EXT_FUNCTION(ret, name, args) \ + gles2_ctx->vtable->name = (void *) ctx->name; + +#define COGL_EXT_END() + +#include "gl-prototypes/cogl-gles2-functions.h" + +#undef COGL_EXT_BEGIN +#undef COGL_EXT_FUNCTION +#undef COGL_EXT_END + + gles2_ctx->vtable->glBindFramebuffer = + (void *) gl_bind_framebuffer_wrapper; + gles2_ctx->vtable->glReadPixels = + (void *) gl_read_pixels_wrapper; + gles2_ctx->vtable->glCopyTexImage2D = + (void *) gl_copy_tex_image_2d_wrapper; + gles2_ctx->vtable->glCopyTexSubImage2D = + (void *) gl_copy_tex_sub_image_2d_wrapper; + + gles2_ctx->vtable->glCreateShader = gl_create_shader_wrapper; + gles2_ctx->vtable->glDeleteShader = gl_delete_shader_wrapper; + gles2_ctx->vtable->glCreateProgram = gl_create_program_wrapper; + gles2_ctx->vtable->glDeleteProgram = gl_delete_program_wrapper; + gles2_ctx->vtable->glUseProgram = gl_use_program_wrapper; + gles2_ctx->vtable->glAttachShader = gl_attach_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->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; + gles2_ctx->vtable->glActiveTexture = gl_active_texture_wrapper; + gles2_ctx->vtable->glDeleteTextures = gl_delete_textures_wrapper; + gles2_ctx->vtable->glBindTexture = gl_bind_texture_wrapper; + gles2_ctx->vtable->glTexImage2D = gl_tex_image_2d_wrapper; + + gles2_ctx->shader_map = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, /* key_destroy */ + (GDestroyNotify) free_shader_data); + gles2_ctx->program_map = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, /* key_destroy */ + (GDestroyNotify) free_program_data); + + gles2_ctx->texture_object_map = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, /* key_destroy */ + (GDestroyNotify) free_texture_object_data); + + gles2_ctx->texture_units = g_array_new (FALSE, /* not zero terminated */ + TRUE, /* clear */ + sizeof (CoglGLES2TextureUnitData)); + gles2_ctx->current_texture_unit = 0; + g_array_set_size (gles2_ctx->texture_units, 1); + + return _cogl_gles2_context_object_new (gles2_ctx); +} + +const CoglGLES2Vtable * +cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx) +{ + return gles2_ctx->vtable; +} + +/* When drawing to a CoglFramebuffer from a separate context we have + * to be able to allocate ancillary buffers for that context... + */ +static CoglGLES2Offscreen * +_cogl_gles2_offscreen_allocate (CoglOffscreen *offscreen, + CoglGLES2Context *gles2_context, + CoglError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen); + const CoglWinsysVtable *winsys; + CoglError *internal_error = NULL; + CoglGLES2Offscreen *gles2_offscreen; + int level_width; + int level_height; + + if (!framebuffer->allocated && + !cogl_framebuffer_allocate (framebuffer, error)) + { + return NULL; + } + + _cogl_list_for_each (gles2_offscreen, + &gles2_context->foreign_offscreens, + link) + { + if (gles2_offscreen->original_offscreen == offscreen) + return gles2_offscreen; + } + + winsys = _cogl_framebuffer_get_winsys (framebuffer); + winsys->save_context (framebuffer->context); + if (!winsys->set_gles2_context (gles2_context, &internal_error)) + { + winsys->restore_context (framebuffer->context); + + cogl_error_free (internal_error); + _cogl_set_error (error, COGL_FRAMEBUFFER_ERROR, + COGL_FRAMEBUFFER_ERROR_ALLOCATE, + "Failed to bind gles2 context to create framebuffer"); + return NULL; + } + + gles2_offscreen = g_slice_new0 (CoglGLES2Offscreen); + + _cogl_texture_get_level_size (offscreen->texture, + offscreen->texture_level, + &level_width, + &level_height, + NULL); + + if (!_cogl_framebuffer_try_creating_gl_fbo (gles2_context->context, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &COGL_FRAMEBUFFER (offscreen)->config, + offscreen->allocation_flags, + &gles2_offscreen->gl_framebuffer)) + { + winsys->restore_context (framebuffer->context); + + g_slice_free (CoglGLES2Offscreen, gles2_offscreen); + + _cogl_set_error (error, COGL_FRAMEBUFFER_ERROR, + COGL_FRAMEBUFFER_ERROR_ALLOCATE, + "Failed to create an OpenGL framebuffer object"); + return NULL; + } + + winsys->restore_context (framebuffer->context); + + gles2_offscreen->original_offscreen = offscreen; + + _cogl_list_insert (&gles2_context->foreign_offscreens, + &gles2_offscreen->link); + + /* So we avoid building up an ever growing collection of ancillary + * buffers for wrapped framebuffers, we make sure that the wrappers + * get freed when the original offscreen framebuffer is freed. */ + cogl_object_set_user_data (COGL_OBJECT (framebuffer), + &offscreen_wrapper_key, + gles2_offscreen, + (CoglUserDataDestroyCallback) + _cogl_gles2_offscreen_free); + + return gles2_offscreen; +} + +CoglBool +cogl_push_gles2_context (CoglContext *ctx, + CoglGLES2Context *gles2_ctx, + CoglFramebuffer *read_buffer, + CoglFramebuffer *write_buffer, + CoglError **error) +{ + const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable; + CoglError *internal_error = NULL; + + _COGL_RETURN_VAL_IF_FAIL (gles2_ctx != NULL, FALSE); + + /* The read/write buffers are properties of the gles2 context and we + * don't currently track the read/write buffers as part of the stack + * entries so we explicitly don't allow the same context to be + * pushed multiple times. */ + if (g_queue_find (&ctx->gles2_context_stack, gles2_ctx)) + { + g_critical ("Pushing the same GLES2 context multiple times isn't " + "supported"); + return FALSE; + } + + if (ctx->gles2_context_stack.length == 0) + { + _cogl_journal_flush (read_buffer->journal); + if (write_buffer != read_buffer) + _cogl_journal_flush (write_buffer->journal); + winsys->save_context (ctx); + } + else + gles2_ctx->vtable->glFlush (); + + if (gles2_ctx->read_buffer != read_buffer) + { + if (cogl_is_offscreen (read_buffer)) + { + gles2_ctx->gles2_read_buffer = + _cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (read_buffer), + gles2_ctx, + error); + /* XXX: what consistency guarantees should this api have? + * + * It should be safe to return at this point but we provide + * no guarantee to the caller whether their given buffers + * may be referenced and old buffers unreferenced even + * if the _push fails. */ + if (!gles2_ctx->gles2_read_buffer) + return FALSE; + } + else + gles2_ctx->gles2_read_buffer = NULL; + if (gles2_ctx->read_buffer) + cogl_object_unref (gles2_ctx->read_buffer); + gles2_ctx->read_buffer = cogl_object_ref (read_buffer); + } + + if (gles2_ctx->write_buffer != write_buffer) + { + if (cogl_is_offscreen (write_buffer)) + { + gles2_ctx->gles2_write_buffer = + _cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (write_buffer), + gles2_ctx, + error); + /* XXX: what consistency guarantees should this api have? + * + * It should be safe to return at this point but we provide + * no guarantee to the caller whether their given buffers + * may be referenced and old buffers unreferenced even + * if the _push fails. */ + if (!gles2_ctx->gles2_write_buffer) + return FALSE; + } + else + gles2_ctx->gles2_write_buffer = NULL; + if (gles2_ctx->write_buffer) + cogl_object_unref (gles2_ctx->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)) + { + winsys->restore_context (ctx); + + cogl_error_free (internal_error); + _cogl_set_error (error, COGL_GLES2_CONTEXT_ERROR, + COGL_GLES2_CONTEXT_ERROR_DRIVER, + "Driver failed to make GLES2 context current"); + return FALSE; + } + + g_queue_push_tail (&ctx->gles2_context_stack, gles2_ctx); + + /* The last time this context was pushed may have been with a + * different offscreen draw framebuffer and so if GL framebuffer 0 + * is bound for this GLES2 context we may need to bind a new, + * corresponding, window system framebuffer... */ + if (gles2_ctx->current_fbo_handle == 0) + { + if (cogl_is_offscreen (gles2_ctx->write_buffer)) + { + CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer; + GLuint handle = write->gl_framebuffer.fbo_handle; + gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER, handle); + } + } + + current_gles2_context = gles2_ctx; + + /* If this is the first time this gles2 context has been used then + * we'll force the viewport and scissor to the right size. GL has + * the semantics that the viewport and scissor default to the size + * of the first surface the context is used with. If the first + * CoglFramebuffer that this context is used with is an offscreen, + * then the surface from GL's point of view will be the 1x1 dummy + * surface so the viewport will be wrong. Therefore we just override + * the default viewport and scissor here */ + if (!gles2_ctx->has_been_bound) + { + int fb_width = cogl_framebuffer_get_width (write_buffer); + int fb_height = cogl_framebuffer_get_height (write_buffer); + + gles2_ctx->vtable->glViewport (0, 0, /* x/y */ + fb_width, fb_height); + gles2_ctx->vtable->glScissor (0, 0, /* x/y */ + fb_width, fb_height); + gles2_ctx->has_been_bound = TRUE; + } + + return TRUE; +} + +CoglGLES2Vtable * +cogl_gles2_get_current_vtable (void) +{ + return current_gles2_context ? current_gles2_context->vtable : NULL; +} + +void +cogl_pop_gles2_context (CoglContext *ctx) +{ + CoglGLES2Context *gles2_ctx; + const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable; + + _COGL_RETURN_IF_FAIL (ctx->gles2_context_stack.length > 0); + + g_queue_pop_tail (&ctx->gles2_context_stack); + + gles2_ctx = g_queue_peek_tail (&ctx->gles2_context_stack); + + if (gles2_ctx) + { + winsys->set_gles2_context (gles2_ctx, NULL); + current_gles2_context = gles2_ctx; + } + else + { + winsys->restore_context (ctx); + current_gles2_context = NULL; + } +} + +CoglTexture2D * +cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx, + CoglGLES2Context *gles2_ctx, + unsigned int handle, + int width, + int height, + CoglPixelFormat format) +{ + return cogl_texture_2d_gl_new_from_foreign (ctx, + handle, + width, + height, + format); +} + +CoglBool +cogl_gles2_texture_get_handle (CoglTexture *texture, + unsigned int *handle, + unsigned int *target) +{ + return cogl_texture_get_gl_texture (texture, handle, target); +} diff --git a/cogl/cogl/cogl-gles2-types.h b/cogl/cogl/cogl-gles2-types.h new file mode 100644 index 000000000..8f41bf8a7 --- /dev/null +++ b/cogl/cogl/cogl-gles2-types.h @@ -0,0 +1,474 @@ +#ifndef __COGL_GLES2_TYPES_H_ +#define __COGL_GLES2_TYPES_H_ + +/* $Revision: 16803 $ on $Date:: 2012-02-02 09:49:18 -0800 #$ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/*------------------------------------------------------------------------- + * Data type definitions + *-----------------------------------------------------------------------*/ + +typedef void GLvoid; +typedef char GLchar; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef float GLfloat; +typedef float GLclampf; +typedef int32_t GLfixed; + +/* GL types for handling large vertex buffer objects */ +typedef signed long int GLintptr; +typedef long GLsizeiptr; + +/* OpenGL ES core versions */ +#define GL_ES_VERSION_2_0 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction (not supported in ES20) */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* BlendEquationSeparate */ +#define GL_FUNC_ADD 0x8006 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_RGB 0x8009 /* same as BLEND_EQUATION */ +#define GL_BLEND_EQUATION_ALPHA 0x883D + +/* BlendSubtract */ +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B + +/* Separate Blend Functions */ +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 + +/* Buffer Objects */ +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 + +#define GL_STREAM_DRAW 0x88E0 +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 + +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_BLEND 0x0BE2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_LINE_WIDTH 0x0B21 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VIEWPORT 0x0BA2 +#define GL_SCISSOR_BOX 0x0C10 +/* GL_SCISSOR_TEST */ +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +/* GL_POLYGON_OFFSET_FILL */ +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB + +/* GetTextureParameter */ +/* GL_TEXTURE_MAG_FILTER */ +/* GL_TEXTURE_MIN_FILTER */ +/* GL_TEXTURE_WRAP_S */ +/* GL_TEXTURE_WRAP_T */ + +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* PixelFormat */ +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* Shaders */ +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_SHADER_TYPE 0x8B4F +#define GL_DELETE_STATUS 0x8B80 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D + +/* StencilFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_INVERT 0x150A +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ +#define GL_TEXTURE 0x1702 + +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_MIRRORED_REPEAT 0x8370 + +/* Uniform Types */ +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 + +/* Vertex Arrays */ +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F + +/* Read Format */ +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B + +/* Shader Source */ +#define GL_COMPILE_STATUS 0x8B81 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_COMPILER 0x8DFA + +/* Shader Binary */ +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 + +/* Shader Precision-Specified Types */ +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 + +/* Framebuffer Object. */ +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 + +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGB565 0x8D62 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_STENCIL_INDEX8 0x8D48 + +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 + +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 + +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 + +#define GL_NONE 0 + +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 + +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 + + +#ifdef __cplusplus +} +#endif + +#endif /* __COGL_GLES2_TYPES_H_ */ diff --git a/cogl/cogl/cogl-gles2.h b/cogl/cogl/cogl-gles2.h new file mode 100644 index 000000000..84c9ba15d --- /dev/null +++ b/cogl/cogl/cogl-gles2.h @@ -0,0 +1,420 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Collabora Ltd. + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Tomeu Vizoso + * Robert Bragg + * + */ + +#ifndef __COGL_GLES2_H__ +#define __COGL_GLES2_H__ + +/* NB: cogl-gles2.h is a top-level header that can be included directly + * but we want to be careful not to define __COGL_H_INSIDE__ when this + * is included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private + * api definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + +#include +#include +#include +#include +#include + +/* CoglGLES2Vtable depends on GLES 2.0 typedefs being available but we + * want to be careful that the public api doesn't expose arbitrary + * system GL headers as part of the Cogl API so although when building + * internally we consistently refer to the system headers to avoid + * conflicts we only expose the minimal set of GLES 2.0 types and enums + * publicly. + */ +#ifdef COGL_COMPILATION +#include "cogl-gl-header.h" +#else +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-gles2 + * @short_description: A portable api to access OpenGLES 2.0 + * + * Cogl provides portable access to the OpenGLES api through a single + * library that is able to smooth over inconsistencies between the + * different vendor drivers for OpenGLES in a single place. + * + * The api is designed to allow Cogl to transparently implement the + * api on top of other drivers, such as OpenGL, D3D or on Cogl's own + * drawing api so even if your platform doesn't come with an + * OpenGLES 2.0 api Cogl may still be able to expose the api to your + * application. + * + * Since Cogl is a library and not an api specification it is possible + * to add OpenGLES 2.0 api features to Cogl which can immidiately + * benefit developers regardless of what platform they are running on. + * + * With this api it's possible to re-use existing OpenGLES 2.0 code + * within applications that are rendering with the Cogl API and also + * it's possible for applications that render using OpenGLES 2.0 to + * incorporate content rendered with Cogl. + * + * Applications can check for OpenGLES 2.0 api support by checking for + * %COGL_FEATURE_ID_GLES2_CONTEXT support with cogl_has_feature(). + * + * Since: 1.12 + * Stability: unstable + */ + +/** + * CoglGLES2Context: + * + * Represents an OpenGLES 2.0 api context used as a sandbox for + * OpenGLES 2.0 state. This is comparable to an EGLContext for those + * who have used OpenGLES 2.0 with EGL before. + * + * Since: 1.12 + * Stability: unstable + */ +typedef struct _CoglGLES2Context CoglGLES2Context; + +/** + * CoglGLES2Vtable: + * + * Provides function pointers for the full OpenGLES 2.0 api. The + * api must be accessed this way and not by directly calling + * symbols of any system OpenGLES 2.0 api. + * + * Since: 1.12 + * Stability: unstable + */ +typedef struct _CoglGLES2Vtable CoglGLES2Vtable; + +struct _CoglGLES2Vtable +{ + /*< private >*/ +#define COGL_EXT_BEGIN(name, \ + min_gl_major, min_gl_minor, \ + gles_availability, \ + extension_suffixes, extension_names) + +#define COGL_EXT_FUNCTION(ret, name, args) \ + ret (* name) args; + +#define COGL_EXT_END() + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +#undef COGL_EXT_BEGIN +#undef COGL_EXT_FUNCTION +#undef COGL_EXT_END +}; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_gles2_context_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_gles2_context_get_gtype (void); +#endif + +uint32_t +_cogl_gles2_context_error_quark (void); + +/** + * COGL_GLES2_CONTEXT_ERROR: + * + * An error domain for runtime exceptions relating to the + * cogl_gles2_context api. + * + * Since: 2.0 + * Stability: unstable + */ +#define COGL_GLES2_CONTEXT_ERROR (_cogl_gles2_context_error_quark ()) + +/** + * CoglGLES2ContextError: + * @COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED: Creating GLES2 contexts + * isn't supported. Applications should use cogl_has_feature() to + * check for the %COGL_FEATURE_ID_GLES2_CONTEXT. + * @COGL_GLES2_CONTEXT_ERROR_DRIVER: An underlying driver error + * occured. + * + * Error codes that relate to the cogl_gles2_context api. + */ +typedef enum { /*< prefix=COGL_GLES2_CONTEXT_ERROR >*/ + COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED, + COGL_GLES2_CONTEXT_ERROR_DRIVER +} CoglGLES2ContextError; + +/** + * cogl_gles2_context_new: + * @ctx: A #CoglContext + * @error: A pointer to a #CoglError for returning exceptions + * + * Allocates a new OpenGLES 2.0 context that can be used to render to + * #CoglOffscreen framebuffers (Rendering to #CoglOnscreen + * framebuffers is not currently supported). + * + * To actually access the OpenGLES 2.0 api itself you need to use + * cogl_gles2_context_get_vtable(). You should not try to directly link + * to and use the symbols provided by the a system OpenGLES 2.0 + * driver. + * + * Once you have allocated an OpenGLES 2.0 context you can make it + * current using cogl_push_gles2_context(). For those familiar with + * using the EGL api, this serves a similar purpose to eglMakeCurrent. + * + * Before using this api applications can check for OpenGLES 2.0 + * api support by checking for %COGL_FEATURE_ID_GLES2_CONTEXT support + * with cogl_has_feature(). This function will return %FALSE and + * return an %COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED error if the + * feature isn't available. + * + * Since: 2.0 + * Return value: A newly allocated #CoglGLES2Context or %NULL if there + * was an error and @error will be updated in that case. + * Stability: unstable + */ +CoglGLES2Context * +cogl_gles2_context_new (CoglContext *ctx, CoglError **error); + +/** + * cogl_gles2_context_get_vtable: + * @gles2_ctx: A #CoglGLES2Context allocated with + * cogl_gles2_context_new() + * + * Queries the OpenGLES 2.0 api function pointers that should be + * used for rendering with the given @gles2_ctx. + * + * You should not try to directly link to and use the symbols + * provided by any system OpenGLES 2.0 driver. + * + * Since: 2.0 + * Return value: A pointer to a #CoglGLES2Vtable providing pointers + * to functions for the full OpenGLES 2.0 api. + * Stability: unstable + */ +const CoglGLES2Vtable * +cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx); + +/** + * cogl_push_gles2_context: + * @ctx: A #CoglContext + * @gles2_ctx: A #CoglGLES2Context allocated with + * cogl_gles2_context_new() + * @read_buffer: A #CoglFramebuffer to access to read operations + * such as glReadPixels. (must be a #CoglOffscreen + * framebuffer currently) + * @write_buffer: A #CoglFramebuffer to access for drawing operations + * such as glDrawArrays. (must be a #CoglOffscreen + * framebuffer currently) + * @error: A pointer to a #CoglError for returning exceptions + * + * Pushes the given @gles2_ctx onto a stack associated with @ctx so + * that the OpenGLES 2.0 api can be used instead of the Cogl + * rendering apis to read and write to the specified framebuffers. + * + * Usage of the api available through a #CoglGLES2Vtable is only + * allowed between cogl_push_gles2_context() and + * cogl_pop_gles2_context() calls. + * + * If there is a runtime problem with switching over to the given + * @gles2_ctx then this function will return %FALSE and return + * an error through @error. + * + * Since: 2.0 + * Return value: %TRUE if operation was successfull or %FALSE + * otherwise and @error will be updated. + * Stability: unstable + */ +CoglBool +cogl_push_gles2_context (CoglContext *ctx, + CoglGLES2Context *gles2_ctx, + CoglFramebuffer *read_buffer, + CoglFramebuffer *write_buffer, + CoglError **error); + +/** + * cogl_pop_gles2_context: + * @ctx: A #CoglContext + * + * Restores the previously active #CoglGLES2Context if there + * were nested calls to cogl_push_gles2_context() or otherwise + * restores the ability to render with the Cogl api instead + * of OpenGLES 2.0. + * + * The behaviour is undefined if calls to cogl_pop_gles2_context() + * are not balenced with the number of corresponding calls to + * cogl_push_gles2_context(). + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pop_gles2_context (CoglContext *ctx); + +/** + * cogl_gles2_get_current_vtable: + * + * Returns the OpenGL ES 2.0 api vtable for the currently pushed + * #CoglGLES2Context (last pushed with cogl_push_gles2_context()) or + * %NULL if no #CoglGLES2Context has been pushed. + * + * Return value: The #CoglGLES2Vtable for the currently pushed + * #CoglGLES2Context or %NULL if none has been pushed. + * Since: 2.0 + * Stability: unstable + */ +CoglGLES2Vtable * +cogl_gles2_get_current_vtable (void); + +/** + * cogl_gles2_texture_2d_new_from_handle: + * @ctx: A #CoglContext + * @gles2_ctx: A #CoglGLES2Context allocated with + * cogl_gles2_context_new() + * @handle: An OpenGL ES 2.0 texture handle created with + * glGenTextures() + * @width: Width of the texture to allocate + * @height: Height of the texture to allocate + * @format: The format of the texture + * + * Creates a #CoglTexture2D from an OpenGL ES 2.0 texture handle that + * was created within the given @gles2_ctx via glGenTextures(). The + * texture needs to have been associated with the GL_TEXTURE_2D target. + * + * This interface is only intended for sharing textures to read + * from. The behaviour is undefined if the texture is modified using + * the Cogl api. + * + * Applications should only pass this function handles that were + * created via a #CoglGLES2Vtable or via libcogl-gles2 and not pass + * handles created directly using the system's native libGLESv2 + * api. + * + * Since: 2.0 + * Stability: unstable + */ +CoglTexture2D * +cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx, + CoglGLES2Context *gles2_ctx, + unsigned int handle, + int width, + int height, + CoglPixelFormat format); + +/** + * cogl_gles2_texture_get_handle: + * @texture: A #CoglTexture + * @handle: A return location for an OpenGL ES 2.0 texture handle + * @target: A return location for an OpenGL ES 2.0 texture target + * + * Gets an OpenGL ES 2.0 texture handle for a #CoglTexture that can + * then be referenced by a #CoglGLES2Context. As well as returning + * a texture handle the texture's target (such as GL_TEXTURE_2D) is + * also returned. + * + * If the #CoglTexture can not be shared with a #CoglGLES2Context then + * this function will return %FALSE. + * + * This api does not affect the lifetime of the CoglTexture and you + * must take care not to reference the returned handle after the + * original texture has been freed. + * + * This interface is only intended for sharing textures to read + * from. The behaviour is undefined if the texture is modified by a + * GLES2 context. + * + * This function will only return %TRUE for low-level + * #CoglTextures such as #CoglTexture2D or #CoglTexture3D but + * not for high level meta textures such as + * #CoglTexture2DSliced + * + * The handle returned should not be passed directly to a system + * OpenGL ES 2.0 library, the handle is only intended to be used via + * a #CoglGLES2Vtable or via libcogl-gles2. + * + * Return value: %TRUE if a handle and target could be returned + * otherwise %FALSE is returned. + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_gles2_texture_get_handle (CoglTexture *texture, + unsigned int *handle, + unsigned int *target); + +/** + * cogl_is_gles2_context: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglGLES2Context. + * + * Return value: %TRUE if the object references a #CoglGLES2Context + * and %FALSE otherwise. + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_is_gles2_context (void *object); + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_GLES2_H__ */ + diff --git a/cogl/cogl/cogl-glib-source.c b/cogl/cogl/cogl-glib-source.c new file mode 100644 index 000000000..fea254478 --- /dev/null +++ b/cogl/cogl/cogl-glib-source.c @@ -0,0 +1,195 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-glib-source.h" +#include "cogl-poll.h" + +typedef struct _CoglGLibSource +{ + GSource source; + + CoglRenderer *renderer; + + GArray *poll_fds; + int poll_fds_age; + + int64_t expiration_time; +} CoglGLibSource; + +static CoglBool +cogl_glib_source_prepare (GSource *source, int *timeout) +{ + CoglGLibSource *cogl_source = (CoglGLibSource *) source; + CoglPollFD *poll_fds; + int n_poll_fds; + int64_t cogl_timeout; + int age; + int i; + + age = cogl_poll_renderer_get_info (cogl_source->renderer, + &poll_fds, + &n_poll_fds, + &cogl_timeout); + + /* We have to be careful not to call g_source_add/remove_poll unless + * the FDs have changed because it will cause the main loop to + * immediately wake up. If we call it every time the source is + * prepared it will effectively never go idle. */ + if (age != cogl_source->poll_fds_age) + { + /* Remove any existing polls before adding the new ones */ + for (i = 0; i < cogl_source->poll_fds->len; i++) + { + GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); + g_source_remove_poll (source, poll_fd); + } + + g_array_set_size (cogl_source->poll_fds, n_poll_fds); + + for (i = 0; i < n_poll_fds; i++) + { + GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); + poll_fd->fd = poll_fds[i].fd; + g_source_add_poll (source, poll_fd); + } + } + + cogl_source->poll_fds_age = age; + + /* Update the events */ + for (i = 0; i < n_poll_fds; i++) + { + GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); + poll_fd->events = poll_fds[i].events; + poll_fd->revents = 0; + } + + if (cogl_timeout == -1) + { + *timeout = -1; + cogl_source->expiration_time = -1; + } + else + { + /* Round up to ensure that we don't try again too early */ + *timeout = (cogl_timeout + 999) / 1000; + cogl_source->expiration_time = (g_source_get_time (source) + + cogl_timeout); + } + + return *timeout == 0; +} + +static CoglBool +cogl_glib_source_check (GSource *source) +{ + CoglGLibSource *cogl_source = (CoglGLibSource *) source; + int i; + + if (cogl_source->expiration_time >= 0 && + g_source_get_time (source) >= cogl_source->expiration_time) + return TRUE; + + for (i = 0; i < cogl_source->poll_fds->len; i++) + { + GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); + if (poll_fd->revents != 0) + return TRUE; + } + + return FALSE; +} + +static CoglBool +cogl_glib_source_dispatch (GSource *source, + GSourceFunc callback, + void *user_data) +{ + CoglGLibSource *cogl_source = (CoglGLibSource *) source; + CoglPollFD *poll_fds = + (CoglPollFD *) &g_array_index (cogl_source->poll_fds, GPollFD, 0); + + cogl_poll_renderer_dispatch (cogl_source->renderer, + poll_fds, + cogl_source->poll_fds->len); + + return TRUE; +} + +static void +cogl_glib_source_finalize (GSource *source) +{ + CoglGLibSource *cogl_source = (CoglGLibSource *) source; + + g_array_free (cogl_source->poll_fds, TRUE); +} + +static GSourceFuncs +cogl_glib_source_funcs = + { + cogl_glib_source_prepare, + cogl_glib_source_check, + cogl_glib_source_dispatch, + cogl_glib_source_finalize + }; + +GSource * +cogl_glib_renderer_source_new (CoglRenderer *renderer, + int priority) +{ + GSource *source; + CoglGLibSource *cogl_source; + + source = g_source_new (&cogl_glib_source_funcs, + sizeof (CoglGLibSource)); + cogl_source = (CoglGLibSource *) source; + + cogl_source->renderer = renderer; + cogl_source->poll_fds = g_array_new (FALSE, FALSE, sizeof (GPollFD)); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + + return source; +} + +GSource * +cogl_glib_source_new (CoglContext *context, + int priority) +{ + return cogl_glib_renderer_source_new (cogl_context_get_renderer (context), + priority); +} + + diff --git a/cogl/cogl/cogl-glib-source.h b/cogl/cogl/cogl-glib-source.h new file mode 100644 index 000000000..ddb7f9957 --- /dev/null +++ b/cogl/cogl/cogl-glib-source.h @@ -0,0 +1,97 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_GSOURCE_H__ +#define __COGL_GSOURCE_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * cogl_glib_source_new: + * @context: A #CoglContext + * @priority: The priority of the #GSource + * + * Creates a #GSource which handles Cogl's internal system event + * processing. This can be used as a convenience instead of + * cogl_poll_renderer_get_info() and cogl_poll_renderer_dispatch() in + * applications that are already using the GLib main loop. After this + * is called the #GSource should be attached to the main loop using + * g_source_attach(). + * + * Applications that manually connect to a #CoglRenderer before they + * create a #CoglContext should instead use + * cogl_glib_renderer_source_new() so that events may be dispatched + * before a context has been created. In that case you don't need to + * use this api in addition later, it is simply enough to use + * cogl_glib_renderer_source_new() instead. + * + * This api is actually just a thin convenience wrapper around + * cogl_glib_renderer_source_new() + * + * Return value: a new #GSource + * + * Stability: unstable + * Since: 1.10 + */ +GSource * +cogl_glib_source_new (CoglContext *context, + int priority); + +/** + * cogl_glib_renderer_source_new: + * @renderer: A #CoglRenderer + * @priority: The priority of the #GSource + * + * Creates a #GSource which handles Cogl's internal system event + * processing. This can be used as a convenience instead of + * cogl_poll_renderer_get_info() and cogl_poll_renderer_dispatch() in + * applications that are already using the GLib main loop. After this + * is called the #GSource should be attached to the main loop using + * g_source_attach(). + * + * Return value: a new #GSource + * + * Stability: unstable + * Since: 1.16 + */ +GSource * +cogl_glib_renderer_source_new (CoglRenderer *renderer, + int priority); + +G_END_DECLS + +#endif /* __COGL_GSOURCE_H__ */ diff --git a/cogl/cogl/cogl-glsl-shader-boilerplate.h b/cogl/cogl/cogl-glsl-shader-boilerplate.h new file mode 100644 index 000000000..6d882dad2 --- /dev/null +++ b/cogl/cogl/cogl-glsl-shader-boilerplate.h @@ -0,0 +1,86 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_SHADER_BOILERPLATE_H +#define __COGL_SHADER_BOILERPLATE_H + +#define _COGL_COMMON_SHADER_BOILERPLATE \ + "#define COGL_VERSION 100\n" \ + "\n" \ + "uniform mat4 cogl_modelview_matrix;\n" \ + "uniform mat4 cogl_modelview_projection_matrix;\n" \ + "uniform mat4 cogl_projection_matrix;\n" + +/* This declares all of the variables that we might need. This is + * working on the assumption that the compiler will optimise them out + * if they are not actually used. The GLSL spec at least implies that + * this will happen for varyings but it doesn't explicitly so for + * attributes */ +#define _COGL_VERTEX_SHADER_BOILERPLATE \ + _COGL_COMMON_SHADER_BOILERPLATE \ + "#define cogl_color_out _cogl_color\n" \ + "varying vec4 _cogl_color;\n" \ + "#define cogl_tex_coord_out _cogl_tex_coord\n" \ + "#define cogl_position_out gl_Position\n" \ + "#define cogl_point_size_out gl_PointSize\n" \ + "\n" \ + "attribute vec4 cogl_color_in;\n" \ + "attribute vec4 cogl_position_in;\n" \ + "#define cogl_tex_coord_in cogl_tex_coord0_in;\n" \ + "attribute vec3 cogl_normal_in;\n" + +#define _COGL_FRAGMENT_SHADER_BOILERPLATE \ + "#ifdef GL_ES\n" \ + "precision highp float;\n" \ + "#endif\n" \ + _COGL_COMMON_SHADER_BOILERPLATE \ + "\n" \ + "varying vec4 _cogl_color;\n" \ + "\n" \ + "#define cogl_color_in _cogl_color\n" \ + "#define cogl_tex_coord_in _cogl_tex_coord\n" \ + "\n" \ + "#define cogl_color_out gl_FragColor\n" \ + "#define cogl_depth_out gl_FragDepth\n" \ + "\n" \ + "#define cogl_front_facing gl_FrontFacing\n" \ + "\n" \ + "#define cogl_point_coord gl_PointCoord\n" +#if 0 + /* GLSL 1.2 has a bottom left origin, though later versions + * allow use of an origin_upper_left keyword which would be + * more appropriate for Cogl. */ + "#define coglFragCoord gl_FragCoord\n" +#endif + +#endif /* __COGL_SHADER_BOILERPLATE_H */ + diff --git a/cogl/cogl/cogl-glsl-shader-private.h b/cogl/cogl/cogl-glsl-shader-private.h new file mode 100644 index 000000000..9899c12c1 --- /dev/null +++ b/cogl/cogl/cogl-glsl-shader-private.h @@ -0,0 +1,41 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _COGL_GLSL_SHADER_PRIVATE_H_ +#define _COGL_GLSL_SHADER_PRIVATE_H_ + +void +_cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, + GLuint shader_gl_handle, + GLenum shader_gl_type, + CoglPipeline *pipeline, + GLsizei count_in, + const char **strings_in, + const GLint *lengths_in); + +#endif /* _COGL_GLSL_SHADER_PRIVATE_H_ */ diff --git a/cogl/cogl/cogl-glsl-shader.c b/cogl/cogl/cogl-glsl-shader.c new file mode 100644 index 000000000..196e0c7ae --- /dev/null +++ b/cogl/cogl/cogl-glsl-shader.c @@ -0,0 +1,192 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-glsl-shader-private.h" +#include "cogl-glsl-shader-boilerplate.h" + +#include + +#include + +static CoglBool +add_layer_vertex_boilerplate_cb (CoglPipelineLayer *layer, + void *user_data) +{ + GString *layer_declarations = user_data; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + g_string_append_printf (layer_declarations, + "attribute vec4 cogl_tex_coord%d_in;\n" + "#define cogl_texture_matrix%i cogl_texture_matrix[%i]\n" + "#define cogl_tex_coord%i_out _cogl_tex_coord[%i]\n", + layer->index, + layer->index, + unit_index, + layer->index, + unit_index); + return TRUE; +} + +static CoglBool +add_layer_fragment_boilerplate_cb (CoglPipelineLayer *layer, + void *user_data) +{ + GString *layer_declarations = user_data; + g_string_append_printf (layer_declarations, + "#define cogl_tex_coord%i_in _cogl_tex_coord[%i]\n", + layer->index, + _cogl_pipeline_layer_get_unit_index (layer)); + return TRUE; +} + +void +_cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, + GLuint shader_gl_handle, + GLenum shader_gl_type, + CoglPipeline *pipeline, + GLsizei count_in, + const char **strings_in, + const GLint *lengths_in) +{ + const char *vertex_boilerplate; + const char *fragment_boilerplate; + + const char **strings = g_alloca (sizeof (char *) * (count_in + 4)); + GLint *lengths = g_alloca (sizeof (GLint) * (count_in + 4)); + char *version_string; + int count = 0; + + int n_layers; + + vertex_boilerplate = _COGL_VERTEX_SHADER_BOILERPLATE; + fragment_boilerplate = _COGL_FRAGMENT_SHADER_BOILERPLATE; + + version_string = g_strdup_printf ("#version %i\n\n", + ctx->glsl_version_to_use); + strings[count] = version_string; + lengths[count++] = -1; + + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) + { + static const char texture_3d_extension[] = + "#extension GL_OES_texture_3D : enable\n"; + strings[count] = texture_3d_extension; + lengths[count++] = sizeof (texture_3d_extension) - 1; + } + + if (shader_gl_type == GL_VERTEX_SHADER) + { + strings[count] = vertex_boilerplate; + lengths[count++] = strlen (vertex_boilerplate); + } + else if (shader_gl_type == GL_FRAGMENT_SHADER) + { + strings[count] = fragment_boilerplate; + lengths[count++] = strlen (fragment_boilerplate); + } + + n_layers = cogl_pipeline_get_n_layers (pipeline); + if (n_layers) + { + GString *layer_declarations = ctx->codegen_boilerplate_buffer; + g_string_set_size (layer_declarations, 0); + + g_string_append_printf (layer_declarations, + "varying vec4 _cogl_tex_coord[%d];\n", + n_layers); + + if (shader_gl_type == GL_VERTEX_SHADER) + { + g_string_append_printf (layer_declarations, + "uniform mat4 cogl_texture_matrix[%d];\n", + n_layers); + + _cogl_pipeline_foreach_layer_internal (pipeline, + add_layer_vertex_boilerplate_cb, + layer_declarations); + } + else if (shader_gl_type == GL_FRAGMENT_SHADER) + { + _cogl_pipeline_foreach_layer_internal (pipeline, + add_layer_fragment_boilerplate_cb, + layer_declarations); + } + + strings[count] = layer_declarations->str; + lengths[count++] = -1; /* null terminated */ + } + + memcpy (strings + count, strings_in, sizeof (char *) * count_in); + if (lengths_in) + memcpy (lengths + count, lengths_in, sizeof (GLint) * count_in); + else + { + int i; + + for (i = 0; i < count_in; i++) + lengths[count + i] = -1; /* null terminated */ + } + count += count_in; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) + { + GString *buf = g_string_new (NULL); + int i; + + g_string_append_printf (buf, + "%s shader:\n", + shader_gl_type == GL_VERTEX_SHADER ? + "vertex" : "fragment"); + for (i = 0; i < count; i++) + if (lengths[i] != -1) + g_string_append_len (buf, strings[i], lengths[i]); + else + g_string_append (buf, strings[i]); + + g_message ("%s", buf->str); + + g_string_free (buf, TRUE); + } + + GE( ctx, glShaderSource (shader_gl_handle, count, + (const char **) strings, lengths) ); + + g_free (version_string); +} diff --git a/cogl/cogl/cogl-glx-display-private.h b/cogl/cogl/cogl-glx-display-private.h new file mode 100644 index 000000000..133c1188c --- /dev/null +++ b/cogl/cogl/cogl-glx-display-private.h @@ -0,0 +1,62 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_DISPLAY_GLX_PRIVATE_H +#define __COGL_DISPLAY_GLX_PRIVATE_H + +#include "cogl-object-private.h" + +typedef struct _CoglGLXCachedConfig +{ + /* This will be -1 if there is no cached config in this slot */ + int depth; + CoglBool found; + GLXFBConfig fb_config; + CoglBool stereo; + CoglBool can_mipmap; +} CoglGLXCachedConfig; + +#define COGL_GLX_N_CACHED_CONFIGS 6 + +typedef struct _CoglGLXDisplay +{ + CoglGLXCachedConfig glx_cached_configs[COGL_GLX_N_CACHED_CONFIGS]; + + CoglBool found_fbconfig; + CoglBool fbconfig_has_rgba_visual; + GLXFBConfig fbconfig; + + /* Single context for all wins */ + GLXContext glx_context; + GLXWindow dummy_glxwin; + Window dummy_xwin; +} CoglGLXDisplay; + +#endif /* __COGL_DISPLAY_GLX_PRIVATE_H */ diff --git a/cogl/cogl/cogl-glx-renderer-private.h b/cogl/cogl/cogl-glx-renderer-private.h new file mode 100644 index 000000000..cb8ff97f8 --- /dev/null +++ b/cogl/cogl/cogl-glx-renderer-private.h @@ -0,0 +1,108 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_RENDERER_GLX_PRIVATE_H +#define __COGL_RENDERER_GLX_PRIVATE_H + +#include +#include "cogl-object-private.h" +#include "cogl-xlib-renderer-private.h" + +typedef struct _CoglGLXRenderer +{ + int glx_major; + int glx_minor; + + int glx_error_base; + int glx_event_base; + + CoglBool is_direct; + + /* Vblank stuff */ + int dri_fd; + + /* enumeration with relatioship between OML_sync_control + * UST (unadjusted-system-time) and the system clock */ + enum { + COGL_GLX_UST_IS_UNKNOWN, + COGL_GLX_UST_IS_GETTIMEOFDAY, + COGL_GLX_UST_IS_MONOTONIC_TIME, + COGL_GLX_UST_IS_OTHER + } ust_type; + + /* GModule pointing to libGL which we use to get glX functions out of */ + GModule *libgl_module; + + CoglClosure *flush_notifications_idle; + + /* Copy of the winsys features that are based purely on the + * information we can get without using a GL context. We want to + * determine this before we have a context so that we can use the + * function pointers from the extensions earlier. This is necessary + * to use the glXCreateContextAttribs function. */ + unsigned long base_winsys_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_WINSYS_FEATURE_N_FEATURES)]; + + CoglFeatureFlags legacy_feature_flags; + + /* Function pointers for core GLX functionality. We can't just link + against these directly because we need to conditionally load + libGL when we are using GLX so that it won't conflict with a GLES + library if we are using EGL + GLES. These are just the functions + that we want to use before calling glXGetProcAddress */ + Bool + (* glXQueryExtension) (Display *dpy, int *errorb, int *event); + const char * + (* glXQueryExtensionsString) (Display *dpy, int screen); + Bool + (* glXQueryVersion) (Display *dpy, int *maj, int *min); + void * + (* glXGetProcAddress) (const GLubyte *procName); + + int + (* glXQueryDrawable) (Display *dpy, GLXDrawable drawable, + int attribute, unsigned int *value); + + /* Function pointers for GLX specific extensions */ +#define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d, e, f, g) + +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ + ret (APIENTRY * name) args; + +#define COGL_WINSYS_FEATURE_END() + +#include "cogl-winsys-glx-feature-functions.h" + +#undef COGL_WINSYS_FEATURE_BEGIN +#undef COGL_WINSYS_FEATURE_FUNCTION +#undef COGL_WINSYS_FEATURE_END +} CoglGLXRenderer; + +#endif /* __COGL_RENDERER_GLX_PRIVATE_H */ diff --git a/cogl/cogl/cogl-glx.h b/cogl/cogl/cogl-glx.h new file mode 100644 index 000000000..15918bba7 --- /dev/null +++ b/cogl/cogl/cogl-glx.h @@ -0,0 +1,95 @@ +/* + * Cogl + * + * A Low-Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_GLX_H__ +#define __COGL_GLX_H__ + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_GLX_H_INSIDE__ */ +#ifndef __COGL_GLX_H_INSIDE__ +#define __COGL_GLX_H_INSIDE__ +#endif + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + + +#include +#include + +COGL_BEGIN_DECLS + +/** + * cogl_glx_context_get_glx_context: + * @context: A #CoglContext pointer + * + * If you have done a runtime check to determine that Cogl is using + * GLX internally then this API can be used to retrieve the GLXContext + * handle that was setup internally. The result is undefined if Cogl + * is not using GLX. + * + * Return value: The internally setup GLXContext handle. + * Since: 1.18 + * Stability: unstable + */ +GLXContext +cogl_glx_context_get_glx_context (CoglContext *context); + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_GLX_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_GLX_H__ */ diff --git a/cogl/cogl/cogl-gpu-info-private.h b/cogl/cogl/cogl-gpu-info-private.h new file mode 100644 index 000000000..e532cdd5c --- /dev/null +++ b/cogl/cogl/cogl-gpu-info-private.h @@ -0,0 +1,117 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_GPU_INFO_PRIVATE_H +#define __COGL_GPU_INFO_PRIVATE_H + +#include "cogl-context.h" + +typedef enum _CoglGpuInfoArchitectureFlag +{ + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE, + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED, + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE, + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_DEFERRED, + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE +} CoglGpuInfoArchitectureFlag; + +typedef enum _CoglGpuInfoArchitecture +{ + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE, + COGL_GPU_INFO_ARCHITECTURE_SGX, + COGL_GPU_INFO_ARCHITECTURE_MALI, + COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE, + COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE, + COGL_GPU_INFO_ARCHITECTURE_SWRAST +} CoglGpuInfoArchitecture; + +typedef enum +{ + COGL_GPU_INFO_VENDOR_UNKNOWN, + COGL_GPU_INFO_VENDOR_INTEL, + COGL_GPU_INFO_VENDOR_IMAGINATION_TECHNOLOGIES, + COGL_GPU_INFO_VENDOR_ARM, + COGL_GPU_INFO_VENDOR_QUALCOMM, + COGL_GPU_INFO_VENDOR_NVIDIA, + COGL_GPU_INFO_VENDOR_ATI, + COGL_GPU_INFO_VENDOR_MESA +} CoglGpuInfoVendor; + +typedef enum +{ + COGL_GPU_INFO_DRIVER_PACKAGE_UNKNOWN, + COGL_GPU_INFO_DRIVER_PACKAGE_MESA +} CoglGpuInfoDriverPackage; + +typedef enum +{ + /* If this bug is present then it is faster to read pixels into a + * PBO and then memcpy out of the PBO into system memory rather than + * directly read into system memory. + * https://bugs.freedesktop.org/show_bug.cgi?id=46631 + */ + COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS = 1 << 0 +} CoglGpuInfoDriverBug; + +typedef struct _CoglGpuInfoVersion CoglGpuInfoVersion; + +typedef struct _CoglGpuInfo CoglGpuInfo; + +struct _CoglGpuInfo +{ + CoglGpuInfoVendor vendor; + const char *vendor_name; + + CoglGpuInfoDriverPackage driver_package; + const char *driver_package_name; + int driver_package_version; + + CoglGpuInfoArchitecture architecture; + const char *architecture_name; + CoglGpuInfoArchitectureFlag architecture_flags; + + CoglGpuInfoDriverBug driver_bugs; +}; + +/* + * _cogl_gpu_info_init: + * @ctx: A #CoglContext + * @gpu: A return location for the GPU information + * + * Determines information about the GPU and driver from the given + * context. + */ +void +_cogl_gpu_info_init (CoglContext *ctx, + CoglGpuInfo *gpu); + +#endif /* __COGL_GPU_INFO_PRIVATE_H */ diff --git a/cogl/cogl/cogl-gpu-info.c b/cogl/cogl/cogl-gpu-info.c new file mode 100644 index 000000000..54dfe18d5 --- /dev/null +++ b/cogl/cogl/cogl-gpu-info.c @@ -0,0 +1,581 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "cogl-gpu-info-private.h" +#include "cogl-context-private.h" +#include "cogl-version.h" + +typedef struct +{ + const char *renderer_string; + const char *version_string; + const char *vendor_string; +} CoglGpuInfoStrings; + +typedef struct CoglGpuInfoArchitectureDescription +{ + CoglGpuInfoArchitecture architecture; + const char *name; + CoglGpuInfoArchitectureFlag flags; + CoglBool (* check_function) (const CoglGpuInfoStrings *strings); + +} CoglGpuInfoArchitectureDescription; + +typedef struct +{ + CoglGpuInfoVendor vendor; + const char *name; + CoglBool (* check_function) (const CoglGpuInfoStrings *strings); + const CoglGpuInfoArchitectureDescription *architectures; + +} CoglGpuInfoVendorDescription; + +typedef struct +{ + CoglGpuInfoDriverPackage driver_package; + const char *name; + CoglBool (* check_function) (const CoglGpuInfoStrings *strings, + int *version_out); +} CoglGpuInfoDriverPackageDescription; + +static CoglBool +_cogl_gpu_info_parse_version_string (const char *version_string, + int n_components, + const char **tail, + int *version_ret) +{ + int version = 0; + uint64_t part; + int i; + + for (i = 0; ; i++) + { + errno = 0; + part = g_ascii_strtoull (version_string, + (char **) &version_string, + 10); + + if (errno || part > COGL_VERSION_MAX_COMPONENT_VALUE) + return FALSE; + + version |= part << ((2 - i) * COGL_VERSION_COMPONENT_BITS); + + if (i + 1 >= n_components) + break; + + if (*version_string != '.') + return FALSE; + + version_string++; + } + + if (version_ret) + *version_ret = version; + if (tail) + *tail = version_string; + + return TRUE; +} + +static CoglBool +match_phrase (const char *string, const char *phrase) +{ + const char *part = strstr (string, phrase); + int len; + + if (part == NULL) + return FALSE; + + /* The match must either be at the beginning of the string or + preceded by a space. */ + if (part > string && part[-1] != ' ') + return FALSE; + + /* Also match must either be at end of string or followed by a + * space. */ + len = strlen (phrase); + if (part[len] != '\0' && part[len] != ' ') + return FALSE; + + return TRUE; +} + +static CoglBool +check_intel_vendor (const CoglGpuInfoStrings *strings) +{ + return match_phrase (strings->renderer_string, "Intel(R)"); +} + +static CoglBool +check_imagination_technologies_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "Imagination Technologies") != 0) + return FALSE; + return TRUE; +} + +static CoglBool +check_arm_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "ARM") != 0) + return FALSE; + return TRUE; +} + +static CoglBool +check_qualcomm_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "Qualcomm") != 0) + return FALSE; + return TRUE; +} + +static CoglBool +check_nvidia_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "NVIDIA") != 0) + return FALSE; + + return TRUE; +} + +static CoglBool +check_ati_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "ATI") != 0) + return FALSE; + + return TRUE; +} + +static CoglBool +check_mesa_vendor (const CoglGpuInfoStrings *strings) +{ + if (strcmp (strings->vendor_string, "Tungsten Graphics, Inc") == 0) + return TRUE; + else if (strcmp (strings->vendor_string, "VMware, Inc.") == 0) + return TRUE; + else if (strcmp (strings->vendor_string, "Mesa Project") == 0) + return TRUE; + + return FALSE; +} + +static CoglBool +check_true (const CoglGpuInfoStrings *strings) +{ + /* This is a last resort so it always matches */ + return TRUE; +} + +static CoglBool +check_sandybridge_architecture (const CoglGpuInfoStrings *strings) +{ + return match_phrase (strings->renderer_string, "Sandybridge"); +} + +static CoglBool +check_llvmpipe_architecture (const CoglGpuInfoStrings *strings) +{ + return match_phrase (strings->renderer_string, "llvmpipe"); +} + +static CoglBool +check_softpipe_architecture (const CoglGpuInfoStrings *strings) +{ + return match_phrase (strings->renderer_string, "softpipe"); +} + +static CoglBool +check_swrast_architecture (const CoglGpuInfoStrings *strings) +{ + return match_phrase (strings->renderer_string, "software rasterizer") || + match_phrase (strings->renderer_string, "Software Rasterizer"); +} + +static CoglBool +check_sgx_architecture (const CoglGpuInfoStrings *strings) +{ + if (strncmp (strings->renderer_string, "PowerVR SGX", 12) != 0) + return FALSE; + + return TRUE; +} + +static CoglBool +check_mali_architecture (const CoglGpuInfoStrings *strings) +{ + if (strncmp (strings->renderer_string, "Mali-", 5) != 0) + return FALSE; + + return TRUE; +} + +static const CoglGpuInfoArchitectureDescription +intel_architectures[] = + { + { + COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE, + "Sandybridge", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + check_sandybridge_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + "Unknown", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + check_true + } + }; + +static const CoglGpuInfoArchitectureDescription +powervr_architectures[] = + { + { + COGL_GPU_INFO_ARCHITECTURE_SGX, + "SGX", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_DEFERRED, + check_sgx_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + "Unknown", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED, + check_true + } + }; + +static const CoglGpuInfoArchitectureDescription +arm_architectures[] = + { + { + COGL_GPU_INFO_ARCHITECTURE_MALI, + "Mali", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + check_mali_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + "Unknown", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + check_true + } + }; + +static const CoglGpuInfoArchitectureDescription +mesa_architectures[] = + { + { + COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE, + "LLVM Pipe", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, + check_llvmpipe_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE, + "Softpipe", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, + check_softpipe_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_SWRAST, + "SWRast", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, + check_swrast_architecture + }, + { + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + "Unknown", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, + check_true + } + }; + +static const CoglGpuInfoArchitectureDescription +unknown_architectures[] = + { + { + COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, + "Unknown", + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | + COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE, + check_true + } + }; + +static const CoglGpuInfoVendorDescription +_cogl_gpu_info_vendors[] = + { + { + COGL_GPU_INFO_VENDOR_INTEL, + "Intel", + check_intel_vendor, + intel_architectures + }, + { + COGL_GPU_INFO_VENDOR_IMAGINATION_TECHNOLOGIES, + "Imagination Technologies", + check_imagination_technologies_vendor, + powervr_architectures + }, + { + COGL_GPU_INFO_VENDOR_ARM, + "ARM", + check_arm_vendor, + arm_architectures + }, + { + COGL_GPU_INFO_VENDOR_QUALCOMM, + "Qualcomm", + check_qualcomm_vendor, + unknown_architectures + }, + { + COGL_GPU_INFO_VENDOR_NVIDIA, + "Nvidia", + check_nvidia_vendor, + unknown_architectures + }, + { + COGL_GPU_INFO_VENDOR_ATI, + "ATI", + check_ati_vendor, + unknown_architectures + }, + /* Must be last */ + { + COGL_GPU_INFO_VENDOR_MESA, + "Mesa", + check_mesa_vendor, + mesa_architectures + }, + { + COGL_GPU_INFO_VENDOR_UNKNOWN, + "Unknown", + check_true, + unknown_architectures + } + }; + +static CoglBool +check_mesa_driver_package (const CoglGpuInfoStrings *strings, + int *version_ret) +{ + uint64_t micro_part; + const char *v; + + /* The version string should always begin a two-part GL version + number */ + if (!_cogl_gpu_info_parse_version_string (strings->version_string, + 2, /* n_components */ + &v, /* tail */ + NULL /* version_ret */)) + return FALSE; + + /* In mesa this will be followed optionally by "(Core Profile)" and + * then "Mesa" */ + v = strstr (v, " Mesa "); + if (!v) + return FALSE; + + v += 6; + + /* Next there will be a version string that is at least two + components. On a git devel build the version will be something + like "-devel" instead */ + if (!_cogl_gpu_info_parse_version_string (v, + 2, /* n_components */ + &v, /* tail */ + version_ret)) + return FALSE; + + /* If it is a development build then we'll just leave the micro + number as 0 */ + if (g_str_has_prefix (v, "-devel")) + return TRUE; + + /* Otherwise there should be a micro version number */ + if (*v != '.') + return FALSE; + + errno = 0; + micro_part = g_ascii_strtoull (v + 1, NULL /* endptr */, 10 /* base */); + if (errno || micro_part > COGL_VERSION_MAX_COMPONENT_VALUE) + return FALSE; + + *version_ret = COGL_VERSION_ENCODE (COGL_VERSION_GET_MAJOR (*version_ret), + COGL_VERSION_GET_MINOR (*version_ret), + micro_part); + + return TRUE; +} + +UNIT_TEST (check_mesa_driver_package_parser, + 0, /* no requirements */ + 0 /* no failure cases */) +{ + /* renderer_string, version_string, vendor_string;*/ + const CoglGpuInfoStrings test_strings[2] = { + { NULL, "3.1 Mesa 9.2-devel15436ad", NULL }, + { NULL, "3.1 (Core Profile) Mesa 9.2.0-devel (git-15436ad)", NULL } + }; + int i; + int version; + + for (i = 0; i < G_N_ELEMENTS (test_strings); i++) + { + g_assert (check_mesa_driver_package (&test_strings[i], &version)); + g_assert_cmpint (version, ==, COGL_VERSION_ENCODE (9, 2, 0)); + } +} + +static CoglBool +check_unknown_driver_package (const CoglGpuInfoStrings *strings, + int *version_out) +{ + *version_out = 0; + + /* This is a last resort so it always matches */ + return TRUE; +} + +static const CoglGpuInfoDriverPackageDescription +_cogl_gpu_info_driver_packages[] = + { + { + COGL_GPU_INFO_DRIVER_PACKAGE_MESA, + "Mesa", + check_mesa_driver_package + }, + /* Must be last */ + { + COGL_GPU_INFO_DRIVER_PACKAGE_UNKNOWN, + "Unknown", + check_unknown_driver_package + } + }; + +void +_cogl_gpu_info_init (CoglContext *ctx, + CoglGpuInfo *gpu) +{ + CoglGpuInfoStrings strings; + int i; + + strings.renderer_string = (const char *) ctx->glGetString (GL_RENDERER); + strings.version_string = _cogl_context_get_gl_version (ctx); + strings.vendor_string = (const char *) ctx->glGetString (GL_VENDOR); + + /* Determine the driver package */ + for (i = 0; ; i++) + { + const CoglGpuInfoDriverPackageDescription *description = + _cogl_gpu_info_driver_packages + i; + + if (description->check_function (&strings, &gpu->driver_package_version)) + { + gpu->driver_package = description->driver_package; + gpu->driver_package_name = description->name; + break; + } + } + + /* Determine the GPU vendor */ + for (i = 0; ; i++) + { + const CoglGpuInfoVendorDescription *description = + _cogl_gpu_info_vendors + i; + + if (description->check_function (&strings)) + { + int j; + + gpu->vendor = description->vendor; + gpu->vendor_name = description->name; + + for (j = 0; ; j++) + { + const CoglGpuInfoArchitectureDescription *architecture = + description->architectures + j; + + if (architecture->check_function (&strings)) + { + gpu->architecture = architecture->architecture; + gpu->architecture_name = architecture->name; + gpu->architecture_flags = architecture->flags; + goto probed; + } + } + } + } + +probed: + + COGL_NOTE (WINSYS, "Driver package = %s, vendor = %s, architecture = %s\n", + gpu->driver_package_name, + gpu->vendor_name, + gpu->architecture_name); + + /* Determine the driver bugs */ + + /* In Mesa the glReadPixels implementation is really slow + when using the Intel driver. The Intel + driver has a fast blit path when reading into a PBO. Reading into + a temporary PBO and then memcpying back out to the application's + memory is faster than a regular glReadPixels in this case */ + if (gpu->vendor == COGL_GPU_INFO_VENDOR_INTEL && + gpu->driver_package == COGL_GPU_INFO_DRIVER_PACKAGE_MESA) + gpu->driver_bugs |= COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS; +} diff --git a/cogl/cogl/cogl-gtype-private.h b/cogl/cogl/cogl-gtype-private.h new file mode 100644 index 000000000..849838563 --- /dev/null +++ b/cogl/cogl/cogl-gtype-private.h @@ -0,0 +1,278 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_GTYPE_PRIVATE_H__ +#define __COGL_GTYPE_PRIVATE_H__ + +#include "config.h" + +#include +#include + +#include "cogl-object-private.h" + +/* Move this to public headers? */ +typedef struct _CoglGtypeObject CoglGtypeObject; +typedef struct _CoglGtypeClass CoglGtypeClass; + +struct _CoglGtypeObject +{ + GTypeInstance parent_instance; + + guint dummy; +}; + +struct _CoglGtypeClass +{ + GTypeClass base_class; + + guint dummy; +}; + +#define I_(str) (g_intern_static_string ((str))) + +/**/ + +#define COGL_GTYPE_DEFINE_BOXED(Name,underscore_name,copy_func,free_func) \ +GType \ +cogl_##underscore_name##_get_gtype (void) \ +{ \ + static volatile size_t type_volatile = 0; \ + if (g_once_init_enter (&type_volatile)) \ + { \ + GType type = \ + g_boxed_type_register_static (g_intern_static_string (I_("Cogl" # Name)), \ + (GBoxedCopyFunc)copy_func, \ + (GBoxedFreeFunc)free_func); \ + g_once_init_leave (&type_volatile, type); \ + } \ + return type_volatile; \ +} + +#define COGL_GTYPE_IMPLEMENT_INTERFACE(name) { \ + const GInterfaceInfo g_implement_interface_info = { \ + (GInterfaceInitFunc) _cogl_gtype_dummy_iface_init, NULL, NULL \ + }; \ + g_type_add_interface_static (fundamental_type_id, \ + cogl_##name##_get_gtype(), \ + &g_implement_interface_info); \ + } + +#define _COGL_GTYPE_DEFINE_BASE_CLASS_BEGIN(Name,name) \ +GType \ +cogl_##name##_get_gtype (void) \ +{ \ + static volatile gsize type_id__volatile = 0; \ + if (g_once_init_enter (&type_id__volatile)) \ + { \ + static const GTypeFundamentalInfo finfo = { \ + (G_TYPE_FLAG_CLASSED | \ + G_TYPE_FLAG_INSTANTIATABLE | \ + G_TYPE_FLAG_DERIVABLE | \ + G_TYPE_FLAG_DEEP_DERIVABLE), \ + }; \ + static const GTypeValueTable value_table = { \ + _cogl_gtype_object_init_value, \ + _cogl_gtype_object_free_value, \ + _cogl_gtype_object_copy_value, \ + _cogl_gtype_object_peek_pointer, \ + "p", \ + _cogl_gtype_object_collect_value, \ + "p", \ + _cogl_gtype_object_lcopy_value, \ + }; \ + const GTypeInfo node_info = { \ + sizeof (CoglObjectClass), \ + (GBaseInitFunc) _cogl_gtype_object_class_base_init, \ + (GBaseFinalizeFunc) _cogl_gtype_object_class_base_finalize, \ + (GClassInitFunc) _cogl_gtype_object_class_init, \ + (GClassFinalizeFunc) NULL, \ + NULL, \ + sizeof (CoglObject), \ + 0, \ + (GInstanceInitFunc) _cogl_gtype_object_init, \ + &value_table, \ + }; \ + GType fundamental_type_id = \ + g_type_register_fundamental (g_type_fundamental_next (), \ + I_("Cogl" # Name), \ + &node_info, &finfo, \ + G_TYPE_FLAG_ABSTRACT); \ + g_once_init_leave (&type_id__volatile, \ + fundamental_type_id); + +#define _COGL_GTYPE_DEFINE_BASE_CLASS_END() \ + } \ + return type_id__volatile; \ + } + +#define COGL_GTYPE_DEFINE_BASE_CLASS(Name,name,...) \ + _COGL_GTYPE_DEFINE_BASE_CLASS_BEGIN(Name,name) \ + {__VA_ARGS__;} \ + _COGL_GTYPE_DEFINE_BASE_CLASS_END() + +#define _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_BEGIN(Name,name) \ + \ + static void name##_default_init (Name##Interface *klass); \ + GType \ + name##_get_gtype (void) \ + { \ + static volatile gsize type_id__volatile = 0; \ + if (g_once_init_enter (&type_id__volatile)) \ + { \ + GType fundamental_type_id = \ + g_type_register_static_simple (G_TYPE_INTERFACE, \ + g_intern_static_string (#Name), \ + sizeof (Name##Interface), \ + (GClassInitFunc)name##_default_init, \ + 0, \ + (GInstanceInitFunc)NULL, \ + (GTypeFlags) 0); \ + g_type_interface_add_prerequisite (fundamental_type_id, \ + cogl_object_get_gtype()); \ + { /* custom code follows */ + +#define _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_END() \ + /* following custom code */ \ + } \ + g_once_init_leave (&type_id__volatile, \ + fundamental_type_id); \ + } \ + return type_id__volatile; \ + } /* closes name##_get_type() */ + + +#define COGL_GTYPE_DEFINE_INTERFACE(Name,name) \ + typedef struct _Cogl##Name##Iface Cogl##Name##Iface; \ + typedef Cogl##Name##Iface Cogl##Name##Interface; \ + struct _Cogl##Name##Iface \ + { \ + /*< private >*/ \ + GTypeInterface g_iface; \ + }; \ + _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_BEGIN (Cogl##Name, cogl_##name) \ + _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_END () \ + static void \ + cogl_##name##_default_init (Cogl##Name##Interface *iface) \ + { \ + } + +#define _COGL_GTYPE_DEFINE_TYPE_EXTENDED_BEGIN(Name,name,parent,flags) \ + \ + static void name##_init (Name *self); \ + static void name##_class_init (Name##Class *klass); \ + static gpointer name##_parent_class = NULL; \ + static gint Name##_private_offset; \ + \ + static void \ + name##_class_intern_init (gpointer klass) \ + { \ + name##_parent_class = g_type_class_peek_parent (klass); \ + name##_class_init ((Name##Class*) klass); \ + } \ + \ + static inline gpointer \ + name##_get_instance_private (Name *self) \ + { \ + return (G_STRUCT_MEMBER_P (self, Name ##_private_offset)); \ + } \ + \ + GType \ + name##_get_gtype (void) \ + { \ + static volatile gsize type_id__volatile = 0; \ + if (g_once_init_enter (&type_id__volatile)) \ + { \ + GType fundamental_type_id = \ + g_type_register_static_simple (parent, \ + g_intern_static_string (#Name), \ + sizeof (Name##Class), \ + (GClassInitFunc) name##_class_intern_init, \ + sizeof (Name), \ + (GInstanceInitFunc) name##_init, \ + (GTypeFlags) flags); \ + { /* custom code follows */ + +#define _COGL_GTYPE_DEFINE_TYPE_EXTENDED_END() \ + /* following custom code */ \ + } \ + g_once_init_leave (&type_id__volatile, \ + fundamental_type_id); \ + } \ + return type_id__volatile; \ + } /* closes name##_get_type() */ + + +#define COGL_GTYPE_DEFINE_CLASS(Name,name,...) \ + typedef struct _Cogl##Name##Class Cogl##Name##Class; \ + struct _Cogl##Name##Class { \ + CoglObjectClass parent_class; \ + }; \ + _COGL_GTYPE_DEFINE_TYPE_EXTENDED_BEGIN(Cogl##Name, \ + cogl_##name, \ + cogl_object_get_gtype(), \ + 0) \ + {__VA_ARGS__;} \ + _COGL_GTYPE_DEFINE_TYPE_EXTENDED_END() \ + static void \ + cogl_##name##_init (Cogl##Name *instance) \ + { \ + } \ + static void \ + cogl_##name##_class_init (Cogl##Name##Class *klass) \ + { \ + } + +void _cogl_gtype_object_init_value (GValue *value); +void _cogl_gtype_object_free_value (GValue *value); +void _cogl_gtype_object_copy_value (const GValue *src, + GValue *dst); +gpointer _cogl_gtype_object_peek_pointer (const GValue *value); +gchar *_cogl_gtype_object_collect_value (GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags); +gchar *_cogl_gtype_object_lcopy_value (const GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags); + +void _cogl_gtype_object_class_base_init (CoglObjectClass *klass); +void _cogl_gtype_object_class_base_finalize (CoglObjectClass *klass); +void _cogl_gtype_object_class_init (CoglObjectClass *klass); +void _cogl_gtype_object_init (CoglObject *object); + +void cogl_object_value_set_object (GValue *value, + gpointer object); +gpointer cogl_object_value_get_object (const GValue *value); + +void _cogl_gtype_dummy_iface_init (gpointer iface); + +#endif /* __COGL_GTYPE_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-gtype.c b/cogl/cogl/cogl-gtype.c new file mode 100644 index 000000000..314d8e41f --- /dev/null +++ b/cogl/cogl/cogl-gtype.c @@ -0,0 +1,153 @@ +#include "cogl-gtype-private.h" + +#include + +void +_cogl_gtype_object_init_value (GValue *value) +{ + value->data[0].v_pointer = NULL; +} + +void +_cogl_gtype_object_free_value (GValue *value) +{ + if (value->data[0].v_pointer != NULL) + cogl_object_unref (value->data[0].v_pointer); +} + +void +_cogl_gtype_object_copy_value (const GValue *src, + GValue *dst) +{ + if (src->data[0].v_pointer != NULL) + dst->data[0].v_pointer = cogl_object_ref (src->data[0].v_pointer); + else + dst->data[0].v_pointer = NULL; +} + +gpointer +_cogl_gtype_object_peek_pointer (const GValue *value) +{ + return value->data[0].v_pointer; +} + +gchar * +_cogl_gtype_object_collect_value (GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags) +{ + CoglObject *object; + + object = collect_values[0].v_pointer; + + if (object == NULL) + { + value->data[0].v_pointer = NULL; + return NULL; + } + + if (object->klass == NULL) + return g_strconcat ("invalid unclassed CoglObject pointer for " + "value type '", + G_VALUE_TYPE_NAME (value), + "'", + NULL); + + value->data[0].v_pointer = cogl_object_ref (object); + + return NULL; +} + +gchar * +_cogl_gtype_object_lcopy_value (const GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags) +{ + CoglObject **object_p = collect_values[0].v_pointer; + + if (object_p == NULL) + return g_strconcat ("value location for '", + G_VALUE_TYPE_NAME (value), + "' passed as NULL", + NULL); + + if (value->data[0].v_pointer == NULL) + *object_p = NULL; + else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) + *object_p = value->data[0].v_pointer; + else + *object_p = cogl_object_ref (value->data[0].v_pointer); + + return NULL; +} + +void +_cogl_gtype_object_class_base_init (CoglObjectClass *klass) +{ +} + +void +_cogl_gtype_object_class_base_finalize (CoglObjectClass *klass) +{ +} + +void +_cogl_gtype_object_class_init (CoglObjectClass *klass) +{ +} + +void +_cogl_gtype_object_init (CoglObject *object) +{ +} + +void +_cogl_gtype_dummy_iface_init (gpointer iface) +{ +} + +/** + * cogl_object_value_set_object: + * @value: a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT + * @object: (type Cogl.GtypeObject) (allow-none): a #CoglGtypeObject, or %NULL + * + * Sets the contents of a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT. + * + */ +void +cogl_object_value_set_object (GValue *value, + gpointer object) +{ + CoglObject *old_object; + + old_object = value->data[0].v_pointer; + + if (object != NULL) + { + /* take over ownership */ + value->data[0].v_pointer = object; + } + else + value->data[0].v_pointer = NULL; + + if (old_object != NULL) + cogl_object_unref (old_object); +} + +/** + * cogl_object_value_get_object: + * @value: a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT + * + * Retrieves a pointer to the #CoglGtypeObject contained inside + * the passed #GValue. + * + * Return value: (transfer none) (type Cogl.GtypeObject): a pointer to + * a #CoglGtypeObject, or %NULL + */ +gpointer +cogl_object_value_get_object (const GValue *value) +{ + return value->data[0].v_pointer; +} diff --git a/cogl/cogl/cogl-i18n-private.h b/cogl/cogl/cogl-i18n-private.h new file mode 100644 index 000000000..94202f791 --- /dev/null +++ b/cogl/cogl/cogl-i18n-private.h @@ -0,0 +1,39 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef _COGL_I18N_PRIVATE_H_ +#define _COGL_I18N_PRIVATE_H_ + +#include + +#define _(X) X +#define N_(X) X + +#endif /* _COGL_I18N_PRIVATE_H_ */ diff --git a/cogl/cogl/cogl-index-buffer-private.h b/cogl/cogl/cogl-index-buffer-private.h new file mode 100644 index 000000000..d9596cd83 --- /dev/null +++ b/cogl/cogl/cogl-index-buffer-private.h @@ -0,0 +1,44 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_INDEX_BUFFER_PRIVATE_H +#define __COGL_INDEX_BUFFER_PRIVATE_H + +#include "cogl-buffer-private.h" + +struct _CoglIndexBuffer +{ + CoglBuffer _parent; +}; + +#endif /* __COGL_INDEX_BUFFER_PRIVATE_H */ diff --git a/cogl/cogl/cogl-index-buffer.c b/cogl/cogl/cogl-index-buffer.c new file mode 100644 index 000000000..cddaf4762 --- /dev/null +++ b/cogl/cogl/cogl-index-buffer.c @@ -0,0 +1,112 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-object-private.h" +#include "cogl-indices.h" +#include "cogl-indices-private.h" +#include "cogl-context-private.h" +#include "cogl-gtype-private.h" + +static void _cogl_index_buffer_free (CoglIndexBuffer *indices); + +COGL_BUFFER_DEFINE (IndexBuffer, index_buffer); +COGL_GTYPE_DEFINE_CLASS (IndexBuffer, index_buffer); + +/* XXX: Unlike the wiki design this just takes a size. A single + * indices buffer should be able to contain multiple ranges of indices + * which the wiki design doesn't currently consider. */ +CoglIndexBuffer * +cogl_index_buffer_new (CoglContext *context, size_t bytes) +{ + CoglIndexBuffer *indices = g_slice_new (CoglIndexBuffer); + + /* parent's constructor */ + _cogl_buffer_initialize (COGL_BUFFER (indices), + context, + bytes, + COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, + COGL_BUFFER_USAGE_HINT_INDEX_BUFFER, + COGL_BUFFER_UPDATE_HINT_STATIC); + + return _cogl_index_buffer_object_new (indices); +} + +static void +_cogl_index_buffer_free (CoglIndexBuffer *indices) +{ + /* parent's destructor */ + _cogl_buffer_fini (COGL_BUFFER (indices)); + + g_slice_free (CoglIndexBuffer, indices); +} + +/* XXX: do we want a convenience function like this as an alternative + * to using cogl_buffer_set_data? The advantage of this is that we can + * track meta data such as the indices type and max_index_value for a + * range as part of the indices buffer. If we just leave people to use + * cogl_buffer_set_data then we either need a way to specify the type + * and max index value at draw time or we'll want a separate way to + * declare the type and max value for a range after uploading the + * data. + * + * XXX: I think in the end it'll be that CoglIndices are to + * CoglIndexBuffers as CoglAttributes are to CoglAttributeBuffers. I.e + * a CoglIndexBuffer is a lite subclass of CoglBuffer that simply + * implies that the buffer will later be bound as indices but doesn't + * track more detailed meta data. CoglIndices build on a + * CoglIndexBuffer and define the type and max_index_value for some + * sub-range of a CoglIndexBuffer. + */ +#if 0 +void +cogl_index_buffer_set_data (CoglIndexBuffer *indices, + CoglIndicesType type, + int max_index_value, + size_t write_offset, + void *user_indices, + int n_indices) +{ + GList *l; + + for (l = indices->ranges; l; l = l->next) + { + + } + cogl_buffer_set +} +#endif + diff --git a/cogl/cogl/cogl-index-buffer.h b/cogl/cogl/cogl-index-buffer.h new file mode 100644 index 000000000..204c85856 --- /dev/null +++ b/cogl/cogl/cogl-index-buffer.h @@ -0,0 +1,107 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_INDEX_BUFFER_H__ +#define __COGL_INDEX_BUFFER_H__ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-index-buffer + * @short_description: Functions for creating and manipulating vertex + * indices. + * + * FIXME + */ + +#define COGL_INDEX_BUFFER(buffer) ((CoglIndexBuffer*) buffer) + +typedef struct _CoglIndexBuffer CoglIndexBuffer; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_index_buffer_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_index_buffer_get_gtype (void); +#endif + +/** + * cogl_index_buffer_new: + * @context: A #CoglContext + * @bytes: The number of bytes to allocate for vertex attribute data. + * + * Declares a new #CoglIndexBuffer of @size bytes to contain vertex + * indices. Once declared, data can be set using + * cogl_buffer_set_data() or by mapping it into the application's + * address space using cogl_buffer_map(). + * + * Return value: (transfer full): A newly allocated #CoglIndexBuffer + * + * Since: 1.4 + * Stability: Unstable + */ +CoglIndexBuffer * +cogl_index_buffer_new (CoglContext *context, + size_t bytes); + +/** + * cogl_is_index_buffer: + * @object: A #CoglObject + * + * Gets whether the given object references a #CoglIndexBuffer. + * + * Returns: %TRUE if the @object references a #CoglIndexBuffer, + * %FALSE otherwise + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_is_index_buffer (void *object); + +COGL_END_DECLS + +#endif /* __COGL_INDEX_BUFFER_H__ */ + diff --git a/cogl/cogl/cogl-indices-private.h b/cogl/cogl/cogl-indices-private.h new file mode 100644 index 000000000..3d5891687 --- /dev/null +++ b/cogl/cogl/cogl-indices-private.h @@ -0,0 +1,60 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_INDICES_PRIVATE_H +#define __COGL_INDICES_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-index-buffer-private.h" +#include "cogl-types.h" + +struct _CoglIndices +{ + CoglObject _parent; + + CoglIndexBuffer *buffer; + size_t offset; + + CoglIndicesType type; + + int immutable_ref; +}; + +CoglIndices * +_cogl_indices_immutable_ref (CoglIndices *indices); + +void +_cogl_indices_immutable_unref (CoglIndices *indices); + +#endif /* __COGL_INDICES_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-indices.c b/cogl/cogl/cogl-indices.c new file mode 100644 index 000000000..22568304c --- /dev/null +++ b/cogl/cogl/cogl-indices.c @@ -0,0 +1,271 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-object-private.h" +#include "cogl-context-private.h" +#include "cogl-indices.h" +#include "cogl-indices-private.h" +#include "cogl-index-buffer.h" +#include "cogl-gtype-private.h" + +#include + +static void _cogl_indices_free (CoglIndices *indices); + +COGL_OBJECT_DEFINE (Indices, indices); +COGL_GTYPE_DEFINE_CLASS (Indices, indices); + +static size_t +sizeof_indices_type (CoglIndicesType type) +{ + switch (type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + return 1; + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + return 2; + case COGL_INDICES_TYPE_UNSIGNED_INT: + return 4; + } + g_return_val_if_reached (0); +} + +CoglIndices * +cogl_indices_new_for_buffer (CoglIndicesType type, + CoglIndexBuffer *buffer, + size_t offset) +{ + CoglIndices *indices = g_slice_new (CoglIndices); + + indices->buffer = cogl_object_ref (buffer); + indices->offset = offset; + + indices->type = type; + + indices->immutable_ref = 0; + + return _cogl_indices_object_new (indices); +} + +CoglIndices * +cogl_indices_new (CoglContext *context, + CoglIndicesType type, + const void *indices_data, + int n_indices) +{ + size_t buffer_bytes = sizeof_indices_type (type) * n_indices; + CoglIndexBuffer *index_buffer = cogl_index_buffer_new (context, buffer_bytes); + CoglBuffer *buffer = COGL_BUFFER (index_buffer); + CoglIndices *indices; + CoglError *ignore_error = NULL; + + _cogl_buffer_set_data (buffer, + 0, + indices_data, + buffer_bytes, + &ignore_error); + if (ignore_error) + { + cogl_error_free (ignore_error); + cogl_object_unref (index_buffer); + return NULL; + } + + indices = cogl_indices_new_for_buffer (type, index_buffer, 0); + cogl_object_unref (index_buffer); + + return indices; +} + +CoglIndexBuffer * +cogl_indices_get_buffer (CoglIndices *indices) +{ + return indices->buffer; +} + +CoglIndicesType +cogl_indices_get_type (CoglIndices *indices) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_indices (indices), + COGL_INDICES_TYPE_UNSIGNED_BYTE); + return indices->type; +} + +size_t +cogl_indices_get_offset (CoglIndices *indices) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_indices (indices), 0); + + return indices->offset; +} + +static void +warn_about_midscene_changes (void) +{ + static CoglBool seen = FALSE; + if (!seen) + { + g_warning ("Mid-scene modification of indices has " + "undefined results\n"); + seen = TRUE; + } +} + +void +cogl_indices_set_offset (CoglIndices *indices, + size_t offset) +{ + _COGL_RETURN_IF_FAIL (cogl_is_indices (indices)); + + if (G_UNLIKELY (indices->immutable_ref)) + warn_about_midscene_changes (); + + indices->offset = offset; +} + +static void +_cogl_indices_free (CoglIndices *indices) +{ + cogl_object_unref (indices->buffer); + g_slice_free (CoglIndices, indices); +} + +CoglIndices * +_cogl_indices_immutable_ref (CoglIndices *indices) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_indices (indices), NULL); + + indices->immutable_ref++; + _cogl_buffer_immutable_ref (COGL_BUFFER (indices->buffer)); + return indices; +} + +void +_cogl_indices_immutable_unref (CoglIndices *indices) +{ + _COGL_RETURN_IF_FAIL (cogl_is_indices (indices)); + _COGL_RETURN_IF_FAIL (indices->immutable_ref > 0); + + indices->immutable_ref--; + _cogl_buffer_immutable_unref (COGL_BUFFER (indices->buffer)); +} + +CoglIndices * +cogl_get_rectangle_indices (CoglContext *ctx, int n_rectangles) +{ + int n_indices = n_rectangles * 6; + + /* Check if the largest index required will fit in a byte array... */ + if (n_indices <= 256 / 4 * 6) + { + /* Generate the byte array if we haven't already */ + if (ctx->rectangle_byte_indices == NULL) + { + uint8_t *byte_array = g_malloc (256 / 4 * 6 * sizeof (uint8_t)); + uint8_t *p = byte_array; + int i, vert_num = 0; + + for (i = 0; i < 256 / 4; i++) + { + *(p++) = vert_num + 0; + *(p++) = vert_num + 1; + *(p++) = vert_num + 2; + *(p++) = vert_num + 0; + *(p++) = vert_num + 2; + *(p++) = vert_num + 3; + vert_num += 4; + } + + ctx->rectangle_byte_indices + = cogl_indices_new (ctx, + COGL_INDICES_TYPE_UNSIGNED_BYTE, + byte_array, + 256 / 4 * 6); + + g_free (byte_array); + } + + return ctx->rectangle_byte_indices; + } + else + { + if (ctx->rectangle_short_indices_len < n_indices) + { + uint16_t *short_array; + uint16_t *p; + int i, vert_num = 0; + + if (ctx->rectangle_short_indices != NULL) + cogl_object_unref (ctx->rectangle_short_indices); + /* Pick a power of two >= MAX (512, n_indices) */ + if (ctx->rectangle_short_indices_len == 0) + ctx->rectangle_short_indices_len = 512; + while (ctx->rectangle_short_indices_len < n_indices) + ctx->rectangle_short_indices_len *= 2; + + /* Over-allocate to generate a whole number of quads */ + p = short_array = g_malloc ((ctx->rectangle_short_indices_len + + 5) / 6 * 6 + * sizeof (uint16_t)); + + /* Fill in the complete quads */ + for (i = 0; i < ctx->rectangle_short_indices_len; i += 6) + { + *(p++) = vert_num + 0; + *(p++) = vert_num + 1; + *(p++) = vert_num + 2; + *(p++) = vert_num + 0; + *(p++) = vert_num + 2; + *(p++) = vert_num + 3; + vert_num += 4; + } + + ctx->rectangle_short_indices + = cogl_indices_new (ctx, + COGL_INDICES_TYPE_UNSIGNED_SHORT, + short_array, + ctx->rectangle_short_indices_len); + + g_free (short_array); + } + + return ctx->rectangle_short_indices; + } +} + diff --git a/cogl/cogl/cogl-indices.h b/cogl/cogl/cogl-indices.h new file mode 100644 index 000000000..3cc923911 --- /dev/null +++ b/cogl/cogl/cogl-indices.h @@ -0,0 +1,165 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_INDICES_H__ +#define __COGL_INDICES_H__ + +/* We forward declare the CoglIndices type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglIndices CoglIndices; + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-indices + * @short_description: Describe vertex indices stored in a #CoglIndexBuffer. + * + * Indices allow you to avoid duplicating vertices in your vertex data + * by virtualizing your data and instead providing a sequence of index + * values that tell the GPU which data should be used for each vertex. + * + * If the GPU is given a sequence of indices it doesn't simply walk + * through each vertex of your data in order it will instead walk + * through the indices which can provide random access to the + * underlying data. + * + * Since it's very common to have duplicate vertices when describing a + * shape as a list of triangles it can often be a significant space + * saving to describe geometry using indices. Reducing the size of + * your models can make it cheaper to map them into the GPU by + * reducing the demand on memory bandwidth and may help to make better + * use of your GPUs internal vertex caching. + * + * For example, to describe a quadrilateral as 2 triangles for the GPU + * you could either provide data with 6 vertices or instead with + * indices you can provide vertex data for just 4 vertices and an + * index buffer that specfies the 6 vertices by indexing the shared + * vertices multiple times. + * + * |[ + * CoglVertex2f quad_vertices[] = { + * {x0, y0}, //0 = top left + * {x1, y1}, //1 = bottom left + * {x2, y2}, //2 = bottom right + * {x3, y3}, //3 = top right + * }; + * //tell the gpu how to interpret the quad as 2 triangles... + * unsigned char indices[] = {0, 1, 2, 0, 2, 3}; + * ]| + * + * Even in the above illustration we see a saving of 10bytes for one + * quad compared to having data for 6 vertices and no indices but if + * you need to draw 100s or 1000s of quads then its really quite + * significant. + * + * Something else to consider is that often indices can be defined + * once and remain static while the vertex data may change for + * animations perhaps. That means you may be able to ignore the + * negligable cost of mapping your indices into the GPU if they don't + * ever change. + * + * The above illustration is actually a good example of static indices + * because it's really common that developers have quad mesh data that + * they need to display and we know exactly what that indices array + * needs to look like depending on the number of quads that need to be + * drawn. It doesn't matter how the quads might be animated and + * changed the indices will remain the same. Cogl even has a utility + * (cogl_get_rectangle_indices()) to get access to re-useable indices + * for drawing quads as above. + */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_indices_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_indices_get_gtype (void); +#endif + +CoglIndices * +cogl_indices_new (CoglContext *context, + CoglIndicesType type, + const void *indices_data, + int n_indices); + +CoglIndices * +cogl_indices_new_for_buffer (CoglIndicesType type, + CoglIndexBuffer *buffer, + size_t offset); + +CoglIndexBuffer * +cogl_indices_get_buffer (CoglIndices *indices); + +CoglIndicesType +cogl_indices_get_type (CoglIndices *indices); + +size_t +cogl_indices_get_offset (CoglIndices *indices); + +void +cogl_indices_set_offset (CoglIndices *indices, + size_t offset); + +CoglIndices * +cogl_get_rectangle_indices (CoglContext *context, int n_rectangles); + +/** + * cogl_is_indices: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglIndices. + * + * Return value: %TRUE if the object references a #CoglIndices + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_indices (void *object); + +COGL_END_DECLS + +#endif /* __COGL_INDICES_H__ */ + diff --git a/cogl/cogl/cogl-journal-private.h b/cogl/cogl/cogl-journal-private.h new file mode 100644 index 000000000..8211359fa --- /dev/null +++ b/cogl/cogl/cogl-journal-private.h @@ -0,0 +1,121 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_JOURNAL_PRIVATE_H +#define __COGL_JOURNAL_PRIVATE_H + +#include "cogl-texture.h" +#include "cogl-object-private.h" +#include "cogl-clip-stack.h" +#include "cogl-fence-private.h" + +#define COGL_JOURNAL_VBO_POOL_SIZE 8 + +typedef struct _CoglJournal +{ + CoglObject _parent; + + /* A pointer the framebuffer that is using this journal. This is + only valid when the journal is not empty. It *does* take a + reference on the framebuffer. Although this creates a circular + reference, the framebuffer has special code to handle the case + where the journal is the only thing holding a reference and it + will cause the journal to flush */ + CoglFramebuffer *framebuffer; + + GArray *entries; + GArray *vertices; + size_t needed_vbo_len; + + /* A pool of attribute buffers is used so that we can avoid repeatedly + reallocating buffers. Only one of these buffers at a time will be + used by Cogl but we keep more than one alive anyway in case the + GL driver is internally using the buffer and it would have to + allocate a new one when we start writing to it */ + CoglAttributeBuffer *vbo_pool[COGL_JOURNAL_VBO_POOL_SIZE]; + /* The next vbo to use from the pool. We just cycle through them in + order */ + unsigned int next_vbo_in_pool; + + int fast_read_pixel_count; + + CoglList pending_fences; + +} CoglJournal; + +/* To improve batching of geometry when submitting vertices to OpenGL we + * log the texture rectangles we want to draw to a journal, so when we + * later flush the journal we aim to batch data, and gl draw calls. */ +typedef struct _CoglJournalEntry +{ + CoglPipeline *pipeline; + CoglMatrixEntry *modelview_entry; + CoglClipStack *clip_stack; + /* Offset into ctx->logged_vertices */ + size_t array_offset; + int n_layers; +} CoglJournalEntry; + +CoglJournal * +_cogl_journal_new (CoglFramebuffer *framebuffer); + +void +_cogl_journal_log_quad (CoglJournal *journal, + const float *position, + CoglPipeline *pipeline, + int n_layers, + CoglTexture *layer0_override_texture, + const float *tex_coords, + unsigned int tex_coords_len); + +void +_cogl_journal_flush (CoglJournal *journal); + +void +_cogl_journal_discard (CoglJournal *journal); + +CoglBool +_cogl_journal_all_entries_within_bounds (CoglJournal *journal, + float clip_x0, + float clip_y0, + float clip_x1, + float clip_y1); + +CoglBool +_cogl_journal_try_read_pixel (CoglJournal *journal, + int x, + int y, + CoglBitmap *bitmap, + CoglBool *found_intersection); + +CoglBool +_cogl_is_journal (void *object); + +#endif /* __COGL_JOURNAL_PRIVATE_H */ diff --git a/cogl/cogl/cogl-journal.c b/cogl/cogl/cogl-journal.c new file mode 100644 index 000000000..8ffe25f97 --- /dev/null +++ b/cogl/cogl/cogl-journal.c @@ -0,0 +1,1853 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-context-private.h" +#include "cogl-journal-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-vertex-buffer-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-profile.h" +#include "cogl-attribute-private.h" +#include "cogl-point-in-poly-private.h" +#include "cogl-private.h" +#include "cogl1-context.h" + +#include +#include +#include + +/* XXX NB: + * The data logged in logged_vertices is formatted as follows: + * + * Per entry: + * 4 RGBA GLubytes for the color + * 2 floats for the top left position + * 2 * n_layers floats for the top left texture coordinates + * 2 floats for the bottom right position + * 2 * n_layers floats for the bottom right texture coordinates + */ +#define GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS(N_LAYERS) \ + (N_LAYERS * 2 + 2) + +/* XXX NB: + * Once in the vertex array, the journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 or 3 GLfloats per position (3 when doing software transforms) + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + * + * Where n_layers corresponds to the number of pipeline layers enabled + * + * To avoid frequent changes in the stride of our vertex data we always pad + * n_layers to be >= 2 + * + * There will be four vertices per quad in the vertex array + * + * When we are transforming quads in software we need to also track the z + * coordinate of transformed vertices. + * + * So for a given number of layers this gets the stride in 32bit words: + */ +#define SW_TRANSFORM (!(COGL_DEBUG_ENABLED \ + (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) +#define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */ +#define N_POS_COMPONENTS POS_STRIDE +#define COLOR_STRIDE 1 /* number of 32bit words */ +#define TEX_STRIDE 2 /* number of 32bit words */ +#define MIN_LAYER_PADING 2 +#define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \ + (POS_STRIDE + COLOR_STRIDE + \ + TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS)) + +/* If a batch is longer than this threshold then we'll assume it's not + worth doing software clipping and it's cheaper to program the GPU + to do the clip */ +#define COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD 8 + +typedef struct _CoglJournalFlushState +{ + CoglContext *ctx; + + CoglJournal *journal; + + CoglAttributeBuffer *attribute_buffer; + GArray *attributes; + int current_attribute; + + size_t stride; + size_t array_offset; + GLuint current_vertex; + + CoglIndices *indices; + size_t indices_type_size; + + CoglPipeline *pipeline; +} CoglJournalFlushState; + +typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, + int n_entries, + void *data); +typedef CoglBool (*CoglJournalBatchTest) (CoglJournalEntry *entry0, + CoglJournalEntry *entry1); + +static void _cogl_journal_free (CoglJournal *journal); + +COGL_OBJECT_INTERNAL_DEFINE (Journal, journal); + +static void +_cogl_journal_free (CoglJournal *journal) +{ + int i; + + if (journal->entries) + g_array_free (journal->entries, TRUE); + if (journal->vertices) + g_array_free (journal->vertices, TRUE); + + for (i = 0; i < COGL_JOURNAL_VBO_POOL_SIZE; i++) + if (journal->vbo_pool[i]) + cogl_object_unref (journal->vbo_pool[i]); + + g_slice_free (CoglJournal, journal); +} + +CoglJournal * +_cogl_journal_new (CoglFramebuffer *framebuffer) +{ + CoglJournal *journal = g_slice_new0 (CoglJournal); + + /* The journal keeps a pointer back to the framebuffer because there + is effectively a 1:1 mapping between journals and framebuffers. + However, to avoid a circular reference the journal doesn't take a + reference unless it is non-empty. The framebuffer has a special + unref implementation to ensure that the journal is flushed when + the journal is the only thing keeping it alive */ + journal->framebuffer = framebuffer; + + journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry)); + journal->vertices = g_array_new (FALSE, FALSE, sizeof (float)); + + _cogl_list_init (&journal->pending_fences); + + return _cogl_journal_object_new (journal); +} + +static void +_cogl_journal_dump_logged_quad (uint8_t *data, int n_layers) +{ + size_t stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); + int i; + + g_print ("n_layers = %d; rgba=0x%02X%02X%02X%02X\n", + n_layers, data[0], data[1], data[2], data[3]); + + data += 4; + + for (i = 0; i < 2; i++) + { + float *v = (float *)data + (i * stride); + int j; + + g_print ("v%d: x = %f, y = %f", i, v[0], v[1]); + + for (j = 0; j < n_layers; j++) + { + float *t = v + 2 + TEX_STRIDE * j; + g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); + } + g_print ("\n"); + } +} + +static void +_cogl_journal_dump_quad_vertices (uint8_t *data, int n_layers) +{ + size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers); + int i; + + g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; " + "tex stride = %d; stride in bytes = %d\n", + n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE, + TEX_STRIDE, (int)stride * 4); + + for (i = 0; i < 4; i++) + { + float *v = (float *)data + (i * stride); + uint8_t *c = data + (POS_STRIDE * 4) + (i * stride * 4); + int j; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED + (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) + g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X", + i, v[0], v[1], c[0], c[1], c[2], c[3]); + else + g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X", + i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]); + for (j = 0; j < n_layers; j++) + { + float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j; + g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); + } + g_print ("\n"); + } +} + +static void +_cogl_journal_dump_quad_batch (uint8_t *data, int n_layers, int n_quads) +{ + size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; + int i; + + g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n", + n_layers, n_quads); + for (i = 0; i < n_quads; i++) + _cogl_journal_dump_quad_vertices (data + byte_stride * 2 * i, n_layers); +} + +static void +batch_and_call (CoglJournalEntry *entries, + int n_entries, + CoglJournalBatchTest can_batch_callback, + CoglJournalBatchCallback batch_callback, + void *data) +{ + int i; + int batch_len = 1; + CoglJournalEntry *batch_start = entries; + + if (n_entries < 1) + return; + + for (i = 1; i < n_entries; i++) + { + CoglJournalEntry *entry0 = &entries[i - 1]; + CoglJournalEntry *entry1 = entry0 + 1; + + if (can_batch_callback (entry0, entry1)) + { + batch_len++; + continue; + } + + batch_callback (batch_start, batch_len, data); + + batch_start = entry1; + batch_len = 1; + } + + /* The last batch... */ + batch_callback (batch_start, batch_len, data); +} + +static void +_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + CoglContext *ctx = state->ctx; + CoglFramebuffer *framebuffer = state->journal->framebuffer; + CoglAttribute **attributes; + CoglDrawFlags draw_flags = (COGL_DRAW_SKIP_JOURNAL_FLUSH | + COGL_DRAW_SKIP_PIPELINE_VALIDATION | + COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH | + COGL_DRAW_SKIP_LEGACY_STATE); + + COGL_STATIC_TIMER (time_flush_modelview_and_entries, + "flush: pipeline+entries", /* parent */ + "flush: modelview+entries", + "The time spent flushing modelview + entries", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, time_flush_modelview_and_entries); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) + g_print ("BATCHING: modelview batch len = %d\n", batch_len); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) + _cogl_context_set_current_modelview_entry (ctx, + batch_start->modelview_entry); + + attributes = (CoglAttribute **)state->attributes->data; + + if (!_cogl_pipeline_get_real_blend_enabled (state->pipeline)) + draw_flags |= COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE; + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS)) + { + /* XXX: it's rather evil that we sneak in the GL_QUADS enum here... */ + _cogl_framebuffer_draw_attributes (framebuffer, + state->pipeline, + GL_QUADS, + state->current_vertex, batch_len * 4, + attributes, + state->attributes->len, + draw_flags); + } + else +#endif /* HAVE_COGL_GL */ + { + if (batch_len > 1) + { + CoglVerticesMode mode = COGL_VERTICES_MODE_TRIANGLES; + int first_vertex = state->current_vertex * 6 / 4; + _cogl_framebuffer_draw_indexed_attributes (framebuffer, + state->pipeline, + mode, + first_vertex, + batch_len * 6, + state->indices, + attributes, + state->attributes->len, + draw_flags); + } + else + { + _cogl_framebuffer_draw_attributes (framebuffer, + state->pipeline, + COGL_VERTICES_MODE_TRIANGLE_FAN, + state->current_vertex, 4, + attributes, + state->attributes->len, + draw_flags); + } + } + + /* DEBUGGING CODE XXX: This path will cause all rectangles to be + * drawn with a coloured outline. Each batch will be rendered with + * the same color. This may e.g. help with debugging texture slicing + * issues, visually seeing what is batched and debugging blending + * issues, plus it looks quite cool. + */ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES))) + { + static CoglPipeline *outline = NULL; + uint8_t color_intensity; + int i; + CoglAttribute *loop_attributes[1]; + + if (outline == NULL) + outline = cogl_pipeline_new (ctx); + + /* The least significant three bits represent the three + components so that the order of colours goes red, green, + yellow, blue, magenta, cyan. Black and white are skipped. The + next two bits give four scales of intensity for those colours + in the order 0xff, 0xcc, 0x99, and 0x66. This gives a total + of 24 colours. If there are more than 24 batches on the stage + then it will wrap around */ + color_intensity = 0xff - 0x33 * (ctx->journal_rectangles_color >> 3); + cogl_pipeline_set_color4ub (outline, + (ctx->journal_rectangles_color & 1) ? + color_intensity : 0, + (ctx->journal_rectangles_color & 2) ? + color_intensity : 0, + (ctx->journal_rectangles_color & 4) ? + color_intensity : 0, + 0xff); + + loop_attributes[0] = attributes[0]; /* we just want the position */ + for (i = 0; i < batch_len; i++) + _cogl_framebuffer_draw_attributes (framebuffer, + outline, + COGL_VERTICES_MODE_LINE_LOOP, + 4 * i + state->current_vertex, 4, + loop_attributes, + 1, + draw_flags); + + /* Go to the next color */ + do + ctx->journal_rectangles_color = ((ctx->journal_rectangles_color + 1) & + ((1 << 5) - 1)); + /* We don't want to use black or white */ + while ((ctx->journal_rectangles_color & 0x07) == 0 + || (ctx->journal_rectangles_color & 0x07) == 0x07); + } + + state->current_vertex += (4 * batch_len); + + COGL_TIMER_STOP (_cogl_uprof_context, time_flush_modelview_and_entries); +} + +static CoglBool +compare_entry_modelviews (CoglJournalEntry *entry0, + CoglJournalEntry *entry1) +{ + /* Batch together quads with the same model view matrix */ + return entry0->modelview_entry == entry1->modelview_entry; +} + +/* At this point we have a run of quads that we know have compatible + * pipelines, but they may not all have the same modelview matrix */ +static void +_cogl_journal_flush_pipeline_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + COGL_STATIC_TIMER (time_flush_pipeline_entries, + "flush: texcoords+pipeline+entries", /* parent */ + "flush: pipeline+entries", + "The time spent flushing pipeline + entries", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, time_flush_pipeline_entries); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) + g_print ("BATCHING: pipeline batch len = %d\n", batch_len); + + state->pipeline = batch_start->pipeline; + + /* If we haven't transformed the quads in software then we need to also break + * up batches according to changes in the modelview matrix... */ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) + { + batch_and_call (batch_start, + batch_len, + compare_entry_modelviews, + _cogl_journal_flush_modelview_and_entries, + data); + } + else + _cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data); + + COGL_TIMER_STOP (_cogl_uprof_context, time_flush_pipeline_entries); +} + +static CoglBool +compare_entry_pipelines (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + /* batch rectangles using compatible pipelines */ + + if (_cogl_pipeline_equal (entry0->pipeline, + entry1->pipeline, + (COGL_PIPELINE_STATE_ALL & + ~COGL_PIPELINE_STATE_COLOR), + COGL_PIPELINE_LAYER_STATE_ALL, + 0)) + return TRUE; + else + return FALSE; +} + +typedef struct _CreateAttributeState +{ + int current; + CoglJournalFlushState *flush_state; +} CreateAttributeState; + +static CoglBool +create_attribute_cb (CoglPipeline *pipeline, + int layer_number, + void *user_data) +{ + CreateAttributeState *state = user_data; + CoglJournalFlushState *flush_state = state->flush_state; + CoglAttribute **attribute_entry = + &g_array_index (flush_state->attributes, + CoglAttribute *, + state->current + 2); + const char *names[] = { + "cogl_tex_coord0_in", + "cogl_tex_coord1_in", + "cogl_tex_coord2_in", + "cogl_tex_coord3_in", + "cogl_tex_coord4_in", + "cogl_tex_coord5_in", + "cogl_tex_coord6_in", + "cogl_tex_coord7_in" + }; + char *name; + + /* XXX NB: + * Our journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 or 3 floats per position (3 when doing software transforms) + * 4 RGBA bytes, + * 2 floats per tex coord * n_layers + * (though n_layers may be padded; see definition of + * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) + */ + name = layer_number < 8 ? (char *)names[layer_number] : + g_strdup_printf ("cogl_tex_coord%d_in", layer_number); + + /* XXX: it may be worth having some form of static initializer for + * attributes... */ + *attribute_entry = + cogl_attribute_new (flush_state->attribute_buffer, + name, + flush_state->stride, + flush_state->array_offset + + (POS_STRIDE + COLOR_STRIDE) * 4 + + TEX_STRIDE * 4 * state->current, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + + if (layer_number >= 8) + g_free (name); + + state->current++; + + return TRUE; +} + +/* Since the stride may not reflect the number of texture layers in use + * (due to padding) we deal with texture coordinate offsets separately + * from vertex and color offsets... */ +static void +_cogl_journal_flush_texcoord_vbo_offsets_and_entries ( + CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + CreateAttributeState create_attrib_state; + int i; + COGL_STATIC_TIMER (time_flush_texcoord_pipeline_entries, + "flush: vbo+texcoords+pipeline+entries", /* parent */ + "flush: texcoords+pipeline+entries", + "The time spent flushing texcoord offsets + pipeline " + "+ entries", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, time_flush_texcoord_pipeline_entries); + + /* NB: attributes 0 and 1 are position and color */ + + for (i = 2; i < state->attributes->len; i++) + cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i)); + + g_array_set_size (state->attributes, batch_start->n_layers + 2); + + create_attrib_state.current = 0; + create_attrib_state.flush_state = state; + + cogl_pipeline_foreach_layer (batch_start->pipeline, + create_attribute_cb, + &create_attrib_state); + + batch_and_call (batch_start, + batch_len, + compare_entry_pipelines, + _cogl_journal_flush_pipeline_and_entries, + data); + COGL_TIMER_STOP (_cogl_uprof_context, time_flush_texcoord_pipeline_entries); +} + +static CoglBool +compare_entry_layer_numbers (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + if (_cogl_pipeline_layer_numbers_equal (entry0->pipeline, entry1->pipeline)) + return TRUE; + else + return FALSE; +} + +/* At this point we know the stride has changed from the previous batch + * of journal entries */ +static void +_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + CoglContext *ctx = state->journal->framebuffer->context; + size_t stride; + int i; + CoglAttribute **attribute_entry; + COGL_STATIC_TIMER (time_flush_vbo_texcoord_pipeline_entries, + "flush: clip+vbo+texcoords+pipeline+entries", /* parent */ + "flush: vbo+texcoords+pipeline+entries", + "The time spent flushing vbo + texcoord offsets + " + "pipeline + entries", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, + time_flush_vbo_texcoord_pipeline_entries); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) + g_print ("BATCHING: vbo offset batch len = %d\n", batch_len); + + /* XXX NB: + * Our journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 or 3 GLfloats per position (3 when doing software transforms) + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + * (though n_layers may be padded; see definition of + * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) + */ + stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers); + stride *= sizeof (float); + state->stride = stride; + + for (i = 0; i < state->attributes->len; i++) + cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i)); + + g_array_set_size (state->attributes, 2); + + attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 0); + *attribute_entry = cogl_attribute_new (state->attribute_buffer, + "cogl_position_in", + stride, + state->array_offset, + N_POS_COMPONENTS, + COGL_ATTRIBUTE_TYPE_FLOAT); + + attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 1); + *attribute_entry = + cogl_attribute_new (state->attribute_buffer, + "cogl_color_in", + stride, + state->array_offset + (POS_STRIDE * 4), + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS)) + state->indices = cogl_get_rectangle_indices (ctx, batch_len); + + /* We only create new Attributes when the stride within the + * AttributeBuffer changes. (due to a change in the number of pipeline + * layers) While the stride remains constant we walk forward through + * the above AttributeBuffer using a vertex offset passed to + * cogl_draw_attributes + */ + state->current_vertex = 0; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL)) && + cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ)) + { + uint8_t *verts; + + /* Mapping a buffer for read is probably a really bad thing to + do but this will only happen during debugging so it probably + doesn't matter */ + verts = ((uint8_t *)_cogl_buffer_map (COGL_BUFFER (state->attribute_buffer), + COGL_BUFFER_ACCESS_READ, 0, + NULL) + + state->array_offset); + + _cogl_journal_dump_quad_batch (verts, + batch_start->n_layers, + batch_len); + + cogl_buffer_unmap (COGL_BUFFER (state->attribute_buffer)); + } + + batch_and_call (batch_start, + batch_len, + compare_entry_layer_numbers, + _cogl_journal_flush_texcoord_vbo_offsets_and_entries, + data); + + /* progress forward through the VBO containing all our vertices */ + state->array_offset += (stride * 4 * batch_len); + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL))) + g_print ("new vbo offset = %lu\n", (unsigned long)state->array_offset); + + COGL_TIMER_STOP (_cogl_uprof_context, + time_flush_vbo_texcoord_pipeline_entries); +} + +static CoglBool +compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + /* Currently the only thing that affects the stride for our vertex arrays + * is the number of pipeline layers. We need to update our VBO offsets + * whenever the stride changes. */ + /* TODO: We should be padding the n_layers == 1 case as if it were + * n_layers == 2 so we can reduce the need to split batches. */ + if (entry0->n_layers == entry1->n_layers || + (entry0->n_layers <= MIN_LAYER_PADING && + entry1->n_layers <= MIN_LAYER_PADING)) + return TRUE; + else + return FALSE; +} + +/* At this point we know the batch has a unique clip stack */ +static void +_cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + CoglFramebuffer *framebuffer = state->journal->framebuffer; + CoglContext *ctx = framebuffer->context; + CoglMatrixStack *projection_stack; + + COGL_STATIC_TIMER (time_flush_clip_stack_pipeline_entries, + "Journal Flush", /* parent */ + "flush: clip+vbo+texcoords+pipeline+entries", + "The time spent flushing clip + vbo + texcoord offsets + " + "pipeline + entries", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, + time_flush_clip_stack_pipeline_entries); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) + g_print ("BATCHING: clip stack batch len = %d\n", batch_len); + + _cogl_clip_stack_flush (batch_start->clip_stack, framebuffer); + + /* XXX: Because we are manually flushing clip state here we need to + * make sure that the clip state gets updated the next time we flush + * framebuffer state by marking the current framebuffer's clip state + * as changed. */ + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + + /* If we have transformed all our quads at log time then we ensure + * no further model transform is applied by loading the identity + * matrix here. We need to do this after flushing the clip stack + * because the clip stack flushing code can modify the current + * modelview matrix entry */ + if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))) + _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); + + /* Setting up the clip state can sometimes also update the current + * projection matrix entry so we should update it again. This will have + * no affect if the clip code didn't modify the projection */ + projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + _cogl_context_set_current_projection_entry (ctx, + projection_stack->last_entry); + + batch_and_call (batch_start, + batch_len, + compare_entry_strides, + _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ + data); + + COGL_TIMER_STOP (_cogl_uprof_context, + time_flush_clip_stack_pipeline_entries); +} + +typedef struct +{ + float x_1, y_1; + float x_2, y_2; +} ClipBounds; + +static CoglBool +can_software_clip_entry (CoglJournalEntry *journal_entry, + CoglJournalEntry *prev_journal_entry, + CoglClipStack *clip_stack, + ClipBounds *clip_bounds_out) +{ + CoglPipeline *pipeline = journal_entry->pipeline; + CoglClipStack *clip_entry; + int layer_num; + + clip_bounds_out->x_1 = -G_MAXFLOAT; + clip_bounds_out->y_1 = -G_MAXFLOAT; + clip_bounds_out->x_2 = G_MAXFLOAT; + clip_bounds_out->y_2 = G_MAXFLOAT; + + /* Check the pipeline is usable. We can short-cut here for + entries using the same pipeline as the previous entry */ + if (prev_journal_entry == NULL || pipeline != prev_journal_entry->pipeline) + { + /* If the pipeline has a user program then we can't reliably modify + the texture coordinates */ + if (cogl_pipeline_get_user_program (pipeline)) + return FALSE; + + /* If any of the pipeline layers have a texture matrix then we can't + reliably modify the texture coordinates */ + for (layer_num = cogl_pipeline_get_n_layers (pipeline) - 1; + layer_num >= 0; + layer_num--) + if (_cogl_pipeline_layer_has_user_matrix (pipeline, layer_num)) + return FALSE; + } + + /* Now we need to verify that each clip entry's matrix is just a + translation of the journal entry's modelview matrix. We can + also work out the bounds of the clip in modelview space using + this translation */ + for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent) + { + float rect_x1, rect_y1, rect_x2, rect_y2; + CoglClipStackRect *clip_rect; + float tx, ty, tz; + CoglMatrixEntry *modelview_entry; + + clip_rect = (CoglClipStackRect *) clip_entry; + + modelview_entry = journal_entry->modelview_entry; + if (!cogl_matrix_entry_calculate_translation (clip_rect->matrix_entry, + modelview_entry, + &tx, &ty, &tz)) + return FALSE; + + if (clip_rect->x0 < clip_rect->x1) + { + rect_x1 = clip_rect->x0; + rect_x2 = clip_rect->x1; + } + else + { + rect_x1 = clip_rect->x1; + rect_x2 = clip_rect->x0; + } + if (clip_rect->y0 < clip_rect->y1) + { + rect_y1 = clip_rect->y0; + rect_y2 = clip_rect->y1; + } + else + { + rect_y1 = clip_rect->y1; + rect_y2 = clip_rect->y0; + } + + clip_bounds_out->x_1 = MAX (clip_bounds_out->x_1, rect_x1 - tx); + clip_bounds_out->y_1 = MAX (clip_bounds_out->y_1, rect_y1 - ty); + clip_bounds_out->x_2 = MIN (clip_bounds_out->x_2, rect_x2 - tx); + clip_bounds_out->y_2 = MIN (clip_bounds_out->y_2, rect_y2 - ty); + } + + if (clip_bounds_out->x_2 <= clip_bounds_out->x_1 || + clip_bounds_out->y_2 <= clip_bounds_out->y_1) + memset (clip_bounds_out, 0, sizeof (ClipBounds)); + + return TRUE; +} + +static void +software_clip_entry (CoglJournalEntry *journal_entry, + float *verts, + ClipBounds *clip_bounds) +{ + size_t stride = + GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (journal_entry->n_layers); + float rx1, ry1, rx2, ry2; + float vx1, vy1, vx2, vy2; + int layer_num; + + /* Remove the clip on the entry */ + _cogl_clip_stack_unref (journal_entry->clip_stack); + journal_entry->clip_stack = NULL; + + vx1 = verts[0]; + vy1 = verts[1]; + vx2 = verts[stride]; + vy2 = verts[stride + 1]; + + if (vx1 < vx2) + { + rx1 = vx1; + rx2 = vx2; + } + else + { + rx1 = vx2; + rx2 = vx1; + } + if (vy1 < vy2) + { + ry1 = vy1; + ry2 = vy2; + } + else + { + ry1 = vy2; + ry2 = vy1; + } + + rx1 = CLAMP (rx1, clip_bounds->x_1, clip_bounds->x_2); + ry1 = CLAMP (ry1, clip_bounds->y_1, clip_bounds->y_2); + rx2 = CLAMP (rx2, clip_bounds->x_1, clip_bounds->x_2); + ry2 = CLAMP (ry2, clip_bounds->y_1, clip_bounds->y_2); + + /* Check if the rectangle intersects the clip at all */ + if (rx1 == rx2 || ry1 == ry2) + /* Will set all of the vertex data to 0 in the hope that this + will create a degenerate rectangle and the GL driver will + be able to clip it quickly */ + memset (verts, 0, sizeof (float) * stride * 2); + else + { + if (vx1 > vx2) + { + float t = rx1; + rx1 = rx2; + rx2 = t; + } + if (vy1 > vy2) + { + float t = ry1; + ry1 = ry2; + ry2 = t; + } + + verts[0] = rx1; + verts[1] = ry1; + verts[stride] = rx2; + verts[stride + 1] = ry2; + + /* Convert the rectangle coordinates to a fraction of the original + rectangle */ + rx1 = (rx1 - vx1) / (vx2 - vx1); + ry1 = (ry1 - vy1) / (vy2 - vy1); + rx2 = (rx2 - vx1) / (vx2 - vx1); + ry2 = (ry2 - vy1) / (vy2 - vy1); + + for (layer_num = 0; layer_num < journal_entry->n_layers; layer_num++) + { + float *t = verts + 2 + 2 * layer_num; + float tx1 = t[0], ty1 = t[1]; + float tx2 = t[stride], ty2 = t[stride + 1]; + t[0] = rx1 * (tx2 - tx1) + tx1; + t[1] = ry1 * (ty2 - ty1) + ty1; + t[stride] = rx2 * (tx2 - tx1) + tx1; + t[stride + 1] = ry2 * (ty2 - ty1) + ty1; + } + } +} + +static void +maybe_software_clip_entries (CoglJournalEntry *batch_start, + int batch_len, + CoglJournalFlushState *state) +{ + CoglContext *ctx; + CoglJournal *journal; + CoglClipStack *clip_stack, *clip_entry; + int entry_num; + + /* This tries to find cases where the entry is logged with a clip + but it would be faster to modify the vertex and texture + coordinates rather than flush the clip so that it can batch + better */ + + /* If the batch is reasonably long then it's worthwhile programming + the GPU to do the clip */ + if (batch_len >= COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD) + return; + + clip_stack = batch_start->clip_stack; + + if (clip_stack == NULL) + return; + + /* Verify that all of the clip stack entries are a simple rectangle + clip */ + for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent) + if (clip_entry->type != COGL_CLIP_STACK_RECT) + return; + + ctx = state->ctx; + journal = state->journal; + + /* This scratch buffer is used to store the translation for each + entry in the journal. We store it in a separate buffer because + it's expensive to calculate but at this point we still don't know + whether we can clip all of the entries so we don't want to do the + rest of the dependant calculations until we're sure we can. */ + if (ctx->journal_clip_bounds == NULL) + ctx->journal_clip_bounds = g_array_new (FALSE, FALSE, sizeof (ClipBounds)); + g_array_set_size (ctx->journal_clip_bounds, batch_len); + + for (entry_num = 0; entry_num < batch_len; entry_num++) + { + CoglJournalEntry *journal_entry = batch_start + entry_num; + CoglJournalEntry *prev_journal_entry = + entry_num ? batch_start + (entry_num - 1) : NULL; + ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, + ClipBounds, entry_num); + + if (!can_software_clip_entry (journal_entry, prev_journal_entry, + clip_stack, + clip_bounds)) + return; + } + + /* If we make it here then we know we can software clip the entire batch */ + + COGL_NOTE (CLIPPING, "Software clipping a batch of length %i", batch_len); + + for (entry_num = 0; entry_num < batch_len; entry_num++) + { + CoglJournalEntry *journal_entry = batch_start + entry_num; + float *verts = &g_array_index (journal->vertices, float, + journal_entry->array_offset + 1); + ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, + ClipBounds, entry_num); + + software_clip_entry (journal_entry, verts, clip_bounds); + } + + return; +} + +static void +_cogl_journal_maybe_software_clip_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + + COGL_STATIC_TIMER (time_check_software_clip, + "Journal Flush", /* parent */ + "flush: software clipping", + "Time spent software clipping", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, + time_check_software_clip); + + maybe_software_clip_entries (batch_start, batch_len, state); + + COGL_TIMER_STOP (_cogl_uprof_context, + time_check_software_clip); +} + +static CoglBool +compare_entry_clip_stacks (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + return entry0->clip_stack == entry1->clip_stack; +} + +/* Gets a new vertex array from the pool. A reference is taken on the + array so it can be treated as if it was just newly allocated */ +static CoglAttributeBuffer * +create_attribute_buffer (CoglJournal *journal, + size_t n_bytes) +{ + CoglAttributeBuffer *vbo; + CoglContext *ctx = journal->framebuffer->context; + + /* If CoglBuffers are being emulated with malloc then there's not + really any point in using the pool so we'll just allocate the + buffer directly */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_VBOS)) + return cogl_attribute_buffer_new_with_size (ctx, n_bytes); + + vbo = journal->vbo_pool[journal->next_vbo_in_pool]; + + if (vbo == NULL) + { + vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes); + journal->vbo_pool[journal->next_vbo_in_pool] = vbo; + } + else if (cogl_buffer_get_size (COGL_BUFFER (vbo)) < n_bytes) + { + /* If the buffer is too small then we'll just recreate it */ + cogl_object_unref (vbo); + vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes); + journal->vbo_pool[journal->next_vbo_in_pool] = vbo; + } + + journal->next_vbo_in_pool = ((journal->next_vbo_in_pool + 1) % + COGL_JOURNAL_VBO_POOL_SIZE); + + return cogl_object_ref (vbo); +} + +static CoglAttributeBuffer * +upload_vertices (CoglJournal *journal, + const CoglJournalEntry *entries, + int n_entries, + size_t needed_vbo_len, + GArray *vertices) +{ + CoglAttributeBuffer *attribute_buffer; + CoglBuffer *buffer; + const float *vin; + float *vout; + int entry_num; + int i; + CoglMatrixEntry *last_modelview_entry = NULL; + CoglMatrix modelview; + + g_assert (needed_vbo_len); + + attribute_buffer = create_attribute_buffer (journal, needed_vbo_len * 4); + buffer = COGL_BUFFER (attribute_buffer); + cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_STATIC); + + vout = _cogl_buffer_map_range_for_fill_or_fallback (buffer, + 0, /* offset */ + needed_vbo_len * 4); + vin = &g_array_index (vertices, float, 0); + + /* Expand the number of vertices from 2 to 4 while uploading */ + for (entry_num = 0; entry_num < n_entries; entry_num++) + { + const CoglJournalEntry *entry = entries + entry_num; + size_t vb_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (entry->n_layers); + size_t array_stride = + GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers); + + /* Copy the color to all four of the vertices */ + for (i = 0; i < 4; i++) + memcpy (vout + vb_stride * i + POS_STRIDE, vin, 4); + vin++; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) + { + vout[vb_stride * 0] = vin[0]; + vout[vb_stride * 0 + 1] = vin[1]; + vout[vb_stride * 1] = vin[0]; + vout[vb_stride * 1 + 1] = vin[array_stride + 1]; + vout[vb_stride * 2] = vin[array_stride]; + vout[vb_stride * 2 + 1] = vin[array_stride + 1]; + vout[vb_stride * 3] = vin[array_stride]; + vout[vb_stride * 3 + 1] = vin[1]; + } + else + { + float v[8]; + + v[0] = vin[0]; + v[1] = vin[1]; + v[2] = vin[0]; + v[3] = vin[array_stride + 1]; + v[4] = vin[array_stride]; + v[5] = vin[array_stride + 1]; + v[6] = vin[array_stride]; + v[7] = vin[1]; + + if (entry->modelview_entry != last_modelview_entry) + cogl_matrix_entry_get (entry->modelview_entry, &modelview); + cogl_matrix_transform_points (&modelview, + 2, /* n_components */ + sizeof (float) * 2, /* stride_in */ + v, /* points_in */ + /* strideout */ + vb_stride * sizeof (float), + vout, /* points_out */ + 4 /* n_points */); + } + + for (i = 0; i < entry->n_layers; i++) + { + const float *tin = vin + 2; + float *tout = vout + POS_STRIDE + COLOR_STRIDE; + + tout[vb_stride * 0 + i * 2] = tin[i * 2]; + tout[vb_stride * 0 + 1 + i * 2] = tin[i * 2 + 1]; + tout[vb_stride * 1 + i * 2] = tin[i * 2]; + tout[vb_stride * 1 + 1 + i * 2] = tin[array_stride + i * 2 + 1]; + tout[vb_stride * 2 + i * 2] = tin[array_stride + i * 2]; + tout[vb_stride * 2 + 1 + i * 2] = tin[array_stride + i * 2 + 1]; + tout[vb_stride * 3 + i * 2] = tin[array_stride + i * 2]; + tout[vb_stride * 3 + 1 + i * 2] = tin[i * 2 + 1]; + } + + vin += array_stride * 2; + vout += vb_stride * 4; + } + + _cogl_buffer_unmap_for_fill_or_fallback (buffer); + + return attribute_buffer; +} + +void +_cogl_journal_discard (CoglJournal *journal) +{ + int i; + + if (journal->entries->len <= 0) + return; + + for (i = 0; i < journal->entries->len; i++) + { + CoglJournalEntry *entry = + &g_array_index (journal->entries, CoglJournalEntry, i); + _cogl_pipeline_journal_unref (entry->pipeline); + cogl_matrix_entry_unref (entry->modelview_entry); + _cogl_clip_stack_unref (entry->clip_stack); + } + + g_array_set_size (journal->entries, 0); + g_array_set_size (journal->vertices, 0); + journal->needed_vbo_len = 0; + journal->fast_read_pixel_count = 0; + + /* The journal only holds a reference to the framebuffer while the + journal is not empty */ + cogl_object_unref (journal->framebuffer); +} + +/* Note: A return value of FALSE doesn't mean 'no' it means + * 'unknown' */ +CoglBool +_cogl_journal_all_entries_within_bounds (CoglJournal *journal, + float clip_x0, + float clip_y0, + float clip_x1, + float clip_y1) +{ + CoglJournalEntry *entry = (CoglJournalEntry *)journal->entries->data; + CoglClipStack *clip_entry; + CoglClipStack *reference = NULL; + int bounds_x0; + int bounds_y0; + int bounds_x1; + int bounds_y1; + int i; + + if (journal->entries->len == 0) + return TRUE; + + /* Find the shortest clip_stack ancestry that leaves us in the + * required bounds */ + for (clip_entry = entry->clip_stack; + clip_entry; + clip_entry = clip_entry->parent) + { + _cogl_clip_stack_get_bounds (clip_entry, + &bounds_x0, &bounds_y0, + &bounds_x1, &bounds_y1); + + if (bounds_x0 >= clip_x0 && bounds_y0 >= clip_y0 && + bounds_x1 <= clip_x1 && bounds_y1 <= clip_y1) + reference = clip_entry; + else + break; + } + + if (!reference) + return FALSE; + + /* For the remaining journal entries we will only verify they share + * 'reference' as an ancestor in their clip stack since that's + * enough to know that they would be within the required bounds. + */ + for (i = 1; i < journal->entries->len; i++) + { + CoglBool found_reference = FALSE; + entry = &g_array_index (journal->entries, CoglJournalEntry, i); + + for (clip_entry = entry->clip_stack; + clip_entry; + clip_entry = clip_entry->parent) + { + if (clip_entry == reference) + { + found_reference = TRUE; + break; + } + } + + if (!found_reference) + return FALSE; + } + + return TRUE; +} + +static void +post_fences (CoglJournal *journal) +{ + CoglFenceClosure *fence, *tmp; + + _cogl_list_for_each_safe (fence, tmp, &journal->pending_fences, link) + { + _cogl_list_remove (&fence->link); + _cogl_fence_submit (fence); + } +} + +/* XXX NB: When _cogl_journal_flush() returns all state relating + * to pipelines, all glEnable flags and current matrix state + * is undefined. + */ +void +_cogl_journal_flush (CoglJournal *journal) +{ + CoglFramebuffer *framebuffer; + CoglContext *ctx; + CoglJournalFlushState state; + int i; + COGL_STATIC_TIMER (flush_timer, + "Mainloop", /* parent */ + "Journal Flush", + "The time spent flushing the Cogl journal", + 0 /* no application private data */); + COGL_STATIC_TIMER (discard_timer, + "Journal Flush", /* parent */ + "flush: discard", + "The time spent discarding the Cogl journal after a flush", + 0 /* no application private data */); + + if (journal->entries->len == 0) + { + post_fences (journal); + return; + } + + framebuffer = journal->framebuffer; + ctx = framebuffer->context; + + /* The entries in this journal may depend on images in other + * framebuffers which may require that we flush the journals + * associated with those framebuffers before we can flush + * this journal... */ + _cogl_framebuffer_flush_dependency_journals (framebuffer); + + /* Note: we start the timer after flushing dependency journals so + * that the timer isn't started recursively. */ + COGL_TIMER_START (_cogl_uprof_context, flush_timer); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) + g_print ("BATCHING: journal len = %d\n", journal->entries->len); + + /* NB: the journal deals with flushing the modelview stack and clip + state manually */ + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_ALL & + ~(COGL_FRAMEBUFFER_STATE_MODELVIEW | + COGL_FRAMEBUFFER_STATE_CLIP)); + + /* We need to mark the current modelview state of the framebuffer as + * dirty because we are going to manually replace it */ + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; + + state.ctx = ctx; + state.journal = journal; + + state.attributes = ctx->journal_flush_attributes_array; + + if (G_UNLIKELY ((COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_CLIP)) == 0)) + { + /* We do an initial walk of the journal to analyse the clip stack + batches to see if we can do software clipping. We do this as a + separate walk of the journal because we can modify entries and + this may end up joining together clip stack batches in the next + iteration. */ + batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */ + journal->entries->len, /* max number of entries to consider */ + compare_entry_clip_stacks, + _cogl_journal_maybe_software_clip_entries, /* callback */ + &state); /* data */ + } + + /* We upload the vertices after the clip stack pass in case it + modifies the entries */ + state.attribute_buffer = + upload_vertices (journal, + &g_array_index (journal->entries, CoglJournalEntry, 0), + journal->entries->len, + journal->needed_vbo_len, + journal->vertices); + state.array_offset = 0; + + /* batch_and_call() batches a list of journal entries according to some + * given criteria and calls a callback once for each determined batch. + * + * The process of flushing the journal is staggered to reduce the amount + * of driver/GPU state changes necessary: + * 1) We split the entries according to the clip state. + * 2) We split the entries according to the stride of the vertices: + * Each time the stride of our vertex data changes we need to call + * gl{Vertex,Color}Pointer to inform GL of new VBO offsets. + * Currently the only thing that affects the stride of our vertex data + * is the number of pipeline layers. + * 3) We split the entries explicitly by the number of pipeline layers: + * We pad our vertex data when the number of layers is < 2 so that we + * can minimize changes in stride. Each time the number of layers + * changes we need to call glTexCoordPointer to inform GL of new VBO + * offsets. + * 4) We then split according to compatible Cogl pipelines: + * This is where we flush pipeline state + * 5) Finally we split according to modelview matrix changes: + * This is when we finally tell GL to draw something. + * Note: Splitting by modelview changes is skipped when are doing the + * vertex transformation in software at log time. + */ + batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */ + journal->entries->len, /* max number of entries to consider */ + compare_entry_clip_stacks, + _cogl_journal_flush_clip_stacks_and_entries, /* callback */ + &state); /* data */ + + for (i = 0; i < state.attributes->len; i++) + cogl_object_unref (g_array_index (state.attributes, CoglAttribute *, i)); + g_array_set_size (state.attributes, 0); + + cogl_object_unref (state.attribute_buffer); + + COGL_TIMER_START (_cogl_uprof_context, discard_timer); + _cogl_journal_discard (journal); + COGL_TIMER_STOP (_cogl_uprof_context, discard_timer); + + post_fences (journal); + + COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); +} + +static CoglBool +add_framebuffer_deps_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglFramebuffer *framebuffer = user_data; + CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer); + const GList *l; + + if (!texture) + return TRUE; + + for (l = _cogl_texture_get_associated_framebuffers (texture); l; l = l->next) + _cogl_framebuffer_add_dependency (framebuffer, l->data); + + return TRUE; +} + +void +_cogl_journal_log_quad (CoglJournal *journal, + const float *position, + CoglPipeline *pipeline, + int n_layers, + CoglTexture *layer0_override_texture, + const float *tex_coords, + unsigned int tex_coords_len) +{ + CoglFramebuffer *framebuffer = journal->framebuffer; + size_t stride; + int next_vert; + float *v; + int i; + int next_entry; + uint32_t disable_layers; + CoglJournalEntry *entry; + CoglPipeline *final_pipeline; + CoglClipStack *clip_stack; + CoglPipelineFlushOptions flush_options; + CoglMatrixStack *modelview_stack; + COGL_STATIC_TIMER (log_timer, + "Mainloop", /* parent */ + "Journal Log", + "The time spent logging in the Cogl journal", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, log_timer); + + /* Adding something to the journal should mean that we are in the + * middle of the scene. Although this will also end up being set + * when the journal is actually flushed, we set it here explicitly + * so that we will know sooner */ + _cogl_framebuffer_mark_mid_scene (framebuffer); + + /* If the framebuffer was previously empty then we'll take a + reference to the current framebuffer. This reference will be + removed when the journal is flushed */ + if (journal->vertices->len == 0) + cogl_object_ref (framebuffer); + + /* The vertex data is logged into a separate array. The data needs + to be copied into a vertex array before it's given to GL so we + only store two vertices per quad and expand it to four while + uploading. */ + + /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS for details + * about how we pack our vertex data */ + stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); + + next_vert = journal->vertices->len; + g_array_set_size (journal->vertices, next_vert + 2 * stride + 1); + v = &g_array_index (journal->vertices, float, next_vert); + + /* We calculate the needed size of the vbo as we go because it + depends on the number of layers in each entry and it's not easy + calculate based on the length of the logged vertices array */ + journal->needed_vbo_len += GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; + + /* XXX: All the jumping around to fill in this strided buffer doesn't + * seem ideal. */ + + /* FIXME: This is a hacky optimization, since it will break if we + * change the definition of CoglColor: */ + _cogl_pipeline_get_colorubv (pipeline, (uint8_t *) v); + v++; + + memcpy (v, position, sizeof (float) * 2); + memcpy (v + stride, position + 2, sizeof (float) * 2); + + for (i = 0; i < n_layers; i++) + { + /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS + * for details about how we pack our vertex data */ + GLfloat *t = v + 2 + i * 2; + + memcpy (t, tex_coords + i * 4, sizeof (float) * 2); + memcpy (t + stride, tex_coords + i * 4 + 2, sizeof (float) * 2); + } + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL))) + { + g_print ("Logged new quad:\n"); + v = &g_array_index (journal->vertices, float, next_vert); + _cogl_journal_dump_logged_quad ((uint8_t *)v, n_layers); + } + + next_entry = journal->entries->len; + g_array_set_size (journal->entries, next_entry + 1); + entry = &g_array_index (journal->entries, CoglJournalEntry, next_entry); + + entry->n_layers = n_layers; + entry->array_offset = next_vert; + + final_pipeline = pipeline; + + flush_options.flags = 0; + if (G_UNLIKELY (cogl_pipeline_get_n_layers (pipeline) != n_layers)) + { + disable_layers = (1 << n_layers) - 1; + disable_layers = ~disable_layers; + flush_options.disable_layers = disable_layers; + flush_options.flags |= COGL_PIPELINE_FLUSH_DISABLE_MASK; + } + if (G_UNLIKELY (layer0_override_texture)) + { + flush_options.flags |= COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE; + flush_options.layer0_override_texture = layer0_override_texture; + } + + if (G_UNLIKELY (flush_options.flags)) + { + final_pipeline = cogl_pipeline_copy (pipeline); + _cogl_pipeline_apply_overrides (final_pipeline, &flush_options); + } + + entry->pipeline = _cogl_pipeline_journal_ref (final_pipeline); + + clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer); + entry->clip_stack = _cogl_clip_stack_ref (clip_stack); + + if (G_UNLIKELY (final_pipeline != pipeline)) + cogl_object_unref (final_pipeline); + + modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + entry->modelview_entry = cogl_matrix_entry_ref (modelview_stack->last_entry); + + _cogl_pipeline_foreach_layer_internal (pipeline, + add_framebuffer_deps_cb, + framebuffer); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BATCHING))) + _cogl_journal_flush (journal); + + COGL_TIMER_STOP (_cogl_uprof_context, log_timer); +} + +static void +entry_to_screen_polygon (CoglFramebuffer *framebuffer, + const CoglJournalEntry *entry, + float *vertices, + float *poly) +{ + size_t array_stride = + GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers); + CoglMatrixStack *projection_stack; + CoglMatrix projection; + CoglMatrix modelview; + int i; + float viewport[4]; + + poly[0] = vertices[0]; + poly[1] = vertices[1]; + poly[2] = 0; + poly[3] = 1; + + poly[4] = vertices[0]; + poly[5] = vertices[array_stride + 1]; + poly[6] = 0; + poly[7] = 1; + + poly[8] = vertices[array_stride]; + poly[9] = vertices[array_stride + 1]; + poly[10] = 0; + poly[11] = 1; + + poly[12] = vertices[array_stride]; + poly[13] = vertices[1]; + poly[14] = 0; + poly[15] = 1; + + /* TODO: perhaps split the following out into a more generalized + * _cogl_transform_points utility... + */ + + cogl_matrix_entry_get (entry->modelview_entry, &modelview); + cogl_matrix_transform_points (&modelview, + 2, /* n_components */ + sizeof (float) * 4, /* stride_in */ + poly, /* points_in */ + /* strideout */ + sizeof (float) * 4, + poly, /* points_out */ + 4 /* n_points */); + + projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + cogl_matrix_stack_get (projection_stack, &projection); + + cogl_matrix_project_points (&projection, + 3, /* n_components */ + sizeof (float) * 4, /* stride_in */ + poly, /* points_in */ + /* strideout */ + sizeof (float) * 4, + poly, /* points_out */ + 4 /* n_points */); + + cogl_framebuffer_get_viewport4fv (framebuffer, viewport); + +/* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) + * to Cogl window/framebuffer coordinates (ranging from 0 to buffer-size) with + * (0,0) being top left. */ +#define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ + ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) +/* Note: for Y we first flip all coordinates around the X axis while in + * normalized device coodinates */ +#define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ + ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) + + /* Scale from normalized device coordinates (in range [-1,1]) to + * window coordinates ranging [0,window-size] ... */ + for (i = 0; i < 4; i++) + { + float w = poly[4 * i + 3]; + + /* Perform perspective division */ + poly[4 * i] /= w; + poly[4 * i + 1] /= w; + + /* Apply viewport transform */ + poly[4 * i] = VIEWPORT_TRANSFORM_X (poly[4 * i], + viewport[0], viewport[2]); + poly[4 * i + 1] = VIEWPORT_TRANSFORM_Y (poly[4 * i + 1], + viewport[1], viewport[3]); + } + +#undef VIEWPORT_TRANSFORM_X +#undef VIEWPORT_TRANSFORM_Y +} + +static CoglBool +try_checking_point_hits_entry_after_clipping (CoglFramebuffer *framebuffer, + CoglJournalEntry *entry, + float *vertices, + float x, + float y, + CoglBool *hit) +{ + CoglBool can_software_clip = TRUE; + CoglBool needs_software_clip = FALSE; + CoglClipStack *clip_entry; + + *hit = TRUE; + + /* Verify that all of the clip stack entries are simple rectangle + * clips */ + for (clip_entry = entry->clip_stack; + clip_entry; + clip_entry = clip_entry->parent) + { + if (x < clip_entry->bounds_x0 || + x >= clip_entry->bounds_x1 || + y < clip_entry->bounds_y0 || + y >= clip_entry->bounds_y1) + { + *hit = FALSE; + return TRUE; + } + + if (clip_entry->type == COGL_CLIP_STACK_WINDOW_RECT) + { + /* XXX: technically we could still run the software clip in + * this case because for our purposes we know this clip + * can be ignored now, but [can_]sofware_clip_entry() doesn't + * know this and will bail out. */ + can_software_clip = FALSE; + } + else if (clip_entry->type == COGL_CLIP_STACK_RECT) + { + CoglClipStackRect *rect_entry = (CoglClipStackRect *)entry; + + if (rect_entry->can_be_scissor == FALSE) + needs_software_clip = TRUE; + /* If can_be_scissor is TRUE then we know it's screen + * aligned and the hit test we did above has determined + * that we are inside this clip. */ + } + else + return FALSE; + } + + if (needs_software_clip) + { + ClipBounds clip_bounds; + float poly[16]; + + if (!can_software_clip) + return FALSE; + + if (!can_software_clip_entry (entry, NULL, + entry->clip_stack, &clip_bounds)) + return FALSE; + + software_clip_entry (entry, vertices, &clip_bounds); + entry_to_screen_polygon (framebuffer, entry, vertices, poly); + + *hit = _cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4); + return TRUE; + } + + return TRUE; +} + +CoglBool +_cogl_journal_try_read_pixel (CoglJournal *journal, + int x, + int y, + CoglBitmap *bitmap, + CoglBool *found_intersection) +{ + CoglContext *ctx; + CoglPixelFormat format; + int i; + + /* XXX: this number has been plucked out of thin air, but the idea + * is that if so many pixels are being read from the same un-changed + * journal than we expect that it will be more efficient to fail + * here so we end up flushing and rendering the journal so that + * further reads can directly read from the framebuffer. There will + * be a bit more lag to flush the render but if there are going to + * continue being lots of arbitrary single pixel reads they will end + * up faster in the end. */ + if (journal->fast_read_pixel_count > 50) + return FALSE; + + format = cogl_bitmap_get_format (bitmap); + + if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE && + format != COGL_PIXEL_FORMAT_RGBA_8888) + return FALSE; + + ctx = _cogl_bitmap_get_context (bitmap); + + *found_intersection = FALSE; + + /* NB: The most recently added journal entry is the last entry, and + * assuming this is a simple scene only comprised of opaque coloured + * rectangles with no special pipelines involved (e.g. enabling + * depth testing) then we can assume painter's algorithm for the + * entries and so our fast read-pixel just needs to walk backwards + * through the journal entries trying to intersect each entry with + * the given point of interest. */ + for (i = journal->entries->len - 1; i >= 0; i--) + { + CoglJournalEntry *entry = + &g_array_index (journal->entries, CoglJournalEntry, i); + uint8_t *color = (uint8_t *)&g_array_index (journal->vertices, float, + entry->array_offset); + float *vertices = (float *)color + 1; + float poly[16]; + CoglFramebuffer *framebuffer = journal->framebuffer; + uint8_t *pixel; + CoglError *ignore_error; + + entry_to_screen_polygon (framebuffer, entry, vertices, poly); + + if (!_cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4)) + continue; + + if (entry->clip_stack) + { + CoglBool hit; + + if (!try_checking_point_hits_entry_after_clipping (framebuffer, + entry, + vertices, + x, y, &hit)) + return FALSE; /* hit couldn't be determined */ + + if (!hit) + continue; + } + + *found_intersection = TRUE; + + /* If we find that the rectangle the point of interest + * intersects has any state more complex than a constant opaque + * color then we bail out. */ + if (!_cogl_pipeline_equal (ctx->opaque_color_pipeline, entry->pipeline, + (COGL_PIPELINE_STATE_ALL & + ~COGL_PIPELINE_STATE_COLOR), + COGL_PIPELINE_LAYER_STATE_ALL, + 0)) + return FALSE; + + + /* we currently only care about cases where the premultiplied or + * unpremultipled colors are equivalent... */ + if (color[3] != 0xff) + return FALSE; + + pixel = _cogl_bitmap_map (bitmap, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + &ignore_error); + if (pixel == NULL) + { + cogl_error_free (ignore_error); + return FALSE; + } + + pixel[0] = color[0]; + pixel[1] = color[1]; + pixel[2] = color[2]; + pixel[3] = color[3]; + + _cogl_bitmap_unmap (bitmap); + + goto success; + } + +success: + journal->fast_read_pixel_count++; + return TRUE; +} diff --git a/cogl/cogl/cogl-kms-display.h b/cogl/cogl/cogl-kms-display.h new file mode 100644 index 000000000..ebf6471ad --- /dev/null +++ b/cogl/cogl/cogl-kms-display.h @@ -0,0 +1,119 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_KMS_DISPLAY_H__ +#define __COGL_KMS_DISPLAY_H__ + +#include +#include + +#include + +COGL_BEGIN_DECLS + +/** + * cogl_kms_display_queue_modes_reset: + * @display: A #CoglDisplay + * + * Asks Cogl to explicitly reset the crtc output modes at the next + * #CoglOnscreen swap_buffers request. For applications that support + * VT switching they may want to re-assert the output modes when + * switching back to the applications VT since the modes are often not + * correctly restored automatically. + * + * The @display must have been either explicitly setup via + * cogl_display_setup() or implicitily setup by having created a + * context using the @display + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_kms_display_queue_modes_reset (CoglDisplay *display); + +typedef struct { + uint32_t id; + uint32_t x, y; + drmModeModeInfo mode; + + uint32_t *connectors; + uint32_t count; + + CoglBool ignore; +} CoglKmsCrtc; + +/** + * cogl_kms_display_set_layout: + * @onscreen: a #CoglDisplay + * @width: the framebuffer width + * @height: the framebuffer height + * @crtcs: the array of #CoglKmsCrtc structure with the desired CRTC layout + * + * Configures @display to use a framebuffer sized @width x @height, covering + * the CRTCS in @crtcs. + * @width and @height must be within the driver framebuffer limits, and @crtcs + * must be valid KMS API IDs. + * + * Calling this function overrides the automatic mode setting done by Cogl, + * and for this reason must be called before the first call to cogl_onscreen_swap_buffers(). + * + * If you want to restore the default behaviour, you can call this function + * with @width and @height set to -1. + * + * Stability: unstable + */ +CoglBool +cogl_kms_display_set_layout (CoglDisplay *display, + int width, + int height, + CoglKmsCrtc **crtcs, + int n_crtcs, + CoglError **error); + + +/** + * cogl_kms_display_set_ignore_crtc: + * @onscreen: a #CoglDisplay + * @id: KMS output id + * @ignore: Ignore ouput or not + * + * Tells cogl to ignore (or stop ignoring) a ctrc which means + * it never flips buffers at this crtc. + * + * Stability: unstable + */ +void +cogl_kms_display_set_ignore_crtc (CoglDisplay *display, + uint32_t id, + CoglBool ignore); +COGL_END_DECLS +#endif /* __COGL_KMS_DISPLAY_H__ */ diff --git a/cogl/cogl/cogl-kms-renderer.h b/cogl/cogl/cogl-kms-renderer.h new file mode 100644 index 000000000..4e4948e56 --- /dev/null +++ b/cogl/cogl/cogl-kms-renderer.h @@ -0,0 +1,74 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_KMS_RENDERER_H__ +#define __COGL_KMS_RENDERER_H__ + +#include +#include + +COGL_BEGIN_DECLS + +/** + * cogl_kms_renderer_set_kms_fd: + * @renderer: A #CoglRenderer + * @fd: The fd to kms to use + * + * Sets the file descriptor Cogl should use to communicate + * to the kms driver. If -1 (the default), then Cogl will + * open its own FD by trying to open "/dev/dri/card0". + * + * Since: 1.18 + * Stability: unstable + */ +void +cogl_kms_renderer_set_kms_fd (CoglRenderer *renderer, + int fd); + +/** + * cogl_kms_renderer_get_kms_fd: + * @renderer: A #CoglRenderer + * + * Queries the file descriptor Cogl is using internally for + * communicating with the kms driver. + * + * Return value: The kms file descriptor or -1 if no kms file + * desriptor has been opened by Cogl. + * Stability: unstable + */ +int +cogl_kms_renderer_get_kms_fd (CoglRenderer *renderer); + +struct gbm_device * +cogl_kms_renderer_get_gbm (CoglRenderer *renderer); +COGL_END_DECLS +#endif /* __COGL_KMS_RENDERER_H__ */ diff --git a/cogl/cogl/cogl-list.c b/cogl/cogl/cogl-list.c new file mode 100644 index 000000000..3fbc675ae --- /dev/null +++ b/cogl/cogl/cogl-list.c @@ -0,0 +1,94 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011, 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* This list implementation is based on the Wayland source code */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cogl-list.h" + +void +_cogl_list_init (CoglList *list) +{ + list->prev = list; + list->next = list; +} + +void +_cogl_list_insert (CoglList *list, CoglList *elm) +{ + elm->prev = list; + elm->next = list->next; + list->next = elm; + elm->next->prev = elm; +} + +void +_cogl_list_remove (CoglList *elm) +{ + elm->prev->next = elm->next; + elm->next->prev = elm->prev; + elm->next = NULL; + elm->prev = NULL; +} + +int +_cogl_list_length (CoglList *list) +{ + CoglList *e; + int count; + + count = 0; + e = list->next; + while (e != list) + { + e = e->next; + count++; + } + + return count; +} + +int +_cogl_list_empty (CoglList *list) +{ + return list->next == list; +} + +void +_cogl_list_insert_list (CoglList *list, + CoglList *other) +{ + if (_cogl_list_empty (other)) + return; + + other->next->prev = list; + other->prev->next = list->next; + list->next->prev = other->prev; + list->next = other->next; +} diff --git a/cogl/cogl/cogl-list.h b/cogl/cogl/cogl-list.h new file mode 100644 index 000000000..20cbec82a --- /dev/null +++ b/cogl/cogl/cogl-list.h @@ -0,0 +1,129 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * Copyright © 2012, 2013 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* This list implementation is based on the Wayland source code */ + +#ifndef COGL_LIST_H +#define COGL_LIST_H + +#include + +/** + * CoglList - linked list + * + * The list head is of "CoglList" type, and must be initialized + * using cogl_list_init(). All entries in the list must be of the same + * type. The item type must have a "CoglList" member. This + * member will be initialized by cogl_list_insert(). There is no need to + * call cogl_list_init() on the individual item. To query if the list is + * empty in O(1), use cogl_list_empty(). + * + * Let's call the list reference "CoglList foo_list", the item type as + * "item_t", and the item member as "CoglList link". The following code + * + * The following code will initialize a list: + * + * cogl_list_init (foo_list); + * cogl_list_insert (foo_list, item1); Pushes item1 at the head + * cogl_list_insert (foo_list, item2); Pushes item2 at the head + * cogl_list_insert (item2, item3); Pushes item3 after item2 + * + * The list now looks like [item2, item3, item1] + * + * Will iterate the list in ascending order: + * + * item_t *item; + * cogl_list_for_each(item, foo_list, link) { + * Do_something_with_item(item); + * } + */ + +typedef struct _CoglList CoglList; + +struct _CoglList +{ + CoglList *prev; + CoglList *next; +}; + +void +_cogl_list_init (CoglList *list); + +void +_cogl_list_insert (CoglList *list, + CoglList *elm); + +void +_cogl_list_remove (CoglList *elm); + +int +_cogl_list_length (CoglList *list); + +int +_cogl_list_empty (CoglList *list); + +void +_cogl_list_insert_list (CoglList *list, + CoglList *other); + +/* This assigns to iterator first so that taking a reference to it + * later in the second step won't be an undefined operation. It + * assigns the value of list_node rather than 0 so that it is possible + * have list_node be based on the previous value of iterator. In that + * respect iterator is just used as a convenient temporary variable. + * The compiler optimises all of this down to a single subtraction by + * a constant */ +#define _cogl_list_set_iterator(list_node, iterator, member) \ + ((iterator) = (void *) (list_node), \ + (iterator) = (void *) ((char *) (iterator) - \ + (((char *) &(iterator)->member) - \ + (char *) (iterator)))) + +#define _cogl_container_of(ptr, type, member) \ + (type *) ((char *) (ptr) - offsetof (type, member)) + +#define _cogl_list_for_each(pos, head, member) \ + for (_cogl_list_set_iterator ((head)->next, pos, member); \ + &pos->member != (head); \ + _cogl_list_set_iterator (pos->member.next, pos, member)) + +#define _cogl_list_for_each_safe(pos, tmp, head, member) \ + for (_cogl_list_set_iterator ((head)->next, pos, member), \ + _cogl_list_set_iterator ((pos)->member.next, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + _cogl_list_set_iterator (pos->member.next, tmp, member)) + +#define _cogl_list_for_each_reverse(pos, head, member) \ + for (_cogl_list_set_iterator ((head)->prev, pos, member); \ + &pos->member != (head); \ + _cogl_list_set_iterator (pos->member.prev, pos, member)) + +#define _cogl_list_for_each_reverse_safe(pos, tmp, head, member) \ + for (_cogl_list_set_iterator ((head)->prev, pos, member), \ + _cogl_list_set_iterator ((pos)->member.prev, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + _cogl_list_set_iterator (pos->member.prev, tmp, member)) + +#endif /* COGL_LIST_H */ diff --git a/cogl/cogl/cogl-macros.h b/cogl/cogl/cogl-macros.h new file mode 100644 index 000000000..96c06591f --- /dev/null +++ b/cogl/cogl/cogl-macros.h @@ -0,0 +1,287 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_MACROS_H__ +#define __COGL_MACROS_H__ + +#include + +/* These macros are used to mark deprecated functions, and thus have + * to be exposed in a public header. + * + * They are only intended for internal use and should not be used by + * other projects. + */ +#if defined(COGL_DISABLE_DEPRECATION_WARNINGS) || defined(COGL_COMPILATION) + +#define COGL_DEPRECATED +#define COGL_DEPRECATED_FOR(f) +#define COGL_UNAVAILABLE(maj,min) + +#else /* COGL_DISABLE_DEPRECATION_WARNINGS */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#define COGL_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +#define COGL_DEPRECATED __declspec(deprecated) +#else +#define COGL_DEPRECATED +#endif + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define COGL_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define COGL_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) +#else +#define COGL_DEPRECATED_FOR(f) G_DEPRECATED +#endif + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define COGL_UNAVAILABLE(maj,min) __attribute__((deprecated("Not available before " #maj "." #min))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define COGL_UNAVAILABLE(maj,min) __declspec(deprecated("is not available before " #maj "." #min)) +#else +#define COGL_UNAVAILABLE(maj,min) +#endif + +#endif /* COGL_DISABLE_DEPRECATION_WARNINGS */ + +/** + * COGL_VERSION_MIN_REQUIRED: + * + * A macro that should be defined by the user prior to including the + * cogl.h header. + * + * The definition should be one of the predefined Cogl version macros, + * such as: %COGL_VERSION_1_8, %COGL_VERSION_1_10, ... + * + * This macro defines the lower bound for the Cogl API to be used. + * + * If a function has been deprecated in a newer version of Cogl, it + * is possible to use this symbol to avoid the compiler warnings without + * disabling warnings for every deprecated function. + * + * Since: 1.16 + */ +#ifndef COGL_VERSION_MIN_REQUIRED +# define COGL_VERSION_MIN_REQUIRED (COGL_VERSION_CURRENT_STABLE) +#endif + +/** + * COGL_VERSION_MAX_ALLOWED: + * + * A macro that should be define by the user prior to including the + * cogl.h header. + * + * The definition should be one of the predefined Cogl version macros, + * such as: %COGL_VERSION_1_0, %COGL_VERSION_1_2, ... + * + * This macro defines the upper bound for the Cogl API to be used. + * + * If a function has been introduced in a newer version of Cogl, it + * is possible to use this symbol to get compiler warnings when trying + * to use that function. + * + * Since: 1.16 + */ +#ifndef COGL_VERSION_MAX_ALLOWED +# if COGL_VERSION_MIN_REQUIRED > COGL_VERSION_PREVIOUS_STABLE +# define COGL_VERSION_MAX_ALLOWED COGL_VERSION_MIN_REQUIRED +# else +# define COGL_VERSION_MAX_ALLOWED COGL_VERSION_CURRENT_STABLE +# endif +#endif + +/* sanity checks */ +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_MIN_REQUIRED +# error "COGL_VERSION_MAX_ALLOWED must be >= COGL_VERSION_MIN_REQUIRED" +#endif +#if COGL_VERSION_MIN_REQUIRED < COGL_VERSION_1_0 +# error "COGL_VERSION_MIN_REQUIRED must be >= COGL_VERSION_1_0" +#endif + +/* XXX: Every new stable minor release should add a set of macros here */ +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_0 +# define COGL_DEPRECATED_IN_1_0 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_0_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_0 +# define COGL_DEPRECATED_IN_1_0_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_0 +# define COGL_AVAILABLE_IN_1_0 COGL_UNAVAILABLE(1, 0) +#else +# define COGL_AVAILABLE_IN_1_0 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_2 +# define COGL_DEPRECATED_IN_1_2 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_2_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_2 +# define COGL_DEPRECATED_IN_1_2_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_2 +# define COGL_AVAILABLE_IN_1_2 COGL_UNAVAILABLE(1, 2) +#else +# define COGL_AVAILABLE_IN_1_2 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_4 +# define COGL_DEPRECATED_IN_1_4 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_4_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_4 +# define COGL_DEPRECATED_IN_1_4_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_4 +# define COGL_AVAILABLE_IN_1_4 COGL_UNAVAILABLE(1, 4) +#else +# define COGL_AVAILABLE_IN_1_4 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_6 +# define COGL_DEPRECATED_IN_1_6 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_6_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_6 +# define COGL_DEPRECATED_IN_1_6_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_6 +# define COGL_AVAILABLE_IN_1_6 COGL_UNAVAILABLE(1, 6) +#else +# define COGL_AVAILABLE_IN_1_6 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_8 +# define COGL_DEPRECATED_IN_1_8 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_8_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_8 +# define COGL_DEPRECATED_IN_1_8_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_8 +# define COGL_AVAILABLE_IN_1_8 COGL_UNAVAILABLE(1, 8) +#else +# define COGL_AVAILABLE_IN_1_8 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_10 +# define COGL_DEPRECATED_IN_1_10 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_10_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_10 +# define COGL_DEPRECATED_IN_1_10_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_10 +# define COGL_AVAILABLE_IN_1_10 COGL_UNAVAILABLE(1, 10) +#else +# define COGL_AVAILABLE_IN_1_10 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_12 +# define COGL_DEPRECATED_IN_1_12 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_12_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_12 +# define COGL_DEPRECATED_IN_1_12_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_12 +# define COGL_AVAILABLE_IN_1_12 COGL_UNAVAILABLE(1, 12) +#else +# define COGL_AVAILABLE_IN_1_12 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_14 +# define COGL_DEPRECATED_IN_1_14 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_14_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_14 +# define COGL_DEPRECATED_IN_1_14_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_14 +# define COGL_AVAILABLE_IN_1_14 COGL_UNAVAILABLE(1, 14) +#else +# define COGL_AVAILABLE_IN_1_14 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_16 +# define COGL_DEPRECATED_IN_1_16 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_16_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_16 +# define COGL_DEPRECATED_IN_1_16_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_16 +# define COGL_AVAILABLE_IN_1_16 COGL_UNAVAILABLE(1, 16) +#else +# define COGL_AVAILABLE_IN_1_16 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_18 +# define COGL_DEPRECATED_IN_1_18 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_18_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_18 +# define COGL_DEPRECATED_IN_1_18_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_18 +# define COGL_AVAILABLE_IN_1_18 COGL_UNAVAILABLE(1, 18) +#else +# define COGL_AVAILABLE_IN_1_18 +#endif + +#if COGL_VERSION_MIN_REQUIRED >= COGL_VERSION_1_20 +# define COGL_DEPRECATED_IN_1_20 COGL_DEPRECATED +# define COGL_DEPRECATED_IN_1_20_FOR(f) COGL_DEPRECATED_FOR(f) +#else +# define COGL_DEPRECATED_IN_1_20 +# define COGL_DEPRECATED_IN_1_20_FOR(f) +#endif + +#if COGL_VERSION_MAX_ALLOWED < COGL_VERSION_1_20 +# define COGL_AVAILABLE_IN_1_20 COGL_UNAVAILABLE(1, 18) +#else +# define COGL_AVAILABLE_IN_1_20 +#endif + +#endif /* __COGL_MACROS_H__ */ diff --git a/cogl/cogl/cogl-magazine-private.h b/cogl/cogl/cogl-magazine-private.h new file mode 100644 index 000000000..091801be1 --- /dev/null +++ b/cogl/cogl/cogl-magazine-private.h @@ -0,0 +1,81 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_MAGAZINE_PRIVATE_H__ +#define __COGL_MAGAZINE_PRIVATE_H__ + +#include + +#include "cogl-memory-stack-private.h" + +typedef struct _CoglMagazineChunk CoglMagazineChunk; + +struct _CoglMagazineChunk +{ + CoglMagazineChunk *next; +}; + +typedef struct _CoglMagazine +{ + size_t chunk_size; + + CoglMemoryStack *stack; + CoglMagazineChunk *head; +} CoglMagazine; + +CoglMagazine * +_cogl_magazine_new (size_t chunk_size, int initial_chunk_count); + +static inline void * +_cogl_magazine_chunk_alloc (CoglMagazine *magazine) +{ + if (G_LIKELY (magazine->head)) + { + CoglMagazineChunk *chunk = magazine->head; + magazine->head = chunk->next; + return chunk; + } + else + return _cogl_memory_stack_alloc (magazine->stack, magazine->chunk_size); +} + +static inline void +_cogl_magazine_chunk_free (CoglMagazine *magazine, void *data) +{ + CoglMagazineChunk *chunk = data; + + chunk->next = magazine->head; + magazine->head = chunk; +} + +void +_cogl_magazine_free (CoglMagazine *magazine); + +#endif /* __COGL_MAGAZINE_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-magazine.c b/cogl/cogl/cogl-magazine.c new file mode 100644 index 000000000..56177515e --- /dev/null +++ b/cogl/cogl/cogl-magazine.c @@ -0,0 +1,84 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * CoglMagazine provides a really light weight allocator for chunks + * of memory with a pre-determined size. + * + * This allocator builds on CoglMemoryStack for making all initial + * allocations but never frees memory back to the stack. + * + * Memory chunks that haven't been allocated yet are stored in a + * singly linked, fifo, list. + * + * Allocating from a magazine is simply a question of popping an entry + * from the head of the fifo list. If no entries are available then + * instead allocate from the memory stack instead. + * + * When an entry is freed, it is put back into the fifo list for + * re-use. + * + * No attempt is ever made to shrink the amount of memory associated + * with a CoglMagazine. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-memory-stack-private.h" +#include "cogl-magazine-private.h" +#include + +#define ROUND_UP_8(X) ((X + (8 - 1)) & ~(8 - 1)) + +CoglMagazine * +_cogl_magazine_new (size_t chunk_size, int initial_chunk_count) +{ + CoglMagazine *magazine = g_new0 (CoglMagazine, 1); + + chunk_size = MAX (chunk_size, sizeof (CoglMagazineChunk)); + chunk_size = ROUND_UP_8 (chunk_size); + + magazine->chunk_size = chunk_size; + magazine->stack = _cogl_memory_stack_new (chunk_size * initial_chunk_count); + magazine->head = NULL; + + return magazine; +} + +void +_cogl_magazine_free (CoglMagazine *magazine) +{ + _cogl_memory_stack_free (magazine->stack); + g_free (magazine); +} diff --git a/cogl/cogl/cogl-matrix-private.h b/cogl/cogl/cogl-matrix-private.h new file mode 100644 index 000000000..d9fc98eab --- /dev/null +++ b/cogl/cogl/cogl-matrix-private.h @@ -0,0 +1,58 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_MATRIX_PRIVATE_H +#define __COGL_MATRIX_PRIVATE_H + +#include + +COGL_BEGIN_DECLS + +#define _COGL_MATRIX_DEBUG_PRINT(MATRIX) \ + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_MATRICES))) \ + { \ + g_print ("%s:\n", G_STRFUNC); \ + cogl_debug_matrix_print (MATRIX); \ + } + +void +_cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix); + +void +_cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, + const CoglMatrix *src); + +COGL_END_DECLS + +#endif /* __COGL_MATRIX_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-matrix-stack-private.h b/cogl/cogl/cogl-matrix-stack-private.h new file mode 100644 index 000000000..0d5caf8b9 --- /dev/null +++ b/cogl/cogl/cogl-matrix-stack-private.h @@ -0,0 +1,200 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Havoc Pennington for litl + * Robert Bragg + */ + +#ifndef _COGL_MATRIX_STACK_PRIVATE_H_ +#define _COGL_MATRIX_STACK_PRIVATE_H_ + +#include "cogl-object-private.h" +#include "cogl-matrix-stack.h" +#include "cogl-context.h" +#include "cogl-framebuffer.h" + +typedef enum _CoglMatrixOp +{ + COGL_MATRIX_OP_LOAD_IDENTITY, + COGL_MATRIX_OP_TRANSLATE, + COGL_MATRIX_OP_ROTATE, + COGL_MATRIX_OP_ROTATE_QUATERNION, + COGL_MATRIX_OP_ROTATE_EULER, + COGL_MATRIX_OP_SCALE, + COGL_MATRIX_OP_MULTIPLY, + COGL_MATRIX_OP_LOAD, + COGL_MATRIX_OP_SAVE, +} CoglMatrixOp; + +struct _CoglMatrixEntry +{ + CoglMatrixEntry *parent; + CoglMatrixOp op; + unsigned int ref_count; + +#ifdef COGL_DEBUG_ENABLED + /* used for performance tracing */ + int composite_gets; +#endif +}; + +typedef struct _CoglMatrixEntryTranslate +{ + CoglMatrixEntry _parent_data; + + float x; + float y; + float z; + +} CoglMatrixEntryTranslate; + +typedef struct _CoglMatrixEntryRotate +{ + CoglMatrixEntry _parent_data; + + float angle; + float x; + float y; + float z; + +} CoglMatrixEntryRotate; + +typedef struct _CoglMatrixEntryRotateEuler +{ + CoglMatrixEntry _parent_data; + + /* This doesn't store an actual CoglEuler in order to avoid the + * padding */ + float heading; + float pitch; + float roll; +} CoglMatrixEntryRotateEuler; + +typedef struct _CoglMatrixEntryRotateQuaternion +{ + CoglMatrixEntry _parent_data; + + /* This doesn't store an actual CoglQuaternion in order to avoid the + * padding */ + float values[4]; +} CoglMatrixEntryRotateQuaternion; + +typedef struct _CoglMatrixEntryScale +{ + CoglMatrixEntry _parent_data; + + float x; + float y; + float z; + +} CoglMatrixEntryScale; + +typedef struct _CoglMatrixEntryMultiply +{ + CoglMatrixEntry _parent_data; + + CoglMatrix *matrix; + +} CoglMatrixEntryMultiply; + +typedef struct _CoglMatrixEntryLoad +{ + CoglMatrixEntry _parent_data; + + CoglMatrix *matrix; + +} CoglMatrixEntryLoad; + +typedef struct _CoglMatrixEntrySave +{ + CoglMatrixEntry _parent_data; + + CoglMatrix *cache; + CoglBool cache_valid; + +} CoglMatrixEntrySave; + +typedef union _CoglMatrixEntryFull +{ + CoglMatrixEntry any; + CoglMatrixEntryTranslate translate; + CoglMatrixEntryRotate rotate; + CoglMatrixEntryRotateEuler rotate_euler; + CoglMatrixEntryRotateQuaternion rotate_quaternion; + CoglMatrixEntryScale scale; + CoglMatrixEntryMultiply multiply; + CoglMatrixEntryLoad load; + CoglMatrixEntrySave save; +} CoglMatrixEntryFull; + +struct _CoglMatrixStack +{ + CoglObject _parent; + + CoglContext *context; + + CoglMatrixEntry *last_entry; +}; + +typedef struct _CoglMatrixEntryCache +{ + CoglMatrixEntry *entry; + CoglBool flushed_identity; + CoglBool flipped; +} CoglMatrixEntryCache; + +void +_cogl_matrix_entry_identity_init (CoglMatrixEntry *entry); + +typedef enum { + COGL_MATRIX_MODELVIEW, + COGL_MATRIX_PROJECTION, + COGL_MATRIX_TEXTURE +} CoglMatrixMode; + +void +_cogl_matrix_entry_flush_to_gl_builtins (CoglContext *ctx, + CoglMatrixEntry *entry, + CoglMatrixMode mode, + CoglFramebuffer *framebuffer, + CoglBool disable_flip); + +void +_cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache); + +CoglBool +_cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache, + CoglMatrixEntry *entry, + CoglBool flip); + +void +_cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache); + +#endif /* _COGL_MATRIX_STACK_PRIVATE_H_ */ diff --git a/cogl/cogl/cogl-matrix-stack.c b/cogl/cogl/cogl-matrix-stack.c new file mode 100644 index 000000000..400855f60 --- /dev/null +++ b/cogl/cogl/cogl-matrix-stack.c @@ -0,0 +1,1211 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Havoc Pennington for litl + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-matrix-stack.h" +#include "cogl-framebuffer-private.h" +#include "cogl-object-private.h" +#include "cogl-offscreen.h" +#include "cogl-matrix-private.h" +#include "cogl-magazine-private.h" +#include "cogl-gtype-private.h" + +static void _cogl_matrix_stack_free (CoglMatrixStack *stack); + +COGL_OBJECT_DEFINE (MatrixStack, matrix_stack); +COGL_GTYPE_DEFINE_CLASS (MatrixStack, matrix_stack); +COGL_GTYPE_DEFINE_BOXED (MatrixEntry, matrix_entry, + cogl_matrix_entry_ref, + cogl_matrix_entry_unref); + +static CoglMagazine *cogl_matrix_stack_magazine; +static CoglMagazine *cogl_matrix_stack_matrices_magazine; + +/* XXX: Note: this leaves entry->parent uninitialized! */ +static CoglMatrixEntry * +_cogl_matrix_entry_new (CoglMatrixOp operation) +{ + CoglMatrixEntry *entry = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_magazine); + + entry->ref_count = 1; + entry->op = operation; + +#ifdef COGL_DEBUG_ENABLED + entry->composite_gets = 0; +#endif + + return entry; +} + +static void * +_cogl_matrix_stack_push_entry (CoglMatrixStack *stack, + CoglMatrixEntry *entry) +{ + /* NB: The initial reference of the entry is transferred to the + * stack here. + * + * The stack only maintains a reference to the top of the stack (the + * last entry pushed) and each entry in-turn maintains a reference + * to its parent. + * + * We don't need to take a reference to the parent from the entry + * here because the we are stealing the reference that was held by + * the stack while that parent was previously the top of the stack. + */ + entry->parent = stack->last_entry; + stack->last_entry = entry; + + return entry; +} + +static void * +_cogl_matrix_stack_push_operation (CoglMatrixStack *stack, + CoglMatrixOp operation) +{ + CoglMatrixEntry *entry = _cogl_matrix_entry_new (operation); + + _cogl_matrix_stack_push_entry (stack, entry); + + return entry; +} + +static void * +_cogl_matrix_stack_push_replacement_entry (CoglMatrixStack *stack, + CoglMatrixOp operation) +{ + CoglMatrixEntry *old_top = stack->last_entry; + CoglMatrixEntry *new_top; + + /* This would only be called for operations that completely replace + * the matrix. In that case we don't need to keep a reference to + * anything up to the last save entry. This optimisation could be + * important for applications that aren't using the stack but + * instead just perform their own matrix manipulations and load a + * new stack every frame. If this optimisation isn't done then the + * stack would just grow endlessly. See the comments + * cogl_matrix_stack_pop for a description of how popping works. */ + for (new_top = old_top; + new_top->op != COGL_MATRIX_OP_SAVE && new_top->parent; + new_top = new_top->parent) + ; + + cogl_matrix_entry_ref (new_top); + cogl_matrix_entry_unref (old_top); + stack->last_entry = new_top; + + return _cogl_matrix_stack_push_operation (stack, operation); +} + +void +_cogl_matrix_entry_identity_init (CoglMatrixEntry *entry) +{ + entry->ref_count = 1; + entry->op = COGL_MATRIX_OP_LOAD_IDENTITY; + entry->parent = NULL; +#ifdef COGL_DEBUG_ENABLED + entry->composite_gets = 0; +#endif +} + +void +cogl_matrix_stack_load_identity (CoglMatrixStack *stack) +{ + _cogl_matrix_stack_push_replacement_entry (stack, + COGL_MATRIX_OP_LOAD_IDENTITY); +} + +void +cogl_matrix_stack_translate (CoglMatrixStack *stack, + float x, + float y, + float z) +{ + CoglMatrixEntryTranslate *entry; + + entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_TRANSLATE); + + entry->x = x; + entry->y = y; + entry->z = z; +} + +void +cogl_matrix_stack_rotate (CoglMatrixStack *stack, + float angle, + float x, + float y, + float z) +{ + CoglMatrixEntryRotate *entry; + + entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_ROTATE); + + entry->angle = angle; + entry->x = x; + entry->y = y; + entry->z = z; +} + +void +cogl_matrix_stack_rotate_quaternion (CoglMatrixStack *stack, + const CoglQuaternion *quaternion) +{ + CoglMatrixEntryRotateQuaternion *entry; + + entry = _cogl_matrix_stack_push_operation (stack, + COGL_MATRIX_OP_ROTATE_QUATERNION); + + entry->values[0] = quaternion->w; + entry->values[1] = quaternion->x; + entry->values[2] = quaternion->y; + entry->values[3] = quaternion->z; +} + +void +cogl_matrix_stack_rotate_euler (CoglMatrixStack *stack, + const CoglEuler *euler) +{ + CoglMatrixEntryRotateEuler *entry; + + entry = _cogl_matrix_stack_push_operation (stack, + COGL_MATRIX_OP_ROTATE_EULER); + + entry->heading = euler->heading; + entry->pitch = euler->pitch; + entry->roll = euler->roll; +} + +void +cogl_matrix_stack_scale (CoglMatrixStack *stack, + float x, + float y, + float z) +{ + CoglMatrixEntryScale *entry; + + entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_SCALE); + + entry->x = x; + entry->y = y; + entry->z = z; +} + +void +cogl_matrix_stack_multiply (CoglMatrixStack *stack, + const CoglMatrix *matrix) +{ + CoglMatrixEntryMultiply *entry; + + entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_MULTIPLY); + + entry->matrix = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_from_array (entry->matrix, (float *)matrix); +} + +void +cogl_matrix_stack_set (CoglMatrixStack *stack, + const CoglMatrix *matrix) +{ + CoglMatrixEntryLoad *entry; + + entry = + _cogl_matrix_stack_push_replacement_entry (stack, + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_from_array (entry->matrix, (float *)matrix); +} + +void +cogl_matrix_stack_frustum (CoglMatrixStack *stack, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far) +{ + CoglMatrixEntryLoad *entry; + + entry = + _cogl_matrix_stack_push_replacement_entry (stack, + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_frustum (entry->matrix, + left, right, bottom, top, + z_near, z_far); +} + +void +cogl_matrix_stack_perspective (CoglMatrixStack *stack, + float fov_y, + float aspect, + float z_near, + float z_far) +{ + CoglMatrixEntryLoad *entry; + + entry = + _cogl_matrix_stack_push_replacement_entry (stack, + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_perspective (entry->matrix, + fov_y, aspect, z_near, z_far); +} + +void +cogl_matrix_stack_orthographic (CoglMatrixStack *stack, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far) +{ + CoglMatrixEntryLoad *entry; + + entry = + _cogl_matrix_stack_push_replacement_entry (stack, + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_orthographic (entry->matrix, + x_1, y_1, x_2, y_2, near, far); +} + +void +cogl_matrix_stack_push (CoglMatrixStack *stack) +{ + CoglMatrixEntrySave *entry; + + entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_SAVE); + + entry->cache_valid = FALSE; +} + +CoglMatrixEntry * +cogl_matrix_entry_ref (CoglMatrixEntry *entry) +{ + /* A NULL pointer is considered a valid stack so we should accept + that as an argument */ + if (entry) + entry->ref_count++; + + return entry; +} + +void +cogl_matrix_entry_unref (CoglMatrixEntry *entry) +{ + CoglMatrixEntry *parent; + + for (; entry && --entry->ref_count <= 0; entry = parent) + { + parent = entry->parent; + + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_TRANSLATE: + case COGL_MATRIX_OP_ROTATE: + case COGL_MATRIX_OP_ROTATE_QUATERNION: + case COGL_MATRIX_OP_ROTATE_EULER: + case COGL_MATRIX_OP_SCALE: + break; + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *multiply = + (CoglMatrixEntryMultiply *)entry; + _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, + multiply->matrix); + break; + } + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, + load->matrix); + break; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; + if (save->cache_valid) + _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, + save->cache); + break; + } + } + + _cogl_magazine_chunk_free (cogl_matrix_stack_magazine, entry); + } +} + +void +cogl_matrix_stack_pop (CoglMatrixStack *stack) +{ + CoglMatrixEntry *old_top; + CoglMatrixEntry *new_top; + + _COGL_RETURN_IF_FAIL (stack != NULL); + + old_top = stack->last_entry; + _COGL_RETURN_IF_FAIL (old_top != NULL); + + /* To pop we are moving the top of the stack to the old top's parent + * node. The stack always needs to have a reference to the top entry + * so we must take a reference to the new top. The stack would have + * previously had a reference to the old top so we need to decrease + * the ref count on that. We need to ref the new head first in case + * this stack was the only thing referencing the old top. In that + * case the call to cogl_matrix_entry_unref will unref the parent. + */ + + /* Find the last save operation and remove it */ + + /* XXX: it would be an error to pop to the very beginning of the + * stack so we don't need to check for NULL pointer dereferencing. */ + for (new_top = old_top; + new_top->op != COGL_MATRIX_OP_SAVE; + new_top = new_top->parent) + ; + + new_top = new_top->parent; + cogl_matrix_entry_ref (new_top); + + cogl_matrix_entry_unref (old_top); + + stack->last_entry = new_top; +} + +CoglBool +cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse) +{ + CoglMatrix matrix; + CoglMatrix *internal = cogl_matrix_stack_get (stack, &matrix); + + if (internal) + return cogl_matrix_get_inverse (internal, inverse); + else + return cogl_matrix_get_inverse (&matrix, inverse); +} + +/* In addition to writing the stack matrix into the give @matrix + * argument this function *may* sometimes also return a pointer + * to a matrix too so if we are querying the inverse matrix we + * should query from the return matrix so that the result can + * be cached within the stack. */ +CoglMatrix * +cogl_matrix_entry_get (CoglMatrixEntry *entry, + CoglMatrix *matrix) +{ + int depth; + CoglMatrixEntry *current; + CoglMatrixEntry **children; + int i; + + for (depth = 0, current = entry; + current; + current = current->parent, depth++) + { + switch (current->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + cogl_matrix_init_identity (matrix); + goto initialized; + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current; + _cogl_matrix_init_from_matrix_without_inverse (matrix, + load->matrix); + goto initialized; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current; + if (!save->cache_valid) + { + CoglMagazine *matrices_magazine = + cogl_matrix_stack_matrices_magazine; + save->cache = _cogl_magazine_chunk_alloc (matrices_magazine); + cogl_matrix_entry_get (current->parent, save->cache); + save->cache_valid = TRUE; + } + _cogl_matrix_init_from_matrix_without_inverse (matrix, save->cache); + goto initialized; + } + default: + continue; + } + } + +initialized: + + if (depth == 0) + { + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_TRANSLATE: + case COGL_MATRIX_OP_ROTATE: + case COGL_MATRIX_OP_ROTATE_QUATERNION: + case COGL_MATRIX_OP_ROTATE_EULER: + case COGL_MATRIX_OP_SCALE: + case COGL_MATRIX_OP_MULTIPLY: + return NULL; + + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + return load->matrix; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; + return save->cache; + } + } + g_warn_if_reached (); + return NULL; + } + +#ifdef COGL_ENABLE_DEBUG + if (!current) + { + g_warning ("Inconsistent matrix stack"); + return NULL; + } + + entry->composite_gets++; +#endif + + children = g_alloca (sizeof (CoglMatrixEntry) * depth); + + /* We need walk the list of entries from the init/load/save entry + * back towards the leaf node but the nodes don't link to their + * children so we need to re-walk them here to add to a separate + * array. */ + for (i = depth - 1, current = entry; + i >= 0 && current; + i--, current = current->parent) + { + children[i] = current; + } + +#ifdef COGL_ENABLE_DEBUG + if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) && + entry->composite_gets >= 2) + { + COGL_NOTE (PERFORMANCE, + "Re-composing a matrix stack entry multiple times"); + } +#endif + + for (i = 0; i < depth; i++) + { + switch (children[i]->op) + { + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate = + (CoglMatrixEntryTranslate *)children[i]; + cogl_matrix_translate (matrix, + translate->x, + translate->y, + translate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate= + (CoglMatrixEntryRotate *)children[i]; + cogl_matrix_rotate (matrix, + rotate->angle, + rotate->x, + rotate->y, + rotate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE_EULER: + { + CoglMatrixEntryRotateEuler *rotate = + (CoglMatrixEntryRotateEuler *)children[i]; + CoglEuler euler; + cogl_euler_init (&euler, + rotate->heading, + rotate->pitch, + rotate->roll); + cogl_matrix_rotate_euler (matrix, + &euler); + continue; + } + case COGL_MATRIX_OP_ROTATE_QUATERNION: + { + CoglMatrixEntryRotateQuaternion *rotate = + (CoglMatrixEntryRotateQuaternion *)children[i]; + CoglQuaternion quaternion; + cogl_quaternion_init_from_array (&quaternion, rotate->values); + cogl_matrix_rotate_quaternion (matrix, &quaternion); + continue; + } + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale = + (CoglMatrixEntryScale *)children[i]; + cogl_matrix_scale (matrix, + scale->x, + scale->y, + scale->z); + continue; + } + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *multiply = + (CoglMatrixEntryMultiply *)children[i]; + cogl_matrix_multiply (matrix, matrix, multiply->matrix); + continue; + } + + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_LOAD: + case COGL_MATRIX_OP_SAVE: + g_warn_if_reached (); + continue; + } + } + + return NULL; +} + +CoglMatrixEntry * +cogl_matrix_stack_get_entry (CoglMatrixStack *stack) +{ + return stack->last_entry; +} + +/* In addition to writing the stack matrix into the give @matrix + * argument this function *may* sometimes also return a pointer + * to a matrix too so if we are querying the inverse matrix we + * should query from the return matrix so that the result can + * be cached within the stack. */ +CoglMatrix * +cogl_matrix_stack_get (CoglMatrixStack *stack, + CoglMatrix *matrix) +{ + return cogl_matrix_entry_get (stack->last_entry, matrix); +} + +static void +_cogl_matrix_stack_free (CoglMatrixStack *stack) +{ + cogl_matrix_entry_unref (stack->last_entry); + g_slice_free (CoglMatrixStack, stack); +} + +CoglMatrixStack * +cogl_matrix_stack_new (CoglContext *ctx) +{ + CoglMatrixStack *stack = g_slice_new (CoglMatrixStack); + + if (G_UNLIKELY (cogl_matrix_stack_magazine == NULL)) + { + cogl_matrix_stack_magazine = + _cogl_magazine_new (sizeof (CoglMatrixEntryFull), 20); + cogl_matrix_stack_matrices_magazine = + _cogl_magazine_new (sizeof (CoglMatrix), 20); + } + + stack->context = ctx; + stack->last_entry = NULL; + + cogl_matrix_entry_ref (&ctx->identity_entry); + _cogl_matrix_stack_push_entry (stack, &ctx->identity_entry); + + return _cogl_matrix_stack_object_new (stack); +} + +static CoglMatrixEntry * +_cogl_matrix_entry_skip_saves (CoglMatrixEntry *entry) +{ + /* We currently assume that every stack starts with an + * _OP_LOAD_IDENTITY so we don't need to worry about + * NULL pointer dereferencing here. */ + while (entry->op == COGL_MATRIX_OP_SAVE) + entry = entry->parent; + + return entry; +} + +CoglBool +cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1, + float *x, + float *y, + float *z) +{ + GSList *head0 = NULL; + GSList *head1 = NULL; + CoglMatrixEntry *node0; + CoglMatrixEntry *node1; + int len0 = 0; + int len1 = 0; + int count; + GSList *common_ancestor0; + GSList *common_ancestor1; + + /* Algorithm: + * + * 1) Ignoring _OP_SAVE entries walk the ancestors of each entry to + * the root node or any non-translation node, adding a pointer to + * each ancestor node to two linked lists. + * + * 2) Compare the lists to find the nodes where they start to + * differ marking the common_ancestor node for each list. + * + * 3) For the list corresponding to entry0, start iterating after + * the common ancestor applying the negative of all translations + * to x, y and z. + * + * 4) For the list corresponding to entry1, start iterating after + * the common ancestor applying the positive of all translations + * to x, y and z. + * + * If we come across any non-translation operations during 3) or 4) + * then bail out returning FALSE. + */ + + for (node0 = entry0; node0; node0 = node0->parent) + { + GSList *link; + + if (node0->op == COGL_MATRIX_OP_SAVE) + continue; + + link = alloca (sizeof (GSList)); + link->next = head0; + link->data = node0; + head0 = link; + len0++; + + if (node0->op != COGL_MATRIX_OP_TRANSLATE) + break; + } + for (node1 = entry1; node1; node1 = node1->parent) + { + GSList *link; + + if (node1->op == COGL_MATRIX_OP_SAVE) + continue; + + link = alloca (sizeof (GSList)); + link->next = head1; + link->data = node1; + head1 = link; + len1++; + + if (node1->op != COGL_MATRIX_OP_TRANSLATE) + break; + } + + if (head0->data != head1->data) + return FALSE; + + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + count = MIN (len0, len1) - 1; + while (count--) + { + if (head0->data != head1->data) + break; + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + } + + *x = 0; + *y = 0; + *z = 0; + + for (head0 = common_ancestor0->next; head0; head0 = head0->next) + { + CoglMatrixEntryTranslate *translate; + + node0 = head0->data; + + if (node0->op != COGL_MATRIX_OP_TRANSLATE) + return FALSE; + + translate = (CoglMatrixEntryTranslate *)node0; + + *x = *x - translate->x; + *y = *y - translate->y; + *z = *z - translate->z; + } + for (head1 = common_ancestor1->next; head1; head1 = head1->next) + { + CoglMatrixEntryTranslate *translate; + + node1 = head1->data; + + if (node1->op != COGL_MATRIX_OP_TRANSLATE) + return FALSE; + + translate = (CoglMatrixEntryTranslate *)node1; + + *x = *x + translate->x; + *y = *y + translate->y; + *z = *z + translate->z; + } + + return TRUE; +} + +CoglBool +cogl_matrix_entry_is_identity (CoglMatrixEntry *entry) +{ + return entry ? entry->op == COGL_MATRIX_OP_LOAD_IDENTITY : FALSE; +} + +static void +_cogl_matrix_flush_to_gl_builtin (CoglContext *ctx, + CoglBool is_identity, + CoglMatrix *matrix, + CoglMatrixMode mode) +{ + g_assert (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)); + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + if (ctx->flushed_matrix_mode != mode) + { + GLenum gl_mode = 0; + + switch (mode) + { + case COGL_MATRIX_MODELVIEW: + gl_mode = GL_MODELVIEW; + break; + + case COGL_MATRIX_PROJECTION: + gl_mode = GL_PROJECTION; + break; + + case COGL_MATRIX_TEXTURE: + gl_mode = GL_TEXTURE; + break; + } + + GE (ctx, glMatrixMode (gl_mode)); + ctx->flushed_matrix_mode = mode; + } + + if (is_identity) + GE (ctx, glLoadIdentity ()); + else + GE (ctx, glLoadMatrixf (cogl_matrix_get_array (matrix))); +#endif +} + +void +_cogl_matrix_entry_flush_to_gl_builtins (CoglContext *ctx, + CoglMatrixEntry *entry, + CoglMatrixMode mode, + CoglFramebuffer *framebuffer, + CoglBool disable_flip) +{ + g_assert (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)); + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + { + CoglBool needs_flip; + CoglMatrixEntryCache *cache; + + if (mode == COGL_MATRIX_PROJECTION) + { + /* Because Cogl defines texture coordinates to have a top left + * origin and because offscreen framebuffers may be used for + * rendering to textures we always render upside down to + * offscreen buffers. Also for some backends we need to render + * onscreen buffers upside-down too. + */ + if (disable_flip) + needs_flip = FALSE; + else + needs_flip = cogl_is_offscreen (framebuffer); + + cache = &ctx->builtin_flushed_projection; + } + else + { + needs_flip = FALSE; + + if (mode == COGL_MATRIX_MODELVIEW) + cache = &ctx->builtin_flushed_modelview; + else + cache = NULL; + } + + /* We don't need to do anything if the state is the same */ + if (!cache || + _cogl_matrix_entry_cache_maybe_update (cache, entry, needs_flip)) + { + CoglBool is_identity; + CoglMatrix matrix; + + if (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY) + is_identity = TRUE; + else + { + is_identity = FALSE; + cogl_matrix_entry_get (entry, &matrix); + } + + if (needs_flip) + { + CoglMatrix flipped_matrix; + + cogl_matrix_multiply (&flipped_matrix, + &ctx->y_flip_matrix, + is_identity ? + &ctx->identity_matrix : + &matrix); + + _cogl_matrix_flush_to_gl_builtin (ctx, + /* not identity */ + FALSE, + &flipped_matrix, + mode); + } + else + { + _cogl_matrix_flush_to_gl_builtin (ctx, + is_identity, + &matrix, + mode); + } + } + } +#endif +} + +CoglBool +cogl_matrix_entry_equal (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1) +{ + for (; + entry0 && entry1; + entry0 = entry0->parent, entry1 = entry1->parent) + { + entry0 = _cogl_matrix_entry_skip_saves (entry0); + entry1 = _cogl_matrix_entry_skip_saves (entry1); + + if (entry0 == entry1) + return TRUE; + + if (entry0->op != entry1->op) + return FALSE; + + switch (entry0->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + return TRUE; + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate0 = + (CoglMatrixEntryTranslate *)entry0; + CoglMatrixEntryTranslate *translate1 = + (CoglMatrixEntryTranslate *)entry1; + /* We could perhaps use an epsilon to compare here? + * I expect the false negatives are probaly never going to + * be a problem and this is a bit cheaper. */ + if (translate0->x != translate1->x || + translate0->y != translate1->y || + translate0->z != translate1->z) + return FALSE; + } + break; + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate0 = + (CoglMatrixEntryRotate *)entry0; + CoglMatrixEntryRotate *rotate1 = + (CoglMatrixEntryRotate *)entry1; + if (rotate0->angle != rotate1->angle || + rotate0->x != rotate1->x || + rotate0->y != rotate1->y || + rotate0->z != rotate1->z) + return FALSE; + } + break; + case COGL_MATRIX_OP_ROTATE_QUATERNION: + { + CoglMatrixEntryRotateQuaternion *rotate0 = + (CoglMatrixEntryRotateQuaternion *)entry0; + CoglMatrixEntryRotateQuaternion *rotate1 = + (CoglMatrixEntryRotateQuaternion *)entry1; + int i; + for (i = 0; i < 4; i++) + if (rotate0->values[i] != rotate1->values[i]) + return FALSE; + } + break; + case COGL_MATRIX_OP_ROTATE_EULER: + { + CoglMatrixEntryRotateEuler *rotate0 = + (CoglMatrixEntryRotateEuler *)entry0; + CoglMatrixEntryRotateEuler *rotate1 = + (CoglMatrixEntryRotateEuler *)entry1; + + if (rotate0->heading != rotate1->heading || + rotate0->pitch != rotate1->pitch || + rotate0->roll != rotate1->roll) + return FALSE; + } + break; + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale0 = (CoglMatrixEntryScale *)entry0; + CoglMatrixEntryScale *scale1 = (CoglMatrixEntryScale *)entry1; + if (scale0->x != scale1->x || + scale0->y != scale1->y || + scale0->z != scale1->z) + return FALSE; + } + break; + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *mult0 = (CoglMatrixEntryMultiply *)entry0; + CoglMatrixEntryMultiply *mult1 = (CoglMatrixEntryMultiply *)entry1; + if (!cogl_matrix_equal (mult0->matrix, mult1->matrix)) + return FALSE; + } + break; + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load0 = (CoglMatrixEntryLoad *)entry0; + CoglMatrixEntryLoad *load1 = (CoglMatrixEntryLoad *)entry1; + /* There's no need to check any further since an + * _OP_LOAD makes all the ancestors redundant as far as + * the final matrix value is concerned. */ + return cogl_matrix_equal (load0->matrix, load1->matrix); + } + case COGL_MATRIX_OP_SAVE: + /* We skip over saves above so we shouldn't see save entries */ + g_warn_if_reached (); + } + } + + return FALSE; +} + +void +cogl_debug_matrix_entry_print (CoglMatrixEntry *entry) +{ + int depth; + CoglMatrixEntry *e; + CoglMatrixEntry **children; + int i; + + for (depth = 0, e = entry; e; e = e->parent) + depth++; + + children = g_alloca (sizeof (CoglMatrixEntry) * depth); + + for (i = depth - 1, e = entry; + i >= 0 && e; + i--, e = e->parent) + { + children[i] = e; + } + + g_print ("MatrixEntry %p =\n", entry); + + for (i = 0; i < depth; i++) + { + entry = children[i]; + + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + g_print (" LOAD IDENTITY\n"); + continue; + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate = + (CoglMatrixEntryTranslate *)entry; + g_print (" TRANSLATE X=%f Y=%f Z=%f\n", + translate->x, + translate->y, + translate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate = + (CoglMatrixEntryRotate *)entry; + g_print (" ROTATE ANGLE=%f X=%f Y=%f Z=%f\n", + rotate->angle, + rotate->x, + rotate->y, + rotate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE_QUATERNION: + { + CoglMatrixEntryRotateQuaternion *rotate = + (CoglMatrixEntryRotateQuaternion *)entry; + g_print (" ROTATE QUATERNION w=%f x=%f y=%f z=%f\n", + rotate->values[0], + rotate->values[1], + rotate->values[2], + rotate->values[3]); + continue; + } + case COGL_MATRIX_OP_ROTATE_EULER: + { + CoglMatrixEntryRotateEuler *rotate = + (CoglMatrixEntryRotateEuler *)entry; + g_print (" ROTATE EULER heading=%f pitch=%f roll=%f\n", + rotate->heading, + rotate->pitch, + rotate->roll); + continue; + } + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)entry; + g_print (" SCALE X=%f Y=%f Z=%f\n", + scale->x, + scale->y, + scale->z); + continue; + } + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *mult = (CoglMatrixEntryMultiply *)entry; + g_print (" MULT:\n"); + _cogl_matrix_prefix_print (" ", mult->matrix); + continue; + } + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + g_print (" LOAD:\n"); + _cogl_matrix_prefix_print (" ", load->matrix); + continue; + } + case COGL_MATRIX_OP_SAVE: + g_print (" SAVE\n"); + } + } +} + +void +_cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache) +{ + cache->entry = NULL; + cache->flushed_identity = FALSE; + cache->flipped = FALSE; +} + +/* NB: This function can report false negatives since it never does a + * deep comparison of the stack matrices. */ +CoglBool +_cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache, + CoglMatrixEntry *entry, + CoglBool flip) +{ + CoglBool is_identity; + CoglBool updated = FALSE; + + if (cache->flipped != flip) + { + cache->flipped = flip; + updated = TRUE; + } + + is_identity = (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY); + if (cache->flushed_identity != is_identity) + { + cache->flushed_identity = is_identity; + updated = TRUE; + } + + if (cache->entry != entry) + { + cogl_matrix_entry_ref (entry); + if (cache->entry) + cogl_matrix_entry_unref (cache->entry); + cache->entry = entry; + + /* We want to make sure here that if the cache->entry and the + * given @entry are both identity matrices then even though they + * are different entries we don't want to consider this an + * update... + */ + updated |= !is_identity; + } + + return updated; +} + +void +_cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache) +{ + if (cache->entry) + cogl_matrix_entry_unref (cache->entry); +} diff --git a/cogl/cogl/cogl-matrix-stack.h b/cogl/cogl/cogl-matrix-stack.h new file mode 100644 index 000000000..6ea323a32 --- /dev/null +++ b/cogl/cogl/cogl-matrix-stack.h @@ -0,0 +1,645 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Havoc Pennington for litl + * Robert Bragg + */ + +#ifndef _COGL_MATRIX_STACK_H_ +#define _COGL_MATRIX_STACK_H_ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#include "cogl-matrix.h" +#include "cogl-context.h" + + +/** + * SECTION:cogl-matrix-stack + * @short_description: Functions for efficiently tracking many + * related transformations + * + * Matrices can be used (for example) to describe the model-view + * transforms of objects, texture transforms, and projective + * transforms. + * + * The #CoglMatrix api provides a good way to manipulate individual + * matrices representing a single transformation but if you need to + * track many-many such transformations for many objects that are + * organized in a scenegraph for example then using a separate + * #CoglMatrix for each object may not be the most efficient way. + * + * A #CoglMatrixStack enables applications to track lots of + * transformations that are related to each other in some kind of + * hierarchy. In a scenegraph for example if you want to know how to + * transform a particular node then you usually have to walk up + * through the ancestors and accumulate their transforms before + * finally applying the transform of the node itself. In this model + * things are grouped together spatially according to their ancestry + * and all siblings with the same parent share the same initial + * transformation. The #CoglMatrixStack API is suited to tracking lots + * of transformations that fit this kind of model. + * + * Compared to using the #CoglMatrix api directly to track many + * related transforms, these can be some advantages to using a + * #CoglMatrixStack: + * + * Faster equality comparisons of transformations + * Efficient comparisons of the differences between arbitrary + * transformations + * Avoid redundant arithmetic related to common transforms + * + * Can be more space efficient (not always though) + * + * + * For reference (to give an idea of when a #CoglMatrixStack can + * provide a space saving) a #CoglMatrix can be expected to take 72 + * bytes whereas a single #CoglMatrixEntry in a #CoglMatrixStack is + * currently around 32 bytes on a 32bit CPU or 36 bytes on a 64bit + * CPU. An entry is needed for each individual operation applied to + * the stack (such as rotate, scale, translate) so if most of your + * leaf node transformations only need one or two simple operations + * relative to their parent then a matrix stack will likely take less + * space than having a #CoglMatrix for each node. + * + * Even without any space saving though the ability to perform fast + * comparisons and avoid redundant arithmetic (especially sine and + * cosine calculations for rotations) can make using a matrix stack + * worthwhile. + */ + +/** + * CoglMatrixStack: + * + * Tracks your current position within a hierarchy and lets you build + * up a graph of transformations as you traverse through a hierarchy + * such as a scenegraph. + * + * A #CoglMatrixStack always maintains a reference to a single + * transformation at any point in time, representing the + * transformation at the current position in the hierarchy. You can + * get a reference to the current transformation by calling + * cogl_matrix_stack_get_entry(). + * + * When a #CoglMatrixStack is first created with + * cogl_matrix_stack_new() then it is conceptually positioned at the + * root of your hierarchy and the current transformation simply + * represents an identity transformation. + * + * As you traverse your object hierarchy (your scenegraph) then you + * should call cogl_matrix_stack_push() whenever you move down one + * level and call cogl_matrix_stack_pop() whenever you move back up + * one level towards the root. + * + * At any time you can apply a set of operations, such as "rotate", + * "scale", "translate" on top of the current transformation of a + * #CoglMatrixStack using functions such as + * cogl_matrix_stack_rotate(), cogl_matrix_stack_scale() and + * cogl_matrix_stack_translate(). These operations will derive a new + * current transformation and will never affect a transformation + * that you have referenced using cogl_matrix_stack_get_entry(). + * + * Internally applying operations to a #CoglMatrixStack builds up a + * graph of #CoglMatrixEntry structures which each represent a single + * immutable transform. + */ +typedef struct _CoglMatrixStack CoglMatrixStack; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_matrix_stack_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_matrix_stack_get_gtype (void); +#endif + +/** + * CoglMatrixEntry: + * + * Represents a single immutable transformation that was retrieved + * from a #CoglMatrixStack using cogl_matrix_stack_get_entry(). + * + * Internally a #CoglMatrixEntry represents a single matrix + * operation (such as "rotate", "scale", "translate") which is applied + * to the transform of a single parent entry. + * + * Using the #CoglMatrixStack api effectively builds up a graph of + * these immutable #CoglMatrixEntry structures whereby operations + * that can be shared between multiple transformations will result + * in shared #CoglMatrixEntry nodes in the graph. + * + * When a #CoglMatrixStack is first created it references one + * #CoglMatrixEntry that represents a single "load identity" + * operation. This serves as the root entry and all operations + * that are then applied to the stack will extend the graph + * starting from this root "load identity" entry. + * + * Given the typical usage model for a #CoglMatrixStack and the way + * the entries are built up while traversing a scenegraph then in most + * cases where an application is interested in comparing two + * transformations for equality then it is enough to simply compare + * two #CoglMatrixEntry pointers directly. Technically this can lead + * to false negatives that could be identified with a deeper + * comparison but often these false negatives are unlikely and + * don't matter anyway so this enables extremely cheap comparisons. + * + * #CoglMatrixEntrys are reference counted using + * cogl_matrix_entry_ref() and cogl_matrix_entry_unref() not with + * cogl_object_ref() and cogl_object_unref(). + */ +typedef struct _CoglMatrixEntry CoglMatrixEntry; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_matrix_entry_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_matrix_entry_get_gtype (void); +#endif + + +/** + * cogl_matrix_stack_new: + * @ctx: A #CoglContext + * + * Allocates a new #CoglMatrixStack that can be used to build up + * transformations relating to objects in a scenegraph like hierarchy. + * (See the description of #CoglMatrixStack and #CoglMatrixEntry for + * more details of what a matrix stack is best suited for) + * + * When a #CoglMatrixStack is first allocated it is conceptually + * positioned at the root of your scenegraph hierarchy. As you + * traverse your scenegraph then you should call + * cogl_matrix_stack_push() whenever you move down a level and + * cogl_matrix_stack_pop() whenever you move back up a level towards + * the root. + * + * Once you have allocated a #CoglMatrixStack you can get a reference + * to the current transformation for the current position in the + * hierarchy by calling cogl_matrix_stack_get_entry(). + * + * Once you have allocated a #CoglMatrixStack you can apply operations + * such as rotate, scale and translate to modify the current transform + * for the current position in the hierarchy by calling + * cogl_matrix_stack_rotate(), cogl_matrix_stack_scale() and + * cogl_matrix_stack_translate(). + * + * Return value: (transfer full): A newly allocated #CoglMatrixStack + */ +CoglMatrixStack * +cogl_matrix_stack_new (CoglContext *ctx); + +/** + * cogl_matrix_stack_push: + * @stack: A #CoglMatrixStack + * + * Saves the current transform and starts a new transform that derives + * from the current transform. + * + * This is usually called while traversing a scenegraph whenever you + * traverse one level deeper. cogl_matrix_stack_pop() can then be + * called when going back up one layer to restore the previous + * transform of an ancestor. + */ +void +cogl_matrix_stack_push (CoglMatrixStack *stack); + +/** + * cogl_matrix_stack_pop: + * @stack: A #CoglMatrixStack + * + * Restores the previous transform that was last saved by calling + * cogl_matrix_stack_push(). + * + * This is usually called while traversing a scenegraph whenever you + * return up one level in the graph towards the root node. + */ +void +cogl_matrix_stack_pop (CoglMatrixStack *stack); + +/** + * cogl_matrix_stack_load_identity: + * @stack: A #CoglMatrixStack + * + * Resets the current matrix to the identity matrix. + */ +void +cogl_matrix_stack_load_identity (CoglMatrixStack *stack); + +/** + * cogl_matrix_stack_scale: + * @stack: A #CoglMatrixStack + * @x: Amount to scale along the x-axis + * @y: Amount to scale along the y-axis + * @z: Amount to scale along the z-axis + * + * Multiplies the current matrix by one that scales the x, y and z + * axes by the given values. + */ +void +cogl_matrix_stack_scale (CoglMatrixStack *stack, + float x, + float y, + float z); + +/** + * cogl_matrix_stack_translate: + * @stack: A #CoglMatrixStack + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Multiplies the current matrix by one that translates along all + * three axes according to the given values. + */ +void +cogl_matrix_stack_translate (CoglMatrixStack *stack, + float x, + float y, + float z); + +/** + * cogl_matrix_stack_rotate: + * @stack: A #CoglMatrixStack + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Multiplies the current matrix by one that rotates the around the + * axis-vector specified by @x, @y and @z. The rotation follows the + * right-hand thumb rule so for example rotating by 10 degrees about + * the axis-vector (0, 0, 1) causes a small counter-clockwise + * rotation. + */ +void +cogl_matrix_stack_rotate (CoglMatrixStack *stack, + float angle, + float x, + float y, + float z); + +/** + * cogl_matrix_stack_rotate_quaternion: + * @stack: A #CoglMatrixStack + * @quaternion: A #CoglQuaternion + * + * Multiplies the current matrix by one that rotates according to the + * rotation described by @quaternion. + */ +void +cogl_matrix_stack_rotate_quaternion (CoglMatrixStack *stack, + const CoglQuaternion *quaternion); + +/** + * cogl_matrix_stack_rotate_euler: + * @stack: A #CoglMatrixStack + * @euler: A #CoglEuler + * + * Multiplies the current matrix by one that rotates according to the + * rotation described by @euler. + */ +void +cogl_matrix_stack_rotate_euler (CoglMatrixStack *stack, + const CoglEuler *euler); + +/** + * cogl_matrix_stack_multiply: + * @stack: A #CoglMatrixStack + * @matrix: the matrix to multiply with the current model-view + * + * Multiplies the current matrix by the given matrix. + */ +void +cogl_matrix_stack_multiply (CoglMatrixStack *stack, + const CoglMatrix *matrix); + +/** + * cogl_matrix_stack_frustum: + * @stack: A #CoglMatrixStack + * @left: X position of the left clipping plane where it + * intersects the near clipping plane + * @right: X position of the right clipping plane where it + * intersects the near clipping plane + * @bottom: Y position of the bottom clipping plane where it + * intersects the near clipping plane + * @top: Y position of the top clipping plane where it intersects + * the near clipping plane + * @z_near: The distance to the near clipping plane (Must be positive) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current matrix with a perspective matrix for a given + * viewing frustum defined by 4 side clip planes that all cross + * through the origin and 2 near and far clip planes. + */ +void +cogl_matrix_stack_frustum (CoglMatrixStack *stack, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far); + +/** + * cogl_matrix_stack_perspective: + * @stack: A #CoglMatrixStack + * @fov_y: Vertical field of view angle in degrees. + * @aspect: The (width over height) aspect ratio for display + * @z_near: The distance to the near clipping plane (Must be positive, + * and must not be 0) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current matrix with a perspective matrix based on the + * provided values. + * + * You should be careful not to have too great a @z_far / @z_near + * ratio since that will reduce the effectiveness of depth testing + * since there wont be enough precision to identify the depth of + * objects near to each other. + */ +void +cogl_matrix_stack_perspective (CoglMatrixStack *stack, + float fov_y, + float aspect, + float z_near, + float z_far); + +/** + * cogl_matrix_stack_orthographic: + * @stack: A #CoglMatrixStack + * @x_1: The x coordinate for the first vertical clipping plane + * @y_1: The y coordinate for the first horizontal clipping plane + * @x_2: The x coordinate for the second vertical clipping plane + * @y_2: The y coordinate for the second horizontal clipping plane + * @near: The distance to the near clipping + * plane (will be negative if the plane is + * behind the viewer) + * @far: The distance to the far clipping + * plane (will be negative if the plane is + * behind the viewer) + * + * Replaces the current matrix with an orthographic projection matrix. + */ +void +cogl_matrix_stack_orthographic (CoglMatrixStack *stack, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far); + +/** + * cogl_matrix_stack_get_inverse: + * @stack: A #CoglMatrixStack + * @inverse: (out): The destination for a 4x4 inverse transformation matrix + * + * Gets the inverse transform of the current matrix and uses it to + * initialize a new #CoglMatrix. + * + * Return value: %TRUE if the inverse was successfully calculated or %FALSE + * for degenerate transformations that can't be inverted (in this case the + * @inverse matrix will simply be initialized with the identity matrix) + */ +CoglBool +cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse); + +/** + * cogl_matrix_stack_get_entry: + * @stack: A #CoglMatrixStack + * + * Gets a reference to the current transform represented by a + * #CoglMatrixEntry pointer. + * + * The transform represented by a #CoglMatrixEntry is + * immutable. + * + * #CoglMatrixEntrys are reference counted using + * cogl_matrix_entry_ref() and cogl_matrix_entry_unref() and you + * should call cogl_matrix_entry_unref() when you are finished with + * and entry you get via cogl_matrix_stack_get_entry(). + * + * Return value: (transfer none): A pointer to the #CoglMatrixEntry + * representing the current matrix stack transform. + */ +CoglMatrixEntry * +cogl_matrix_stack_get_entry (CoglMatrixStack *stack); + +/** + * cogl_matrix_stack_get: + * @stack: A #CoglMatrixStack + * @matrix: (out): The potential destination for the current matrix + * + * Resolves the current @stack transform into a #CoglMatrix by + * combining the operations that have been applied to build up the + * current transform. + * + * There are two possible ways that this function may return its + * result depending on whether the stack is able to directly point + * to an internal #CoglMatrix or whether the result needs to be + * composed of multiple operations. + * + * If an internal matrix contains the required result then this + * function will directly return a pointer to that matrix, otherwise + * if the function returns %NULL then @matrix will be initialized + * to match the current transform of @stack. + * + * @matrix will be left untouched if a direct pointer is + * returned. + * + * Return value: A direct pointer to the current transform or %NULL + * and in that case @matrix will be initialized with + * the value of the current transform. + */ +CoglMatrix * +cogl_matrix_stack_get (CoglMatrixStack *stack, + CoglMatrix *matrix); + +/** + * cogl_matrix_entry_get: + * @entry: A #CoglMatrixEntry + * @matrix: (out): The potential destination for the transform as + * a matrix + * + * Resolves the current @entry transform into a #CoglMatrix by + * combining the sequence of operations that have been applied to + * build up the current transform. + * + * There are two possible ways that this function may return its + * result depending on whether it's possible to directly point + * to an internal #CoglMatrix or whether the result needs to be + * composed of multiple operations. + * + * If an internal matrix contains the required result then this + * function will directly return a pointer to that matrix, otherwise + * if the function returns %NULL then @matrix will be initialized + * to match the transform of @entry. + * + * @matrix will be left untouched if a direct pointer is + * returned. + * + * Return value: A direct pointer to a #CoglMatrix transform or %NULL + * and in that case @matrix will be initialized with + * the effective transform represented by @entry. + */ +CoglMatrix * +cogl_matrix_entry_get (CoglMatrixEntry *entry, + CoglMatrix *matrix); + +/** + * cogl_matrix_stack_set: + * @stack: A #CoglMatrixStack + * @matrix: A #CoglMatrix replace the current matrix value with + * + * Replaces the current @stack matrix value with the value of @matrix. + * This effectively discards any other operations that were applied + * since the last time cogl_matrix_stack_push() was called or since + * the stack was initialized. + */ +void +cogl_matrix_stack_set (CoglMatrixStack *stack, + const CoglMatrix *matrix); + +/** + * cogl_is_matrix_stack: + * @object: a #CoglObject + * + * Determines if the given #CoglObject refers to a #CoglMatrixStack. + * + * Return value: %TRUE if @object is a #CoglMatrixStack, otherwise + * %FALSE. + */ +CoglBool +cogl_is_matrix_stack (void *object); + +/** + * cogl_matrix_entry_calculate_translation: + * @entry0: The first reference transform + * @entry1: A second reference transform + * @x: (out): The destination for the x-component of the translation + * @y: (out): The destination for the y-component of the translation + * @z: (out): The destination for the z-component of the translation + * + * Determines if the only difference between two transforms is a + * translation and if so returns what the @x, @y, and @z components of + * the translation are. + * + * If the difference between the two translations involves anything + * other than a translation then the function returns %FALSE. + * + * Return value: %TRUE if the only difference between the transform of + * @entry0 and the transform of @entry1 is a translation, + * otherwise %FALSE. + */ +CoglBool +cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1, + float *x, + float *y, + float *z); + +/** + * cogl_matrix_entry_is_identity: + * @entry: A #CoglMatrixEntry + * + * Determines whether @entry is known to represent an identity + * transform. + * + * If this returns %TRUE then the entry is definitely the identity + * matrix. If it returns %FALSE it may or may not be the identity + * matrix but no expensive comparison is performed to verify it. + * + * Return value: %TRUE if @entry is definitely an identity transform, + * otherwise %FALSE. + */ +CoglBool +cogl_matrix_entry_is_identity (CoglMatrixEntry *entry); + +/** + * cogl_matrix_entry_equal: + * @entry0: The first #CoglMatrixEntry to compare + * @entry1: A second #CoglMatrixEntry to compare + * + * Compares two arbitrary #CoglMatrixEntry transforms for equality + * returning %TRUE if they are equal or %FALSE otherwise. + * + * In many cases it is unnecessary to use this api and instead + * direct pointer comparisons of entries are good enough and much + * cheaper too. + * + * Return value: %TRUE if @entry0 represents the same transform as + * @entry1, otherwise %FALSE. + */ +CoglBool +cogl_matrix_entry_equal (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1); + +/** + * cogl_debug_matrix_entry_print: + * @entry: A #CoglMatrixEntry + * + * Allows visualizing the operations that build up the given @entry + * for debugging purposes by printing to stdout. + */ +void +cogl_debug_matrix_entry_print (CoglMatrixEntry *entry); + +/** + * cogl_matrix_entry_ref: + * @entry: A #CoglMatrixEntry + * + * Takes a reference on the given @entry to ensure the @entry stays + * alive and remains valid. When you are finished with the @entry then + * you should call cogl_matrix_entry_unref(). + * + * It is an error to pass an @entry pointer to cogl_object_ref() and + * cogl_object_unref() + */ +CoglMatrixEntry * +cogl_matrix_entry_ref (CoglMatrixEntry *entry); + +/** + * cogl_matrix_entry_unref: + * @entry: A #CoglMatrixEntry + * + * Releases a reference on @entry either taken by calling + * cogl_matrix_entry_unref() or to release the reference given when + * calling cogl_matrix_stack_get_entry(). + */ +void +cogl_matrix_entry_unref (CoglMatrixEntry *entry); + +#endif /* _COGL_MATRIX_STACK_H_ */ diff --git a/cogl/cogl/cogl-matrix.c b/cogl/cogl/cogl-matrix.c new file mode 100644 index 000000000..fd0f6af3c --- /dev/null +++ b/cogl/cogl/cogl-matrix.c @@ -0,0 +1,2313 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2011 Intel Corporation. + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ +/* + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Note: a lot of this code is based on code that was taken from Mesa. + * + * Changes compared to the original code from Mesa: + * + * - instead of allocating matrix->m and matrix->inv using malloc, our + * public CoglMatrix typedef is large enough to directly contain the + * matrix, its inverse, a type and a set of flags. + * - instead of having a _cogl_matrix_analyse which updates the type, + * flags and inverse, we have _cogl_matrix_update_inverse which + * essentially does the same thing (internally making use of + * _cogl_matrix_update_type_and_flags()) but with additional guards in + * place to bail out when the inverse matrix is still valid. + * - when initializing a matrix with the identity matrix we don't + * immediately initialize the inverse matrix; rather we just set the + * dirty flag for the inverse (since it's likely the user won't request + * the inverse of the identity matrix) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +COGL_GTYPE_DEFINE_BOXED (Matrix, matrix, + cogl_matrix_copy, + cogl_matrix_free); + +/* + * Symbolic names to some of the entries in the matrix + * + * These are handy for the viewport mapping, which is expressed as a matrix. + */ +#define MAT_SX 0 +#define MAT_SY 5 +#define MAT_SZ 10 +#define MAT_TX 12 +#define MAT_TY 13 +#define MAT_TZ 14 + +/* + * These identify different kinds of 4x4 transformation matrices and we use + * this information to find fast-paths when available. + */ +enum CoglMatrixType { + COGL_MATRIX_TYPE_GENERAL, /**< general 4x4 matrix */ + COGL_MATRIX_TYPE_IDENTITY, /**< identity matrix */ + COGL_MATRIX_TYPE_3D_NO_ROT, /**< orthogonal projection and others... */ + COGL_MATRIX_TYPE_PERSPECTIVE, /**< perspective projection matrix */ + COGL_MATRIX_TYPE_2D, /**< 2-D transformation */ + COGL_MATRIX_TYPE_2D_NO_ROT, /**< 2-D scale & translate only */ + COGL_MATRIX_TYPE_3D, /**< 3-D transformation */ + COGL_MATRIX_N_TYPES +} ; + +#define DEG2RAD (G_PI/180.0) + +/* Dot product of two 2-element vectors */ +#define DOT2(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] ) + +/* Dot product of two 3-element vectors */ +#define DOT3(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] ) + +#define CROSS3(N, U, V) \ +do { \ + (N)[0] = (U)[1]*(V)[2] - (U)[2]*(V)[1]; \ + (N)[1] = (U)[2]*(V)[0] - (U)[0]*(V)[2]; \ + (N)[2] = (U)[0]*(V)[1] - (U)[1]*(V)[0]; \ +} while (0) + +#define SUB_3V(DST, SRCA, SRCB) \ +do { \ + (DST)[0] = (SRCA)[0] - (SRCB)[0]; \ + (DST)[1] = (SRCA)[1] - (SRCB)[1]; \ + (DST)[2] = (SRCA)[2] - (SRCB)[2]; \ +} while (0) + +#define LEN_SQUARED_3FV( V ) ((V)[0]*(V)[0]+(V)[1]*(V)[1]+(V)[2]*(V)[2]) + +/* + * \defgroup MatFlags MAT_FLAG_XXX-flags + * + * Bitmasks to indicate different kinds of 4x4 matrices in CoglMatrix::flags + */ +#define MAT_FLAG_IDENTITY 0 /*< is an identity matrix flag. + * (Not actually used - the identity + * matrix is identified by the absense + * of all other flags.) + */ +#define MAT_FLAG_GENERAL 0x1 /*< is a general matrix flag */ +#define MAT_FLAG_ROTATION 0x2 /*< is a rotation matrix flag */ +#define MAT_FLAG_TRANSLATION 0x4 /*< is a translation matrix flag */ +#define MAT_FLAG_UNIFORM_SCALE 0x8 /*< is an uniform scaling matrix flag */ +#define MAT_FLAG_GENERAL_SCALE 0x10 /*< is a general scaling matrix flag */ +#define MAT_FLAG_GENERAL_3D 0x20 /*< general 3D matrix flag */ +#define MAT_FLAG_PERSPECTIVE 0x40 /*< is a perspective proj matrix flag */ +#define MAT_FLAG_SINGULAR 0x80 /*< is a singular matrix flag */ +#define MAT_DIRTY_TYPE 0x100 /*< matrix type is dirty */ +#define MAT_DIRTY_FLAGS 0x200 /*< matrix flags are dirty */ +#define MAT_DIRTY_INVERSE 0x400 /*< matrix inverse is dirty */ + +/* angle preserving matrix flags mask */ +#define MAT_FLAGS_ANGLE_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE) + +/* geometry related matrix flags mask */ +#define MAT_FLAGS_GEOMETRY (MAT_FLAG_GENERAL | \ + MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D | \ + MAT_FLAG_PERSPECTIVE | \ + MAT_FLAG_SINGULAR) + +/* length preserving matrix flags mask */ +#define MAT_FLAGS_LENGTH_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION) + + +/* 3D (non-perspective) matrix flags mask */ +#define MAT_FLAGS_3D (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D) + +/* dirty matrix flags mask */ +#define MAT_DIRTY_ALL (MAT_DIRTY_TYPE | \ + MAT_DIRTY_FLAGS | \ + MAT_DIRTY_INVERSE) + + +/* + * Test geometry related matrix flags. + * + * @mat a pointer to a CoglMatrix structure. + * @a flags mask. + * + * Returns: non-zero if all geometry related matrix flags are contained within + * the mask, or zero otherwise. + */ +#define TEST_MAT_FLAGS(mat, a) \ + ((MAT_FLAGS_GEOMETRY & (~(a)) & ((mat)->flags) ) == 0) + + + +/* + * Names of the corresponding CoglMatrixType values. + */ +static const char *types[] = { + "COGL_MATRIX_TYPE_GENERAL", + "COGL_MATRIX_TYPE_IDENTITY", + "COGL_MATRIX_TYPE_3D_NO_ROT", + "COGL_MATRIX_TYPE_PERSPECTIVE", + "COGL_MATRIX_TYPE_2D", + "COGL_MATRIX_TYPE_2D_NO_ROT", + "COGL_MATRIX_TYPE_3D" +}; + + +/* + * Identity matrix. + */ +static float identity[16] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +}; + + +#define A(row,col) a[(col<<2)+row] +#define B(row,col) b[(col<<2)+row] +#define R(row,col) result[(col<<2)+row] + +/* + * Perform a full 4x4 matrix multiplication. + * + * It's assumed that @result != @b. @product == @a is allowed. + * + * KW: 4*16 = 64 multiplications + */ +static void +matrix_multiply4x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 4; i++) + { + const float ai0 = A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); + } +} + +/* + * Multiply two matrices known to occupy only the top three rows, such + * as typical model matrices, and orthogonal matrices. + * + * @a matrix. + * @b matrix. + * @product will receive the product of \p a and \p b. + */ +static void +matrix_multiply3x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 3; i++) + { + const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3; + } + R(3,0) = 0; + R(3,1) = 0; + R(3,2) = 0; + R(3,3) = 1; +} + +#undef A +#undef B +#undef R + +/* + * Multiply a matrix by an array of floats with known properties. + * + * @mat pointer to a CoglMatrix structure containing the left multiplication + * matrix, and that will receive the product result. + * @m right multiplication matrix array. + * @flags flags of the matrix \p m. + * + * Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +static void +matrix_multiply_array_with_flags (CoglMatrix *result, + const float *array, + unsigned int flags) +{ + result->flags |= (flags | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS (result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)result, array); + else + matrix_multiply4x4 ((float *)result, (float *)result, array); +} + +/* Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +static void +_cogl_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b) +{ + result->flags = (a->flags | + b->flags | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS(result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)a, (float *)b); + else + matrix_multiply4x4 ((float *)result, (float *)a, (float *)b); +} + +void +cogl_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b) +{ + _cogl_matrix_multiply (result, a, b); + _COGL_MATRIX_DEBUG_PRINT (result); +} + +#if 0 +/* Marks the matrix flags with general flag, and type and inverse dirty flags. + * Calls matrix_multiply4x4() for the multiplication. + */ +static void +_cogl_matrix_multiply_array (CoglMatrix *result, const float *array) +{ + result->flags |= (MAT_FLAG_GENERAL | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE | + MAT_DIRTY_FLAGS); + + matrix_multiply4x4 ((float *)result, (float *)result, (float *)array); +} +#endif + +/* + * Print a matrix array. + * + * Called by _cogl_matrix_print() to print a matrix or its inverse. + */ +static void +print_matrix_floats (const char *prefix, const float m[16]) +{ + int i; + for (i = 0;i < 4; i++) + g_print ("%s\t%f %f %f %f\n", prefix, m[i], m[4+i], m[8+i], m[12+i] ); +} + +void +_cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix) +{ + if (!(matrix->flags & MAT_DIRTY_TYPE)) + { + _COGL_RETURN_IF_FAIL (matrix->type < COGL_MATRIX_N_TYPES); + g_print ("%sMatrix type: %s, flags: %x\n", + prefix, types[matrix->type], (int)matrix->flags); + } + else + g_print ("%sMatrix type: DIRTY, flags: %x\n", + prefix, (int)matrix->flags); + + print_matrix_floats (prefix, (float *)matrix); + g_print ("%sInverse: \n", prefix); + if (!(matrix->flags & MAT_DIRTY_INVERSE)) + { + float prod[16]; + print_matrix_floats (prefix, matrix->inv); + matrix_multiply4x4 (prod, (float *)matrix, matrix->inv); + g_print ("%sMat * Inverse:\n", prefix); + print_matrix_floats (prefix, prod); + } + else + g_print ("%s - not available\n", prefix); +} + +/* + * Dumps the contents of a CoglMatrix structure. + */ +void +cogl_debug_matrix_print (const CoglMatrix *matrix) +{ + _cogl_matrix_prefix_print ("", matrix); +} + +/* + * References an element of 4x4 matrix. + * + * @m matrix array. + * @c column of the desired element. + * @r row of the desired element. + * + * Returns: value of the desired element. + * + * Calculate the linear storage index of the element and references it. + */ +#define MAT(m,r,c) (m)[(c)*4+(r)] + +/* + * Swaps the values of two floating pointer variables. + * + * Used by invert_matrix_general() to swap the row pointers. + */ +#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } + +/* + * Compute inverse of 4x4 transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * \author + * Code contributed by Jacques Leroy jle@star.be + * + * Calculates the inverse matrix by performing the gaussian matrix reduction + * with partial pivoting followed by back/substitution with the loops manually + * unrolled. + */ +static CoglBool +invert_matrix_general (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + float *out = matrix->inv; + float wtmp[4][8]; + float m0, m1, m2, m3, s; + float *r0, *r1, *r2, *r3; + + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + + r0[0] = MAT (m, 0, 0), r0[1] = MAT (m, 0, 1), + r0[2] = MAT (m, 0, 2), r0[3] = MAT (m, 0, 3), + r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, + + r1[0] = MAT (m, 1, 0), r1[1] = MAT (m, 1, 1), + r1[2] = MAT (m, 1, 2), r1[3] = MAT (m, 1, 3), + r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, + + r2[0] = MAT (m, 2, 0), r2[1] = MAT (m, 2, 1), + r2[2] = MAT (m, 2, 2), r2[3] = MAT (m, 2, 3), + r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, + + r3[0] = MAT (m, 3, 0), r3[1] = MAT (m, 3, 1), + r3[2] = MAT (m, 3, 2), r3[3] = MAT (m, 3, 3), + r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; + + /* choose pivot - or die */ + if (fabsf (r3[0]) > fabsf (r2[0])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[0]) > fabsf (r1[0])) + SWAP_ROWS (r2, r1); + if (fabsf (r1[0]) > fabsf (r0[0])) + SWAP_ROWS (r1, r0); + if (0.0 == r0[0]) + return FALSE; + + /* eliminate first variable */ + m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0]; + s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s; + s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s; + s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r0[5]; + if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r0[6]; + if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r0[7]; + if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[1]) > fabsf (r2[1])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[1]) > fabsf (r1[1])) + SWAP_ROWS (r2, r1); + if (0.0 == r1[1]) + return FALSE; + + /* eliminate second variable */ + m2 = r2[1] / r1[1]; m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; + s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[2]) > fabsf (r2[2])) + SWAP_ROWS (r3, r2); + if (0.0 == r2[2]) + return FALSE; + + /* eliminate third variable */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], + r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], + r3[7] -= m3 * r2[7]; + + /* last check */ + if (0.0 == r3[3]) + return FALSE; + + s = 1.0f / r3[3]; /* now back substitute row 3 */ + r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s; + + m2 = r2[3]; /* now back substitute row 2 */ + s = 1.0f / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), + r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, + r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, + r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; + + m1 = r1[2]; /* now back substitute row 1 */ + s = 1.0f / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), + r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, + r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; + + m0 = r0[1]; /* now back substitute row 0 */ + s = 1.0f / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), + r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); + + MAT (out, 0, 0) = r0[4]; MAT (out, 0, 1) = r0[5], + MAT (out, 0, 2) = r0[6]; MAT (out, 0, 3) = r0[7], + MAT (out, 1, 0) = r1[4]; MAT (out, 1, 1) = r1[5], + MAT (out, 1, 2) = r1[6]; MAT (out, 1, 3) = r1[7], + MAT (out, 2, 0) = r2[4]; MAT (out, 2, 1) = r2[5], + MAT (out, 2, 2) = r2[6]; MAT (out, 2, 3) = r2[7], + MAT (out, 3, 0) = r3[4]; MAT (out, 3, 1) = r3[5], + MAT (out, 3, 2) = r3[6]; MAT (out, 3, 3) = r3[7]; + + return TRUE; +} +#undef SWAP_ROWS + +/* + * Compute inverse of a general 3d transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * \author Adapted from graphics gems II. + * + * Calculates the inverse of the upper left by first calculating its + * determinant and multiplying it to the symmetric adjust matrix of each + * element. Finally deals with the translation part by transforming the + * original translation vector using by the calculated submatrix inverse. + */ +static CoglBool +invert_matrix_3d_general (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + float pos, neg, t; + float det; + + /* Calculate the determinant of upper left 3x3 submatrix and + * determine if the matrix is singular. + */ + pos = neg = 0.0; + t = MAT (in,0,0) * MAT (in,1,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,1,0) * MAT (in,2,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,2,0) * MAT (in,0,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,2,0) * MAT (in,1,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,1,0) * MAT (in,0,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,0,0) * MAT (in,2,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + det = pos + neg; + + if (det*det < 1e-25) + return FALSE; + + det = 1.0f / det; + MAT (out,0,0) = + ( (MAT (in, 1, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 1, 2) )*det); + MAT (out,0,1) = + (- (MAT (in, 0, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 0, 2) )*det); + MAT (out,0,2) = + ( (MAT (in, 0, 1)*MAT (in, 1, 2) - MAT (in, 1, 1)*MAT (in, 0, 2) )*det); + MAT (out,1,0) = + (- (MAT (in,1,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,1,2) )*det); + MAT (out,1,1) = + ( (MAT (in,0,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,0,2) )*det); + MAT (out,1,2) = + (- (MAT (in,0,0)*MAT (in,1,2) - MAT (in,1,0)*MAT (in,0,2) )*det); + MAT (out,2,0) = + ( (MAT (in,1,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,1,1) )*det); + MAT (out,2,1) = + (- (MAT (in,0,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,0,1) )*det); + MAT (out,2,2) = + ( (MAT (in,0,0)*MAT (in,1,1) - MAT (in,1,0)*MAT (in,0,1) )*det); + + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2 ,0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + + return TRUE; +} + +/* + * Compute inverse of a 3d transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * If the matrix is not an angle preserving matrix then calls + * invert_matrix_3d_general for the actual calculation. Otherwise calculates + * the inverse matrix analyzing and inverting each of the scaling, rotation and + * translation parts. + */ +static CoglBool +invert_matrix_3d (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + memcpy (out, identity, 16 * sizeof (float)); + + if (!TEST_MAT_FLAGS(matrix, MAT_FLAGS_ANGLE_PRESERVING)) + return invert_matrix_3d_general (matrix); + + if (matrix->flags & MAT_FLAG_UNIFORM_SCALE) + { + float scale = (MAT (in, 0, 0) * MAT (in, 0, 0) + + MAT (in, 0, 1) * MAT (in, 0, 1) + + MAT (in, 0, 2) * MAT (in, 0, 2)); + + if (scale == 0.0) + return FALSE; + + scale = 1.0f / scale; + + /* Transpose and scale the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = scale * MAT (in, 0, 0); + MAT (out, 1, 0) = scale * MAT (in, 0, 1); + MAT (out, 2, 0) = scale * MAT (in, 0, 2); + MAT (out, 0, 1) = scale * MAT (in, 1, 0); + MAT (out, 1, 1) = scale * MAT (in, 1, 1); + MAT (out, 2, 1) = scale * MAT (in, 1, 2); + MAT (out, 0, 2) = scale * MAT (in, 2, 0); + MAT (out, 1, 2) = scale * MAT (in, 2, 1); + MAT (out, 2, 2) = scale * MAT (in, 2, 2); + } + else if (matrix->flags & MAT_FLAG_ROTATION) + { + /* Transpose the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = MAT (in, 0, 0); + MAT (out, 1, 0) = MAT (in, 0, 1); + MAT (out, 2, 0) = MAT (in, 0, 2); + MAT (out, 0, 1) = MAT (in, 1, 0); + MAT (out, 1, 1) = MAT (in, 1, 1); + MAT (out, 2, 1) = MAT (in, 1, 2); + MAT (out, 0, 2) = MAT (in, 2, 0); + MAT (out, 1, 2) = MAT (in, 2, 1); + MAT (out, 2, 2) = MAT (in, 2, 2); + } + else + { + /* pure translation */ + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 3) = - MAT (in, 0, 3); + MAT (out, 1, 3) = - MAT (in, 1, 3); + MAT (out, 2, 3) = - MAT (in, 2, 3); + return TRUE; + } + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2, 0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + } + else + MAT (out, 0, 3) = MAT (out, 1, 3) = MAT (out, 2, 3) = 0.0; + + return TRUE; +} + +/* + * Compute inverse of an identity transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: always %TRUE. + * + * Simply copies identity into CoglMatrix::inv. + */ +static CoglBool +invert_matrix_identity (CoglMatrix *matrix) +{ + memcpy (matrix->inv, identity, 16 * sizeof (float)); + return TRUE; +} + +/* + * Compute inverse of a no-rotation 3d transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * Calculates the + */ +static CoglBool +invert_matrix_3d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in,0,0) == 0 || MAT (in,1,1) == 0 || MAT (in,2,2) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out,0,0) = 1.0f / MAT (in,0,0); + MAT (out,1,1) = 1.0f / MAT (in,1,1); + MAT (out,2,2) = 1.0f / MAT (in,2,2); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out,0,3) = - (MAT (in,0,3) * MAT (out,0,0)); + MAT (out,1,3) = - (MAT (in,1,3) * MAT (out,1,1)); + MAT (out,2,3) = - (MAT (in,2,3) * MAT (out,2,2)); + } + + return TRUE; +} + +/* + * Compute inverse of a no-rotation 2d transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * Calculates the inverse matrix by applying the inverse scaling and + * translation to the identity matrix. + */ +static CoglBool +invert_matrix_2d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in, 0, 0) == 0 || MAT (in, 1, 1) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out, 0, 3) = - (MAT (in, 0, 3) * MAT (out, 0, 0)); + MAT (out, 1, 3) = - (MAT (in, 1, 3) * MAT (out, 1, 1)); + } + + return TRUE; +} + +#if 0 +/* broken */ +static CoglBool +invert_matrix_perspective (CoglMatrix *matrix) +{ + const float *in = matrix; + float *out = matrix->inv; + + if (MAT (in,2,3) == 0) + return FALSE; + + memcpy( out, identity, 16 * sizeof(float) ); + + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + MAT (out, 0, 3) = MAT (in, 0, 2); + MAT (out, 1, 3) = MAT (in, 1, 2); + + MAT (out,2,2) = 0; + MAT (out,2,3) = -1; + + MAT (out,3,2) = 1.0f / MAT (in,2,3); + MAT (out,3,3) = MAT (in,2,2) * MAT (out,3,2); + + return TRUE; +} +#endif + +/* + * Matrix inversion function pointer type. + */ +typedef CoglBool (*inv_mat_func)(CoglMatrix *matrix); + +/* + * Table of the matrix inversion functions according to the matrix type. + */ +static inv_mat_func inv_mat_tab[7] = { + invert_matrix_general, + invert_matrix_identity, + invert_matrix_3d_no_rotation, +#if 0 + /* Don't use this function for now - it fails when the projection matrix + * is premultiplied by a translation (ala Chromium's tilesort SPU). + */ + invert_matrix_perspective, +#else + invert_matrix_general, +#endif + invert_matrix_3d, /* lazy! */ + invert_matrix_2d_no_rotation, + invert_matrix_3d +}; + +#define ZERO(x) (1<flags &= ~MAT_FLAGS_GEOMETRY; + + /* Check for translation - no-one really cares + */ + if ((mask & MASK_NO_TRX) != MASK_NO_TRX) + matrix->flags |= MAT_FLAG_TRANSLATION; + + /* Do the real work + */ + if (mask == (unsigned int) MASK_IDENTITY) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if ((mask & MASK_2D_NO_ROT) == (unsigned int) MASK_2D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + + if ((mask & MASK_NO_2D_SCALE) != MASK_NO_2D_SCALE) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_2D) == (unsigned int) MASK_2D) + { + float mm = DOT2 (m, m); + float m4m4 = DOT2 (m+4,m+4); + float mm4 = DOT2 (m,m+4); + + matrix->type = COGL_MATRIX_TYPE_2D; + + /* Check for scale */ + if (SQ (mm-1) > SQ (1e-6) || + SQ (m4m4-1) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (mm4) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_3D; + else + matrix->flags |= MAT_FLAG_ROTATION; + + } + else if ((mask & MASK_3D_NO_ROT) == (unsigned int) MASK_3D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + + /* Check for scale */ + if (SQ (m[0]-m[5]) < SQ (1e-6) && + SQ (m[0]-m[10]) < SQ (1e-6)) + { + if (SQ (m[0]-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_3D) == (unsigned int) MASK_3D) + { + float c1 = DOT3 (m,m); + float c2 = DOT3 (m+4,m+4); + float c3 = DOT3 (m+8,m+8); + float d1 = DOT3 (m, m+4); + float cp[3]; + + matrix->type = COGL_MATRIX_TYPE_3D; + + /* Check for scale */ + if (SQ (c1-c2) < SQ (1e-6) && SQ (c1-c3) < SQ (1e-6)) + { + if (SQ (c1-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + /* else no scale at all */ + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (d1) < SQ (1e-6)) + { + CROSS3 ( cp, m, m+4); + SUB_3V ( cp, cp, (m+8)); + if (LEN_SQUARED_3FV(cp) < SQ(1e-6)) + matrix->flags |= MAT_FLAG_ROTATION; + else + matrix->flags |= MAT_FLAG_GENERAL_3D; + } + else + matrix->flags |= MAT_FLAG_GENERAL_3D; /* shear, etc */ + } + else if ((mask & MASK_PERSPECTIVE) == MASK_PERSPECTIVE && m[11]==-1.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + matrix->flags |= MAT_FLAG_GENERAL; + } + else + { + matrix->type = COGL_MATRIX_TYPE_GENERAL; + matrix->flags |= MAT_FLAG_GENERAL; + } +} + +/* + * Analyze a matrix given that its flags are accurate. + * + * This is the more common operation, hopefully. + */ +static void +analyse_from_flags (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + + if (TEST_MAT_FLAGS(matrix, 0)) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if (TEST_MAT_FLAGS(matrix, (MAT_FLAG_TRANSLATION | + MAT_FLAG_UNIFORM_SCALE | + MAT_FLAG_GENERAL_SCALE))) + { + if ( m[10] == 1.0f && m[14] == 0.0f ) + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + else + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + } + else if (TEST_MAT_FLAGS (matrix, MAT_FLAGS_3D)) + { + if ( m[ 8]==0.0f + && m[ 9]==0.0f + && m[2]==0.0f && m[6]==0.0f && m[10]==1.0f && m[14]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_2D; + } + else + matrix->type = COGL_MATRIX_TYPE_3D; + } + else if ( m[4]==0.0f && m[12]==0.0f + && m[1]==0.0f && m[13]==0.0f + && m[2]==0.0f && m[6]==0.0f + && m[3]==0.0f && m[7]==0.0f && m[11]==-1.0f && m[15]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + } + else + matrix->type = COGL_MATRIX_TYPE_GENERAL; +} + +/* + * Analyze and update the type and flags of a matrix. + * + * If the matrix type is dirty then calls either analyse_from_scratch() or + * analyse_from_flags() to determine its type, according to whether the flags + * are dirty or not, respectively. If the matrix has an inverse and it's dirty + * then calls matrix_invert(). Finally clears the dirty flags. + */ +static void +_cogl_matrix_update_type_and_flags (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_TYPE) + { + if (matrix->flags & MAT_DIRTY_FLAGS) + analyse_from_scratch (matrix); + else + analyse_from_flags (matrix); + } + + matrix->flags &= ~(MAT_DIRTY_FLAGS | MAT_DIRTY_TYPE); +} + +/* + * Compute inverse of a transformation matrix. + * + * @mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). + * + * Calls the matrix inversion function in inv_mat_tab corresponding to the + * given matrix type. In case of failure, updates the MAT_FLAG_SINGULAR flag, + * and copies the identity matrix into CoglMatrix::inv. + */ +static CoglBool +_cogl_matrix_update_inverse (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_FLAGS || + matrix->flags & MAT_DIRTY_INVERSE) + { + _cogl_matrix_update_type_and_flags (matrix); + + if (inv_mat_tab[matrix->type](matrix)) + matrix->flags &= ~MAT_FLAG_SINGULAR; + else + { + matrix->flags |= MAT_FLAG_SINGULAR; + memcpy (matrix->inv, identity, 16 * sizeof (float)); + } + + matrix->flags &= ~MAT_DIRTY_INVERSE; + } + + if (matrix->flags & MAT_FLAG_SINGULAR) + return FALSE; + else + return TRUE; +} + +CoglBool +cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse) +{ + if (_cogl_matrix_update_inverse ((CoglMatrix *)matrix)) + { + cogl_matrix_init_from_array (inverse, matrix->inv); + return TRUE; + } + else + { + cogl_matrix_init_identity (inverse); + return FALSE; + } +} + +/* + * Generate a 4x4 transformation matrix from glRotate parameters, and + * post-multiply the input matrix by it. + * + * \author + * This function was contributed by Erich Boleyn (erich@uruk.org). + * Optimizations contributed by Rudolf Opalla (rudi@khm.de). + */ +static void +_cogl_matrix_rotate (CoglMatrix *matrix, + float angle, + float x, + float y, + float z) +{ + float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; + float m[16]; + CoglBool optimized; + + s = sinf (angle * DEG2RAD); + c = cosf (angle * DEG2RAD); + + memcpy (m, identity, 16 * sizeof (float)); + optimized = FALSE; + +#define M(row,col) m[col*4+row] + + if (x == 0.0f) + { + if (y == 0.0f) + { + if (z != 0.0f) + { + optimized = TRUE; + /* rotate only around z-axis */ + M (0,0) = c; + M (1,1) = c; + if (z < 0.0f) + { + M (0,1) = s; + M (1,0) = -s; + } + else + { + M (0,1) = -s; + M (1,0) = s; + } + } + } + else if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around y-axis */ + M (0,0) = c; + M (2,2) = c; + if (y < 0.0f) + { + M (0,2) = -s; + M (2,0) = s; + } + else + { + M (0,2) = s; + M (2,0) = -s; + } + } + } + else if (y == 0.0f) + { + if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around x-axis */ + M (1,1) = c; + M (2,2) = c; + if (x < 0.0f) + { + M (1,2) = s; + M (2,1) = -s; + } + else + { + M (1,2) = -s; + M (2,1) = s; + } + } + } + + if (!optimized) + { + const float mag = sqrtf (x * x + y * y + z * z); + + if (mag <= 1.0e-4) + { + /* no rotation, leave mat as-is */ + return; + } + + x /= mag; + y /= mag; + z /= mag; + + + /* + * Arbitrary axis rotation matrix. + * + * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied + * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation + * (which is about the X-axis), and the two composite transforms + * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary + * from the arbitrary axis to the X-axis then back. They are + * all elementary rotations. + * + * Rz' is a rotation about the Z-axis, to bring the axis vector + * into the x-z plane. Then Ry' is applied, rotating about the + * Y-axis to bring the axis vector parallel with the X-axis. The + * rotation about the X-axis is then performed. Ry and Rz are + * simply the respective inverse transforms to bring the arbitrary + * axis back to it's original orientation. The first transforms + * Rz' and Ry' are considered inverses, since the data from the + * arbitrary axis gives you info on how to get to it, not how + * to get away from it, and an inverse must be applied. + * + * The basic calculation used is to recognize that the arbitrary + * axis vector (x, y, z), since it is of unit length, actually + * represents the sines and cosines of the angles to rotate the + * X-axis to the same orientation, with theta being the angle about + * Z and phi the angle about Y (in the order described above) + * as follows: + * + * cos ( theta ) = x / sqrt ( 1 - z^2 ) + * sin ( theta ) = y / sqrt ( 1 - z^2 ) + * + * cos ( phi ) = sqrt ( 1 - z^2 ) + * sin ( phi ) = z + * + * Note that cos ( phi ) can further be inserted to the above + * formulas: + * + * cos ( theta ) = x / cos ( phi ) + * sin ( theta ) = y / sin ( phi ) + * + * ...etc. Because of those relations and the standard trigonometric + * relations, it is pssible to reduce the transforms down to what + * is used below. It may be that any primary axis chosen will give the + * same results (modulo a sign convention) using thie method. + * + * Particularly nice is to notice that all divisions that might + * have caused trouble when parallel to certain planes or + * axis go away with care paid to reducing the expressions. + * After checking, it does perform correctly under all cases, since + * in all the cases of division where the denominator would have + * been zero, the numerator would have been zero as well, giving + * the expected result. + */ + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * s; + ys = y * s; + zs = z * s; + one_c = 1.0f - c; + + /* We already hold the identity-matrix so we can skip some statements */ + M (0,0) = (one_c * xx) + c; + M (0,1) = (one_c * xy) - zs; + M (0,2) = (one_c * zx) + ys; + /* M (0,3) = 0.0f; */ + + M (1,0) = (one_c * xy) + zs; + M (1,1) = (one_c * yy) + c; + M (1,2) = (one_c * yz) - xs; + /* M (1,3) = 0.0f; */ + + M (2,0) = (one_c * zx) - ys; + M (2,1) = (one_c * yz) + xs; + M (2,2) = (one_c * zz) + c; + /* M (2,3) = 0.0f; */ + + /* + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; + */ + } +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_ROTATION); +} + +void +cogl_matrix_rotate (CoglMatrix *matrix, + float angle, + float x, + float y, + float z) +{ + _cogl_matrix_rotate (matrix, angle, x, y, z); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_matrix_rotate_quaternion (CoglMatrix *matrix, + const CoglQuaternion *quaternion) +{ + CoglMatrix rotation_transform; + + cogl_matrix_init_from_quaternion (&rotation_transform, quaternion); + cogl_matrix_multiply (matrix, matrix, &rotation_transform); +} + +void +cogl_matrix_rotate_euler (CoglMatrix *matrix, + const CoglEuler *euler) +{ + CoglMatrix rotation_transform; + + cogl_matrix_init_from_euler (&rotation_transform, euler); + cogl_matrix_multiply (matrix, matrix, &rotation_transform); +} + +/* + * Apply a perspective projection matrix. + * + * Creates the projection matrix and multiplies it with matrix, marking the + * MAT_FLAG_PERSPECTIVE flag. + */ +static void +_cogl_matrix_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float nearval, + float farval) +{ + float x, y, a, b, c, d; + float m[16]; + + x = (2.0f * nearval) / (right - left); + y = (2.0f * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0f * farval * nearval) / (farval - nearval); /* error? */ + +#define M(row,col) m[col*4+row] + M (0,0) = x; M (0,1) = 0.0f; M (0,2) = a; M (0,3) = 0.0f; + M (1,0) = 0.0f; M (1,1) = y; M (1,2) = b; M (1,3) = 0.0f; + M (2,0) = 0.0f; M (2,1) = 0.0f; M (2,2) = c; M (2,3) = d; + M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = -1.0f; M (3,3) = 0.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_PERSPECTIVE); +} + +void +cogl_matrix_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far) +{ + _cogl_matrix_frustum (matrix, left, right, bottom, top, z_near, z_far); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_matrix_perspective (CoglMatrix *matrix, + float fov_y, + float aspect, + float z_near, + float z_far) +{ + float ymax = z_near * tan (fov_y * G_PI / 360.0); + + cogl_matrix_frustum (matrix, + -ymax * aspect, /* left */ + ymax * aspect, /* right */ + -ymax, /* bottom */ + ymax, /* top */ + z_near, + z_far); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +/* + * Apply an orthographic projection matrix. + * + * Creates the projection matrix and multiplies it with matrix, marking the + * MAT_FLAG_GENERAL_SCALE and MAT_FLAG_TRANSLATION flags. + */ +static void +_cogl_matrix_orthographic (CoglMatrix *matrix, + float x_1, + float y_1, + float x_2, + float y_2, + float nearval, + float farval) +{ + float m[16]; + +#define M(row, col) m[col * 4 + row] + M (0,0) = 2.0f / (x_2 - x_1); + M (0,1) = 0.0f; + M (0,2) = 0.0f; + M (0,3) = -(x_2 + x_1) / (x_2 - x_1); + + M (1,0) = 0.0f; + M (1,1) = 2.0f / (y_1 - y_2); + M (1,2) = 0.0f; + M (1,3) = -(y_1 + y_2) / (y_1 - y_2); + + M (2,0) = 0.0f; + M (2,1) = 0.0f; + M (2,2) = -2.0f / (farval - nearval); + M (2,3) = -(farval + nearval) / (farval - nearval); + + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, + (MAT_FLAG_GENERAL_SCALE | + MAT_FLAG_TRANSLATION)); +} + +void +cogl_matrix_ortho (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float near, + float far) +{ + _cogl_matrix_orthographic (matrix, left, top, right, bottom, near, far); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +cogl_matrix_orthographic (CoglMatrix *matrix, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far) +{ + _cogl_matrix_orthographic (matrix, x_1, y_1, x_2, y_2, near, far); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +/* + * Multiply a matrix with a general scaling matrix. + * + * Multiplies in-place the elements of matrix by the scale factors. Checks if + * the scales factors are roughly the same, marking the MAT_FLAG_UNIFORM_SCALE + * flag, or MAT_FLAG_GENERAL_SCALE. Marks the MAT_DIRTY_TYPE and + * MAT_DIRTY_INVERSE dirty flags. + */ +static void +_cogl_matrix_scale (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[0] *= x; m[4] *= y; m[8] *= z; + m[1] *= x; m[5] *= y; m[9] *= z; + m[2] *= x; m[6] *= y; m[10] *= z; + m[3] *= x; m[7] *= y; m[11] *= z; + + if (fabsf (x - y) < 1e-8 && fabsf (x - z) < 1e-8) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + matrix->flags |= (MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); +} + +void +cogl_matrix_scale (CoglMatrix *matrix, + float sx, + float sy, + float sz) +{ + _cogl_matrix_scale (matrix, sx, sy, sz); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +/* + * Multiply a matrix with a translation matrix. + * + * Adds the translation coordinates to the elements of matrix in-place. Marks + * the MAT_FLAG_TRANSLATION flag, and the MAT_DIRTY_TYPE and MAT_DIRTY_INVERSE + * dirty flags. + */ +static void +_cogl_matrix_translate (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[12] = m[0] * x + m[4] * y + m[8] * z + m[12]; + m[13] = m[1] * x + m[5] * y + m[9] * z + m[13]; + m[14] = m[2] * x + m[6] * y + m[10] * z + m[14]; + m[15] = m[3] * x + m[7] * y + m[11] * z + m[15]; + + matrix->flags |= (MAT_FLAG_TRANSLATION | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); +} + +void +cogl_matrix_translate (CoglMatrix *matrix, + float x, + float y, + float z) +{ + _cogl_matrix_translate (matrix, x, y, z); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +#if 0 +/* + * Set matrix to do viewport and depthrange mapping. + * Transforms Normalized Device Coords to window/Z values. + */ +static void +_cogl_matrix_viewport (CoglMatrix *matrix, + float x, float y, + float width, float height, + float zNear, float zFar, float depthMax) +{ + float *m = (float *)matrix; + m[MAT_SX] = width / 2.0f; + m[MAT_TX] = m[MAT_SX] + x; + m[MAT_SY] = height / 2.0f; + m[MAT_TY] = m[MAT_SY] + y; + m[MAT_SZ] = depthMax * ((zFar - zNear) / 2.0f); + m[MAT_TZ] = depthMax * ((zFar - zNear) / 2.0f + zNear); + matrix->flags = MAT_FLAG_GENERAL_SCALE | MAT_FLAG_TRANSLATION; + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; +} +#endif + +/* + * Set a matrix to the identity matrix. + * + * @mat matrix. + * + * Copies ::identity into \p CoglMatrix::m, and into CoglMatrix::inv if + * not NULL. Sets the matrix type to identity, resets the flags. It + * doesn't initialize the inverse matrix, it just marks it dirty. + */ +static void +_cogl_matrix_init_identity (CoglMatrix *matrix) +{ + memcpy (matrix, identity, 16 * sizeof (float)); + + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + matrix->flags = MAT_DIRTY_INVERSE; +} + +void +cogl_matrix_init_identity (CoglMatrix *matrix) +{ + _cogl_matrix_init_identity (matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +/* + * Set a matrix to the (tx, ty, tz) translation matrix. + * + * @matix matrix. + * @tx x coordinate of the translation vector + * @ty y coordinate of the translation vector + * @tz z coordinate of the translation vector + */ +static void +_cogl_matrix_init_translation (CoglMatrix *matrix, + float tx, + float ty, + float tz) +{ + memcpy (matrix, identity, 16 * sizeof (float)); + + matrix->xw = tx; + matrix->yw = ty; + matrix->zw = tz; + + matrix->type = COGL_MATRIX_TYPE_3D; + matrix->flags = MAT_FLAG_TRANSLATION | MAT_DIRTY_INVERSE; +} + +void +cogl_matrix_init_translation (CoglMatrix *matrix, + float tx, + float ty, + float tz) +{ + _cogl_matrix_init_translation (matrix, tx, ty, tz); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +#if 0 +/* + * Test if the given matrix preserves vector lengths. + */ +static CoglBool +_cogl_matrix_is_length_preserving (const CoglMatrix *m) +{ + return TEST_MAT_FLAGS (m, MAT_FLAGS_LENGTH_PRESERVING); +} + +/* + * Test if the given matrix does any rotation. + * (or perhaps if the upper-left 3x3 is non-identity) + */ +static CoglBool +_cogl_matrix_has_rotation (const CoglMatrix *matrix) +{ + if (matrix->flags & (MAT_FLAG_GENERAL | + MAT_FLAG_ROTATION | + MAT_FLAG_GENERAL_3D | + MAT_FLAG_PERSPECTIVE)) + return TRUE; + else + return FALSE; +} + +static CoglBool +_cogl_matrix_is_general_scale (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_FLAG_GENERAL_SCALE) ? TRUE : FALSE; +} + +static CoglBool +_cogl_matrix_is_dirty (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_DIRTY_ALL) ? TRUE : FALSE; +} +#endif + +/* + * Loads a matrix array into CoglMatrix. + * + * @m matrix array. + * @mat matrix. + * + * Copies \p m into CoglMatrix::m and marks the MAT_FLAG_GENERAL and + * MAT_DIRTY_ALL + * flags. + */ +static void +_cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) +{ + memcpy (matrix, array, 16 * sizeof (float)); + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + +void +cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) +{ + _cogl_matrix_init_from_array (matrix, array); + _COGL_MATRIX_DEBUG_PRINT (matrix); +} + +void +_cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, + const CoglMatrix *src) +{ + memcpy (matrix, src, 16 * sizeof (float)); + matrix->type = src->type; + matrix->flags = src->flags | MAT_DIRTY_INVERSE; +} + +static void +_cogl_matrix_init_from_quaternion (CoglMatrix *matrix, + const CoglQuaternion *quaternion) +{ + float qnorm = _COGL_QUATERNION_NORM (quaternion); + float s = (qnorm > 0.0f) ? (2.0f / qnorm) : 0.0f; + float xs = quaternion->x * s; + float ys = quaternion->y * s; + float zs = quaternion->z * s; + float wx = quaternion->w * xs; + float wy = quaternion->w * ys; + float wz = quaternion->w * zs; + float xx = quaternion->x * xs; + float xy = quaternion->x * ys; + float xz = quaternion->x * zs; + float yy = quaternion->y * ys; + float yz = quaternion->y * zs; + float zz = quaternion->z * zs; + + matrix->xx = 1.0f - (yy + zz); + matrix->yx = xy + wz; + matrix->zx = xz - wy; + matrix->xy = xy - wz; + matrix->yy = 1.0f - (xx + zz); + matrix->zy = yz + wx; + matrix->xz = xz + wy; + matrix->yz = yz - wx; + matrix->zz = 1.0f - (xx + yy); + matrix->xw = matrix->yw = matrix->zw = 0.0f; + matrix->wx = matrix->wy = matrix->wz = 0.0f; + matrix->ww = 1.0f; + + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + +void +cogl_matrix_init_from_quaternion (CoglMatrix *matrix, + const CoglQuaternion *quaternion) +{ + _cogl_matrix_init_from_quaternion (matrix, quaternion); +} + +void +cogl_matrix_init_from_euler (CoglMatrix *matrix, + const CoglEuler *euler) +{ + /* Convert angles to radians */ + float heading_rad = euler->heading / 180.0f * G_PI; + float pitch_rad = euler->pitch / 180.0f * G_PI; + float roll_rad = euler->roll / 180.0f * G_PI; + /* Pre-calculate the sin and cos */ + float sin_heading = sinf (heading_rad); + float cos_heading = cosf (heading_rad); + float sin_pitch = sinf (pitch_rad); + float cos_pitch = cosf (pitch_rad); + float sin_roll = sinf (roll_rad); + float cos_roll = cosf (roll_rad); + + /* These calculations are based on the following website but they + * use a different order for the rotations so it has been modified + * slightly. + * http://www.euclideanspace.com/maths/geometry/ + * rotations/conversions/eulerToMatrix/index.htm + */ + + /* Heading rotation x=0, y=1, z=0 gives: + * + * [ ch 0 sh 0 ] + * [ 0 1 0 0 ] + * [ -sh 0 ch 0 ] + * [ 0 0 0 1 ] + * + * Pitch rotation x=1, y=0, z=0 gives: + * [ 1 0 0 0 ] + * [ 0 cp -sp 0 ] + * [ 0 sp cp 0 ] + * [ 0 0 0 1 ] + * + * Roll rotation x=0, y=0, z=1 gives: + * [ cr -sr 0 0 ] + * [ sr cr 0 0 ] + * [ 0 0 1 0 ] + * [ 0 0 0 1 ] + * + * Heading matrix * pitch matrix = + * [ ch sh*sp cp*sh 0 ] + * [ 0 cp -sp 0 ] + * [ -sh ch*sp ch*cp 0 ] + * [ 0 0 0 1 ] + * + * That matrix * roll matrix = + * [ ch*cr + sh*sp*sr sh*sp*cr - ch*sr sh*cp 0 ] + * [ cp*sr cp*cr -sp 0 ] + * [ ch*sp*sr - sh*cr sh*sr + ch*sp*cr ch*cp 0 ] + * [ 0 0 0 1 ] + */ + + matrix->xx = cos_heading * cos_roll + sin_heading * sin_pitch * sin_roll; + matrix->yx = cos_pitch * sin_roll; + matrix->zx = cos_heading * sin_pitch * sin_roll - sin_heading * cos_roll; + matrix->wx = 0.0f; + + matrix->xy = sin_heading * sin_pitch * cos_roll - cos_heading * sin_roll; + matrix->yy = cos_pitch * cos_roll; + matrix->zy = sin_heading * sin_roll + cos_heading * sin_pitch * cos_roll; + matrix->wy = 0.0f; + + matrix->xz = sin_heading * cos_pitch; + matrix->yz = -sin_pitch; + matrix->zz = cos_heading * cos_pitch; + matrix->wz = 0; + + matrix->xw = 0; + matrix->yw = 0; + matrix->zw = 0; + matrix->ww = 1; + + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + +/* + * Transpose a float matrix. + */ +static void +_cogl_matrix_util_transposef (float to[16], const float from[16]) +{ + to[0] = from[0]; + to[1] = from[4]; + to[2] = from[8]; + to[3] = from[12]; + to[4] = from[1]; + to[5] = from[5]; + to[6] = from[9]; + to[7] = from[13]; + to[8] = from[2]; + to[9] = from[6]; + to[10] = from[10]; + to[11] = from[14]; + to[12] = from[3]; + to[13] = from[7]; + to[14] = from[11]; + to[15] = from[15]; +} + +void +cogl_matrix_view_2d_in_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float z_near, + float z_2d, + float width_2d, + float height_2d) +{ + float left_2d_plane = left / z_near * z_2d; + float right_2d_plane = right / z_near * z_2d; + float bottom_2d_plane = bottom / z_near * z_2d; + float top_2d_plane = top / z_near * z_2d; + + float width_2d_start = right_2d_plane - left_2d_plane; + float height_2d_start = top_2d_plane - bottom_2d_plane; + + /* Factors to scale from framebuffer geometry to frustum + * cross-section geometry. */ + float width_scale = width_2d_start / width_2d; + float height_scale = height_2d_start / height_2d; + + cogl_matrix_translate (matrix, + left_2d_plane, top_2d_plane, -z_2d); + + cogl_matrix_scale (matrix, width_scale, -height_scale, width_scale); +} + +/* Assuming a symmetric perspective matrix is being used for your + * projective transform this convenience function lets you compose a + * view transform such that geometry on the z=0 plane will map to + * screen coordinates with a top left origin of (0,0) and with the + * given width and height. + */ +void +cogl_matrix_view_2d_in_perspective (CoglMatrix *matrix, + float fov_y, + float aspect, + float z_near, + float z_2d, + float width_2d, + float height_2d) +{ + float top = z_near * tan (fov_y * G_PI / 360.0); + cogl_matrix_view_2d_in_frustum (matrix, + -top * aspect, + top * aspect, + -top, + top, + z_near, + z_2d, + width_2d, + height_2d); +} + +CoglBool +cogl_matrix_equal (const void *v1, const void *v2) +{ + const CoglMatrix *a = v1; + const CoglMatrix *b = v2; + + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + /* We want to avoid having a fuzzy _equal() function (e.g. that uses + * an arbitrary epsilon value) since this function noteably conforms + * to the prototype suitable for use with g_hash_table_new() and a + * fuzzy hash function isn't really appropriate for comparing hash + * table keys since it's possible that you could end up fetching + * different values if you end up with multiple similar keys in use + * at the same time. If you consider that fuzzyness allows cases + * such as A == B == C but A != C then you could also end up loosing + * values in a hash table. + * + * We do at least use the == operator to compare values though so + * that -0 is considered equal to 0. + */ + + /* XXX: We don't compare the flags, inverse matrix or padding */ + if (a->xx == b->xx && + a->xy == b->xy && + a->xz == b->xz && + a->xw == b->xw && + a->yx == b->yx && + a->yy == b->yy && + a->yz == b->yz && + a->yw == b->yw && + a->zx == b->zx && + a->zy == b->zy && + a->zz == b->zz && + a->zw == b->zw && + a->wx == b->wx && + a->wy == b->wy && + a->wz == b->wz && + a->ww == b->ww) + return TRUE; + else + return FALSE; +} + +CoglMatrix * +cogl_matrix_copy (const CoglMatrix *matrix) +{ + if (G_LIKELY (matrix)) + return g_slice_dup (CoglMatrix, matrix); + + return NULL; +} + +void +cogl_matrix_free (CoglMatrix *matrix) +{ + g_slice_free (CoglMatrix, matrix); +} + +const float * +cogl_matrix_get_array (const CoglMatrix *matrix) +{ + return (float *)matrix; +} + +void +cogl_matrix_transform_point (const CoglMatrix *matrix, + float *x, + float *y, + float *z, + float *w) +{ + float _x = *x, _y = *y, _z = *z, _w = *w; + + *x = matrix->xx * _x + matrix->xy * _y + matrix->xz * _z + matrix->xw * _w; + *y = matrix->yx * _x + matrix->yy * _y + matrix->yz * _z + matrix->yw * _w; + *z = matrix->zx * _x + matrix->zy * _y + matrix->zz * _z + matrix->zw * _w; + *w = matrix->wx * _x + matrix->wy * _y + matrix->wz * _z + matrix->ww * _w; +} + +typedef struct _Point2f +{ + float x; + float y; +} Point2f; + +typedef struct _Point3f +{ + float x; + float y; + float z; +} Point3f; + +typedef struct _Point4f +{ + float x; + float y; + float z; + float w; +} Point4f; + +static void +_cogl_matrix_transform_points_f2 (const CoglMatrix *matrix, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + int i; + + for (i = 0; i < n_points; i++) + { + Point2f p = *(Point2f *)((uint8_t *)points_in + i * stride_in); + Point3f *o = (Point3f *)((uint8_t *)points_out + i * stride_out); + + o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xw; + o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yw; + o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zw; + } +} + +static void +_cogl_matrix_project_points_f2 (const CoglMatrix *matrix, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + int i; + + for (i = 0; i < n_points; i++) + { + Point2f p = *(Point2f *)((uint8_t *)points_in + i * stride_in); + Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); + + o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xw; + o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yw; + o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zw; + o->w = matrix->wx * p.x + matrix->wy * p.y + matrix->ww; + } +} + +static void +_cogl_matrix_transform_points_f3 (const CoglMatrix *matrix, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + int i; + + for (i = 0; i < n_points; i++) + { + Point3f p = *(Point3f *)((uint8_t *)points_in + i * stride_in); + Point3f *o = (Point3f *)((uint8_t *)points_out + i * stride_out); + + o->x = matrix->xx * p.x + matrix->xy * p.y + + matrix->xz * p.z + matrix->xw; + o->y = matrix->yx * p.x + matrix->yy * p.y + + matrix->yz * p.z + matrix->yw; + o->z = matrix->zx * p.x + matrix->zy * p.y + + matrix->zz * p.z + matrix->zw; + } +} + +static void +_cogl_matrix_project_points_f3 (const CoglMatrix *matrix, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + int i; + + for (i = 0; i < n_points; i++) + { + Point3f p = *(Point3f *)((uint8_t *)points_in + i * stride_in); + Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); + + o->x = matrix->xx * p.x + matrix->xy * p.y + + matrix->xz * p.z + matrix->xw; + o->y = matrix->yx * p.x + matrix->yy * p.y + + matrix->yz * p.z + matrix->yw; + o->z = matrix->zx * p.x + matrix->zy * p.y + + matrix->zz * p.z + matrix->zw; + o->w = matrix->wx * p.x + matrix->wy * p.y + + matrix->wz * p.z + matrix->ww; + } +} + +static void +_cogl_matrix_project_points_f4 (const CoglMatrix *matrix, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + int i; + + for (i = 0; i < n_points; i++) + { + Point4f p = *(Point4f *)((uint8_t *)points_in + i * stride_in); + Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); + + o->x = matrix->xx * p.x + matrix->xy * p.y + + matrix->xz * p.z + matrix->xw * p.w; + o->y = matrix->yx * p.x + matrix->yy * p.y + + matrix->yz * p.z + matrix->yw * p.w; + o->z = matrix->zx * p.x + matrix->zy * p.y + + matrix->zz * p.z + matrix->zw * p.w; + o->w = matrix->wx * p.x + matrix->wy * p.y + + matrix->wz * p.z + matrix->ww * p.w; + } +} + +void +cogl_matrix_transform_points (const CoglMatrix *matrix, + int n_components, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + /* The results of transforming always have three components... */ + _COGL_RETURN_IF_FAIL (stride_out >= sizeof (Point3f)); + + if (n_components == 2) + _cogl_matrix_transform_points_f2 (matrix, + stride_in, points_in, + stride_out, points_out, + n_points); + else + { + _COGL_RETURN_IF_FAIL (n_components == 3); + + _cogl_matrix_transform_points_f3 (matrix, + stride_in, points_in, + stride_out, points_out, + n_points); + } +} + +void +cogl_matrix_project_points (const CoglMatrix *matrix, + int n_components, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points) +{ + if (n_components == 2) + _cogl_matrix_project_points_f2 (matrix, + stride_in, points_in, + stride_out, points_out, + n_points); + else if (n_components == 3) + _cogl_matrix_project_points_f3 (matrix, + stride_in, points_in, + stride_out, points_out, + n_points); + else + { + _COGL_RETURN_IF_FAIL (n_components == 4); + + _cogl_matrix_project_points_f4 (matrix, + stride_in, points_in, + stride_out, points_out, + n_points); + } +} + +CoglBool +cogl_matrix_is_identity (const CoglMatrix *matrix) +{ + if (!(matrix->flags & MAT_DIRTY_TYPE) && + matrix->type == COGL_MATRIX_TYPE_IDENTITY) + return TRUE; + else + return memcmp (matrix, identity, sizeof (float) * 16) == 0; +} + +void +cogl_matrix_look_at (CoglMatrix *matrix, + float eye_position_x, + float eye_position_y, + float eye_position_z, + float object_x, + float object_y, + float object_z, + float world_up_x, + float world_up_y, + float world_up_z) +{ + CoglMatrix tmp; + float forward[3]; + float side[3]; + float up[3]; + + /* Get a unit viewing direction vector */ + cogl_vector3_init (forward, + object_x - eye_position_x, + object_y - eye_position_y, + object_z - eye_position_z); + cogl_vector3_normalize (forward); + + cogl_vector3_init (up, world_up_x, world_up_y, world_up_z); + + /* Take the sideways direction as being perpendicular to the viewing + * direction and the word up vector. */ + cogl_vector3_cross_product (side, forward, up); + cogl_vector3_normalize (side); + + /* Now we have unit sideways and forward-direction vectors calculate + * a new mutually perpendicular up vector. */ + cogl_vector3_cross_product (up, side, forward); + + tmp.xx = side[0]; + tmp.yx = side[1]; + tmp.zx = side[2]; + tmp.wx = 0; + + tmp.xy = up[0]; + tmp.yy = up[1]; + tmp.zy = up[2]; + tmp.wy = 0; + + tmp.xz = -forward[0]; + tmp.yz = -forward[1]; + tmp.zz = -forward[2]; + tmp.wz = 0; + + tmp.xw = 0; + tmp.yw = 0; + tmp.zw = 0; + tmp.ww = 1; + + tmp.flags = (MAT_FLAG_GENERAL_3D | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); + + cogl_matrix_translate (&tmp, -eye_position_x, -eye_position_y, -eye_position_z); + + cogl_matrix_multiply (matrix, matrix, &tmp); +} + +void +cogl_matrix_transpose (CoglMatrix *matrix) +{ + float new_values[16]; + + /* We don't need to do anything if the matrix is the identity matrix */ + if (!(matrix->flags & MAT_DIRTY_TYPE) && + matrix->type == COGL_MATRIX_TYPE_IDENTITY) + return; + + _cogl_matrix_util_transposef (new_values, cogl_matrix_get_array (matrix)); + + cogl_matrix_init_from_array (matrix, new_values); +} + +#ifdef COGL_HAS_GTYPE_SUPPORT +GType +cogl_gtype_matrix_get_type (void) +{ + return cogl_matrix_get_gtype (); +} +#endif diff --git a/cogl/cogl/cogl-matrix.h b/cogl/cogl/cogl-matrix.h new file mode 100644 index 000000000..58ec5c760 --- /dev/null +++ b/cogl/cogl/cogl-matrix.h @@ -0,0 +1,821 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_MATRIX_H +#define __COGL_MATRIX_H + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif /* COGL_HAS_GTYPE_SUPPORT */ + +#include +#include + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +#include +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-matrix + * @short_description: Functions for initializing and manipulating 4x4 matrices + * + * Matrices are used in Cogl to describe affine model-view transforms, texture + * transforms, and projective transforms. This exposes a utility API that can + * be used for direct manipulation of these matrices. + */ + +/** + * CoglMatrix: + * + * A CoglMatrix holds a 4x4 transform matrix. This is a single precision, + * column-major matrix which means it is compatible with what OpenGL expects. + * + * A CoglMatrix can represent transforms such as, rotations, scaling, + * translation, sheering, and linear projections. You can combine these + * transforms by multiplying multiple matrices in the order you want them + * applied. + * + * The transformation of a vertex (x, y, z, w) by a CoglMatrix is given by: + * + * |[ + * x_new = xx * x + xy * y + xz * z + xw * w + * y_new = yx * x + yy * y + yz * z + yw * w + * z_new = zx * x + zy * y + zz * z + zw * w + * w_new = wx * x + wy * y + wz * z + ww * w + * ]| + * + * Where w is normally 1 + * + * You must consider the members of the CoglMatrix structure read only, + * and all matrix modifications must be done via the cogl_matrix API. This + * allows Cogl to annotate the matrices internally. Violation of this will give + * undefined results. If you need to initialize a matrix with a constant other + * than the identity matrix you can use cogl_matrix_init_from_array(). + */ +struct _CoglMatrix +{ + /* column 0 */ + float xx; + float yx; + float zx; + float wx; + + /* column 1 */ + float xy; + float yy; + float zy; + float wy; + + /* column 2 */ + float xz; + float yz; + float zz; + float wz; + + /* column 3 */ + float xw; + float yw; + float zw; + float ww; + + /*< private >*/ + + /* Note: we may want to extend this later with private flags + * and a cache of the inverse transform matrix. */ + float COGL_PRIVATE (inv)[16]; + unsigned long COGL_PRIVATE (type); + unsigned long COGL_PRIVATE (flags); + unsigned long COGL_PRIVATE (_padding3); +}; +COGL_STRUCT_SIZE_ASSERT (CoglMatrix, 128 + sizeof (unsigned long) * 3); + + +/** + * cogl_matrix_init_identity: + * @matrix: A 4x4 transformation matrix + * + * Resets matrix to the identity matrix: + * + * |[ + * .xx=1; .xy=0; .xz=0; .xw=0; + * .yx=0; .yy=1; .yz=0; .yw=0; + * .zx=0; .zy=0; .zz=1; .zw=0; + * .wx=0; .wy=0; .wz=0; .ww=1; + * ]| + */ +void +cogl_matrix_init_identity (CoglMatrix *matrix); + +/** + * cogl_matrix_init_translation: + * @matrix: A 4x4 transformation matrix + * @tx: x coordinate of the translation vector + * @ty: y coordinate of the translation vector + * @tz: z coordinate of the translation vector + * + * Resets matrix to the (tx, ty, tz) translation matrix: + * + * |[ + * .xx=1; .xy=0; .xz=0; .xw=tx; + * .yx=0; .yy=1; .yz=0; .yw=ty; + * .zx=0; .zy=0; .zz=1; .zw=tz; + * .wx=0; .wy=0; .wz=0; .ww=1; + * ]| + * + * Since: 2.0 + */ +void +cogl_matrix_init_translation (CoglMatrix *matrix, + float tx, + float ty, + float tz); + +/** + * cogl_matrix_multiply: + * @result: The address of a 4x4 matrix to store the result in + * @a: A 4x4 transformation matrix + * @b: A 4x4 transformation matrix + * + * Multiplies the two supplied matrices together and stores + * the resulting matrix inside @result. + * + * It is possible to multiply the @a matrix in-place, so + * @result can be equal to @a but can't be equal to @b. + */ +void +cogl_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b); + +/** + * cogl_matrix_rotate: + * @matrix: A 4x4 transformation matrix + * @angle: The angle you want to rotate in degrees + * @x: X component of your rotation vector + * @y: Y component of your rotation vector + * @z: Z component of your rotation vector + * + * Multiplies @matrix with a rotation matrix that applies a rotation + * of @angle degrees around the specified 3D vector. + */ +void +cogl_matrix_rotate (CoglMatrix *matrix, + float angle, + float x, + float y, + float z); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_rotate_quaternion: + * @matrix: A 4x4 transformation matrix + * @quaternion: A quaternion describing a rotation + * + * Multiplies @matrix with a rotation transformation described by the + * given #CoglQuaternion. + * + * Since: 2.0 + */ +void +cogl_matrix_rotate_quaternion (CoglMatrix *matrix, + const CoglQuaternion *quaternion); + +/** + * cogl_matrix_rotate_euler: + * @matrix: A 4x4 transformation matrix + * @euler: A euler describing a rotation + * + * Multiplies @matrix with a rotation transformation described by the + * given #CoglEuler. + * + * Since: 2.0 + */ +void +cogl_matrix_rotate_euler (CoglMatrix *matrix, + const CoglEuler *euler); +#endif + +/** + * cogl_matrix_translate: + * @matrix: A 4x4 transformation matrix + * @x: The X translation you want to apply + * @y: The Y translation you want to apply + * @z: The Z translation you want to apply + * + * Multiplies @matrix with a transform matrix that translates along + * the X, Y and Z axis. + */ +void +cogl_matrix_translate (CoglMatrix *matrix, + float x, + float y, + float z); + +/** + * cogl_matrix_scale: + * @matrix: A 4x4 transformation matrix + * @sx: The X scale factor + * @sy: The Y scale factor + * @sz: The Z scale factor + * + * Multiplies @matrix with a transform matrix that scales along the X, + * Y and Z axis. + */ +void +cogl_matrix_scale (CoglMatrix *matrix, + float sx, + float sy, + float sz); + +/** + * cogl_matrix_look_at: + * @matrix: A 4x4 transformation matrix + * @eye_position_x: The X coordinate to look from + * @eye_position_y: The Y coordinate to look from + * @eye_position_z: The Z coordinate to look from + * @object_x: The X coordinate of the object to look at + * @object_y: The Y coordinate of the object to look at + * @object_z: The Z coordinate of the object to look at + * @world_up_x: The X component of the world's up direction vector + * @world_up_y: The Y component of the world's up direction vector + * @world_up_z: The Z component of the world's up direction vector + * + * Applies a view transform @matrix that positions the camera at + * the coordinate (@eye_position_x, @eye_position_y, @eye_position_z) + * looking towards an object at the coordinate (@object_x, @object_y, + * @object_z). The top of the camera is aligned to the given world up + * vector, which is normally simply (0, 1, 0) to map up to the + * positive direction of the y axis. + * + * Because there is a lot of missleading documentation online for + * gluLookAt regarding the up vector we want to try and be a bit + * clearer here. + * + * The up vector should simply be relative to your world coordinates + * and does not need to change as you move the eye and object + * positions. Many online sources may claim that the up vector needs + * to be perpendicular to the vector between the eye and object + * position (partly because the man page is somewhat missleading) but + * that is not necessary for this function. + * + * You should never look directly along the world-up + * vector. + * + * It is assumed you are using a typical projection matrix where + * your origin maps to the center of your viewport. + * + * Almost always when you use this function it should be the first + * transform applied to a new modelview transform + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_matrix_look_at (CoglMatrix *matrix, + float eye_position_x, + float eye_position_y, + float eye_position_z, + float object_x, + float object_y, + float object_z, + float world_up_x, + float world_up_y, + float world_up_z); + +/** + * cogl_matrix_frustum: + * @matrix: A 4x4 transformation matrix + * @left: X position of the left clipping plane where it + * intersects the near clipping plane + * @right: X position of the right clipping plane where it + * intersects the near clipping plane + * @bottom: Y position of the bottom clipping plane where it + * intersects the near clipping plane + * @top: Y position of the top clipping plane where it intersects + * the near clipping plane + * @z_near: The distance to the near clipping plane (Must be positive) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Multiplies @matrix by the given frustum perspective matrix. + */ +void +cogl_matrix_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float z_near, + float z_far); + +/** + * cogl_matrix_perspective: + * @matrix: A 4x4 transformation matrix + * @fov_y: Vertical field of view angle in degrees. + * @aspect: The (width over height) aspect ratio for display + * @z_near: The distance to the near clipping plane (Must be positive, + * and must not be 0) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Multiplies @matrix by the described perspective matrix + * + * You should be careful not to have to great a @z_far / @z_near + * ratio since that will reduce the effectiveness of depth testing + * since there wont be enough precision to identify the depth of + * objects near to each other. + */ +void +cogl_matrix_perspective (CoglMatrix *matrix, + float fov_y, + float aspect, + float z_near, + float z_far); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_orthographic: + * @matrix: A 4x4 transformation matrix + * @x_1: The x coordinate for the first vertical clipping plane + * @y_1: The y coordinate for the first horizontal clipping plane + * @x_2: The x coordinate for the second vertical clipping plane + * @y_2: The y coordinate for the second horizontal clipping plane + * @near: The distance to the near clipping + * plane (will be negative if the plane is + * behind the viewer) + * @far: The distance to the far clipping + * plane (will be negative if the plane is + * behind the viewer) + * + * Multiplies @matrix by a parallel projection matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_matrix_orthographic (CoglMatrix *matrix, + float x_1, + float y_1, + float x_2, + float y_2, + float near, + float far); +#endif + +/** + * cogl_matrix_ortho: + * @matrix: A 4x4 transformation matrix + * @left: The coordinate for the left clipping plane + * @right: The coordinate for the right clipping plane + * @bottom: The coordinate for the bottom clipping plane + * @top: The coordinate for the top clipping plane + * @near: The distance to the near clipping + * plane (will be negative if the plane is + * behind the viewer) + * @far: The distance to the far clipping + * plane (will be negative if the plane is + * behind the viewer) + * + * Multiplies @matrix by a parallel projection matrix. + * + * Deprecated: 1.10: Use cogl_matrix_orthographic() + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_matrix_orthographic) +void +cogl_matrix_ortho (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float near, + float far); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_view_2d_in_frustum: + * @matrix: A 4x4 transformation matrix + * @left: coord of left vertical clipping plane + * @right: coord of right vertical clipping plane + * @bottom: coord of bottom horizontal clipping plane + * @top: coord of top horizontal clipping plane + * @z_near: The distance to the near clip plane. Never pass 0 and always pass + * a positive number. + * @z_2d: The distance to the 2D plane. (Should always be positive and + * be between @z_near and the z_far value that was passed to + * cogl_matrix_frustum()) + * @width_2d: The width of the 2D coordinate system + * @height_2d: The height of the 2D coordinate system + * + * Multiplies @matrix by a view transform that maps the 2D coordinates + * (0,0) top left and (@width_2d,@height_2d) bottom right the full viewport + * size. Geometry at a depth of 0 will now lie on this 2D plane. + * + * Note: this doesn't multiply the matrix by any projection matrix, + * but it assumes you have a perspective projection as defined by + * passing the corresponding arguments to cogl_matrix_frustum(). + + * Toolkits such as Clutter that mix 2D and 3D drawing can use this to + * create a 2D coordinate system within a 3D perspective projected + * view frustum. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_matrix_view_2d_in_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float z_near, + float z_2d, + float width_2d, + float height_2d); + +/** + * cogl_matrix_view_2d_in_perspective: + * @fov_y: A field of view angle for the Y axis + * @aspect: The ratio of width to height determining the field of view angle + * for the x axis. + * @z_near: The distance to the near clip plane. Never pass 0 and always pass + * a positive number. + * @z_2d: The distance to the 2D plane. (Should always be positive and + * be between @z_near and the z_far value that was passed to + * cogl_matrix_frustum()) + * @width_2d: The width of the 2D coordinate system + * @height_2d: The height of the 2D coordinate system + * + * Multiplies @matrix by a view transform that maps the 2D coordinates + * (0,0) top left and (@width_2d,@height_2d) bottom right the full viewport + * size. Geometry at a depth of 0 will now lie on this 2D plane. + * + * Note: this doesn't multiply the matrix by any projection matrix, + * but it assumes you have a perspective projection as defined by + * passing the corresponding arguments to cogl_matrix_perspective(). + * + * Toolkits such as Clutter that mix 2D and 3D drawing can use this to + * create a 2D coordinate system within a 3D perspective projected + * view frustum. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_matrix_view_2d_in_perspective (CoglMatrix *matrix, + float fov_y, + float aspect, + float z_near, + float z_2d, + float width_2d, + float height_2d); + +#endif + +/** + * cogl_matrix_init_from_array: + * @matrix: A 4x4 transformation matrix + * @array: A linear array of 16 floats (column-major order) + * + * Initializes @matrix with the contents of @array + */ +void +cogl_matrix_init_from_array (CoglMatrix *matrix, + const float *array); + +/** + * cogl_matrix_get_array: + * @matrix: A 4x4 transformation matrix + * + * Casts @matrix to a float array which can be directly passed to OpenGL. + * + * Return value: a pointer to the float array + */ +const float * +cogl_matrix_get_array (const CoglMatrix *matrix); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_init_from_quaternion: + * @matrix: A 4x4 transformation matrix + * @quaternion: A #CoglQuaternion + * + * Initializes @matrix from a #CoglQuaternion rotation. + */ +void +cogl_matrix_init_from_quaternion (CoglMatrix *matrix, + const CoglQuaternion *quaternion); + +/** + * cogl_matrix_init_from_euler: + * @matrix: A 4x4 transformation matrix + * @euler: A #CoglEuler + * + * Initializes @matrix from a #CoglEuler rotation. + */ +void +cogl_matrix_init_from_euler (CoglMatrix *matrix, + const CoglEuler *euler); +#endif + +/** + * cogl_matrix_equal: + * @v1: A 4x4 transformation matrix + * @v2: A 4x4 transformation matrix + * + * Compares two matrices to see if they represent the same + * transformation. Although internally the matrices may have different + * annotations associated with them and may potentially have a cached + * inverse matrix these are not considered in the comparison. + * + * Since: 1.4 + */ +CoglBool +cogl_matrix_equal (const void *v1, const void *v2); + +/** + * cogl_matrix_copy: + * @matrix: A 4x4 transformation matrix you want to copy + * + * Allocates a new #CoglMatrix on the heap and initializes it with + * the same values as @matrix. + * + * Return value: (transfer full): A newly allocated #CoglMatrix which + * should be freed using cogl_matrix_free() + * + * Since: 1.6 + */ +CoglMatrix * +cogl_matrix_copy (const CoglMatrix *matrix); + +/** + * cogl_matrix_free: + * @matrix: A 4x4 transformation matrix you want to free + * + * Frees a #CoglMatrix that was previously allocated via a call to + * cogl_matrix_copy(). + * + * Since: 1.6 + */ +void +cogl_matrix_free (CoglMatrix *matrix); + +/** + * cogl_matrix_get_inverse: + * @matrix: A 4x4 transformation matrix + * @inverse: (out): The destination for a 4x4 inverse transformation matrix + * + * Gets the inverse transform of a given matrix and uses it to initialize + * a new #CoglMatrix. + * + * Although the first parameter is annotated as const to indicate + * that the transform it represents isn't modified this function may + * technically save a copy of the inverse transform within the given + * #CoglMatrix so that subsequent requests for the inverse transform may + * avoid costly inversion calculations. + * + * Return value: %TRUE if the inverse was successfully calculated or %FALSE + * for degenerate transformations that can't be inverted (in this case the + * @inverse matrix will simply be initialized with the identity matrix) + * + * Since: 1.2 + */ +CoglBool +cogl_matrix_get_inverse (const CoglMatrix *matrix, + CoglMatrix *inverse); + +/* FIXME: to be consistent with cogl_matrix_{transform,project}_points + * this could be renamed to cogl_matrix_project_point for Cogl 2.0... + */ + +/** + * cogl_matrix_transform_point: + * @matrix: A 4x4 transformation matrix + * @x: (inout): The X component of your points position + * @y: (inout): The Y component of your points position + * @z: (inout): The Z component of your points position + * @w: (inout): The W component of your points position + * + * Transforms a point whos position is given and returned as four float + * components. + */ +void +cogl_matrix_transform_point (const CoglMatrix *matrix, + float *x, + float *y, + float *z, + float *w); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_transform_points: + * @matrix: A transformation matrix + * @n_components: The number of position components for each input point. + * (either 2 or 3) + * @stride_in: The stride in bytes between input points. + * @points_in: A pointer to the first component of the first input point. + * @stride_out: The stride in bytes between output points. + * @points_out: A pointer to the first component of the first output point. + * @n_points: The number of points to transform. + * + * Transforms an array of input points and writes the result to + * another array of output points. The input points can either have 2 + * or 3 components each. The output points always have 3 components. + * The output array can simply point to the input array to do the + * transform in-place. + * + * If you need to transform 4 component points see + * cogl_matrix_project_points(). + * + * Here's an example with differing input/output strides: + * |[ + * typedef struct { + * float x,y; + * uint8_t r,g,b,a; + * float s,t,p; + * } MyInVertex; + * typedef struct { + * uint8_t r,g,b,a; + * float x,y,z; + * } MyOutVertex; + * MyInVertex vertices[N_VERTICES]; + * MyOutVertex results[N_VERTICES]; + * CoglMatrix matrix; + * + * my_load_vertices (vertices); + * my_get_matrix (&matrix); + * + * cogl_matrix_transform_points (&matrix, + * 2, + * sizeof (MyInVertex), + * &vertices[0].x, + * sizeof (MyOutVertex), + * &results[0].x, + * N_VERTICES); + * ]| + * + * Stability: unstable + */ +void +cogl_matrix_transform_points (const CoglMatrix *matrix, + int n_components, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points); + +/** + * cogl_matrix_project_points: + * @matrix: A projection matrix + * @n_components: The number of position components for each input point. + * (either 2, 3 or 4) + * @stride_in: The stride in bytes between input points. + * @points_in: A pointer to the first component of the first input point. + * @stride_out: The stride in bytes between output points. + * @points_out: A pointer to the first component of the first output point. + * @n_points: The number of points to transform. + * + * Projects an array of input points and writes the result to another + * array of output points. The input points can either have 2, 3 or 4 + * components each. The output points always have 4 components (known + * as homogenous coordinates). The output array can simply point to + * the input array to do the transform in-place. + * + * Here's an example with differing input/output strides: + * |[ + * typedef struct { + * float x,y; + * uint8_t r,g,b,a; + * float s,t,p; + * } MyInVertex; + * typedef struct { + * uint8_t r,g,b,a; + * float x,y,z; + * } MyOutVertex; + * MyInVertex vertices[N_VERTICES]; + * MyOutVertex results[N_VERTICES]; + * CoglMatrix matrix; + * + * my_load_vertices (vertices); + * my_get_matrix (&matrix); + * + * cogl_matrix_project_points (&matrix, + * 2, + * sizeof (MyInVertex), + * &vertices[0].x, + * sizeof (MyOutVertex), + * &results[0].x, + * N_VERTICES); + * ]| + * + * Stability: unstable + */ +void +cogl_matrix_project_points (const CoglMatrix *matrix, + int n_components, + size_t stride_in, + const void *points_in, + size_t stride_out, + void *points_out, + int n_points); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +/** + * cogl_matrix_is_identity: + * @matrix: A #CoglMatrix + * + * Determines if the given matrix is an identity matrix. + * + * Returns: %TRUE if @matrix is an identity matrix else %FALSE + * Since: 1.8 + */ +CoglBool +cogl_matrix_is_identity (const CoglMatrix *matrix); + +/** + * cogl_matrix_transpose: + * @matrix: A #CoglMatrix + * + * Replaces @matrix with its transpose. Ie, every element (i,j) in the + * new matrix is taken from element (j,i) in the old matrix. + * + * Since: 1.10 + */ +void +cogl_matrix_transpose (CoglMatrix *matrix); + +/** + * cogl_debug_matrix_print: + * @matrix: A #CoglMatrix + * + * Prints the contents of a #CoglMatrix to stdout. + * + * Since: 2.0 + */ +void +cogl_debug_matrix_print (const CoglMatrix *matrix); + +#ifdef COGL_HAS_GTYPE_SUPPORT + +#define COGL_GTYPE_TYPE_MATRIX (cogl_matrix_get_gtype ()) + +/** + * cogl_matrix_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_matrix_get_gtype (void); + +/** + * cogl_gtype_matrix_get_type: + * + * Returns: the GType for the registered "CoglMatrix" boxed type. This + * can be used for example to define GObject properties that accept a + * #CoglMatrix value. + * + * Deprecated: 1.18: Use cogl_matrix_get_gtype() instead. + */ +GType +cogl_gtype_matrix_get_type (void); + +#endif /* COGL_HAS_GTYPE_SUPPORT*/ + +COGL_END_DECLS + +#endif /* __COGL_MATRIX_H */ diff --git a/cogl/cogl/cogl-memory-stack-private.h b/cogl/cogl/cogl-memory-stack-private.h new file mode 100644 index 000000000..2b6ee828c --- /dev/null +++ b/cogl/cogl/cogl-memory-stack-private.h @@ -0,0 +1,50 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_MEMORY_STACK__ +#define __COGL_MEMORY_STACK__ + +#include + +typedef struct _CoglMemoryStack CoglMemoryStack; + +CoglMemoryStack * +_cogl_memory_stack_new (size_t initial_size_bytes); + +void * +_cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes); + +void +_cogl_memory_stack_rewind (CoglMemoryStack *stack); + +void +_cogl_memory_stack_free (CoglMemoryStack *stack); + +#endif /* __COGL_MEMORY_STACK__ */ diff --git a/cogl/cogl/cogl-memory-stack.c b/cogl/cogl/cogl-memory-stack.c new file mode 100644 index 000000000..f723abc12 --- /dev/null +++ b/cogl/cogl/cogl-memory-stack.c @@ -0,0 +1,196 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * CoglMemoryStack provides a really simple, but lightning fast + * memory stack allocation strategy: + * + * - The underlying pool of memory is grow-only. + * - The pool is considered to be a stack which may be comprised + * of multiple smaller stacks. Allocation is done as follows: + * - If there's enough memory in the current sub-stack then the + * stack-pointer will be returned as the allocation and the + * stack-pointer will be incremented by the allocation size. + * - If there isn't enough memory in the current sub-stack + * then a new sub-stack is allocated twice as big as the current + * sub-stack or twice as big as the requested allocation size if + * that's bigger and the stack-pointer is set to the start of the + * new sub-stack. + * - Allocations can't be freed in a random-order, you can only + * rewind the entire stack back to the start. There is no + * the concept of stack frames to allow partial rewinds. + * + * For example; we plan to use this in our tesselator which has to + * allocate lots of small vertex, edge and face structures because + * when tesselation has been finished we just want to free the whole + * lot in one go. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-memory-stack-private.h" +#include "cogl-list.h" + +#include + +#include + +typedef struct _CoglMemorySubStack +{ + CoglList link; + size_t bytes; + uint8_t *data; +} CoglMemorySubStack; + +struct _CoglMemoryStack +{ + CoglList sub_stacks; + + CoglMemorySubStack *sub_stack; + size_t sub_stack_offset; +}; + +static CoglMemorySubStack * +_cogl_memory_sub_stack_alloc (size_t bytes) +{ + CoglMemorySubStack *sub_stack = g_slice_new (CoglMemorySubStack); + sub_stack->bytes = bytes; + sub_stack->data = g_malloc (bytes); + return sub_stack; +} + +static void +_cogl_memory_stack_add_sub_stack (CoglMemoryStack *stack, + size_t sub_stack_bytes) +{ + CoglMemorySubStack *sub_stack = + _cogl_memory_sub_stack_alloc (sub_stack_bytes); + _cogl_list_insert (stack->sub_stacks.prev, &sub_stack->link); + stack->sub_stack = sub_stack; + stack->sub_stack_offset = 0; +} + +CoglMemoryStack * +_cogl_memory_stack_new (size_t initial_size_bytes) +{ + CoglMemoryStack *stack = g_slice_new0 (CoglMemoryStack); + + _cogl_list_init (&stack->sub_stacks); + + _cogl_memory_stack_add_sub_stack (stack, initial_size_bytes); + + return stack; +} + +void * +_cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes) +{ + CoglMemorySubStack *sub_stack; + void *ret; + + sub_stack = stack->sub_stack; + if (G_LIKELY (sub_stack->bytes - stack->sub_stack_offset >= bytes)) + { + ret = sub_stack->data + stack->sub_stack_offset; + stack->sub_stack_offset += bytes; + return ret; + } + + /* If the stack has been rewound and then a large initial allocation + * is made then we may need to skip over one or more of the + * sub-stacks that are too small for the requested allocation + * size... */ + for (_cogl_list_set_iterator (sub_stack->link.next, sub_stack, link); + &sub_stack->link != &stack->sub_stacks; + _cogl_list_set_iterator (sub_stack->link.next, sub_stack, link)) + { + if (sub_stack->bytes >= bytes) + { + ret = sub_stack->data; + stack->sub_stack = sub_stack; + stack->sub_stack_offset = bytes; + return ret; + } + } + + /* Finally if we couldn't find a free sub-stack with enough space + * for the requested allocation we allocate another sub-stack that's + * twice as big as the last sub-stack or twice as big as the + * requested allocation if that's bigger. + */ + + sub_stack = _cogl_container_of (stack->sub_stacks.prev, + CoglMemorySubStack, + link); + + _cogl_memory_stack_add_sub_stack (stack, MAX (sub_stack->bytes, bytes) * 2); + + sub_stack = _cogl_container_of (stack->sub_stacks.prev, + CoglMemorySubStack, + link); + + stack->sub_stack_offset += bytes; + + return sub_stack->data; +} + +void +_cogl_memory_stack_rewind (CoglMemoryStack *stack) +{ + stack->sub_stack = _cogl_container_of (stack->sub_stacks.next, + CoglMemorySubStack, + link); + stack->sub_stack_offset = 0; +} + +static void +_cogl_memory_sub_stack_free (CoglMemorySubStack *sub_stack) +{ + g_free (sub_stack->data); + g_slice_free (CoglMemorySubStack, sub_stack); +} + +void +_cogl_memory_stack_free (CoglMemoryStack *stack) +{ + + while (!_cogl_list_empty (&stack->sub_stacks)) + { + CoglMemorySubStack *sub_stack = + _cogl_container_of (stack->sub_stacks.next, CoglMemorySubStack, link); + _cogl_list_remove (&sub_stack->link); + _cogl_memory_sub_stack_free (sub_stack); + } + + g_slice_free (CoglMemoryStack, stack); +} diff --git a/cogl/cogl/cogl-meta-texture.c b/cogl/cogl/cogl-meta-texture.c new file mode 100644 index 000000000..554aaad6e --- /dev/null +++ b/cogl/cogl/cogl-meta-texture.c @@ -0,0 +1,642 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-texture.h" +#include "cogl-matrix.h" +#include "cogl-spans.h" +#include "cogl-meta-texture.h" +#include "cogl-texture-rectangle-private.h" + +#include +#include + +typedef struct _ForeachData +{ + float meta_region_coords[4]; + CoglPipelineWrapMode wrap_s; + CoglPipelineWrapMode wrap_t; + CoglMetaTextureCallback callback; + void *user_data; + + int width; + int height; + + CoglTexture *padded_textures[9]; + const float *grid_slice_texture_coords; + float slice_offset_s; + float slice_offset_t; + float slice_range_s; + float slice_range_t; +} ForeachData; + +static void +padded_grid_repeat_cb (CoglTexture *slice_texture, + const float *slice_texture_coords, + const float *meta_coords, + void *user_data) +{ + ForeachData *data; + float mapped_coords[4]; + + /* Ignore padding slices for the current grid */ + if (!slice_texture) + return; + + data = user_data; + + /* NB: the slice_texture_coords[] we get here will always be + * normalized. + * + * We now need to map the normalized slice_texture_coords[] we have + * here back to the real slice coordinates we saved in the previous + * stage... + */ + mapped_coords[0] = + slice_texture_coords[0] * data->slice_range_s + data->slice_offset_s; + mapped_coords[1] = + slice_texture_coords[1] * data->slice_range_t + data->slice_offset_t; + mapped_coords[2] = + slice_texture_coords[2] * data->slice_range_s + data->slice_offset_s; + mapped_coords[3] = + slice_texture_coords[3] * data->slice_range_t + data->slice_offset_t; + + data->callback (slice_texture, + mapped_coords, meta_coords, data->user_data); +} + +static int +setup_padded_spans (CoglSpan *spans, + float start, + float end, + float range, + int *real_index) +{ + int span_index = 0; + + if (start > 0) + { + spans[0].start = 0; + spans[0].size = start; + spans[0].waste = 0; + span_index++; + spans[1].start = spans[0].size; + } + else + spans[span_index].start = 0; + + spans[span_index].size = end - start; + spans[span_index].waste = 0; + *real_index = span_index; + span_index++; + + if (end < range) + { + spans[span_index].start = + spans[span_index - 1].start + spans[span_index - 1].size; + spans[span_index].size = range - end; + spans[span_index].waste = 0; + span_index++; + } + + return span_index; +} + +/* This handles each sub-texture within the range [0,1] of our + * original meta texture and repeats each one separately across the + * users requested virtual texture coordinates. + * + * A notable advantage of this approach is that we will batch + * together callbacks corresponding to the same underlying slice + * together. + */ +static void +create_grid_and_repeat_cb (CoglTexture *slice_texture, + const float *slice_texture_coords, + const float *meta_coords, + void *user_data) +{ + ForeachData *data = user_data; + CoglSpan x_spans[3]; + int n_x_spans; + int x_real_index; + CoglSpan y_spans[3]; + int n_y_spans; + int y_real_index; + + /* NB: This callback is called for each slice of the meta-texture + * in the range [0,1]. + * + * We define a "padded grid" for each slice of the meta-texture in + * the range [0,1]. The x axis and y axis grid lines are defined + * using CoglSpans. + * + * The padded grid maps over the meta-texture coordinates in the + * range [0,1] but only contains one valid cell that corresponds to + * current slice being iterated and all the surrounding cells just + * provide padding. + * + * Once we've defined our padded grid we then repeat that across + * the user's original region, calling their callback whenever + * we see our current slice - ignoring padding. + * + * NB: we can assume meta_coords[] are normalized at this point + * since TextureRectangles aren't iterated with this code-path. + * + * NB: spans are always defined using non-normalized coordinates + */ + n_x_spans = setup_padded_spans (x_spans, + meta_coords[0] * data->width, + meta_coords[2] * data->width, + data->width, + &x_real_index); + n_y_spans = setup_padded_spans (y_spans, + meta_coords[1] * data->height, + meta_coords[3] * data->height, + data->height, + &y_real_index); + + data->padded_textures[n_x_spans * y_real_index + x_real_index] = + slice_texture; + + /* Our callback is going to be passed normalized slice texture + * coordinates, and we will need to map the range [0,1] to the real + * slice_texture_coords we have here... */ + data->grid_slice_texture_coords = slice_texture_coords; + data->slice_range_s = fabs (data->grid_slice_texture_coords[2] - + data->grid_slice_texture_coords[0]); + data->slice_range_t = fabs (data->grid_slice_texture_coords[3] - + data->grid_slice_texture_coords[1]); + data->slice_offset_s = MIN (data->grid_slice_texture_coords[0], + data->grid_slice_texture_coords[2]); + data->slice_offset_t = MIN (data->grid_slice_texture_coords[1], + data->grid_slice_texture_coords[3]); + + /* Now actually iterate the region the user originally requested + * using the current padded grid */ + _cogl_texture_spans_foreach_in_region (x_spans, + n_x_spans, + y_spans, + n_y_spans, + data->padded_textures, + data->meta_region_coords, + data->width, + data->height, + data->wrap_s, + data->wrap_t, + padded_grid_repeat_cb, + data); + + /* Clear the padded_textures ready for the next iteration */ + data->padded_textures[n_x_spans * y_real_index + x_real_index] = NULL; +} + +#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) + +typedef struct _ClampData +{ + float start; + float end; + CoglBool s_flipped; + CoglBool t_flipped; + CoglMetaTextureCallback callback; + void *user_data; +} ClampData; + +static void +clamp_s_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + ClampData *clamp_data = user_data; + float mapped_meta_coords[4] = { + clamp_data->start, + meta_coords[1], + clamp_data->end, + meta_coords[3] + }; + if (clamp_data->s_flipped) + SWAP (mapped_meta_coords[0], mapped_meta_coords[2]); + /* NB: we never need to flip the t coords when dealing with + * s-axis clamping so no need to check if ->t_flipped */ + clamp_data->callback (sub_texture, + sub_texture_coords, mapped_meta_coords, + clamp_data->user_data); +} + +static void +clamp_t_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + ClampData *clamp_data = user_data; + float mapped_meta_coords[4] = { + meta_coords[0], + clamp_data->start, + meta_coords[2], + clamp_data->end, + }; + if (clamp_data->s_flipped) + SWAP (mapped_meta_coords[0], mapped_meta_coords[2]); + if (clamp_data->t_flipped) + SWAP (mapped_meta_coords[1], mapped_meta_coords[3]); + clamp_data->callback (sub_texture, + sub_texture_coords, mapped_meta_coords, + clamp_data->user_data); +} + +static CoglBool +foreach_clamped_region (CoglMetaTexture *meta_texture, + float *tx_1, + float *ty_1, + float *tx_2, + float *ty_2, + CoglPipelineWrapMode wrap_s, + CoglPipelineWrapMode wrap_t, + CoglMetaTextureCallback callback, + void *user_data) +{ + float width = cogl_texture_get_width (COGL_TEXTURE (meta_texture)); + ClampData clamp_data; + + /* Consider that *tx_1 may be > *tx_2 and to simplify things + * we just flip them around if that's the case and keep a note + * of the fact that they are flipped. */ + if (*tx_1 > *tx_2) + { + SWAP (*tx_1, *tx_2); + clamp_data.s_flipped = TRUE; + } + else + clamp_data.s_flipped = FALSE; + + /* The same goes for ty_1 and ty_2... */ + if (*ty_1 > *ty_2) + { + SWAP (*ty_1, *ty_2); + clamp_data.t_flipped = TRUE; + } + else + clamp_data.t_flipped = FALSE; + + clamp_data.callback = callback; + clamp_data.user_data = user_data; + + if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) + { + float max_s_coord; + float half_texel_width; + + /* Consider that rectangle textures have non-normalized + * coordinates... */ + if (cogl_is_texture_rectangle (meta_texture)) + max_s_coord = width; + else + max_s_coord = 1.0; + + half_texel_width = max_s_coord / (width * 2); + + /* Handle any left clamped region */ + if (*tx_1 < 0) + { + clamp_data.start = *tx_1; + clamp_data.end = MIN (0, *tx_2);; + cogl_meta_texture_foreach_in_region (meta_texture, + half_texel_width, *ty_1, + half_texel_width, *ty_2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + wrap_t, + clamp_s_cb, + &clamp_data); + /* Have we handled everything? */ + if (*tx_2 <= 0) + return TRUE; + + /* clamp tx_1 since we've handled everything with x < 0 */ + *tx_1 = 0; + } + + /* Handle any right clamped region - including the corners */ + if (*tx_2 > max_s_coord) + { + clamp_data.start = MAX (max_s_coord, *tx_1); + clamp_data.end = *tx_2; + cogl_meta_texture_foreach_in_region (meta_texture, + max_s_coord - half_texel_width, + *ty_1, + max_s_coord - half_texel_width, + *ty_2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + wrap_t, + clamp_s_cb, + &clamp_data); + /* Have we handled everything? */ + if (*tx_1 >= max_s_coord) + return TRUE; + + /* clamp tx_2 since we've handled everything with x > + * max_s_coord */ + *tx_2 = max_s_coord; + } + } + + if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) + { + float height = cogl_texture_get_height (COGL_TEXTURE (meta_texture)); + float max_t_coord; + float half_texel_height; + + /* Consider that rectangle textures have non-normalized + * coordinates... */ + if (cogl_is_texture_rectangle (meta_texture)) + max_t_coord = height; + else + max_t_coord = 1.0; + + half_texel_height = max_t_coord / (height * 2); + + /* Handle any top clamped region */ + if (*ty_1 < 0) + { + clamp_data.start = *ty_1; + clamp_data.end = MIN (0, *ty_2);; + cogl_meta_texture_foreach_in_region (meta_texture, + *tx_1, half_texel_height, + *tx_2, half_texel_height, + wrap_s, + COGL_PIPELINE_WRAP_MODE_REPEAT, + clamp_t_cb, + &clamp_data); + /* Have we handled everything? */ + if (*tx_2 <= 0) + return TRUE; + + /* clamp ty_1 since we've handled everything with y < 0 */ + *ty_1 = 0; + } + + /* Handle any bottom clamped region */ + if (*ty_2 > max_t_coord) + { + clamp_data.start = MAX (max_t_coord, *ty_1);; + clamp_data.end = *ty_2; + cogl_meta_texture_foreach_in_region (meta_texture, + *tx_1, + max_t_coord - half_texel_height, + *tx_2, + max_t_coord - half_texel_height, + wrap_s, + COGL_PIPELINE_WRAP_MODE_REPEAT, + clamp_t_cb, + &clamp_data); + /* Have we handled everything? */ + if (*ty_1 >= max_t_coord) + return TRUE; + + /* clamp ty_2 since we've handled everything with y > + * max_t_coord */ + *ty_2 = max_t_coord; + } + } + + if (clamp_data.s_flipped) + SWAP (*tx_1, *tx_2); + if (clamp_data.t_flipped) + SWAP (*ty_1, *ty_2); + + return FALSE; +} + +typedef struct _NormalizeData +{ + CoglMetaTextureCallback callback; + void *user_data; + float s_normalize_factor; + float t_normalize_factor; +} NormalizeData; + +static void +normalize_meta_coords_cb (CoglTexture *slice_texture, + const float *slice_coords, + const float *meta_coords, + void *user_data) +{ + NormalizeData *data = user_data; + float normalized_meta_coords[4] = { + meta_coords[0] * data->s_normalize_factor, + meta_coords[1] * data->t_normalize_factor, + meta_coords[2] * data->s_normalize_factor, + meta_coords[3] * data->t_normalize_factor + }; + + data->callback (slice_texture, + slice_coords, normalized_meta_coords, + data->user_data); +} + +typedef struct _UnNormalizeData +{ + CoglMetaTextureCallback callback; + void *user_data; + float width; + float height; +} UnNormalizeData; + +static void +un_normalize_slice_coords_cb (CoglTexture *slice_texture, + const float *slice_coords, + const float *meta_coords, + void *user_data) +{ + UnNormalizeData *data = user_data; + float un_normalized_slice_coords[4] = { + slice_coords[0] * data->width, + slice_coords[1] * data->height, + slice_coords[2] * data->width, + slice_coords[3] * data->height + }; + + data->callback (slice_texture, + un_normalized_slice_coords, meta_coords, + data->user_data); +} + +void +cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture, + float tx_1, + float ty_1, + float tx_2, + float ty_2, + CoglPipelineWrapMode wrap_s, + CoglPipelineWrapMode wrap_t, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglTexture *texture = COGL_TEXTURE (meta_texture); + float width = cogl_texture_get_width (texture); + float height = cogl_texture_get_height (texture); + NormalizeData normalize_data; + + if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_s = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_t = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + + if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE || + wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) + { + CoglBool finished = foreach_clamped_region (meta_texture, + &tx_1, &ty_1, &tx_2, &ty_2, + wrap_s, wrap_t, + callback, + user_data); + if (finished) + return; + + /* Since clamping has been handled we now want to normalize our + * wrap modes we se can assume from this point on we don't + * need to consider CLAMP_TO_EDGE. (NB: The spans code will + * assert that CLAMP_TO_EDGE isn't requested) */ + if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) + wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT; + if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) + wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT; + } + + /* It makes things simpler to deal with non-normalized region + * coordinates beyond this point and only re-normalize just before + * calling the user's callback... */ + + if (!cogl_is_texture_rectangle (COGL_TEXTURE (meta_texture))) + { + normalize_data.callback = callback; + normalize_data.user_data = user_data; + normalize_data.s_normalize_factor = 1.0f / width; + normalize_data.t_normalize_factor = 1.0f / height; + callback = normalize_meta_coords_cb; + user_data = &normalize_data; + tx_1 *= width; + ty_1 *= height; + tx_2 *= width; + ty_2 *= height; + } + + /* XXX: at some point this wont be routed through the CoglTexture + * vtable, instead there will be a separate CoglMetaTexture + * interface vtable. */ + + if (texture->vtable->foreach_sub_texture_in_region) + { + ForeachData data; + + data.meta_region_coords[0] = tx_1; + data.meta_region_coords[1] = ty_1; + data.meta_region_coords[2] = tx_2; + data.meta_region_coords[3] = ty_2; + data.wrap_s = wrap_s; + data.wrap_t = wrap_t; + data.callback = callback; + data.user_data = user_data; + + data.width = width; + data.height = height; + + memset (data.padded_textures, 0, sizeof (data.padded_textures)); + + /* + * 1) We iterate all the slices of the meta-texture only within + * the range [0,1]. + * + * 2) We define a "padded grid" for each slice of the + * meta-texture in the range [0,1]. + * + * The padded grid maps over the meta-texture coordinates in + * the range [0,1] but only contains one valid cell that + * corresponds to current slice being iterated and all the + * surrounding cells just provide padding. + * + * 3) Once we've defined our padded grid we then repeat that + * across the user's original region, calling their callback + * whenever we see our current slice - ignoring padding. + * + * A notable benefit of this design is that repeating a texture + * made of multiple slices will result in us repeating each + * slice in-turn so the user gets repeat callbacks for the same + * texture batched together. For manual emulation of texture + * repeats done by drawing geometry this makes it more likely + * that we can batch geometry. + */ + + texture->vtable->foreach_sub_texture_in_region (texture, + 0, 0, 1, 1, + create_grid_and_repeat_cb, + &data); + } + else + { + CoglSpan x_span = { 0, width, 0 }; + CoglSpan y_span = { 0, height, 0 }; + float meta_region_coords[4] = { tx_1, ty_1, tx_2, ty_2 }; + UnNormalizeData un_normalize_data; + + /* If we are dealing with a CoglTextureRectangle then we need a shim + * callback that un_normalizes the slice coordinates we get from + * _cogl_texture_spans_foreach_in_region before passing them to + * the user's callback. */ + if (cogl_is_texture_rectangle (meta_texture)) + { + un_normalize_data.callback = callback; + un_normalize_data.user_data = user_data; + un_normalize_data.width = width; + un_normalize_data.height = height; + callback = un_normalize_slice_coords_cb; + user_data = &un_normalize_data; + } + + _cogl_texture_spans_foreach_in_region (&x_span, 1, + &y_span, 1, + &texture, + meta_region_coords, + width, + height, + wrap_s, + wrap_t, + callback, + user_data); + } +} +#undef SWAP diff --git a/cogl/cogl/cogl-meta-texture.h b/cogl/cogl/cogl-meta-texture.h new file mode 100644 index 000000000..69c8cb04a --- /dev/null +++ b/cogl/cogl/cogl-meta-texture.h @@ -0,0 +1,194 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_META_TEXTURE_H__ +#define __COGL_META_TEXTURE_H__ + +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-meta-texture + * @short_description: Interface for high-level textures built from + * low-level textures like #CoglTexture2D and + * #CoglTexture3D. + * + * Cogl helps to make it easy to deal with high level textures such + * as #CoglAtlasTextures, #CoglSubTextures, + * #CoglTexturePixmapX11 textures and #CoglTexture2DSliced textures + * consistently. + * + * A #CoglMetaTexture is a texture that might internally be + * represented by one or more low-level #CoglTextures + * such as #CoglTexture2D or #CoglTexture3D. These low-level textures + * are the only ones that a GPU really understands but because + * applications often want more high-level texture abstractions + * (such as storing multiple textures inside one larger "atlas" + * texture) it's desirable to be able to deal with these + * using a common interface. + * + * For example the GPU is not able to automatically handle repeating a + * texture that is part of a larger atlas texture but if you use + * %COGL_PIPELINE_WRAP_MODE_REPEAT with an atlas texture when drawing + * with cogl_rectangle() you should see that it "Just Works™" - at + * least if you don't use multi-texturing. The reason this works is + * because cogl_rectangle() internally understands the #CoglMetaTexture + * interface and is able to manually resolve the low-level textures + * using this interface and by making multiple draw calls it can + * emulate the texture repeat modes. + * + * Cogl doesn't aim to pretend that meta-textures are just like real + * textures because it would get extremely complex to try and emulate + * low-level GPU semantics transparently for these textures. The low + * level drawing APIs of Cogl, such as cogl_primitive_draw() don't + * actually know anything about the #CoglMetaTexture interface and its + * the developer's responsibility to resolve all textures referenced + * by a #CoglPipeline to low-level textures before drawing. + * + * If you want to develop custom primitive APIs like + * cogl_framebuffer_draw_rectangle() and you want to support drawing + * with #CoglAtlasTextures or #CoglSubTextures for + * example, then you will need to use this #CoglMetaTexture interface + * to be able to resolve high-level textures into low-level textures + * before drawing with Cogl's low-level drawing APIs such as + * cogl_primitive_draw(). + * + * Most developers won't need to use this interface directly + * but still it is worth understanding the distinction between + * low-level and meta textures because you may find other references + * in the documentation that detail limitations of using + * meta-textures. + */ + +#ifdef __COGL_H_INSIDE__ +/* For the public C api we typedef interface types as void to avoid needing + * lots of casting in code and instead we will rely on runtime type checking + * for these objects. */ +typedef void CoglMetaTexture; +#else +typedef struct _CoglMetaTexture CoglMetaTexture; +#define COGL_META_TEXTURE(X) ((CoglMetaTexture *)X) +#endif + +/** + * CoglMetaTextureCallback: + * @sub_texture: A low-level #CoglTexture making up part of a + * #CoglMetaTexture. + * @sub_texture_coords: A float 4-tuple ordered like + * (tx1,ty1,tx2,ty2) defining what region of the + * current @sub_texture maps to a sub-region of a + * #CoglMetaTexture. (tx1,ty1) is the top-left + * sub-region coordinate and (tx2,ty2) is the + * bottom-right. These are low-level texture + * coordinates. + * @meta_coords: A float 4-tuple ordered like (tx1,ty1,tx2,ty2) + * defining what sub-region of a #CoglMetaTexture this + * low-level @sub_texture maps too. (tx1,ty1) is + * the top-left sub-region coordinate and (tx2,ty2) is + * the bottom-right. These are high-level meta-texture + * coordinates. + * @user_data: A private pointer passed to + * cogl_meta_texture_foreach_in_region(). + * + * A callback used with cogl_meta_texture_foreach_in_region() to + * retrieve details of all the low-level #CoglTextures that + * make up a given #CoglMetaTexture. + * + * Since: 1.10 + * Stability: unstable + */ +typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data); + +/** + * cogl_meta_texture_foreach_in_region: + * @meta_texture: An object implementing the #CoglMetaTexture + * interface. + * @tx_1: The top-left x coordinate of the region to iterate + * @ty_1: The top-left y coordinate of the region to iterate + * @tx_2: The bottom-right x coordinate of the region to iterate + * @ty_2: The bottom-right y coordinate of the region to iterate + * @wrap_s: The wrap mode for the x-axis + * @wrap_t: The wrap mode for the y-axis + * @callback: A #CoglMetaTextureCallback pointer to be called + * for each low-level texture within the specified region. + * @user_data: A private pointer that is passed to @callback. + * + * Allows you to manually iterate the low-level textures that define a + * given region of a high-level #CoglMetaTexture. + * + * For example cogl_texture_2d_sliced_new_with_size() can be used to + * create a meta texture that may slice a large image into multiple, + * smaller power-of-two sized textures. These high level textures are + * not directly understood by a GPU and so this API must be used to + * manually resolve the underlying textures for drawing. + * + * All high level textures (#CoglAtlasTexture, #CoglSubTexture, + * #CoglTexturePixmapX11, and #CoglTexture2DSliced) can be handled + * consistently using this interface which greately simplifies + * implementing primitives that support all texture types. + * + * For example if you use the cogl_rectangle() API then Cogl will + * internally use this API to resolve the low level textures of any + * meta textures you have associated with CoglPipeline layers. + * + * The low level drawing APIs such as cogl_primitive_draw() + * don't understand the #CoglMetaTexture interface and so it is your + * responsibility to use this API to resolve all CoglPipeline textures + * into low-level textures before drawing. + * + * For each low-level texture that makes up part of the given region + * of the @meta_texture, @callback is called specifying how the + * low-level texture maps to the original region. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture, + float tx_1, + float ty_1, + float tx_2, + float ty_2, + CoglPipelineWrapMode wrap_s, + CoglPipelineWrapMode wrap_t, + CoglMetaTextureCallback callback, + void *user_data); + +COGL_END_DECLS + +#endif /* __COGL_META_TEXTURE_H__ */ diff --git a/cogl/cogl/cogl-node-private.h b/cogl/cogl/cogl-node-private.h new file mode 100644 index 000000000..8fe24775d --- /dev/null +++ b/cogl/cogl/cogl-node-private.h @@ -0,0 +1,89 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_NODE_PRIVATE_H +#define __COGL_NODE_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-list.h" + +typedef struct _CoglNode CoglNode; + +/* Pipelines and layers represent their state in a tree structure where + * some of the state relating to a given pipeline or layer may actually + * be owned by one if is ancestors in the tree. We have a common data + * type to track the tree heirachy so we can share code... */ +struct _CoglNode +{ + /* the parent in terms of class hierarchy, so anything inheriting + * from CoglNode also inherits from CoglObject. */ + CoglObject _parent; + + /* The parent pipeline/layer */ + CoglNode *parent; + + /* The list entry here contains pointers to the node's siblings */ + CoglList link; + + /* List of children */ + CoglList children; + + /* TRUE if the node took a strong reference on its parent. Weak + * pipelines for instance don't take a reference on their parent. */ + CoglBool has_parent_reference; +}; + +#define COGL_NODE(X) ((CoglNode *)(X)) + +void +_cogl_pipeline_node_init (CoglNode *node); + +typedef void (*CoglNodeUnparentVFunc) (CoglNode *node); + +void +_cogl_pipeline_node_set_parent_real (CoglNode *node, + CoglNode *parent, + CoglNodeUnparentVFunc unparent, + CoglBool take_strong_reference); + +void +_cogl_pipeline_node_unparent_real (CoglNode *node); + +typedef CoglBool (*CoglNodeChildCallback) (CoglNode *child, void *user_data); + +void +_cogl_pipeline_node_foreach_child (CoglNode *node, + CoglNodeChildCallback callback, + void *user_data); + +#endif /* __COGL_NODE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-node.c b/cogl/cogl/cogl-node.c new file mode 100644 index 000000000..60d3e7338 --- /dev/null +++ b/cogl/cogl/cogl-node.c @@ -0,0 +1,110 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-node-private.h" + +void +_cogl_pipeline_node_init (CoglNode *node) +{ + node->parent = NULL; + _cogl_list_init (&node->children); +} + +void +_cogl_pipeline_node_set_parent_real (CoglNode *node, + CoglNode *parent, + CoglNodeUnparentVFunc unparent, + CoglBool take_strong_reference) +{ + /* NB: the old parent may indirectly be keeping the new parent alive + * so we have to ref the new parent before unrefing the old. + * + * Note: we take a reference here regardless of + * take_strong_reference because weak children may need special + * handling when the parent disposes itself which relies on a + * consistent link to all weak nodes. Once the node is linked to its + * parent then we remove the reference at the end if + * take_strong_reference == FALSE. */ + cogl_object_ref (parent); + + if (node->parent) + unparent (node); + + _cogl_list_insert (&parent->children, &node->link); + + node->parent = parent; + node->has_parent_reference = take_strong_reference; + + /* Now that there is a consistent parent->child link we can remove + * the parent reference if no reference was requested. If it turns + * out that the new parent was only being kept alive by the old + * parent then it will be disposed of here. */ + if (!take_strong_reference) + cogl_object_unref (parent); +} + +void +_cogl_pipeline_node_unparent_real (CoglNode *node) +{ + CoglNode *parent = node->parent; + + if (parent == NULL) + return; + + _COGL_RETURN_IF_FAIL (!_cogl_list_empty (&parent->children)); + + _cogl_list_remove (&node->link); + + if (node->has_parent_reference) + cogl_object_unref (parent); + + node->parent = NULL; +} + +void +_cogl_pipeline_node_foreach_child (CoglNode *node, + CoglNodeChildCallback callback, + void *user_data) +{ + CoglNode *child, *next; + + _cogl_list_for_each_safe (child, next, &node->children, link) + callback (child, user_data); +} + + diff --git a/cogl/cogl/cogl-object-private.h b/cogl/cogl/cogl-object-private.h new file mode 100644 index 000000000..7955a35cc --- /dev/null +++ b/cogl/cogl/cogl-object-private.h @@ -0,0 +1,323 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_OBJECT_PRIVATE_H +#define __COGL_OBJECT_PRIVATE_H + +#include + +#include "cogl-types.h" +#include "cogl-object.h" +#include "cogl-debug.h" + +/* For compatability until all components have been converted */ +typedef struct _CoglObjectClass CoglHandleClass; +typedef struct _CoglObject CoglHandleObject; + +/* XXX: sadly we didn't fully consider when we copied the cairo API + * for _set_user_data that the callback doesn't get a pointer to the + * instance which is desired in most cases. This means you tend to end + * up creating micro allocations for the private data just so you can + * pair up the data of interest with the original instance for + * identification when it is later destroyed. + * + * Internally we use a small hack to avoid needing these micro + * allocations by actually passing the instance as a second argument + * to the callback */ +typedef void (*CoglUserDataDestroyInternalCallback) (void *user_data, + void *instance); + +typedef struct _CoglObjectClass +{ +#ifdef COGL_HAS_GTYPE_SUPPORT + GTypeClass base_class; +#endif + const char *name; + void *virt_free; + void *virt_unref; +} CoglObjectClass; + +#define COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES 2 + +typedef struct +{ + CoglUserDataKey *key; + void *user_data; + CoglUserDataDestroyInternalCallback destroy; +} CoglUserDataEntry; + +/* All Cogl objects inherit from this base object by adding a member: + * + * CoglObject _parent; + * + * at the top of its main structure. This structure is initialized + * when you call _cogl_#type_name#_object_new (new_object); + */ +struct _CoglObject +{ + CoglObjectClass *klass; /* equivalent to GTypeInstance */ + + CoglUserDataEntry user_data_entry[ + COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES]; + GArray *user_data_array; + int n_user_data_entries; + + unsigned int ref_count; +}; + +/* Helper macro to encapsulate the common code for COGL reference + counted objects */ + +#ifdef COGL_OBJECT_DEBUG + +#define _COGL_OBJECT_DEBUG_NEW(type_name, obj) \ + COGL_NOTE (OBJECT, "COGL " G_STRINGIFY (type_name) " NEW %p %i", \ + (obj), (obj)->ref_count) + +#define _COGL_OBJECT_DEBUG_REF(type_name, object) G_STMT_START { \ + CoglObject *__obj = (CoglObject *)object; \ + COGL_NOTE (OBJECT, "COGL %s REF %p %i", \ + (__obj)->klass->name, \ + (__obj), (__obj)->ref_count); } G_STMT_END + +#define _COGL_OBJECT_DEBUG_UNREF(type_name, object) G_STMT_START { \ + CoglObject *__obj = (CoglObject *)object; \ + COGL_NOTE (OBJECT, "COGL %s UNREF %p %i", \ + (__obj)->klass->name, \ + (__obj), (__obj)->ref_count - 1); } G_STMT_END + +#define COGL_OBJECT_DEBUG_FREE(obj) \ + COGL_NOTE (OBJECT, "COGL %s FREE %p", \ + (obj)->klass->name, (obj)) + +#else /* !COGL_OBJECT_DEBUG */ + +#define _COGL_OBJECT_DEBUG_NEW(type_name, obj) +#define _COGL_OBJECT_DEBUG_REF(type_name, obj) +#define _COGL_OBJECT_DEBUG_UNREF(type_name, obj) +#define COGL_OBJECT_DEBUG_FREE(obj) + +#endif /* COGL_OBJECT_DEBUG */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +#define _COGL_GTYPE_INIT_CLASS(type_name) do { \ + _cogl_##type_name##_class.base_class.g_type = cogl_##type_name##_get_gtype (); \ +} while (0) +#else +#define _COGL_GTYPE_INIT_CLASS(type_name) +#endif + +#define COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +CoglObjectClass _cogl_##type_name##_class; \ +static unsigned long _cogl_object_##type_name##_count; \ + \ +static inline void \ +_cogl_object_##type_name##_inc (void) \ +{ \ + _cogl_object_##type_name##_count++; \ +} \ + \ +static inline void \ +_cogl_object_##type_name##_dec (void) \ +{ \ + _cogl_object_##type_name##_count--; \ +} \ + \ +static void \ +_cogl_object_##type_name##_indirect_free (CoglObject *obj) \ +{ \ + _cogl_##type_name##_free ((Cogl##TypeName *) obj); \ + _cogl_object_##type_name##_dec (); \ +} \ + \ +static void \ +_cogl_object_##type_name##_class_init (void) \ +{ \ + _cogl_object_##type_name##_count = 0; \ + \ + if (_cogl_debug_instances == NULL) \ + _cogl_debug_instances = \ + g_hash_table_new (g_str_hash, g_str_equal); \ + \ + _cogl_##type_name##_class.virt_free = \ + _cogl_object_##type_name##_indirect_free; \ + _cogl_##type_name##_class.virt_unref = \ + _cogl_object_default_unref; \ + _cogl_##type_name##_class.name = "Cogl"#TypeName; \ + \ + g_hash_table_insert (_cogl_debug_instances, \ + (void *) _cogl_##type_name##_class.name, \ + &_cogl_object_##type_name##_count); \ + \ + { code; } \ +} \ + \ +static Cogl##TypeName * \ +_cogl_##type_name##_object_new (Cogl##TypeName *new_obj) \ +{ \ + CoglObject *obj = (CoglObject *)&new_obj->_parent; \ + obj->ref_count = 0; \ + cogl_object_ref (obj); \ + obj->n_user_data_entries = 0; \ + obj->user_data_array = NULL; \ + \ + obj->klass = &_cogl_##type_name##_class; \ + if (!obj->klass->virt_free) \ + { \ + _cogl_object_##type_name##_class_init (); \ + } \ + \ + _cogl_object_##type_name##_inc (); \ + _COGL_OBJECT_DEBUG_NEW (TypeName, obj); \ + return new_obj; \ +} + +#define COGL_OBJECT_DEFINE_WITH_CODE_GTYPE(TypeName, type_name, code) \ + \ +COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, \ + type_name, \ + do { code; } while (0); \ + _COGL_GTYPE_INIT_CLASS (type_name)) \ + \ +CoglBool \ +cogl_is_##type_name (void *object) \ +{ \ + CoglObject *obj = object; \ + \ + if (object == NULL) \ + return FALSE; \ + \ + return obj->klass == &_cogl_##type_name##_class; \ +} + +#define COGL_OBJECT_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +CoglBool \ +cogl_is_##type_name (void *object) \ +{ \ + CoglObject *obj = object; \ + \ + if (object == NULL) \ + return FALSE; \ + \ + return obj->klass == &_cogl_##type_name##_class; \ +} + +#define COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +CoglBool \ +_cogl_is_##type_name (void *object) \ +{ \ + CoglObject *obj = object; \ + \ + if (object == NULL) \ + return FALSE; \ + \ + return obj->klass == &_cogl_##type_name##_class; \ +} + +#define COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING(type_name) \ + \ +void * G_GNUC_DEPRECATED \ +cogl_##type_name##_ref (void *object) \ +{ \ + if (!cogl_is_##type_name (object)) \ + return NULL; \ + \ + _COGL_OBJECT_DEBUG_REF (TypeName, object); \ + \ + cogl_handle_ref (object); \ + \ + return object; \ +} \ + \ +void G_GNUC_DEPRECATED \ +cogl_##type_name##_unref (void *object) \ +{ \ + if (!cogl_is_##type_name (object)) \ + { \ + g_warning (G_STRINGIFY (cogl_##type_name##_unref) \ + ": Ignoring unref of Cogl handle " \ + "due to type mismatch"); \ + return; \ + } \ + \ + _COGL_OBJECT_DEBUG_UNREF (TypeName, object); \ + \ + cogl_handle_unref (object); \ +} + +#define COGL_OBJECT_DEFINE(TypeName, type_name) \ + COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (TypeName, type_name, (void) 0) + +#define COGL_OBJECT_INTERNAL_DEFINE(TypeName, type_name) \ + COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE (TypeName, type_name, (void) 0) + +/* For temporary compatability */ +#define COGL_HANDLE_INTERNAL_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE (TypeName, type_name, code) \ + \ +static Cogl##TypeName * \ +_cogl_##type_name##_handle_new (CoglHandle handle) \ +{ \ + return _cogl_##type_name##_object_new (handle); \ +} + +#define COGL_HANDLE_DEFINE_WITH_CODE(TypeName, type_name, code) \ + \ +COGL_OBJECT_DEFINE_WITH_CODE (TypeName, type_name, code) \ + \ +static Cogl##TypeName * \ +_cogl_##type_name##_handle_new (CoglHandle handle) \ +{ \ + return _cogl_##type_name##_object_new (handle); \ +} + +#define COGL_HANDLE_DEFINE(TypeName, type_name) \ + COGL_HANDLE_DEFINE_WITH_CODE (TypeName, type_name, (void) 0) + +void +_cogl_object_set_user_data (CoglObject *object, + CoglUserDataKey *key, + void *user_data, + CoglUserDataDestroyInternalCallback destroy); + +void +_cogl_object_default_unref (void *obj); + +#endif /* __COGL_OBJECT_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-object.c b/cogl/cogl/cogl-object.c new file mode 100644 index 000000000..452f625f9 --- /dev/null +++ b/cogl/cogl/cogl-object.c @@ -0,0 +1,304 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cogl-util.h" +#include "cogl-types.h" +#include "cogl-object-private.h" +#include "cogl-gtype-private.h" + +COGL_GTYPE_DEFINE_BASE_CLASS (Object, object); + +void * +cogl_object_ref (void *object) +{ + CoglObject *obj = object; + + _COGL_RETURN_VAL_IF_FAIL (object != NULL, NULL); + + obj->ref_count++; + return object; +} + +CoglHandle +cogl_handle_ref (CoglHandle handle) +{ + return cogl_object_ref (handle); +} + +void +_cogl_object_default_unref (void *object) +{ + CoglObject *obj = object; + + _COGL_RETURN_IF_FAIL (object != NULL); + _COGL_RETURN_IF_FAIL (obj->ref_count > 0); + + if (--obj->ref_count < 1) + { + void (*free_func)(void *obj); + + if (obj->n_user_data_entries) + { + int i; + int count = MIN (obj->n_user_data_entries, + COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); + + for (i = 0; i < count; i++) + { + CoglUserDataEntry *entry = &obj->user_data_entry[i]; + if (entry->destroy) + entry->destroy (entry->user_data, obj); + } + + if (obj->user_data_array != NULL) + { + for (i = 0; i < obj->user_data_array->len; i++) + { + CoglUserDataEntry *entry = + &g_array_index (obj->user_data_array, + CoglUserDataEntry, i); + + if (entry->destroy) + entry->destroy (entry->user_data, obj); + } + g_array_free (obj->user_data_array, TRUE); + } + } + + COGL_OBJECT_DEBUG_FREE (obj); + free_func = obj->klass->virt_free; + free_func (obj); + } +} + +void +cogl_object_unref (void *obj) +{ + void (* unref_func) (void *) = ((CoglObject *) obj)->klass->virt_unref; + unref_func (obj); +} + +void +cogl_handle_unref (CoglHandle handle) +{ + cogl_object_unref (handle); +} + +GType +cogl_handle_get_type (void) +{ + static GType our_type = 0; + + /* XXX: We are keeping the "CoglHandle" name for now incase it would + * break bindings to change to "CoglObject" */ + if (G_UNLIKELY (our_type == 0)) + our_type = g_boxed_type_register_static (g_intern_static_string ("CoglHandle"), + (GBoxedCopyFunc) cogl_object_ref, + (GBoxedFreeFunc) cogl_object_unref); + + return our_type; +} + +/* XXX: Unlike for cogl_object_get_user_data this code will return + * an empty entry if available and no entry for the given key can be + * found. */ +static CoglUserDataEntry * +_cogl_object_find_entry (CoglObject *object, CoglUserDataKey *key) +{ + CoglUserDataEntry *entry = NULL; + int count; + int i; + + count = MIN (object->n_user_data_entries, + COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); + + for (i = 0; i < count; i++) + { + CoglUserDataEntry *current = &object->user_data_entry[i]; + if (current->key == key) + return current; + if (current->user_data == NULL) + entry = current; + } + + if (G_UNLIKELY (object->user_data_array != NULL)) + { + for (i = 0; i < object->user_data_array->len; i++) + { + CoglUserDataEntry *current = + &g_array_index (object->user_data_array, CoglUserDataEntry, i); + + if (current->key == key) + return current; + if (current->user_data == NULL) + entry = current; + } + } + + return entry; +} + +void +_cogl_object_set_user_data (CoglObject *object, + CoglUserDataKey *key, + void *user_data, + CoglUserDataDestroyInternalCallback destroy) +{ + CoglUserDataEntry new_entry; + CoglUserDataEntry *entry; + + if (user_data) + { + new_entry.key = key; + new_entry.user_data = user_data; + new_entry.destroy = destroy; + } + else + memset (&new_entry, 0, sizeof (new_entry)); + + entry = _cogl_object_find_entry (object, key); + if (entry) + { + if (G_LIKELY (entry->destroy)) + entry->destroy (entry->user_data, object); + } + else + { + /* NB: Setting a value of NULL is documented to delete the + * corresponding entry so we can return immediately in this + * case. */ + if (user_data == NULL) + return; + + if (G_LIKELY (object->n_user_data_entries < + COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES)) + entry = &object->user_data_entry[object->n_user_data_entries++]; + else + { + if (G_UNLIKELY (object->user_data_array == NULL)) + { + object->user_data_array = + g_array_new (FALSE, FALSE, sizeof (CoglUserDataEntry)); + } + + g_array_set_size (object->user_data_array, + object->user_data_array->len + 1); + entry = + &g_array_index (object->user_data_array, CoglUserDataEntry, + object->user_data_array->len - 1); + + object->n_user_data_entries++; + } + } + + *entry = new_entry; +} + +void +cogl_object_set_user_data (CoglObject *object, + CoglUserDataKey *key, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + _cogl_object_set_user_data (object, key, user_data, + (CoglUserDataDestroyInternalCallback)destroy); +} + +void * +cogl_object_get_user_data (CoglObject *object, CoglUserDataKey *key) +{ + int count; + int i; + + count = MIN (object->n_user_data_entries, + COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); + + for (i = 0; i < count; i++) + { + CoglUserDataEntry *entry = &object->user_data_entry[i]; + if (entry->key == key) + return entry->user_data; + } + + if (object->user_data_array != NULL) + { + for (i = 0; i < object->user_data_array->len; i++) + { + CoglUserDataEntry *entry = + &g_array_index (object->user_data_array, CoglUserDataEntry, i); + + if (entry->key == key) + return entry->user_data; + } + } + + return NULL; +} + +void +cogl_debug_object_foreach_type (CoglDebugObjectForeachTypeCallback func, + void *user_data) +{ + GHashTableIter iter; + unsigned long *instance_count; + CoglDebugObjectTypeInfo info; + + g_hash_table_iter_init (&iter, _cogl_debug_instances); + while (g_hash_table_iter_next (&iter, + (void *) &info.name, + (void *) &instance_count)) + { + info.instance_count = *instance_count; + func (&info, user_data); + } +} + +static void +print_instances_cb (const CoglDebugObjectTypeInfo *info, + void *user_data) +{ + g_print ("\t%s: %lu\n", info->name, info->instance_count); +} + +void +cogl_debug_object_print_instances (void) +{ + g_print ("Cogl instances:\n"); + + cogl_debug_object_foreach_type (print_instances_cb, NULL); +} diff --git a/cogl/cogl/cogl-object.h b/cogl/cogl/cogl-object.h new file mode 100644 index 000000000..a0bed88dc --- /dev/null +++ b/cogl/cogl/cogl-object.h @@ -0,0 +1,251 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_OBJECT_H +#define __COGL_OBJECT_H + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +typedef struct _CoglObject CoglObject; + +#define COGL_OBJECT(X) ((CoglObject *)X) + +/** + * CoglObject: + * + * Ref Func: cogl_object_ref + * Unref Func: cogl_object_unref + * Set Value Func: cogl_object_value_set_object + * Get Value Func: cogl_object_value_get_object + */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_object_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_object_get_gtype (void); +#endif + +/** + * cogl_object_ref: (skip) + * @object: a #CoglObject + * + * Increases the reference count of @object by 1 + * + * Returns: the @object, with its reference count increased + */ +void * +cogl_object_ref (void *object); + +/** + * cogl_object_unref: (skip) + * @object: a #CoglObject + * + * Drecreases the reference count of @object by 1; if the reference + * count reaches 0, the resources allocated by @object will be freed + */ +void +cogl_object_unref (void *object); + +/** + * CoglUserDataKey: + * @unused: ignored. + * + * A #CoglUserDataKey is used to declare a key for attaching data to a + * #CoglObject using cogl_object_set_user_data. The typedef only exists as a + * formality to make code self documenting since only the unique address of a + * #CoglUserDataKey is used. + * + * Typically you would declare a static #CoglUserDataKey and set private data + * on an object something like this: + * + * |[ + * static CoglUserDataKey path_private_key; + * + * static void + * destroy_path_private_cb (void *data) + * { + * g_free (data); + * } + * + * static void + * my_path_set_data (CoglPath *path, void *data) + * { + * cogl_object_set_user_data (COGL_OBJECT (path), + * &private_key, + * data, + * destroy_path_private_cb); + * } + * ]| + * + * Since: 1.4 + */ +typedef struct { + int unused; +} CoglUserDataKey; + +/** + * CoglUserDataDestroyCallback: + * @user_data: The data whos association with a #CoglObject has been + * destoyed. + * + * When associating private data with a #CoglObject a callback can be + * given which will be called either if the object is destroyed or if + * cogl_object_set_user_data() is called with NULL user_data for the + * same key. + * + * Since: 1.4 + */ +#ifdef COGL_HAS_GTYPE_SUPPORT +typedef GDestroyNotify CoglUserDataDestroyCallback; +#else +typedef void (*CoglUserDataDestroyCallback) (void *user_data); +#endif + +/** + * CoglDebugObjectTypeInfo: + * @name: A human readable name for the type. + * @instance_count: The number of objects of this type that are + * currently in use + * + * This struct is used to pass information to the callback when + * cogl_debug_object_foreach_type() is called. + * + * Since: 1.8 + * Stability: unstable + */ +typedef struct { + const char *name; + unsigned long instance_count; +} CoglDebugObjectTypeInfo; + +/** + * CoglDebugObjectForeachTypeCallback: + * @info: A pointer to a struct containing information about the type. + * + * A callback function to use for cogl_debug_object_foreach_type(). + * + * Since: 1.8 + * Stability: unstable + */ +typedef void +(* CoglDebugObjectForeachTypeCallback) (const CoglDebugObjectTypeInfo *info, + void *user_data); + +/** + * cogl_object_set_user_data: (skip) + * @object: The object to associate private data with + * @key: The address of a #CoglUserDataKey which provides a unique value + * with which to index the private data. + * @user_data: The data to associate with the given object, + * or %NULL to remove a previous association. + * @destroy: A #CoglUserDataDestroyCallback to call if the object is + * destroyed or if the association is removed by later setting + * %NULL data for the same key. + * + * Associates some private @user_data with a given #CoglObject. To + * later remove the association call cogl_object_set_user_data() with + * the same @key but NULL for the @user_data. + * + * Since: 1.4 + */ +void +cogl_object_set_user_data (CoglObject *object, + CoglUserDataKey *key, + void *user_data, + CoglUserDataDestroyCallback destroy); + +/** + * cogl_object_get_user_data: (skip) + * @object: The object with associated private data to query + * @key: The address of a #CoglUserDataKey which provides a unique value + * with which to index the private data. + * + * Finds the user data previously associated with @object using + * the given @key. If no user data has been associated with @object + * for the given @key this function returns NULL. + * + * Returns: (transfer none): The user data previously associated + * with @object using the given @key; or %NULL if no associated + * data is found. + * + * Since: 1.4 + */ +void * +cogl_object_get_user_data (CoglObject *object, + CoglUserDataKey *key); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * cogl_debug_object_foreach_type: + * @func: (scope call): A callback function for each type + * @user_data: (closure): A pointer to pass to @func + * + * Invokes @func once for each type of object that Cogl uses and + * passes a count of the number of objects for that type. This is + * intended to be used solely for debugging purposes to track down + * issues with objects leaking. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_debug_object_foreach_type (CoglDebugObjectForeachTypeCallback func, + void *user_data); + +/** + * cogl_debug_object_print_instances: + * + * Prints a list of all the object types that Cogl uses along with the + * number of objects of that type that are currently in use. This is + * intended to be used solely for debugging purposes to track down + * issues with objects leaking. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_debug_object_print_instances (void); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +#endif /* __COGL_OBJECT_H */ + diff --git a/cogl/cogl/cogl-offscreen.h b/cogl/cogl/cogl-offscreen.h new file mode 100644 index 000000000..9c844426c --- /dev/null +++ b/cogl/cogl/cogl-offscreen.h @@ -0,0 +1,172 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_OFFSCREEN_H__ +#define __COGL_OFFSCREEN_H__ + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-offscreen + * @short_description: Functions for creating and manipulating offscreen + * framebuffers. + * + * Cogl allows creating and operating on offscreen framebuffers. + */ + +typedef struct _CoglOffscreen CoglOffscreen; + +#define COGL_OFFSCREEN(X) ((CoglOffscreen *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_offscreen_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_offscreen_get_gtype (void); +#endif + +/* Offscreen api */ + +/** + * cogl_offscreen_new_with_texture: + * @texture: A #CoglTexture pointer + * + * This creates an offscreen framebuffer object using the given + * @texture as the primary color buffer. It doesn't just initialize + * the contents of the offscreen buffer with the @texture; they are + * tightly bound so that drawing to the offscreen buffer effectively + * updates the contents of the given texture. You don't need to + * destroy the offscreen buffer before you can use the @texture again. + * + * This api only works with low-level #CoglTexture types such as + * #CoglTexture2D, #CoglTexture3D and #CoglTextureRectangle, and not + * with meta-texture types such as #CoglTexture2DSliced. + * + * The storage for the framebuffer is actually allocated lazily + * so this function will never return %NULL to indicate a runtime + * error. This means it is still possible to configure the framebuffer + * before it is really allocated. + * + * Simple applications without full error handling can simply rely on + * Cogl to lazily allocate the storage of framebuffers but you should + * be aware that if Cogl encounters an error (such as running out of + * GPU memory) then your application will simply abort with an error + * message. If you need to be able to catch such exceptions at runtime + * then you can explicitly allocate your framebuffer when you have + * finished configuring it by calling cogl_framebuffer_allocate() and + * passing in a #CoglError argument to catch any exceptions. + * + * Return value: (transfer full): a newly instantiated #CoglOffscreen + * framebuffer. + */ +CoglOffscreen * +cogl_offscreen_new_with_texture (CoglTexture *texture); + +/** + * cogl_offscreen_new_to_texture: + * @texture: A #CoglTexture pointer + * + * This creates an offscreen buffer object using the given @texture as the + * primary color buffer. It doesn't just initialize the contents of the + * offscreen buffer with the @texture; they are tightly bound so that + * drawing to the offscreen buffer effectivly updates the contents of the + * given texture. You don't need to destroy the offscreen buffer before + * you can use the @texture again. + * + * This only works with low-level #CoglTexture types such as + * #CoglTexture2D, #CoglTexture3D and #CoglTextureRectangle, and not + * with meta-texture types such as #CoglTexture2DSliced. + * + * Return value: (transfer full): a newly instantiated #CoglOffscreen + * framebuffer or %NULL if it wasn't possible to create the + * buffer. + * Deprecated: 1.16: Use cogl_offscreen_new_with_texture instead. + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_offscreen_new_with_texture) +CoglOffscreen * +cogl_offscreen_new_to_texture (CoglTexture *texture); + +/** + * cogl_is_offscreen: + * @object: A pointer to a #CoglObject + * + * Determines whether the given #CoglObject references an offscreen + * framebuffer object. + * + * Returns: %TRUE if @object is a #CoglOffscreen framebuffer, + * %FALSE otherwise + */ +CoglBool +cogl_is_offscreen (void *object); + +/** + * cogl_offscreen_ref: + * @offscreen: A pointer to a #CoglOffscreen framebuffer + * + * Increments the reference count on the @offscreen framebuffer. + * + * Return value: (transfer none): For convenience it returns the + * given @offscreen + * + * Deprecated: 1.2: cogl_object_ref() should be used in new code. + */ +COGL_DEPRECATED_FOR (cogl_object_ref) +void * +cogl_offscreen_ref (void *offscreen); + +/** + * cogl_offscreen_unref: + * @offscreen: A pointer to a #CoglOffscreen framebuffer + * + * Decreases the reference count for the @offscreen buffer and frees it when + * the count reaches 0. + * + * Deprecated: 1.2: cogl_object_unref() should be used in new code. + */ +COGL_DEPRECATED_FOR (cogl_object_unref) +void +cogl_offscreen_unref (void *offscreen); + +COGL_END_DECLS + +#endif /* __COGL_OFFSCREEN_H__ */ diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h new file mode 100644 index 000000000..0a67832ac --- /dev/null +++ b/cogl/cogl/cogl-onscreen-private.h @@ -0,0 +1,115 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_ONSCREEN_PRIVATE_H +#define __COGL_ONSCREEN_PRIVATE_H + +#include "cogl-onscreen.h" +#include "cogl-framebuffer-private.h" +#include "cogl-closure-list-private.h" +#include "cogl-list.h" + +#include + +typedef struct _CoglOnscreenEvent +{ + CoglList link; + + CoglOnscreen *onscreen; + CoglFrameInfo *info; + CoglFrameEvent type; +} CoglOnscreenEvent; + +typedef struct _CoglOnscreenQueuedDirty +{ + CoglList link; + + CoglOnscreen *onscreen; + CoglOnscreenDirtyInfo info; +} CoglOnscreenQueuedDirty; + +struct _CoglOnscreen +{ + CoglFramebuffer _parent; + +#ifdef COGL_HAS_X11_SUPPORT + uint32_t foreign_xid; + CoglOnscreenX11MaskCallback foreign_update_mask_callback; + void *foreign_update_mask_data; +#endif + + CoglBool swap_throttled; + + CoglList frame_closures; + + CoglBool resizable; + CoglList resize_closures; + + CoglList dirty_closures; + + int64_t frame_counter; + int64_t swap_frame_counter; /* frame counter at last all to + * cogl_onscreen_swap_region() or + * cogl_onscreen_swap_buffers() */ + GQueue pending_frame_infos; + + void *winsys; +}; + +CoglOnscreen * +_cogl_onscreen_new (void); + +void +_cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer, + int width, int height); + +void +_cogl_onscreen_queue_event (CoglOnscreen *onscreen, + CoglFrameEvent type, + CoglFrameInfo *info); + +void +_cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info); + +void +_cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info); + +void +_cogl_onscreen_notify_resize (CoglOnscreen *onscreen); + +void +_cogl_onscreen_queue_dirty (CoglOnscreen *onscreen, + const CoglOnscreenDirtyInfo *info); + + +void +_cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen); + +#endif /* __COGL_ONSCREEN_PRIVATE_H */ diff --git a/cogl/cogl/cogl-onscreen-template-private.h b/cogl/cogl/cogl-onscreen-template-private.h new file mode 100644 index 000000000..1b19d35d7 --- /dev/null +++ b/cogl/cogl/cogl-onscreen-template-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_ONSCREEN_TEMPLATE_PRIVATE_H +#define __COGL_ONSCREEN_TEMPLATE_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-swap-chain.h" +#include "cogl-framebuffer-private.h" + +struct _CoglOnscreenTemplate +{ + CoglObject _parent; + + CoglFramebufferConfig config; +}; + +#endif /* __COGL_ONSCREEN_TEMPLATE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-onscreen-template.c b/cogl/cogl/cogl-onscreen-template.c new file mode 100644 index 000000000..3940627c4 --- /dev/null +++ b/cogl/cogl/cogl-onscreen-template.c @@ -0,0 +1,105 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-object.h" + +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-gtype-private.h" + +#include + +static void _cogl_onscreen_template_free (CoglOnscreenTemplate *onscreen_template); + +COGL_OBJECT_DEFINE (OnscreenTemplate, onscreen_template); +COGL_GTYPE_DEFINE_CLASS (OnscreenTemplate, onscreen_template); + +static void +_cogl_onscreen_template_free (CoglOnscreenTemplate *onscreen_template) +{ + g_slice_free (CoglOnscreenTemplate, onscreen_template); +} + +CoglOnscreenTemplate * +cogl_onscreen_template_new (CoglSwapChain *swap_chain) +{ + CoglOnscreenTemplate *onscreen_template = g_slice_new0 (CoglOnscreenTemplate); + char *user_config; + + onscreen_template->config.swap_chain = swap_chain; + if (swap_chain) + cogl_object_ref (swap_chain); + else + onscreen_template->config.swap_chain = cogl_swap_chain_new (); + + onscreen_template->config.swap_throttled = TRUE; + onscreen_template->config.need_stencil = TRUE; + onscreen_template->config.samples_per_pixel = 0; + + user_config = getenv ("COGL_POINT_SAMPLES_PER_PIXEL"); + if (user_config) + { + unsigned long samples_per_pixel = strtoul (user_config, NULL, 10); + if (samples_per_pixel != ULONG_MAX) + onscreen_template->config.samples_per_pixel = + samples_per_pixel; + } + + return _cogl_onscreen_template_object_new (onscreen_template); +} + +void +cogl_onscreen_template_set_samples_per_pixel ( + CoglOnscreenTemplate *onscreen_template, + int samples_per_pixel) +{ + onscreen_template->config.samples_per_pixel = samples_per_pixel; +} + +void +cogl_onscreen_template_set_swap_throttled ( + CoglOnscreenTemplate *onscreen_template, + CoglBool throttled) +{ + onscreen_template->config.swap_throttled = throttled; +} + +void +cogl_onscreen_template_set_stereo_enabled ( + CoglOnscreenTemplate *onscreen_template, + CoglBool enabled) +{ + onscreen_template->config.stereo_enabled = enabled; +} diff --git a/cogl/cogl/cogl-onscreen-template.h b/cogl/cogl/cogl-onscreen-template.h new file mode 100644 index 000000000..d8714fabf --- /dev/null +++ b/cogl/cogl/cogl-onscreen-template.h @@ -0,0 +1,143 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_ONSCREEN_TEMPLATE_H__ +#define __COGL_ONSCREEN_TEMPLATE_H__ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +typedef struct _CoglOnscreenTemplate CoglOnscreenTemplate; + +#define COGL_ONSCREEN_TEMPLATE(OBJECT) ((CoglOnscreenTemplate *)OBJECT) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_onscreen_template_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_onscreen_template_get_gtype (void); +#endif + +CoglOnscreenTemplate * +cogl_onscreen_template_new (CoglSwapChain *swap_chain); + +/** + * cogl_onscreen_template_set_samples_per_pixel: + * @onscreen_template: A #CoglOnscreenTemplate template framebuffer + * @n: The minimum number of samples per pixel + * + * Requires that any future CoglOnscreen framebuffers derived from + * this template must support making at least @n samples per pixel + * which will all contribute to the final resolved color for that + * pixel. + * + * By default this value is usually set to 0 and that is referred to + * as "single-sample" rendering. A value of 1 or greater is referred + * to as "multisample" rendering. + * + * There are some semantic differences between single-sample + * rendering and multisampling with just 1 point sample such as it + * being redundant to use the cogl_framebuffer_resolve_samples() and + * cogl_framebuffer_resolve_samples_region() apis with single-sample + * rendering. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_onscreen_template_set_samples_per_pixel ( + CoglOnscreenTemplate *onscreen_template, + int n); + +/** + * cogl_onscreen_template_set_swap_throttled: + * @onscreen_template: A #CoglOnscreenTemplate template framebuffer + * @throttled: Whether throttling should be enabled + * + * Requests that any future #CoglOnscreen framebuffers derived from this + * template should enable or disable swap throttling according to the given + * @throttled argument. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_onscreen_template_set_swap_throttled ( + CoglOnscreenTemplate *onscreen_template, + CoglBool throttled); + +/** + * cogl_onscreen_template_set_stereo_enabled: + * @onscreen_template: A #CoglOnscreenTemplate template framebuffer + * @enabled: Whether framebuffers are created with stereo buffers + * + * Sets whether future #CoglOnscreen framebuffers derived from this + * template are attempted to be created with both left and right + * buffers, for use with stereo display. If the display system + * does not support stereo, then creation of the framebuffer will + * fail. + * + * Since: 1.20 + * Stability: unstable + */ +void +cogl_onscreen_template_set_stereo_enabled ( + CoglOnscreenTemplate *onscreen_template, + CoglBool enabled); +/** + * cogl_is_onscreen_template: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglOnscreenTemplate. + * + * Return value: %TRUE if the object references a #CoglOnscreenTemplate + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_onscreen_template (void *object); + +COGL_END_DECLS + +#endif /* __COGL_ONSCREEN_TEMPLATE_H__ */ diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c new file mode 100644 index 000000000..4b53bb25e --- /dev/null +++ b/cogl/cogl/cogl-onscreen.c @@ -0,0 +1,720 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-onscreen-private.h" +#include "cogl-frame-info-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl1-context.h" +#include "cogl-closure-list-private.h" +#include "cogl-poll-private.h" +#include "cogl-gtype-private.h" + +#ifdef COGL_HAS_X11_SUPPORT +#include "cogl-xlib-renderer.h" +#endif + +static void _cogl_onscreen_free (CoglOnscreen *onscreen); + +COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (Onscreen, onscreen, + _cogl_onscreen_class.virt_unref = + _cogl_framebuffer_unref); +COGL_GTYPE_DEFINE_CLASS (Onscreen, onscreen, + COGL_GTYPE_IMPLEMENT_INTERFACE (framebuffer)); + +static gpointer +cogl_dummy_copy (gpointer data) +{ + return data; +} + +static void +cogl_dummy_free (gpointer data) +{ +} + +COGL_GTYPE_DEFINE_BOXED (FrameClosure, frame_closure, + cogl_dummy_copy, + cogl_dummy_free); +COGL_GTYPE_DEFINE_BOXED (OnscreenResizeClosure, + onscreen_resize_closure, + cogl_dummy_copy, + cogl_dummy_free); +COGL_GTYPE_DEFINE_BOXED (OnscreenDirtyClosure, + onscreen_dirty_closure, + cogl_dummy_copy, + cogl_dummy_free); + +static void +_cogl_onscreen_init_from_template (CoglOnscreen *onscreen, + CoglOnscreenTemplate *onscreen_template) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + + _cogl_list_init (&onscreen->frame_closures); + _cogl_list_init (&onscreen->resize_closures); + _cogl_list_init (&onscreen->dirty_closures); + + framebuffer->config = onscreen_template->config; + cogl_object_ref (framebuffer->config.swap_chain); +} + +/* XXX: While we still have backend in Clutter we need a dummy object + * to represent the CoglOnscreen framebuffer that the backend + * creates... */ +CoglOnscreen * +_cogl_onscreen_new (void) +{ + CoglOnscreen *onscreen = g_new0 (CoglOnscreen, 1); + + _COGL_GET_CONTEXT (ctx, NULL); + + _cogl_framebuffer_init (COGL_FRAMEBUFFER (onscreen), + ctx, + COGL_FRAMEBUFFER_TYPE_ONSCREEN, + 0x1eadbeef, /* width */ + 0x1eadbeef); /* height */ + /* NB: make sure to pass positive width/height numbers here + * because otherwise we'll hit input validation assertions!*/ + + _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); + + COGL_FRAMEBUFFER (onscreen)->allocated = TRUE; + + /* XXX: Note we don't initialize onscreen->winsys in this case. */ + + return _cogl_onscreen_object_new (onscreen); +} + +CoglOnscreen * +cogl_onscreen_new (CoglContext *ctx, int width, int height) +{ + CoglOnscreen *onscreen; + + /* FIXME: We are assuming onscreen buffers will always be + premultiplied so we'll set the premult flag on the bitmap + format. This will usually be correct because the result of the + default blending operations for Cogl ends up with premultiplied + data in the framebuffer. However it is possible for the + framebuffer to be in whatever format depending on what + CoglPipeline is used to render to it. Eventually we may want to + add a way for an application to inform Cogl that the framebuffer + is not premultiplied in case it is being used for some special + purpose. */ + + onscreen = g_new0 (CoglOnscreen, 1); + _cogl_framebuffer_init (COGL_FRAMEBUFFER (onscreen), + ctx, + COGL_FRAMEBUFFER_TYPE_ONSCREEN, + width, /* width */ + height); /* height */ + + _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); + + return _cogl_onscreen_object_new (onscreen); +} + +static void +_cogl_onscreen_free (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); + CoglFrameInfo *frame_info; + + _cogl_closure_list_disconnect_all (&onscreen->resize_closures); + _cogl_closure_list_disconnect_all (&onscreen->frame_closures); + _cogl_closure_list_disconnect_all (&onscreen->dirty_closures); + + while ((frame_info = g_queue_pop_tail (&onscreen->pending_frame_infos))) + cogl_object_unref (frame_info); + g_queue_clear (&onscreen->pending_frame_infos); + + if (framebuffer->context->window_buffer == COGL_FRAMEBUFFER (onscreen)) + framebuffer->context->window_buffer = NULL; + + winsys->onscreen_deinit (onscreen); + _COGL_RETURN_IF_FAIL (onscreen->winsys == NULL); + + /* Chain up to parent */ + _cogl_framebuffer_free (framebuffer); + + g_free (onscreen); +} + +static void +notify_event (CoglOnscreen *onscreen, + CoglFrameEvent event, + CoglFrameInfo *info) +{ + _cogl_closure_list_invoke (&onscreen->frame_closures, + CoglFrameCallback, + onscreen, event, info); +} + +static void +_cogl_dispatch_onscreen_cb (CoglContext *context) +{ + CoglOnscreenEvent *event, *tmp; + CoglList queue; + + /* Dispatching the event callback may cause another frame to be + * drawn which in may cause another event to be queued immediately. + * To make sure this loop will only dispatch one set of events we'll + * steal the queue and iterate that separately */ + _cogl_list_init (&queue); + _cogl_list_insert_list (&queue, &context->onscreen_events_queue); + _cogl_list_init (&context->onscreen_events_queue); + + _cogl_closure_disconnect (context->onscreen_dispatch_idle); + context->onscreen_dispatch_idle = NULL; + + _cogl_list_for_each_safe (event, tmp, &queue, link) + { + CoglOnscreen *onscreen = event->onscreen; + CoglFrameInfo *info = event->info; + + notify_event (onscreen, event->type, info); + + cogl_object_unref (onscreen); + cogl_object_unref (info); + + g_slice_free (CoglOnscreenEvent, event); + } + + while (!_cogl_list_empty (&context->onscreen_dirty_queue)) + { + CoglOnscreenQueuedDirty *qe = + _cogl_container_of (context->onscreen_dirty_queue.next, + CoglOnscreenQueuedDirty, + link); + + _cogl_list_remove (&qe->link); + + _cogl_closure_list_invoke (&qe->onscreen->dirty_closures, + CoglOnscreenDirtyCallback, + qe->onscreen, + &qe->info); + + cogl_object_unref (qe->onscreen); + + g_slice_free (CoglOnscreenQueuedDirty, qe); + } +} + +static void +_cogl_onscreen_queue_dispatch_idle (CoglOnscreen *onscreen) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + + if (!ctx->onscreen_dispatch_idle) + { + ctx->onscreen_dispatch_idle = + _cogl_poll_renderer_add_idle (ctx->display->renderer, + (CoglIdleCallback) + _cogl_dispatch_onscreen_cb, + ctx, + NULL); + } +} + +void +_cogl_onscreen_queue_dirty (CoglOnscreen *onscreen, + const CoglOnscreenDirtyInfo *info) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + CoglOnscreenQueuedDirty *qe = g_slice_new (CoglOnscreenQueuedDirty); + + qe->onscreen = cogl_object_ref (onscreen); + qe->info = *info; + _cogl_list_insert (ctx->onscreen_dirty_queue.prev, &qe->link); + + _cogl_onscreen_queue_dispatch_idle (onscreen); +} + +void +_cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenDirtyInfo info; + + info.x = 0; + info.y = 0; + info.width = framebuffer->width; + info.height = framebuffer->height; + + _cogl_onscreen_queue_dirty (onscreen, &info); +} + +void +_cogl_onscreen_queue_event (CoglOnscreen *onscreen, + CoglFrameEvent type, + CoglFrameInfo *info) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + + CoglOnscreenEvent *event = g_slice_new (CoglOnscreenEvent); + + event->onscreen = cogl_object_ref (onscreen); + event->info = cogl_object_ref (info); + event->type = type; + + _cogl_list_insert (ctx->onscreen_events_queue.prev, &event->link); + + _cogl_onscreen_queue_dispatch_idle (onscreen); +} + +void +cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + const CoglWinsysVtable *winsys; + CoglFrameInfo *info; + + _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); + + info = _cogl_frame_info_new (); + info->frame_counter = onscreen->frame_counter; + g_queue_push_tail (&onscreen->pending_frame_infos, info); + + /* FIXME: we shouldn't need to flush *all* journals here! */ + cogl_flush (); + + winsys = _cogl_framebuffer_get_winsys (framebuffer); + winsys->onscreen_swap_buffers_with_damage (onscreen, + rectangles, n_rectangles); + cogl_framebuffer_discard_buffers (framebuffer, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH | + COGL_BUFFER_BIT_STENCIL); + + if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) + { + CoglFrameInfo *info; + + g_warn_if_fail (onscreen->pending_frame_infos.length == 1); + + info = g_queue_pop_tail (&onscreen->pending_frame_infos); + + _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info); + _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); + + cogl_object_unref (info); + } + + onscreen->frame_counter++; + framebuffer->mid_scene = FALSE; +} + +void +cogl_onscreen_swap_buffers (CoglOnscreen *onscreen) +{ + cogl_onscreen_swap_buffers_with_damage (onscreen, NULL, 0); +} + +void +cogl_onscreen_swap_region (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + const CoglWinsysVtable *winsys; + CoglFrameInfo *info; + + _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); + + info = _cogl_frame_info_new (); + info->frame_counter = onscreen->frame_counter; + g_queue_push_tail (&onscreen->pending_frame_infos, info); + + /* FIXME: we shouldn't need to flush *all* journals here! */ + cogl_flush (); + + winsys = _cogl_framebuffer_get_winsys (framebuffer); + + /* This should only be called if the winsys advertises + COGL_WINSYS_FEATURE_SWAP_REGION */ + _COGL_RETURN_IF_FAIL (winsys->onscreen_swap_region != NULL); + + winsys->onscreen_swap_region (COGL_ONSCREEN (framebuffer), + rectangles, + n_rectangles); + + cogl_framebuffer_discard_buffers (framebuffer, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH | + COGL_BUFFER_BIT_STENCIL); + + if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) + { + CoglFrameInfo *info; + + g_warn_if_fail (onscreen->pending_frame_infos.length == 1); + + info = g_queue_pop_tail (&onscreen->pending_frame_infos); + + _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info); + _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); + + cogl_object_unref (info); + } + + onscreen->frame_counter++; + framebuffer->mid_scene = FALSE; +} + +int +cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + const CoglWinsysVtable *winsys; + + _COGL_RETURN_VAL_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN, 0); + + winsys = _cogl_framebuffer_get_winsys (framebuffer); + + if (!winsys->onscreen_get_buffer_age) + return 0; + + return winsys->onscreen_get_buffer_age (onscreen); +} + +#ifdef COGL_HAS_X11_SUPPORT +void +cogl_x11_onscreen_set_foreign_window_xid (CoglOnscreen *onscreen, + uint32_t xid, + CoglOnscreenX11MaskCallback update, + void *user_data) +{ + /* We don't wan't applications to get away with being lazy here and not + * passing an update callback... */ + _COGL_RETURN_IF_FAIL (update); + + onscreen->foreign_xid = xid; + onscreen->foreign_update_mask_callback = update; + onscreen->foreign_update_mask_data = user_data; +} + +uint32_t +cogl_x11_onscreen_get_window_xid (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + + if (onscreen->foreign_xid) + return onscreen->foreign_xid; + else + { + const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); + + /* This should only be called for x11 onscreens */ + _COGL_RETURN_VAL_IF_FAIL (winsys->onscreen_x11_get_window_xid != NULL, 0); + + return winsys->onscreen_x11_get_window_xid (onscreen); + } +} + +uint32_t +cogl_x11_onscreen_get_visual_xid (CoglOnscreen *onscreen) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + XVisualInfo *visinfo; + uint32_t id; + + /* This should only be called for xlib based onscreens */ + visinfo = cogl_xlib_renderer_get_visual_info (ctx->display->renderer); + if (visinfo == NULL) + return 0; + + id = (uint32_t)visinfo->visualid; + + return id; +} +#endif /* COGL_HAS_X11_SUPPORT */ + +CoglFrameClosure * +cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, + CoglFrameCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + return _cogl_closure_list_add (&onscreen->frame_closures, + callback, + user_data, + destroy); +} + +void +cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen, + CoglFrameClosure *closure) +{ + _COGL_RETURN_IF_FAIL (closure); + + _cogl_closure_disconnect (closure); +} + +typedef struct _SwapBufferCallbackState +{ + CoglSwapBuffersNotify callback; + void *user_data; +} SwapBufferCallbackState; + +static void +destroy_swap_buffers_callback_state (void *user_data) +{ + g_slice_free (SwapBufferCallbackState, user_data); +} + +static void +shim_swap_buffers_callback (CoglOnscreen *onscreen, + CoglFrameEvent event, + CoglFrameInfo *info, + void *user_data) +{ + SwapBufferCallbackState *state = user_data; + + /* XXX: Note that technically it is a change in semantics for this + * interface to forward _SYNC events here and also makes the api + * name somewhat missleading. + * + * In practice though this interface is currently used by + * applications for throttling, not because they are strictly + * interested in knowing when a frame has been presented and so + * forwarding _SYNC events should serve them better. + */ + if (event == COGL_FRAME_EVENT_SYNC) + state->callback (COGL_FRAMEBUFFER (onscreen), state->user_data); +} + +unsigned int +cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen, + CoglSwapBuffersNotify callback, + void *user_data) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + SwapBufferCallbackState *state = g_slice_new (SwapBufferCallbackState); + CoglFrameClosure *closure; + unsigned int id = ctx->next_swap_callback_id++; + + state->callback = callback; + state->user_data = user_data; + + closure = + cogl_onscreen_add_frame_callback (onscreen, + shim_swap_buffers_callback, + state, + destroy_swap_buffers_callback_state); + + g_hash_table_insert (ctx->swap_callback_closures, + GINT_TO_POINTER (id), + closure); + + return id; +} + +void +cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen, + unsigned int id) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; + CoglFrameClosure *closure = g_hash_table_lookup (ctx->swap_callback_closures, + GINT_TO_POINTER (id)); + + _COGL_RETURN_IF_FAIL (closure); + + cogl_onscreen_remove_frame_callback (onscreen, closure); +} + +void +cogl_onscreen_set_swap_throttled (CoglOnscreen *onscreen, + CoglBool throttled) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + framebuffer->config.swap_throttled = throttled; + if (framebuffer->allocated) + { + const CoglWinsysVtable *winsys = + _cogl_framebuffer_get_winsys (framebuffer); + winsys->onscreen_update_swap_throttled (onscreen); + } +} + +void +cogl_onscreen_show (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + const CoglWinsysVtable *winsys; + + if (!framebuffer->allocated) + { + if (!cogl_framebuffer_allocate (framebuffer, NULL)) + return; + } + + winsys = _cogl_framebuffer_get_winsys (framebuffer); + if (winsys->onscreen_set_visibility) + winsys->onscreen_set_visibility (onscreen, TRUE); +} + +void +cogl_onscreen_hide (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + + if (framebuffer->allocated) + { + const CoglWinsysVtable *winsys = + _cogl_framebuffer_get_winsys (framebuffer); + if (winsys->onscreen_set_visibility) + winsys->onscreen_set_visibility (onscreen, FALSE); + } +} + +void +_cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info) +{ + notify_event (onscreen, COGL_FRAME_EVENT_SYNC, info); +} + +void +_cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info) +{ + notify_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); +} + +void +_cogl_onscreen_notify_resize (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + + _cogl_closure_list_invoke (&onscreen->resize_closures, + CoglOnscreenResizeCallback, + onscreen, + framebuffer->width, + framebuffer->height); +} + +void +_cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer, + int width, int height) +{ + if (framebuffer->width == width && framebuffer->height == height) + return; + + framebuffer->width = width; + framebuffer->height = height; + + cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); + + if (!_cogl_has_private_feature (framebuffer->context, + COGL_PRIVATE_FEATURE_DIRTY_EVENTS)) + _cogl_onscreen_queue_full_dirty (COGL_ONSCREEN (framebuffer)); +} + +void +cogl_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + CoglFramebuffer *framebuffer; + const CoglWinsysVtable *winsys; + + if (onscreen->resizable == resizable) + return; + + onscreen->resizable = resizable; + + framebuffer = COGL_FRAMEBUFFER (onscreen); + if (framebuffer->allocated) + { + winsys = _cogl_framebuffer_get_winsys (COGL_FRAMEBUFFER (onscreen)); + + if (winsys->onscreen_set_resizable) + winsys->onscreen_set_resizable (onscreen, resizable); + } +} + +CoglBool +cogl_onscreen_get_resizable (CoglOnscreen *onscreen) +{ + return onscreen->resizable; +} + +CoglOnscreenResizeClosure * +cogl_onscreen_add_resize_callback (CoglOnscreen *onscreen, + CoglOnscreenResizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + return _cogl_closure_list_add (&onscreen->resize_closures, + callback, + user_data, + destroy); +} + +void +cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen, + CoglOnscreenResizeClosure *closure) +{ + _cogl_closure_disconnect (closure); +} + +CoglOnscreenDirtyClosure * +cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen, + CoglOnscreenDirtyCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + return _cogl_closure_list_add (&onscreen->dirty_closures, + callback, + user_data, + destroy); +} + +void +cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen, + CoglOnscreenDirtyClosure *closure) +{ + _COGL_RETURN_IF_FAIL (closure); + + _cogl_closure_disconnect (closure); +} + +int64_t +cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen) +{ + return onscreen->frame_counter; +} diff --git a/cogl/cogl/cogl-onscreen.h b/cogl/cogl/cogl-onscreen.h new file mode 100644 index 000000000..6a55c4c1d --- /dev/null +++ b/cogl/cogl/cogl-onscreen.h @@ -0,0 +1,911 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011,2012,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_ONSCREEN_H +#define __COGL_ONSCREEN_H + +#include +#include +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +typedef struct _CoglOnscreen CoglOnscreen; +#define COGL_ONSCREEN(X) ((CoglOnscreen *)(X)) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_onscreen_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_onscreen_get_gtype (void); +#endif + +/** + * cogl_onscreen_new: (constructor) + * @context: A #CoglContext + * @width: The desired framebuffer width + * @height: The desired framebuffer height + * + * Instantiates an "unallocated" #CoglOnscreen framebuffer that may be + * configured before later being allocated, either implicitly when + * it is first used or explicitly via cogl_framebuffer_allocate(). + * + * Return value: (transfer full): A newly instantiated #CoglOnscreen framebuffer + * Since: 1.8 + * Stability: unstable + */ +CoglOnscreen * +cogl_onscreen_new (CoglContext *context, int width, int height); + +#ifdef COGL_HAS_X11 +typedef void (*CoglOnscreenX11MaskCallback) (CoglOnscreen *onscreen, + uint32_t event_mask, + void *user_data); + +/** + * cogl_x11_onscreen_set_foreign_window_xid: + * @onscreen: The unallocated framebuffer to associated with an X + * window. + * @xid: The XID of an existing X window + * @update: A callback that notifies of updates to what Cogl requires + * to be in the core X protocol event mask. + * @user_data: user data passed to @update + * + * Ideally we would recommend that you let Cogl be responsible for + * creating any X window required to back an onscreen framebuffer but + * if you really need to target a window created manually this + * function can be called before @onscreen has been allocated to set a + * foreign XID for your existing X window. + * + * Since Cogl needs, for example, to track changes to the size of an X + * window it requires that certain events be selected for via the core + * X protocol. This requirement may also be changed asynchronously so + * you must pass in an @update callback to inform you of Cogl's + * required event mask. + * + * For example if you are using Xlib you could use this API roughly + * as follows: + * [{ + * static void + * my_update_cogl_x11_event_mask (CoglOnscreen *onscreen, + * uint32_t event_mask, + * void *user_data) + * { + * XSetWindowAttributes attrs; + * MyData *data = user_data; + * attrs.event_mask = event_mask | data->my_event_mask; + * XChangeWindowAttributes (data->xdpy, + * data->xwin, + * CWEventMask, + * &attrs); + * } + * + * { + * *snip* + * cogl_x11_onscreen_set_foreign_window_xid (onscreen, + * data->xwin, + * my_update_cogl_x11_event_mask, + * data); + * *snip* + * } + * }] + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_x11_onscreen_set_foreign_window_xid (CoglOnscreen *onscreen, + uint32_t xid, + CoglOnscreenX11MaskCallback update, + void *user_data); + +/** + * cogl_x11_onscreen_get_window_xid: + * @onscreen: A #CoglOnscreen framebuffer + * + * Assuming you know the given @onscreen framebuffer is based on an x11 window + * this queries the XID of that window. If + * cogl_x11_onscreen_set_foreign_window_xid() was previously called then it + * will return that same XID otherwise it will be the XID of a window Cogl + * created internally. If the window has not been allocated yet and a foreign + * xid has not been set then it's undefined what value will be returned. + * + * It's undefined what this function does if called when not using an x11 based + * renderer. + * + * Since: 1.10 + * Stability: unstable + */ +uint32_t +cogl_x11_onscreen_get_window_xid (CoglOnscreen *onscreen); + +/* XXX: we should maybe remove this, since nothing currently uses + * it and the current implementation looks dubious. */ +uint32_t +cogl_x11_onscreen_get_visual_xid (CoglOnscreen *onscreen); +#endif /* COGL_HAS_X11 */ + +/** + * cogl_onscreen_set_swap_throttled: + * @onscreen: A #CoglOnscreen framebuffer + * @throttled: Whether swap throttling is wanted or not. + * + * Requests that the given @onscreen framebuffer should have swap buffer + * requests (made using cogl_onscreen_swap_buffers()) throttled either by a + * displays vblank period or perhaps some other mechanism in a composited + * environment. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_onscreen_set_swap_throttled (CoglOnscreen *onscreen, + CoglBool throttled); + +/** + * cogl_onscreen_show: + * @onscreen: The onscreen framebuffer to make visible + * + * This requests to make @onscreen visible to the user. + * + * Actually the precise semantics of this function depend on the + * window system currently in use, and if you don't have a + * multi-windowining system this function may in-fact do nothing. + * + * This function will implicitly allocate the given @onscreen + * framebuffer before showing it if it hasn't already been allocated. + * + * When using the Wayland winsys calling this will set the surface to + * a toplevel type which will make it appear. If the application wants + * to set a different type for the surface, it can avoid calling + * cogl_onscreen_show() and set its own type directly with the Wayland + * client API via cogl_wayland_onscreen_get_surface(). + * + * Since Cogl doesn't explicitly track the visibility status of + * onscreen framebuffers it wont try to avoid redundant window system + * requests e.g. to show an already visible window. This also means + * that it's acceptable to alternatively use native APIs to show and + * hide windows without confusing Cogl. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_onscreen_show (CoglOnscreen *onscreen); + +/** + * cogl_onscreen_hide: + * @onscreen: The onscreen framebuffer to make invisible + * + * This requests to make @onscreen invisible to the user. + * + * Actually the precise semantics of this function depend on the + * window system currently in use, and if you don't have a + * multi-windowining system this function may in-fact do nothing. + * + * This function does not implicitly allocate the given @onscreen + * framebuffer before hiding it. + * + * Since Cogl doesn't explicitly track the visibility status of + * onscreen framebuffers it wont try to avoid redundant window system + * requests e.g. to show an already visible window. This also means + * that it's acceptable to alternatively use native APIs to show and + * hide windows without confusing Cogl. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_onscreen_hide (CoglOnscreen *onscreen); + +/** + * cogl_onscreen_swap_buffers: + * @onscreen: A #CoglOnscreen framebuffer + * + * Swaps the current back buffer being rendered too, to the front for display. + * + * This function also implicitly discards the contents of the color, depth and + * stencil buffers as if cogl_framebuffer_discard_buffers() were used. The + * significance of the discard is that you should not expect to be able to + * start a new frame that incrementally builds on the contents of the previous + * frame. + * + * It is highly recommended that applications use + * cogl_onscreen_swap_buffers_with_damage() instead whenever possible + * and also use the cogl_onscreen_get_buffer_age() api so they can + * perform incremental updates to older buffers instead of having to + * render a full buffer for every frame. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_onscreen_swap_buffers (CoglOnscreen *onscreen); + + +/** + * cogl_onscreen_get_buffer_age: + * @onscreen: A #CoglOnscreen framebuffer + * + * Gets the current age of the buffer contents. + * + * This function allows applications to query the age of the current + * back buffer contents for a #CoglOnscreen as the number of frames + * elapsed since the contents were most recently defined. + * + * These age values exposes enough information to applications about + * how Cogl internally manages back buffers to allow applications to + * re-use the contents of old frames and minimize how much must be + * redrawn for the next frame. + * + * The back buffer contents can either be reported as invalid (has an + * age of 0) or it may be reported to be the same contents as from n + * frames prior to the current frame. + * + * The queried value remains valid until the next buffer swap. + * + * One caveat is that under X11 the buffer age does not reflect + * changes to buffer contents caused by the window systems. X11 + * applications must track Expose events to determine what buffer + * regions need to additionally be repaired each frame. + * + * The recommended way to take advantage of this buffer age api is to + * build up a circular buffer of length 3 for tracking damage regions + * over the last 3 frames and when starting a new frame look at the + * age of the buffer and combine the damage regions for the current + * frame with the damage regions of previous @age frames so you know + * everything that must be redrawn to update the old contents for the + * new frame. + * + * If the system doesn't not support being able to track the age + * of back buffers then this function will always return 0 which + * implies that the contents are undefined. + * + * The %COGL_FEATURE_ID_BUFFER_AGE feature can optionally be + * explicitly checked to determine if Cogl is currently tracking the + * age of #CoglOnscreen back buffer contents. If this feature is + * missing then this function will always return 0. + * + * Return value: The age of the buffer contents or 0 when the buffer + * contents are undefined. + * + * Since: 1.14 + * Stability: stable + */ +int +cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen); + +/** + * cogl_onscreen_swap_buffers_with_damage: + * @onscreen: A #CoglOnscreen framebuffer + * @rectangles: An array of integer 4-tuples representing damaged + * rectangles as (x, y, width, height) tuples. + * @n_rectangles: The number of 4-tuples to be read from @rectangles + * + * Swaps the current back buffer being rendered too, to the front for + * display and provides information to any system compositor about + * what regions of the buffer have changed (damage) with respect to + * the last swapped buffer. + * + * This function has the same semantics as + * cogl_framebuffer_swap_buffers() except that it additionally allows + * applications to pass a list of damaged rectangles which may be + * passed on to a compositor so that it can minimize how much of the + * screen is redrawn in response to this applications newly swapped + * front buffer. + * + * For example if your application is only animating a small object in + * the corner of the screen and everything else is remaining static + * then it can help the compositor to know that only the bottom right + * corner of your newly swapped buffer has really changed with respect + * to your previously swapped front buffer. + * + * If @n_rectangles is 0 then the whole buffer will implicitly be + * reported as damaged as if cogl_onscreen_swap_buffers() had been + * called. + * + * This function also implicitly discards the contents of the color, + * depth and stencil buffers as if cogl_framebuffer_discard_buffers() + * were used. The significance of the discard is that you should not + * expect to be able to start a new frame that incrementally builds on + * the contents of the previous frame. If you want to perform + * incremental updates to older back buffers then please refer to the + * cogl_onscreen_get_buffer_age() api. + * + * Whenever possible it is recommended that applications use this + * function instead of cogl_onscreen_swap_buffers() to improve + * performance when running under a compositor. + * + * It is highly recommended to use this API in conjunction with + * the cogl_onscreen_get_buffer_age() api so that your application can + * perform incremental rendering based on old back buffers. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles); + +/** + * cogl_onscreen_swap_region: + * @onscreen: A #CoglOnscreen framebuffer + * @rectangles: An array of integer 4-tuples representing rectangles as + * (x, y, width, height) tuples. + * @n_rectangles: The number of 4-tuples to be read from @rectangles + * + * Swaps a region of the back buffer being rendered too, to the front for + * display. @rectangles represents the region as array of @n_rectangles each + * defined by 4 sequential (x, y, width, height) integers. + * + * This function also implicitly discards the contents of the color, depth and + * stencil buffers as if cogl_framebuffer_discard_buffers() were used. The + * significance of the discard is that you should not expect to be able to + * start a new frame that incrementally builds on the contents of the previous + * frame. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_onscreen_swap_region (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles); + +/** + * CoglFrameEvent: + * @COGL_FRAME_EVENT_SYNC: Notifies that the system compositor has + * acknowledged a frame and is ready for a + * new frame to be created. + * @COGL_FRAME_EVENT_COMPLETE: Notifies that a frame has ended. This + * is a good time for applications to + * collect statistics about the frame + * since the #CoglFrameInfo should hold + * the most data at this point. No other + * events should be expected after a + * @COGL_FRAME_EVENT_COMPLETE event. + * + * Identifiers that are passed to #CoglFrameCallback functions + * (registered using cogl_onscreen_add_frame_callback()) that + * mark the progression of a frame in some way which usually + * means that new information will have been accumulated in the + * frame's corresponding #CoglFrameInfo object. + * + * The last event that will be sent for a frame will be a + * @COGL_FRAME_EVENT_COMPLETE event and so these are a good + * opportunity to collect statistics about a frame since the + * #CoglFrameInfo should hold the most data at this point. + * + * A frame may not be completed before the next frame can start + * so applications should avoid needing to collect all statistics for + * a particular frame before they can start a new frame. + * + * Since: 1.14 + * Stability: unstable + */ +typedef enum _CoglFrameEvent +{ + COGL_FRAME_EVENT_SYNC = 1, + COGL_FRAME_EVENT_COMPLETE +} CoglFrameEvent; + +/** + * CoglFrameCallback: + * @onscreen: The onscreen that the frame is associated with + * @event: A #CoglFrameEvent notifying how the frame has progressed + * @info: The meta information, such as timing information, about + * the frame that has progressed. + * @user_data: The user pointer passed to + * cogl_onscreen_add_frame_callback() + * + * Is a callback that can be registered via + * cogl_onscreen_add_frame_callback() to be called when a frame + * progresses in some notable way. + * + * Please see the documentation for #CoglFrameEvent and + * cogl_onscreen_add_frame_callback() for more details about what + * events can be notified. + * + * Since: 1.14 + * Stability: unstable + */ +typedef void (*CoglFrameCallback) (CoglOnscreen *onscreen, + CoglFrameEvent event, + CoglFrameInfo *info, + void *user_data); + +/** + * CoglFrameClosure: + * + * An opaque type that tracks a #CoglFrameCallback and associated user + * data. A #CoglFrameClosure pointer will be returned from + * cogl_onscreen_add_frame_callback() and it allows you to remove a + * callback later using cogl_onscreen_remove_frame_callback(). + * + * Since: 1.14 + * Stability: unstable + */ +typedef struct _CoglClosure CoglFrameClosure; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_frame_closure_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_frame_closure_get_gtype (void); +#endif + +/** + * cogl_onscreen_add_frame_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @callback: (scope notified): A callback function to call for frame events + * @user_data: (closure): A private pointer to be passed to @callback + * @destroy: (allow-none): An optional callback to destroy @user_data + * when the @callback is removed or @onscreen is freed. + * + * Installs a @callback function that will be called for significant + * events relating to the given @onscreen framebuffer. + * + * The @callback will be used to notify when the system compositor is + * ready for this application to render a new frame. In this case + * %COGL_FRAME_EVENT_SYNC will be passed as the event argument to the + * given @callback in addition to the #CoglFrameInfo corresponding to + * the frame beeing acknowledged by the compositor. + * + * The @callback will also be called to notify when the frame has + * ended. In this case %COGL_FRAME_EVENT_COMPLETE will be passed as + * the event argument to the given @callback in addition to the + * #CoglFrameInfo corresponding to the newly presented frame. The + * meaning of "ended" here simply means that no more timing + * information will be collected within the corresponding + * #CoglFrameInfo and so this is a good opportunity to analyse the + * given info. It does not necessarily mean that the GPU has finished + * rendering the corresponding frame. + * + * We highly recommend throttling your application according to + * %COGL_FRAME_EVENT_SYNC events so that your application can avoid + * wasting resources, drawing more frames than your system compositor + * can display. + * + * Return value: a #CoglFrameClosure pointer that can be used to + * remove the callback and associated @user_data later. + * Since: 1.14 + * Stability: unstable + */ +CoglFrameClosure * +cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, + CoglFrameCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); + +/** + * cogl_onscreen_remove_frame_callback: + * @onscreen: A #CoglOnscreen + * @closure: A #CoglFrameClosure returned from + * cogl_onscreen_add_frame_callback() + * + * Removes a callback and associated user data that were previously + * registered using cogl_onscreen_add_frame_callback(). + * + * If a destroy callback was passed to + * cogl_onscreen_add_frame_callback() to destroy the user data then + * this will get called. + * + * Since: 1.14 + * Stability: unstable + */ +void +cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen, + CoglFrameClosure *closure); + +typedef void (*CoglSwapBuffersNotify) (CoglFramebuffer *framebuffer, + void *user_data); + +/** + * cogl_onscreen_add_swap_buffers_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @callback: (scope notified): A callback function to call when a swap + * has completed + * @user_data: (closure): A private pointer to be passed to @callback + * + * Installs a @callback function that should be called whenever a swap buffers + * request (made using cogl_onscreen_swap_buffers()) for the given + * @onscreen completes. + * + * Applications should check for the %COGL_FEATURE_ID_SWAP_BUFFERS_EVENT + * feature before using this API. It's currently undefined when and if + * registered callbacks will be called if this feature is not supported. + * + * We recommend using this mechanism when available to manually throttle your + * applications (in conjunction with cogl_onscreen_set_swap_throttled()) so + * your application will be able to avoid long blocks in the driver caused by + * throttling when you request to swap buffers too quickly. + * + * Return value: a unique identifier that can be used to remove to remove + * the callback later. + * Since: 1.10 + * Stability: unstable + * Deprecated: 1.14: Use cogl_onscreen_add_frame_callback() instead + */ +COGL_DEPRECATED_IN_1_14_FOR (cogl_onscreen_add_frame_callback) +unsigned int +cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen, + CoglSwapBuffersNotify callback, + void *user_data); + +/** + * cogl_onscreen_remove_swap_buffers_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @id: An identifier returned from cogl_onscreen_add_swap_buffers_callback() + * + * Removes a callback that was previously registered + * using cogl_onscreen_add_swap_buffers_callback(). + * + * Since: 1.10 + * Stability: unstable + * Deprecated: 1.14: Use cogl_onscreen_remove_frame_callback() instead + */ + +COGL_DEPRECATED_IN_1_14_FOR (cogl_onscreen_remove_frame_callback) +void +cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen, + unsigned int id); + +/** + * cogl_onscreen_set_resizable: + * @onscreen: A #CoglOnscreen framebuffer + * + * Lets you request Cogl to mark an @onscreen framebuffer as + * resizable or not. + * + * By default, if possible, a @onscreen will be created by Cogl + * as non resizable, but it is not guaranteed that this is always + * possible for all window systems. + * + * Cogl does not know whether marking the @onscreen framebuffer + * is truly meaningful for your current window system (consider + * applications being run fullscreen on a phone or TV) so this + * function may not have any useful effect. If you are running on a + * multi windowing system such as X11 or Win32 or OSX then Cogl will + * request to the window system that users be allowed to resize the + * @onscreen, although it's still possible that some other window + * management policy will block this possibility. + * + * Whenever an @onscreen framebuffer is resized the viewport + * will be automatically updated to match the new size of the + * framebuffer with an origin of (0,0). If your application needs more + * specialized control of the viewport it will need to register a + * resize handler using cogl_onscreen_add_resize_callback() so that it + * can track when the viewport has been changed automatically. + * + * Since: 2.0 + */ +void +cogl_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable); + +/** + * cogl_onscreen_get_resizable: + * @onscreen: A #CoglOnscreen framebuffer + * + * Lets you query whether @onscreen has been marked as resizable via + * the cogl_onscreen_set_resizable() api. + * + * By default, if possible, a @onscreen will be created by Cogl + * as non resizable, but it is not guaranteed that this is always + * possible for all window systems. + * + * If cogl_onscreen_set_resizable(@onscreen, %TRUE) has been + * previously called then this function will return %TRUE, but it's + * possible that the current windowing system being used does not + * support window resizing (consider fullscreen windows on a phone or + * a TV). This function is not aware of whether resizing is truly + * meaningful with your window system, only whether the @onscreen has + * been marked as resizable. + * + * Return value: Returns whether @onscreen has been marked as + * resizable or not. + * Since: 2.0 + */ +CoglBool +cogl_onscreen_get_resizable (CoglOnscreen *onscreen); + +/** + * CoglOnscreenResizeCallback: + * @onscreen: A #CoglOnscreen framebuffer that was resized + * @width: The new width of @onscreen + * @height: The new height of @onscreen + * @user_data: The private passed to + * cogl_onscreen_add_resize_callback() + * + * Is a callback type used with the + * cogl_onscreen_add_resize_callback() allowing applications to be + * notified whenever an @onscreen framebuffer is resized. + * + * Cogl automatically updates the viewport of an @onscreen + * framebuffer that is resized so this callback is also an indication + * that the viewport has been modified too + * + * A resize callback will only ever be called while dispatching + * Cogl events from the system mainloop; so for example during + * cogl_poll_renderer_dispatch(). This is so that callbacks shouldn't + * occur while an application might have arbitrary locks held for + * example. + * + * Since: 2.0 + */ +typedef void (*CoglOnscreenResizeCallback) (CoglOnscreen *onscreen, + int width, + int height, + void *user_data); + +/** + * CoglOnscreenResizeClosure: + * + * An opaque type that tracks a #CoglOnscreenResizeCallback and + * associated user data. A #CoglOnscreenResizeClosure pointer will be + * returned from cogl_onscreen_add_resize_callback() and it allows you + * to remove a callback later using + * cogl_onscreen_remove_resize_callback(). + * + * Since: 2.0 + * Stability: unstable + */ +typedef struct _CoglClosure CoglOnscreenResizeClosure; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_onscreen_resize_closure_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_onscreen_resize_closure_get_gtype (void); +#endif + +/** + * cogl_onscreen_add_resize_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @callback: (scope notified): A #CoglOnscreenResizeCallback to call when + * the @onscreen changes size. + * @user_data: (closure): Private data to be passed to @callback. + * @destroy: (allow-none): An optional callback to destroy @user_data + * when the @callback is removed or @onscreen is freed. + * + * Registers a @callback with @onscreen that will be called whenever + * the @onscreen framebuffer changes size. + * + * The @callback can be removed using + * cogl_onscreen_remove_resize_callback() passing the returned closure + * pointer. + * + * Since Cogl automatically updates the viewport of an @onscreen + * framebuffer that is resized, a resize callback can also be used to + * track when the viewport has been changed automatically by Cogl in + * case your application needs more specialized control over the + * viewport. + * + * A resize callback will only ever be called while dispatching + * Cogl events from the system mainloop; so for example during + * cogl_poll_renderer_dispatch(). This is so that callbacks shouldn't + * occur while an application might have arbitrary locks held for + * example. + * + * Return value: a #CoglOnscreenResizeClosure pointer that can be used to + * remove the callback and associated @user_data later. + * Since: 2.0 + */ +CoglOnscreenResizeClosure * +cogl_onscreen_add_resize_callback (CoglOnscreen *onscreen, + CoglOnscreenResizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); + +/** + * cogl_onscreen_remove_resize_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @closure: An identifier returned from cogl_onscreen_add_resize_callback() + * + * Removes a resize @callback and @user_data pair that were previously + * associated with @onscreen via cogl_onscreen_add_resize_callback(). + * + * Since: 2.0 + */ +void +cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen, + CoglOnscreenResizeClosure *closure); + +/** + * CoglOnscreenDirtyInfo: + * @x: Left edge of the dirty rectangle + * @y: Top edge of the dirty rectangle, measured from the top of the window + * @width: Width of the dirty rectangle + * @height: Height of the dirty rectangle + * + * A structure passed to callbacks registered using + * cogl_onscreen_add_dirty_callback(). The members describe a + * rectangle within the onscreen buffer that should be redrawn. + * + * Since: 1.16 + * Stability: unstable + */ +typedef struct _CoglOnscreenDirtyInfo CoglOnscreenDirtyInfo; + +struct _CoglOnscreenDirtyInfo +{ + int x, y; + int width, height; +}; + +/** + * CoglOnscreenDirtyCallback: + * @onscreen: The onscreen that the frame is associated with + * @info: A #CoglOnscreenDirtyInfo struct containing the details of the + * dirty area + * @user_data: The user pointer passed to + * cogl_onscreen_add_frame_callback() + * + * Is a callback that can be registered via + * cogl_onscreen_add_dirty_callback() to be called when the windowing + * system determines that a region of the onscreen window has been + * lost and the application should redraw it. + * + * Since: 1.16 + * Stability: unstable + */ +typedef void (*CoglOnscreenDirtyCallback) (CoglOnscreen *onscreen, + const CoglOnscreenDirtyInfo *info, + void *user_data); + +/** + * CoglOnscreenDirtyClosure: + * + * An opaque type that tracks a #CoglOnscreenDirtyCallback and associated + * user data. A #CoglOnscreenDirtyClosure pointer will be returned from + * cogl_onscreen_add_dirty_callback() and it allows you to remove a + * callback later using cogl_onscreen_remove_dirty_callback(). + * + * Since: 1.16 + * Stability: unstable + */ +typedef struct _CoglClosure CoglOnscreenDirtyClosure; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_onscreen_dirty_closure_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_onscreen_dirty_closure_get_gtype (void); +#endif + +/** + * cogl_onscreen_add_dirty_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @callback: (scope notified): A callback function to call for dirty events + * @user_data: (closure): A private pointer to be passed to @callback + * @destroy: (allow-none): An optional callback to destroy @user_data when the + * @callback is removed or @onscreen is freed. + * + * Installs a @callback function that will be called whenever the + * window system has lost the contents of a region of the onscreen + * buffer and the application should redraw it to repair the buffer. + * For example this may happen in a window system without a compositor + * if a window that was previously covering up the onscreen window has + * been moved causing a region of the onscreen to be exposed. + * + * The @callback will be passed a #CoglOnscreenDirtyInfo struct which + * decribes a rectangle containing the newly dirtied region. Note that + * this may be called multiple times to describe a non-rectangular + * region composed of multiple smaller rectangles. + * + * The dirty events are separate from %COGL_FRAME_EVENT_SYNC events so + * the application should also listen for this event before rendering + * the dirty region to ensure that the framebuffer is actually ready + * for rendering. + * + * Return value: a #CoglOnscreenDirtyClosure pointer that can be used to + * remove the callback and associated @user_data later. + * Since: 1.16 + * Stability: unstable + */ +CoglOnscreenDirtyClosure * +cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen, + CoglOnscreenDirtyCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); + +/** + * cogl_onscreen_remove_dirty_callback: + * @onscreen: A #CoglOnscreen + * @closure: A #CoglOnscreenDirtyClosure returned from + * cogl_onscreen_add_dirty_callback() + * + * Removes a callback and associated user data that were previously + * registered using cogl_onscreen_add_dirty_callback(). + * + * If a destroy callback was passed to + * cogl_onscreen_add_dirty_callback() to destroy the user data then + * this will also get called. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen, + CoglOnscreenDirtyClosure *closure); + +/** + * cogl_is_onscreen: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglOnscreen. + * + * Return value: %TRUE if the object references a #CoglOnscreen + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_onscreen (void *object); + +/** + * cogl_onscreen_get_frame_counter: + * + * Gets the value of the framebuffers frame counter. This is + * a counter that increases by one each time + * cogl_onscreen_swap_buffers() or cogl_onscreen_swap_region() + * is called. + * + * Return value: the current frame counter value + * Since: 1.14 + * Stability: unstable + */ +int64_t +cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen); + +COGL_END_DECLS + +#endif /* __COGL_ONSCREEN_H */ diff --git a/cogl/cogl/cogl-output-private.h b/cogl/cogl/cogl-output-private.h new file mode 100644 index 000000000..d264d482b --- /dev/null +++ b/cogl/cogl/cogl-output-private.h @@ -0,0 +1,57 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_OUTPUT_PRIVATE_H +#define __COGL_OUTPUT_PRIVATE_H + +#include "cogl-output.h" +#include "cogl-object-private.h" + +struct _CoglOutput +{ + CoglObject _parent; + + char *name; + + int x; /* Must be first field for _cogl_output_values_equal() */ + int y; + int width; + int height; + int mm_width; + int mm_height; + float refresh_rate; + CoglSubpixelOrder subpixel_order; +}; + +CoglOutput *_cogl_output_new (const char *name); +CoglBool _cogl_output_values_equal (CoglOutput *output, + CoglOutput *other); + +#endif /* __COGL_OUTPUT_PRIVATE_H */ diff --git a/cogl/cogl/cogl-output.c b/cogl/cogl/cogl-output.c new file mode 100644 index 000000000..ae8560518 --- /dev/null +++ b/cogl/cogl/cogl-output.c @@ -0,0 +1,119 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-output-private.h" +#include "cogl-gtype-private.h" + +#include + +static void _cogl_output_free (CoglOutput *output); + +COGL_OBJECT_DEFINE (Output, output); +COGL_GTYPE_DEFINE_CLASS (Output, output); + +CoglOutput * +_cogl_output_new (const char *name) +{ + CoglOutput *output; + + output = g_slice_new0 (CoglOutput); + output->name = g_strdup (name); + + return _cogl_output_object_new (output); +} + +static void +_cogl_output_free (CoglOutput *output) +{ + g_free (output->name); + + g_slice_free (CoglOutput, output); +} + +gboolean +_cogl_output_values_equal (CoglOutput *output, + CoglOutput *other) +{ + return memcmp ((const char *)output + G_STRUCT_OFFSET (CoglOutput, x), + (const char *)other + G_STRUCT_OFFSET (CoglOutput, x), + sizeof (CoglOutput) - G_STRUCT_OFFSET (CoglOutput, x)) == 0; +} + +int +cogl_output_get_x (CoglOutput *output) +{ + return output->x; +} + +int +cogl_output_get_y (CoglOutput *output) +{ + return output->y; +} + +int +cogl_output_get_width (CoglOutput *output) +{ + return output->width; +} + +int +cogl_output_get_height (CoglOutput *output) +{ + return output->height; +} + +int +cogl_output_get_mm_width (CoglOutput *output) +{ + return output->mm_width; +} + +int +cogl_output_get_mm_height (CoglOutput *output) +{ + return output->mm_height; +} + +CoglSubpixelOrder +cogl_output_get_subpixel_order (CoglOutput *output) +{ + return output->subpixel_order; +} + +float +cogl_output_get_refresh_rate (CoglOutput *output) +{ + return output->refresh_rate; +} diff --git a/cogl/cogl/cogl-output.h b/cogl/cogl/cogl-output.h new file mode 100644 index 000000000..6eec10eeb --- /dev/null +++ b/cogl/cogl/cogl-output.h @@ -0,0 +1,261 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Owen Taylor + */ +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_OUTPUT_H +#define __COGL_OUTPUT_H + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-output + * @short_description: information about an output device + * + * The #CoglOutput object holds information about an output device + * such as a monitor or laptop display. It can be queried to find + * out the position of the output with respect to the screen + * coordinate system and other information such as the resolution + * and refresh rate of the device. + * + * There can be any number of outputs which may overlap: the + * same area of the screen may be displayed by multiple output + * devices. + * + * XXX: though it's possible to query the position of the output + * with respect to screen coordinates, there is currently no way + * of finding out the position of a #CoglOnscreen in screen + * coordinates, at least without using windowing-system specific + * API's, so it's not easy to get the output positions relative + * to the #CoglOnscreen. + */ + +typedef struct _CoglOutput CoglOutput; +#define COGL_OUTPUT(X) ((CoglOutput *)(X)) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_output_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_output_get_gtype (void); +#endif + +/** + * CoglSubpixelOrder: + * @COGL_SUBPIXEL_ORDER_UNKNOWN: the layout of subpixel + * components for the device is unknown. + * @COGL_SUBPIXEL_ORDER_NONE: the device displays colors + * without geometrically-separated subpixel components, + * or the positioning or colors of the components do not + * match any of the values in the enumeration. + * @COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: the device has + * horizontally arranged components in the order + * red-green-blue from left to right. + * @COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: the device has + * horizontally arranged components in the order + * blue-green-red from left to right. + * @COGL_SUBPIXEL_ORDER_VERTICAL_RGB: the device has + * vertically arranged components in the order + * red-green-blue from top to bottom. + * @COGL_SUBPIXEL_ORDER_VERTICAL_BGR: the device has + * vertically arranged components in the order + * blue-green-red from top to bottom. + * + * Some output devices (such as LCD panels) display colors + * by making each pixel consist of smaller "subpixels" + * that each have a particular color. By using knowledge + * of the layout of this subpixel components, it is possible + * to create image content with higher resolution than the + * pixel grid. + * + * Since: 1.14 + * Stability: unstable + */ +typedef enum { + COGL_SUBPIXEL_ORDER_UNKNOWN, + COGL_SUBPIXEL_ORDER_NONE, + COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB, + COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR, + COGL_SUBPIXEL_ORDER_VERTICAL_RGB, + COGL_SUBPIXEL_ORDER_VERTICAL_BGR +} CoglSubpixelOrder; + +/** + * cogl_is_output: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglOutput. + * + * Return value: %TRUE if the object references a #CoglOutput + * and %FALSE otherwise. + * Since: 1.14 + * Stability: unstable + */ +CoglBool +cogl_is_output (void *object); + +/** + * cogl_output_get_x: + * @output: a #CoglOutput + * + * Gets the X position of the output with respect to the coordinate + * system of the screen. + * + * Return value: the X position of the output as a pixel offset + * from the left side of the screen coordinate space + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_x (CoglOutput *output); + +/** + * cogl_output_get_y: + * @output: a #CoglOutput + * + * Gets the Y position of the output with respect to the coordinate + * system of the screen. + * + * Return value: the Y position of the output as a pixel offset + * from the top side of the screen coordinate space + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_y (CoglOutput *output); + +/** + * cogl_output_get_width: + * @output: a #CoglOutput + * + * Gets the width of the output in pixels. + * + * Return value: the width of the output in pixels + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_width (CoglOutput *output); + +/** + * cogl_output_get_height: + * @output: a #CoglOutput + * + * Gets the height of the output in pixels. + * + * Return value: the height of the output in pixels + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_height (CoglOutput *output); + +/** + * cogl_output_get_mm_width: + * @output: a #CoglOutput + * + * Gets the physical width of the output. In some cases (such as + * as a projector), the value returned here might correspond to + * nominal resolution rather than the actual physical size of the + * output device. + * + * Return value: the height of the output in millimeters. A value + * of 0 indicates the width is unknown + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_mm_width (CoglOutput *output); + +/** + * cogl_output_get_mm_height: + * @output: a #CoglOutput + * + * Gets the physical height of the output. In some cases (such as + * as a projector), the value returned here might correspond to + * nominal resolution rather than the actual physical size of the + * output device. + * + * Return value: the height of the output in millimeters. A value + * of 0 indicates that the height is unknown + * Since: 1.14 + * Stability: unstable + */ +int +cogl_output_get_mm_height (CoglOutput *output); + +/** + * cogl_output_get_subpixel_order: + * @output: a #CoglOutput + * + * For an output device where each pixel is made up of smaller components + * with different colors, returns the layout of the subpixel + * components. + * + * Return value: the order of subpixel components for the output device + * Since: 1.14 + * Stability: unstable + */ +CoglSubpixelOrder +cogl_output_get_subpixel_order (CoglOutput *output); + +/** + * cogl_output_get_refresh_rate: + * @output: a #CoglOutput + * + * Gets the number of times per second that the output device refreshes + * the display contents. + * + * Return value: the refresh rate of the output device. A value of zero + * indicates that the refresh rate is unknown. + * Since: 1.14 + * Stability: unstable + */ +float +cogl_output_get_refresh_rate (CoglOutput *output); + +COGL_END_DECLS + +#endif /* __COGL_OUTPUT_H */ + + + diff --git a/cogl/cogl/cogl-pango.h b/cogl/cogl/cogl-pango.h new file mode 100644 index 000000000..b24c1b829 --- /dev/null +++ b/cogl/cogl/cogl-pango.h @@ -0,0 +1,40 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ +#ifndef __COGL_PANGO_H_COMPAT__ +#define __COGL_PANGO_H_COMPAT__ + +#ifdef COGL_ENABLE_EXPERIMENTAL_2_0_API +#error "#include is unsupported; please #include " +#else +#warning "#include is deprecated; please #include " +#include +#endif + +#endif /* __COGL_PANGO_H_COMPAT__ */ diff --git a/cogl/cogl/cogl-pipeline-cache.c b/cogl/cogl/cogl-pipeline-cache.c new file mode 100644 index 000000000..0a6bb6a90 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-cache.c @@ -0,0 +1,216 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-context-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-cache.h" +#include "cogl-pipeline-hash-table.h" + +struct _CoglPipelineCache +{ + CoglPipelineHashTable fragment_hash; + CoglPipelineHashTable vertex_hash; + CoglPipelineHashTable combined_hash; +}; + +CoglPipelineCache * +_cogl_pipeline_cache_new (void) +{ + CoglPipelineCache *cache = g_new (CoglPipelineCache, 1); + unsigned long vertex_state; + unsigned long layer_vertex_state; + unsigned int fragment_state; + unsigned int layer_fragment_state; + + _COGL_GET_CONTEXT (ctx, 0); + + vertex_state = + _cogl_pipeline_get_state_for_vertex_codegen (ctx); + layer_vertex_state = + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; + fragment_state = + _cogl_pipeline_get_state_for_fragment_codegen (ctx); + layer_fragment_state = + _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); + + _cogl_pipeline_hash_table_init (&cache->vertex_hash, + vertex_state, + layer_vertex_state, + "vertex shaders"); + _cogl_pipeline_hash_table_init (&cache->fragment_hash, + fragment_state, + layer_fragment_state, + "fragment shaders"); + _cogl_pipeline_hash_table_init (&cache->combined_hash, + vertex_state | fragment_state, + layer_vertex_state | layer_fragment_state, + "programs"); + + return cache; +} + +void +_cogl_pipeline_cache_free (CoglPipelineCache *cache) +{ + _cogl_pipeline_hash_table_destroy (&cache->fragment_hash); + _cogl_pipeline_hash_table_destroy (&cache->vertex_hash); + _cogl_pipeline_hash_table_destroy (&cache->combined_hash); + g_free (cache); +} + +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline) +{ + return _cogl_pipeline_hash_table_get (&cache->fragment_hash, + key_pipeline); +} + +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline) +{ + return _cogl_pipeline_hash_table_get (&cache->vertex_hash, + key_pipeline); +} + +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline) +{ + return _cogl_pipeline_hash_table_get (&cache->combined_hash, + key_pipeline); +} + +#ifdef ENABLE_UNIT_TESTS + +static void +create_pipelines (CoglPipeline **pipelines, + int n_pipelines) +{ + int i; + + for (i = 0; i < n_pipelines; i++) + { + char *source = g_strdup_printf (" cogl_color_out = " + "vec4 (%f, 0.0, 0.0, 1.0);\n", + i / 255.0f); + CoglSnippet *snippet = + cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + source); + + g_free (source); + + pipelines[i] = cogl_pipeline_new (test_ctx); + cogl_pipeline_add_snippet (pipelines[i], snippet); + cogl_object_unref (snippet); + } + + /* Test that drawing with them works. This should create the entries + * in the cache */ + for (i = 0; i < n_pipelines; i++) + { + cogl_framebuffer_draw_rectangle (test_fb, + pipelines[i], + i, 0, + i + 1, 1); + test_utils_check_pixel_rgb (test_fb, i, 0, i, 0, 0); + } + +} + +UNIT_TEST (check_pipeline_pruning, + TEST_REQUIREMENT_GLSL, /* requirements */ + 0 /* no failure cases */) +{ + CoglPipeline *pipelines[18]; + int fb_width, fb_height; + CoglPipelineHashTable *fragment_hash = + &test_ctx->pipeline_cache->fragment_hash; + CoglPipelineHashTable *combined_hash = + &test_ctx->pipeline_cache->combined_hash; + int i; + + fb_width = cogl_framebuffer_get_width (test_fb); + fb_height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + fb_width, + fb_height, + -1, + 100); + + /* Create 18 unique pipelines. This should end up being more than + * the initial expected minimum size so it will trigger the garbage + * collection. However all of the pipelines will be in use so they + * won't be collected */ + create_pipelines (pipelines, 18); + + /* These pipelines should all have unique entries in the cache. We + * should have run the garbage collection once and at that point the + * expected minimum size would have been 17 */ + g_assert_cmpint (g_hash_table_size (fragment_hash->table), ==, 18); + g_assert_cmpint (g_hash_table_size (combined_hash->table), ==, 18); + g_assert_cmpint (fragment_hash->expected_min_size, ==, 17); + g_assert_cmpint (combined_hash->expected_min_size, ==, 17); + + /* Destroy the original pipelines and create some new ones. This + * should run the garbage collector again but this time the + * pipelines won't be in use so it should free some of them */ + for (i = 0; i < 18; i++) + cogl_object_unref (pipelines[i]); + + create_pipelines (pipelines, 18); + + /* The garbage collection should have freed half of the original 18 + * pipelines which means there should now be 18*1.5 = 27 */ + g_assert_cmpint (g_hash_table_size (fragment_hash->table), ==, 27); + g_assert_cmpint (g_hash_table_size (combined_hash->table), ==, 27); + /* The 35th pipeline would have caused the garbage collection. At + * that point there would be 35-18=17 used unique pipelines. */ + g_assert_cmpint (fragment_hash->expected_min_size, ==, 17); + g_assert_cmpint (combined_hash->expected_min_size, ==, 17); + + for (i = 0; i < 18; i++) + cogl_object_unref (pipelines[i]); +} + +#endif /* ENABLE_UNIT_TESTS */ diff --git a/cogl/cogl/cogl-pipeline-cache.h b/cogl/cogl/cogl-pipeline-cache.h new file mode 100644 index 000000000..bd5862ee5 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-cache.h @@ -0,0 +1,93 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PIPELINE_CACHE_H__ +#define __COGL_PIPELINE_CACHE_H__ + +#include "cogl-pipeline.h" + +typedef struct _CoglPipelineCache CoglPipelineCache; + +typedef struct +{ + CoglPipeline *pipeline; + + /* Number of usages of this template. If this drops to zero then it + * will be a candidate for removal from the cache */ + int usage_count; +} CoglPipelineCacheEntry; + +CoglPipelineCache * +_cogl_pipeline_cache_new (void); + +void +_cogl_pipeline_cache_free (CoglPipelineCache *cache); + +/* + * Gets a pipeline from the cache that has the same state as + * @key_pipeline for the state in + * COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no + * matching pipline already then a copy of key_pipeline is stored in + * the cache so that it will be used next time the function is called + * with a similar pipeline. In that case the copy itself will be + * returned + */ +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline); + +/* + * Gets a pipeline from the cache that has the same state as + * @key_pipeline for the state in + * COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN. If there is no + * matching pipline already then a copy of key_pipeline is stored in + * the cache so that it will be used next time the function is called + * with a similar pipeline. In that case the copy itself will be + * returned + */ +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline); + +/* + * Gets a pipeline from the cache that has the same state as + * @key_pipeline for the combination of the state state in + * COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN and + * COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no + * matching pipline already then a copy of key_pipeline is stored in + * the cache so that it will be used next time the function is called + * with a similar pipeline. In that case the copy itself will be + * returned + */ +CoglPipelineCacheEntry * +_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, + CoglPipeline *key_pipeline); + +#endif /* __COGL_PIPELINE_CACHE_H__ */ diff --git a/cogl/cogl/cogl-pipeline-debug.c b/cogl/cogl/cogl-pipeline-debug.c new file mode 100644 index 000000000..beb35b34e --- /dev/null +++ b/cogl/cogl/cogl-pipeline-debug.c @@ -0,0 +1,301 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-layer-private.h" +#include "cogl-node-private.h" + +#include + +typedef struct +{ + int parent_id; + int *node_id_ptr; + GString *graph; + int indent; +} PrintDebugState; + +static CoglBool +dump_layer_cb (CoglNode *node, void *user_data) +{ + CoglPipelineLayer *layer = COGL_PIPELINE_LAYER (node); + PrintDebugState *state = user_data; + int layer_id = *state->node_id_ptr; + PrintDebugState state_out; + GString *changes_label; + CoglBool changes = FALSE; + + if (state->parent_id >= 0) + g_string_append_printf (state->graph, "%*slayer%p -> layer%p;\n", + state->indent, "", + layer->_parent.parent, + layer); + + g_string_append_printf (state->graph, + "%*slayer%p [label=\"layer=0x%p\\n" + "ref count=%d\" " + "color=\"blue\"];\n", + state->indent, "", + layer, + layer, + COGL_OBJECT (layer)->ref_count); + + changes_label = g_string_new (""); + g_string_append_printf (changes_label, + "%*slayer%p -> layer_state%d [weight=100];\n" + "%*slayer_state%d [shape=box label=\"", + state->indent, "", + layer, + layer_id, + state->indent, "", + layer_id); + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_UNIT) + { + changes = TRUE; + g_string_append_printf (changes_label, + "\\lunit=%u\\n", + layer->unit_index); + } + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) + { + changes = TRUE; + g_string_append_printf (changes_label, + "\\ltexture=%p\\n", + layer->texture); + } + + if (changes) + { + g_string_append_printf (changes_label, "\"];\n"); + g_string_append (state->graph, changes_label->str); + g_string_free (changes_label, TRUE); + } + + state_out.parent_id = layer_id; + + state_out.node_id_ptr = state->node_id_ptr; + (*state_out.node_id_ptr)++; + + state_out.graph = state->graph; + state_out.indent = state->indent + 2; + + _cogl_pipeline_node_foreach_child (COGL_NODE (layer), + dump_layer_cb, + &state_out); + + return TRUE; +} + +static CoglBool +dump_layer_ref_cb (CoglPipelineLayer *layer, void *data) +{ + PrintDebugState *state = data; + int pipeline_id = *state->node_id_ptr; + + g_string_append_printf (state->graph, + "%*spipeline_state%d -> layer%p;\n", + state->indent, "", + pipeline_id, + layer); + + return TRUE; +} + +static CoglBool +dump_pipeline_cb (CoglNode *node, void *user_data) +{ + CoglPipeline *pipeline = COGL_PIPELINE (node); + PrintDebugState *state = user_data; + int pipeline_id = *state->node_id_ptr; + PrintDebugState state_out; + GString *changes_label; + CoglBool changes = FALSE; + CoglBool layers = FALSE; + + if (state->parent_id >= 0) + g_string_append_printf (state->graph, "%*spipeline%d -> pipeline%d;\n", + state->indent, "", + state->parent_id, + pipeline_id); + + g_string_append_printf (state->graph, + "%*spipeline%d [label=\"pipeline=0x%p\\n" + "ref count=%d\\n" + "breadcrumb=\\\"%s\\\"\" color=\"red\"];\n", + state->indent, "", + pipeline_id, + pipeline, + COGL_OBJECT (pipeline)->ref_count, + pipeline->has_static_breadcrumb ? +#ifdef COGL_DEBUG_ENABLED + pipeline->static_breadcrumb : "NULL" +#else + "NULL" +#endif + ); + + changes_label = g_string_new (""); + g_string_append_printf (changes_label, + "%*spipeline%d -> pipeline_state%d [weight=100];\n" + "%*spipeline_state%d [shape=box label=\"", + state->indent, "", + pipeline_id, + pipeline_id, + state->indent, "", + pipeline_id); + + + if (pipeline->differences & COGL_PIPELINE_STATE_COLOR) + { + changes = TRUE; + g_string_append_printf (changes_label, + "\\lcolor=0x%02X%02X%02X%02X\\n", + cogl_color_get_red_byte (&pipeline->color), + cogl_color_get_green_byte (&pipeline->color), + cogl_color_get_blue_byte (&pipeline->color), + cogl_color_get_alpha_byte (&pipeline->color)); + } + + if (pipeline->differences & COGL_PIPELINE_STATE_BLEND) + { + const char *blend_enable_name; + + changes = TRUE; + + switch (pipeline->blend_enable) + { + case COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC: + blend_enable_name = "AUTO"; + break; + case COGL_PIPELINE_BLEND_ENABLE_ENABLED: + blend_enable_name = "ENABLED"; + break; + case COGL_PIPELINE_BLEND_ENABLE_DISABLED: + blend_enable_name = "DISABLED"; + break; + default: + blend_enable_name = "UNKNOWN"; + } + g_string_append_printf (changes_label, + "\\lblend=%s\\n", + blend_enable_name); + } + + if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) + { + changes = TRUE; + layers = TRUE; + g_string_append_printf (changes_label, "\\ln_layers=%d\\n", + pipeline->n_layers); + } + + if (changes) + { + g_string_append_printf (changes_label, "\"];\n"); + g_string_append (state->graph, changes_label->str); + g_string_free (changes_label, TRUE); + } + + if (layers) + { + g_list_foreach (pipeline->layer_differences, + (GFunc)dump_layer_ref_cb, + state); + } + + state_out.parent_id = pipeline_id; + + state_out.node_id_ptr = state->node_id_ptr; + (*state_out.node_id_ptr)++; + + state_out.graph = state->graph; + state_out.indent = state->indent + 2; + + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + dump_pipeline_cb, + &state_out); + + return TRUE; +} + +/* This function is just here to be called from GDB so we don't really + want to put a declaration in a header and we just add it here to + avoid a warning */ +void +_cogl_debug_dump_pipelines_dot_file (const char *filename); + +void +_cogl_debug_dump_pipelines_dot_file (const char *filename) +{ + GString *graph; + PrintDebugState layer_state; + PrintDebugState pipeline_state; + int layer_id = 0; + int pipeline_id = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!ctx->default_pipeline) + return; + + graph = g_string_new (""); + g_string_append_printf (graph, "digraph {\n"); + + layer_state.graph = graph; + layer_state.parent_id = -1; + layer_state.node_id_ptr = &layer_id; + layer_state.indent = 0; + dump_layer_cb ((CoglNode *)ctx->default_layer_0, &layer_state); + + pipeline_state.graph = graph; + pipeline_state.parent_id = -1; + pipeline_state.node_id_ptr = &pipeline_id; + pipeline_state.indent = 0; + dump_pipeline_cb ((CoglNode *)ctx->default_pipeline, &pipeline_state); + + g_string_append_printf (graph, "}\n"); + + if (filename) + g_file_set_contents (filename, graph->str, -1, NULL); + else + g_print ("%s", graph->str); + + g_string_free (graph, TRUE); +} diff --git a/cogl/cogl/cogl-pipeline-hash-table.c b/cogl/cogl/cogl-pipeline-hash-table.c new file mode 100644 index 000000000..9c8e3df86 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-hash-table.c @@ -0,0 +1,233 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-hash-table.h" +#include "cogl-pipeline-cache.h" + +typedef struct +{ + CoglPipelineCacheEntry parent; + + /* Calculating the hash is a little bit expensive for pipelines so + * we don't want to do it repeatedly for entries that are already in + * the hash table. Instead we cache the value here and calculate it + * outside of the GHashTable. */ + unsigned int hash_value; + + /* GHashTable annoyingly doesn't let us pass a user data pointer to + * the hash and equal functions so to work around it we have to + * store the pointer in every hash table entry. We will use this + * entry as both the key and the value */ + CoglPipelineHashTable *hash; + + /* The number of unique pipelines that had been created when this + * pipeline was last accessed */ + int age; +} CoglPipelineHashTableEntry; + +static void +value_destroy_cb (void *value) +{ + CoglPipelineHashTableEntry *entry = value; + + cogl_object_unref (entry->parent.pipeline); + + g_slice_free (CoglPipelineHashTableEntry, entry); +} + +static unsigned int +entry_hash (const void *data) +{ + const CoglPipelineHashTableEntry *entry = data; + + return entry->hash_value; +} + +static CoglBool +entry_equal (const void *a, + const void *b) +{ + const CoglPipelineHashTableEntry *entry_a = a; + const CoglPipelineHashTableEntry *entry_b = b; + const CoglPipelineHashTable *hash = entry_a->hash; + + return _cogl_pipeline_equal (entry_a->parent.pipeline, + entry_b->parent.pipeline, + hash->main_state, + hash->layer_state, + 0); +} + +void +_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash, + unsigned int main_state, + unsigned int layer_state, + const char *debug_string) +{ + hash->n_unique_pipelines = 0; + hash->debug_string = debug_string; + hash->main_state = main_state; + hash->layer_state = layer_state; + /* We'll only start pruning once we get to 16 unique pipelines */ + hash->expected_min_size = 8; + hash->table = g_hash_table_new_full (entry_hash, + entry_equal, + NULL, /* key destroy */ + value_destroy_cb); +} + +void +_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash) +{ + g_hash_table_destroy (hash->table); +} + +static void +collect_prunable_entries_cb (void *key, + void *value, + void *user_data) +{ + GQueue *entries = user_data; + CoglPipelineCacheEntry *entry = value; + + if (entry->usage_count == 0) + g_queue_push_tail (entries, entry); +} + +static int +compare_pipeline_age_cb (const void *a, + const void *b) +{ + const CoglPipelineHashTableEntry *ae = a; + const CoglPipelineHashTableEntry *be = b; + + return be->age - ae->age; +} + +static void +prune_old_pipelines (CoglPipelineHashTable *hash) +{ + GQueue entries; + GList *l; + int i; + + /* Collect all of the prunable entries into a GQueue */ + g_queue_init (&entries); + g_hash_table_foreach (hash->table, + collect_prunable_entries_cb, + &entries); + + /* Sort the entries by increasing order of age */ + entries.head = g_list_sort (entries.head, compare_pipeline_age_cb); + + /* The +1 is to include the pipeline that we're about to add */ + hash->expected_min_size = (g_hash_table_size (hash->table) - + entries.length + + 1); + + /* Remove oldest half of the prunable pipelines. We still want to + * keep some of the prunable entries that are recently used because + * it's not unlikely that the application will recreate the same + * pipeline */ + for (l = entries.head, i = 0; i < entries.length / 2; l = l->next, i++) + { + CoglPipelineCacheEntry *entry = l->data; + + g_hash_table_remove (hash->table, entry); + } + + g_list_free (entries.head); +} + +CoglPipelineCacheEntry * +_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash, + CoglPipeline *key_pipeline) +{ + CoglPipelineHashTableEntry dummy_entry; + CoglPipelineHashTableEntry *entry; + unsigned int copy_state; + + dummy_entry.parent.pipeline = key_pipeline; + dummy_entry.hash = hash; + dummy_entry.hash_value = _cogl_pipeline_hash (key_pipeline, + hash->main_state, + hash->layer_state, + 0); + entry = g_hash_table_lookup (hash->table, &dummy_entry); + + if (entry) + { + entry->age = hash->n_unique_pipelines; + return &entry->parent; + } + + if (hash->n_unique_pipelines == 50) + g_warning ("Over 50 separate %s have been generated which is very " + "unusual, so something is probably wrong!\n", + hash->debug_string); + + /* If we are going to have more than twice the expected minimum + * number of pipelines in the hash then we'll try pruning and update + * the minimum */ + if (g_hash_table_size (hash->table) >= hash->expected_min_size * 2) + prune_old_pipelines (hash); + + entry = g_slice_new (CoglPipelineHashTableEntry); + entry->parent.usage_count = 0; + entry->hash = hash; + entry->hash_value = dummy_entry.hash_value; + entry->age = hash->n_unique_pipelines; + + copy_state = hash->main_state; + if (hash->layer_state) + copy_state |= COGL_PIPELINE_STATE_LAYERS; + + /* Create a new pipeline that is a child of the root pipeline + * instead of a normal copy so that the template pipeline won't hold + * a reference to the original pipeline */ + entry->parent.pipeline = _cogl_pipeline_deep_copy (key_pipeline, + copy_state, + hash->layer_state); + + g_hash_table_insert (hash->table, entry, entry); + + hash->n_unique_pipelines++; + + return &entry->parent; +} diff --git a/cogl/cogl/cogl-pipeline-hash-table.h b/cogl/cogl/cogl-pipeline-hash-table.h new file mode 100644 index 000000000..035e8dfb3 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-hash-table.h @@ -0,0 +1,81 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PIPELINE_HASH_H__ +#define __COGL_PIPELINE_HASH_H__ + +#include "cogl-pipeline-cache.h" + +typedef struct +{ + /* Total number of pipelines that were ever added to the hash. This + * is not decremented when a pipeline is removed. It is only used to + * generate a warning if an unusually high number of pipelines are + * generated */ + int n_unique_pipelines; + + /* This is the expected minimum size we could prune the hash table + * to if we were to remove all pipelines that are not in use. This + * is only updated after we prune the table */ + int expected_min_size; + + /* String that will be used to describe the usage of this hash table + * in the debug warning when too many pipelines are generated. This + * must be a static string because it won't be copied or freed */ + const char *debug_string; + + unsigned int main_state; + unsigned int layer_state; + + GHashTable *table; +} CoglPipelineHashTable; + +void +_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash, + unsigned int main_state, + unsigned int layer_state, + const char *debug_string); + +void +_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash); + +/* + * Gets a pipeline from the hash that has the same state as + * @key_pipeline according to the limited state bits passed to + * _cogl_pipeline_hash_table_init(). If there is no matching pipelines + * already then a copy of key_pipeline is stored in the hash so that + * it will be used next time the function is called with a similar + * pipeline. In that case the copy itself will be returned + */ +CoglPipelineCacheEntry * +_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash, + CoglPipeline *key_pipeline); + +#endif /* __COGL_PIPELINE_HASH_H__ */ diff --git a/cogl/cogl/cogl-pipeline-layer-private.h b/cogl/cogl/cogl-pipeline-layer-private.h new file mode 100644 index 000000000..38cd3b5eb --- /dev/null +++ b/cogl/cogl/cogl-pipeline-layer-private.h @@ -0,0 +1,390 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_LAYER_PRIVATE_H +#define __COGL_PIPELINE_LAYER_PRIVATE_H + +#include "cogl-private.h" +#include "cogl-pipeline.h" +#include "cogl-node-private.h" +#include "cogl-texture.h" +#include "cogl-matrix.h" +#include "cogl-pipeline-layer-state.h" +#include "cogl-pipeline-snippet-private.h" +#include "cogl-sampler-cache-private.h" + +#include + +typedef struct _CoglPipelineLayer CoglPipelineLayer; +#define COGL_PIPELINE_LAYER(OBJECT) ((CoglPipelineLayer *)OBJECT) + +/* XXX: should I rename these as + * COGL_PIPELINE_LAYER_STATE_INDEX_XYZ... ? + */ +typedef enum +{ + /* sparse state */ + COGL_PIPELINE_LAYER_STATE_UNIT_INDEX, + COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX, + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX, + COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, + COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, + COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, + COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, + COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX, + COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX, + + /* note: layers don't currently have any non-sparse state */ + + COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT, + COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT +} CoglPipelineLayerStateIndex; + +/* XXX: If you add or remove state groups here you may need to update + * some of the state masks following this enum too! + * + * FIXME: perhaps it would be better to rename this enum to + * CoglPipelineLayerStateGroup to better convey the fact that a single + * enum here can map to multiple properties. + */ +typedef enum +{ + COGL_PIPELINE_LAYER_STATE_UNIT = + 1L< TEXTURE0 to store + very large layer numbers */ + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE, + COGL_PIPELINE_COMBINE_SOURCE_CONSTANT, + COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR, + COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS, + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 +} CoglPipelineCombineSource; + +typedef enum +{ + /* These are the same values as GL */ + COGL_PIPELINE_COMBINE_OP_SRC_COLOR = 0x0300, + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR = 0x0301, + COGL_PIPELINE_COMBINE_OP_SRC_ALPHA = 0x0302, + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA = 0x0303 +} CoglPipelineCombineOp; + +typedef struct +{ + /* The texture combine state determines how the color of individual + * texture fragments are calculated. */ + CoglPipelineCombineFunc texture_combine_rgb_func; + CoglPipelineCombineSource texture_combine_rgb_src[3]; + CoglPipelineCombineOp texture_combine_rgb_op[3]; + + CoglPipelineCombineFunc texture_combine_alpha_func; + CoglPipelineCombineSource texture_combine_alpha_src[3]; + CoglPipelineCombineOp texture_combine_alpha_op[3]; + + float texture_combine_constant[4]; + + /* The texture matrix dscribes how to transform texture coordinates */ + CoglMatrix matrix; + + CoglPipelineSnippetList vertex_snippets; + CoglPipelineSnippetList fragment_snippets; + + CoglBool point_sprite_coords; +} CoglPipelineLayerBigState; + +struct _CoglPipelineLayer +{ + /* XXX: Please think twice about adding members that *have* be + * initialized during a _cogl_pipeline_layer_copy. We are aiming + * to have copies be as cheap as possible and copies may be + * done by the primitives APIs which means they may happen + * in performance critical code paths. + * + * XXX: If you are extending the state we track please consider if + * the state is expected to vary frequently across many pipelines or + * if the state can be shared among many derived pipelines instead. + * This will determine if the state should be added directly to this + * structure which will increase the memory overhead for *all* + * layers or if instead it can go under ->big_state. + */ + + /* Layers represent their state in a tree structure where some of + * the state relating to a given pipeline or layer may actually be + * owned by one if is ancestors in the tree. We have a common data + * type to track the tree heirachy so we can share code... */ + CoglNode _parent; + + /* Some layers have a pipeline owner, which is to say that the layer + * is referenced in that pipelines->layer_differences list. A layer + * doesn't always have an owner and may simply be an ancestor for + * other layers that keeps track of some shared state. */ + CoglPipeline *owner; + + /* The lowest index is blended first then others on top */ + int index; + + /* A mask of which state groups are different in this layer + * in comparison to its parent. */ + unsigned int differences; + + /* Common differences + * + * As a basic way to reduce memory usage we divide the layer + * state into two groups; the minimal state modified in 90% of + * all layers and the rest, so that the second group can + * be allocated dynamically when required. + */ + + /* Each layer is directly associated with a single texture unit */ + int unit_index; + + /* The type of the texture. This is always set even if the texture + is NULL and it will be used to determine what type of texture + lookups to use in any shaders generated by the pipeline + backends. */ + CoglTextureType texture_type; + /* The texture for this layer, or NULL for an empty + * layer */ + CoglTexture *texture; + + const CoglSamplerCacheEntry *sampler_cache_entry; + + /* Infrequent differences aren't currently tracked in + * a separate, dynamically allocated structure as they are + * for pipelines... */ + CoglPipelineLayerBigState *big_state; + + /* bitfields */ + + /* Determines if layer->big_state is valid */ + unsigned int has_big_state:1; + +}; + +typedef CoglBool +(*CoglPipelineLayerStateComparitor) (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + + + +void +_cogl_pipeline_init_default_layers (void); + +static inline CoglPipelineLayer * +_cogl_pipeline_layer_get_parent (CoglPipelineLayer *layer) +{ + CoglNode *parent_node = COGL_NODE (layer)->parent; + return COGL_PIPELINE_LAYER (parent_node); +} + +CoglPipelineLayer * +_cogl_pipeline_layer_copy (CoglPipelineLayer *layer); + +void +_cogl_pipeline_layer_resolve_authorities (CoglPipelineLayer *layer, + unsigned long differences, + CoglPipelineLayer **authorities); + +CoglBool +_cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, + CoglPipelineLayer *layer1, + unsigned long differences_mask, + CoglPipelineEvalFlags flags); + +CoglPipelineLayer * +_cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change); + +void +_cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer); + +CoglBool +_cogl_pipeline_layer_has_alpha (CoglPipelineLayer *layer); + +CoglBool +_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, + int layer_index); + +/* + * Calls the pre_paint method on the layer texture if there is + * one. This will determine whether mipmaps are needed based on the + * filter settings. + */ +void +_cogl_pipeline_layer_pre_paint (CoglPipelineLayer *layerr); + +void +_cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, + CoglSamplerCacheWrapMode *wrap_mode_s, + CoglSamplerCacheWrapMode *wrap_mode_t, + CoglSamplerCacheWrapMode *wrap_mode_r); + +void +_cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter); + +const CoglSamplerCacheEntry * +_cogl_pipeline_layer_get_sampler_state (CoglPipelineLayer *layer); + +void +_cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter); + +typedef enum { + COGL_PIPELINE_LAYER_TYPE_TEXTURE +} CoglPipelineLayerType; + +CoglPipelineLayerType +_cogl_pipeline_layer_get_type (CoglPipelineLayer *layer); + +CoglTexture * +_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer); + +CoglTexture * +_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer); + +CoglTextureType +_cogl_pipeline_layer_get_texture_type (CoglPipelineLayer *layer); + +CoglPipelineFilter +_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer); + +CoglPipelineFilter +_cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer); + +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer); + +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer); + +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer); + +void +_cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest, + CoglPipelineLayer *src, + unsigned long differences); + +unsigned long +_cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0, + CoglPipelineLayer *layer1); + +CoglPipelineLayer * +_cogl_pipeline_layer_get_authority (CoglPipelineLayer *layer, + unsigned long difference); + +CoglTexture * +_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer); + +int +_cogl_pipeline_layer_get_unit_index (CoglPipelineLayer *layer); + +CoglBool +_cogl_pipeline_layer_needs_combine_separate + (CoglPipelineLayer *combine_authority); + +#endif /* __COGL_PIPELINE_LAYER_PRIVATE_H */ diff --git a/cogl/cogl/cogl-pipeline-layer-state-private.h b/cogl/cogl/cogl-pipeline-layer-state-private.h new file mode 100644 index 000000000..64e70cf3d --- /dev/null +++ b/cogl/cogl/cogl-pipeline-layer-state-private.h @@ -0,0 +1,141 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_LAYER_STATE_PRIVATE_H +#define __COGL_PIPELINE_LAYER_STATE_PRIVATE_H + +#include "cogl-pipeline-layer-state.h" +#include "cogl-pipeline-private.h" + +CoglPipelineLayer * +_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner, + CoglPipelineLayer *layer, + int unit_index); + +CoglPipelineFilter +_cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, + int layer_index); + +CoglPipelineFilter +_cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, + int layer_index); + +CoglBool +_cogl_pipeline_layer_texture_type_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags); + +CoglBool +_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags); + +CoglBool +_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_sampler_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +CoglBool +_cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1); + +void +_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_texture_type_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_sampler_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +void +_cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +#endif /* __COGL_PIPELINE_LAYER_STATE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-pipeline-layer-state.c b/cogl/cogl/cogl-pipeline-layer-state.c new file mode 100644 index 000000000..fb201925b --- /dev/null +++ b/cogl/cogl/cogl-pipeline-layer-state.c @@ -0,0 +1,1814 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-blend-string.h" +#include "cogl-util.h" +#include "cogl-matrix.h" +#include "cogl-snippet-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline-layer-state-private.h" +#include "cogl-error-private.h" + +#include "string.h" +#if 0 +#include "cogl-context-private.h" +#include "cogl-color-private.h" + +#endif + +/* + * XXX: consider special casing layer->unit_index so it's not a sparse + * property so instead we can assume it's valid for all layer + * instances. + * - We would need to initialize ->unit_index in + * _cogl_pipeline_layer_copy (). + * + * XXX: If you use this API you should consider that the given layer + * might not be writeable and so a new derived layer will be allocated + * and modified instead. The layer modified will be returned so you + * can identify when this happens. + */ +CoglPipelineLayer * +_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner, + CoglPipelineLayer *layer, + int unit_index) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_UNIT; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, change); + CoglPipelineLayer *new; + + if (authority->unit_index == unit_index) + return layer; + + new = + _cogl_pipeline_layer_pre_change_notify (required_owner, + layer, + change); + if (new != layer) + layer = new; + else + { + /* If the layer we found is currently the authority on the state + * we are changing see if we can revert to one of our ancestors + * being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->unit_index == unit_index) + { + layer->differences &= ~change; + return layer; + } + } + } + + layer->unit_index = unit_index; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + + return layer; +} + +CoglTexture * +_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); + + return authority->texture; +} + +CoglTexture * +cogl_pipeline_get_layer_texture (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayer *layer = + _cogl_pipeline_get_layer (pipeline, layer_index); + return _cogl_pipeline_layer_get_texture (layer); +} + +CoglTextureType +_cogl_pipeline_layer_get_texture_type (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE); + + return authority->texture_type; +} + +static void +_cogl_pipeline_set_layer_texture_type (CoglPipeline *pipeline, + int layer_index, + CoglTextureType texture_type) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (texture_type == authority->texture_type) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->texture_type == texture_type) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + layer->texture_type = texture_type; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + pipeline->dirty_real_blend_enable = TRUE; +} + +static void +_cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline, + int layer_index, + CoglTexture *texture) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (authority->texture == texture) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->texture == texture) + { + layer->differences &= ~change; + + if (layer->texture != NULL) + cogl_object_unref (layer->texture); + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + if (texture != NULL) + cogl_object_ref (texture); + if (layer == authority && + layer->texture != NULL) + cogl_object_unref (layer->texture); + layer->texture = texture; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +cogl_pipeline_set_layer_texture (CoglPipeline *pipeline, + int layer_index, + CoglTexture *texture) +{ + /* For the convenience of fragend code we separate texture state + * into the "type" and the "data", and setting a layer texture + * updates both of these properties. + * + * One example for why this is helpful is that the fragends may + * cache programs they generate and want to re-use those programs + * with all pipelines having equivalent fragment processing state. + * For the sake of determining if pipelines have equivalent fragment + * processing state we don't need to compare that the same + * underlying texture objects are referenced by the pipelines but we + * do need to see if they use the same texture types. Making this + * distinction is much simpler if they are in different state + * groups. + * + * Note: if a NULL texture is set then we leave the type unchanged + * so we can avoid needlessly invalidating any associated fragment + * program. + */ + if (texture) + { + CoglTextureType texture_type = + _cogl_texture_get_type (texture); + _cogl_pipeline_set_layer_texture_type (pipeline, + layer_index, + texture_type); + } + _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, texture); +} + +void +cogl_pipeline_set_layer_null_texture (CoglPipeline *pipeline, + int layer_index, + CoglTextureType texture_type) +{ + CoglContext *ctx = _cogl_context_get_default (); + + /* Disallow setting texture types that aren't supported */ + switch (texture_type) + { + case COGL_TEXTURE_TYPE_2D: + break; + + case COGL_TEXTURE_TYPE_3D: + if (ctx->default_gl_texture_3d_tex == NULL) + { + g_warning ("The default 3D texture was set on a pipeline but " + "3D textures are not supported"); + texture_type = COGL_TEXTURE_TYPE_2D; + return; + } + break; + + case COGL_TEXTURE_TYPE_RECTANGLE: + if (ctx->default_gl_texture_rect_tex == NULL) + { + g_warning ("The default rectangle texture was set on a pipeline but " + "rectangle textures are not supported"); + texture_type = COGL_TEXTURE_TYPE_2D; + } + break; + } + + _cogl_pipeline_set_layer_texture_type (pipeline, layer_index, texture_type); + _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, NULL); +} + +static void +_cogl_pipeline_set_layer_sampler_state (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglPipelineLayer *authority, + const CoglSamplerCacheEntry *state) +{ + CoglPipelineLayer *new; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + + if (authority->sampler_cache_entry == state) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->sampler_cache_entry == state) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return; + } + } + } + + layer->sampler_cache_entry = state; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +static CoglSamplerCacheWrapMode +public_to_internal_wrap_mode (CoglPipelineWrapMode mode) +{ + return (CoglSamplerCacheWrapMode)mode; +} + +static CoglPipelineWrapMode +internal_to_public_wrap_mode (CoglSamplerCacheWrapMode internal_mode) +{ + _COGL_RETURN_VAL_IF_FAIL (internal_mode != + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER, + COGL_PIPELINE_WRAP_MODE_AUTOMATIC); + return (CoglPipelineWrapMode)internal_mode; +} + +void +cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglSamplerCacheWrapMode internal_mode = + public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + internal_mode, + authority->sampler_cache_entry-> + wrap_mode_t, + authority->sampler_cache_entry-> + wrap_mode_p); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); +} + +void +cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglSamplerCacheWrapMode internal_mode = + public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + authority->sampler_cache_entry-> + wrap_mode_s, + internal_mode, + authority->sampler_cache_entry-> + wrap_mode_p); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); +} + +/* The rationale for naming the third texture coordinate 'p' instead + of OpenGL's usual 'r' is that 'r' conflicts with the usual naming + of the 'red' component when treating a vector as a color. Under + GLSL this is awkward because the texture swizzling for a vector + uses a single letter for each component and the names for colors, + textures and positions are synonymous. GLSL works around this by + naming the components of the texture s, t, p and q. Cogl already + effectively already exposes this naming because it exposes GLSL so + it makes sense to use that naming consistently. Another alternative + could be u, v and w. This is what Blender and Direct3D use. However + the w component conflicts with the w component of a position + vertex. */ +void +cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglSamplerCacheWrapMode internal_mode = + public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + authority->sampler_cache_entry-> + wrap_mode_s, + authority->sampler_cache_entry-> + wrap_mode_t, + internal_mode); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); +} + +void +cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglSamplerCacheWrapMode internal_mode = + public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + internal_mode, + internal_mode, + internal_mode); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); + /* XXX: I wonder if we should really be duplicating the mode into + * the 'p' wrap mode too? */ +} + +/* FIXME: deprecate this API */ +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), FALSE); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_s); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + return _cogl_pipeline_layer_get_wrap_mode_s (layer); +} + +/* FIXME: deprecate this API */ +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), FALSE); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_t); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + return _cogl_pipeline_layer_get_wrap_mode_t (layer); +} + +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, change); + const CoglSamplerCacheEntry *sampler_state; + + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_p); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + return _cogl_pipeline_layer_get_wrap_mode_p (layer); +} + +void +_cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, + CoglSamplerCacheWrapMode *wrap_mode_s, + CoglSamplerCacheWrapMode *wrap_mode_t, + CoglSamplerCacheWrapMode *wrap_mode_p) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + *wrap_mode_s = authority->sampler_cache_entry->wrap_mode_s; + *wrap_mode_t = authority->sampler_cache_entry->wrap_mode_t; + *wrap_mode_p = authority->sampler_cache_entry->wrap_mode_p; +} + +CoglBool +cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index, + CoglBool enable, + CoglError **error) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + CoglPipelineLayer *layer; + CoglPipelineLayer *new; + CoglPipelineLayer *authority; + + _COGL_GET_CONTEXT (ctx, FALSE); + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Don't allow point sprite coordinates to be enabled if the driver + doesn't support it */ + if (enable && !cogl_has_feature (ctx, COGL_FEATURE_ID_POINT_SPRITE)) + { + if (error) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Point sprite texture coordinates are enabled for " + "a layer but the GL driver does not support it."); + } + else + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + g_warning ("Point sprite texture coordinates are enabled " + "for a layer but the GL driver does not support it."); + warning_seen = TRUE; + } + + return FALSE; + } + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (authority->big_state->point_sprite_coords == enable) + return TRUE; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->big_state->point_sprite_coords == enable) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return TRUE; + } + } + } + + layer->big_state->point_sprite_coords = enable; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + + return TRUE; +} + +CoglBool +cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + authority = _cogl_pipeline_layer_get_authority (layer, change); + + return authority->big_state->point_sprite_coords; +} + +static void +_cogl_pipeline_layer_add_vertex_snippet (CoglPipeline *pipeline, + int layer_index, + CoglSnippet *snippet) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; + CoglPipelineLayer *layer, *authority; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + + _cogl_pipeline_snippet_list_add (&layer->big_state->vertex_snippets, + snippet); + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +static void +_cogl_pipeline_layer_add_fragment_snippet (CoglPipeline *pipeline, + int layer_index, + CoglSnippet *snippet) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; + CoglPipelineLayer *layer, *authority; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + + _cogl_pipeline_snippet_list_add (&layer->big_state->fragment_snippets, + snippet); + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +void +cogl_pipeline_add_layer_snippet (CoglPipeline *pipeline, + int layer_index, + CoglSnippet *snippet) +{ + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (cogl_is_snippet (snippet)); + _COGL_RETURN_IF_FAIL (snippet->hook >= COGL_SNIPPET_FIRST_LAYER_HOOK); + + if (snippet->hook < COGL_SNIPPET_FIRST_LAYER_FRAGMENT_HOOK) + _cogl_pipeline_layer_add_vertex_snippet (pipeline, + layer_index, + snippet); + else + _cogl_pipeline_layer_add_fragment_snippet (pipeline, + layer_index, + snippet); +} + +CoglBool +_cogl_pipeline_layer_texture_type_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags) +{ + return authority0->texture_type == authority1->texture_type; +} + +CoglBool +_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags) +{ + if (authority0->texture == NULL) + { + if (authority1->texture == NULL) + return (_cogl_pipeline_layer_get_texture_type (authority0) == + _cogl_pipeline_layer_get_texture_type (authority1)); + else + return FALSE; + } + else if (authority1->texture == NULL) + return FALSE; + else + { + GLuint gl_handle0, gl_handle1; + + cogl_texture_get_gl_texture (authority0->texture, &gl_handle0, NULL); + cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, NULL); + + return gl_handle0 == gl_handle1; + } +} + +CoglBool +_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + int n_args; + int i; + + if (big_state0->texture_combine_rgb_func != + big_state1->texture_combine_rgb_func) + return FALSE; + + if (big_state0->texture_combine_alpha_func != + big_state1->texture_combine_alpha_func) + return FALSE; + + n_args = + _cogl_get_n_args_for_combine_func (big_state0->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + if ((big_state0->texture_combine_rgb_src[i] != + big_state1->texture_combine_rgb_src[i]) || + (big_state0->texture_combine_rgb_op[i] != + big_state1->texture_combine_rgb_op[i])) + return FALSE; + } + + n_args = + _cogl_get_n_args_for_combine_func (big_state0->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + if ((big_state0->texture_combine_alpha_src[i] != + big_state1->texture_combine_alpha_src[i]) || + (big_state0->texture_combine_alpha_op[i] != + big_state1->texture_combine_alpha_op[i])) + return FALSE; + } + + return TRUE; +} + +CoglBool +_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + return memcmp (authority0->big_state->texture_combine_constant, + authority1->big_state->texture_combine_constant, + sizeof (float) * 4) == 0 ? TRUE : FALSE; +} + +CoglBool +_cogl_pipeline_layer_sampler_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + /* We compare the actual sampler objects rather than just the entry + pointers because two states with different values can lead to the + same state in GL terms when AUTOMATIC is used as a wrap mode */ + return (authority0->sampler_cache_entry->sampler_object == + authority1->sampler_cache_entry->sampler_object); +} + +CoglBool +_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + + if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix)) + return FALSE; + + return TRUE; +} + +CoglBool +_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + + return big_state0->point_sprite_coords == big_state1->point_sprite_coords; +} + +CoglBool +_cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> + vertex_snippets, + &authority1->big_state-> + vertex_snippets); +} + +CoglBool +_cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> + fragment_snippets, + &authority1->big_state-> + fragment_snippets); +} + +static void +setup_texture_combine_state (CoglBlendStringStatement *statement, + CoglPipelineCombineFunc *texture_combine_func, + CoglPipelineCombineSource *texture_combine_src, + CoglPipelineCombineOp *texture_combine_op) +{ + int i; + + switch (statement->function->type) + { + case COGL_BLEND_STRING_FUNCTION_REPLACE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_REPLACE; + break; + case COGL_BLEND_STRING_FUNCTION_MODULATE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE; + break; + case COGL_BLEND_STRING_FUNCTION_ADD: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD; + break; + case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED; + break; + case COGL_BLEND_STRING_FUNCTION_INTERPOLATE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE; + break; + case COGL_BLEND_STRING_FUNCTION_SUBTRACT: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_SUBTRACT; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGB: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA; + break; + } + + for (i = 0; i < statement->function->argc; i++) + { + CoglBlendStringArgument *arg = &statement->args[i]; + + switch (arg->source.info->type) + { + case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_CONSTANT; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N: + texture_combine_src[i] = + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 + arg->source.texture; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; + break; + default: + g_warning ("Unexpected texture combine source"); + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + } + + if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR; + else + texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR; + } + else + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA; + else + texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; + } + } +} + +CoglBool +cogl_pipeline_set_layer_combine (CoglPipeline *pipeline, + int layer_index, + const char *combine_description, + CoglError **error) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE; + CoglPipelineLayer *authority; + CoglPipelineLayer *layer; + CoglBlendStringStatement statements[2]; + CoglBlendStringStatement split[2]; + CoglBlendStringStatement *rgb; + CoglBlendStringStatement *a; + int count; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + count = + _cogl_blend_string_compile (combine_description, + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE, + statements, + error); + if (!count) + return FALSE; + + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + _cogl_blend_string_split_rgba_statement (statements, + &split[0], &split[1]); + rgb = &split[0]; + a = &split[1]; + } + else + { + rgb = &statements[0]; + a = &statements[1]; + } + + /* FIXME: compare the new state with the current state! */ + + /* possibly flush primitives referencing the current state... */ + layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + + setup_texture_combine_state (rgb, + &layer->big_state->texture_combine_rgb_func, + layer->big_state->texture_combine_rgb_src, + layer->big_state->texture_combine_rgb_op); + + setup_texture_combine_state (a, + &layer->big_state->texture_combine_alpha_func, + layer->big_state->texture_combine_alpha_src, + layer->big_state->texture_combine_alpha_op); + + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + + if (_cogl_pipeline_layer_combine_state_equal (authority, + old_authority)) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + pipeline->dirty_real_blend_enable = TRUE; + return TRUE; +} + +void +cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + const CoglColor *constant_color) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + float color_as_floats[4]; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + color_as_floats[0] = cogl_color_get_red_float (constant_color); + color_as_floats[1] = cogl_color_get_green_float (constant_color); + color_as_floats[2] = cogl_color_get_blue_float (constant_color); + color_as_floats[3] = cogl_color_get_alpha_float (constant_color); + + if (memcmp (authority->big_state->texture_combine_constant, + color_as_floats, sizeof (float) * 4) == 0) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + CoglPipelineLayerBigState *old_big_state = old_authority->big_state; + + if (memcmp (old_big_state->texture_combine_constant, + color_as_floats, sizeof (float) * 4) == 0) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + memcpy (layer->big_state->texture_combine_constant, + color_as_floats, + sizeof (color_as_floats)); + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +_cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + float *constant) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + authority = _cogl_pipeline_layer_get_authority (layer, change); + memcpy (constant, authority->big_state->texture_combine_constant, + sizeof (float) * 4); +} + +/* We should probably make a public API version of this that has a + matrix out-param. For an internal API it's good to be able to avoid + copying the matrix */ +const CoglMatrix * +_cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), NULL); + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = _cogl_pipeline_layer_get_authority (layer, change); + return &authority->big_state->matrix; +} + +void +cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline, + int layer_index, + const CoglMatrix *matrix) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + if (cogl_matrix_equal (matrix, &authority->big_state->matrix)) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + + if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix)) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return; + } + } + } + + layer->big_state->matrix = *matrix; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +CoglTexture * +_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer) +{ + _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), NULL); + + return _cogl_pipeline_layer_get_texture_real (layer); +} + +CoglBool +_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_USER_MATRIX); + + /* If the authority is the default pipeline then no, otherwise yes */ + return _cogl_pipeline_layer_get_parent (authority) ? TRUE : FALSE; +} + +void +_cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + *min_filter = authority->sampler_cache_entry->min_filter; + *mag_filter = authority->sampler_cache_entry->mag_filter; +} + +void +_cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter) +{ + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + *min_filter = authority->sampler_cache_entry->min_filter; + *mag_filter = authority->sampler_cache_entry->mag_filter; +} + +CoglPipelineFilter +cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineFilter min_filter; + CoglPipelineFilter mag_filter; + + _cogl_pipeline_get_layer_filters (pipeline, layer_index, + &min_filter, &mag_filter); + return min_filter; +} + +CoglPipelineFilter +cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineFilter min_filter; + CoglPipelineFilter mag_filter; + + _cogl_pipeline_get_layer_filters (pipeline, layer_index, + &min_filter, &mag_filter); + return mag_filter; +} + +CoglPipelineFilter +_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), 0); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + return authority->sampler_cache_entry->min_filter; +} + +CoglPipelineFilter +_cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), 0); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + return authority->sampler_cache_entry->mag_filter; +} + +void +cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter min_filter, + CoglPipelineFilter mag_filter) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + _COGL_RETURN_IF_FAIL (mag_filter == COGL_PIPELINE_FILTER_NEAREST || + mag_filter == COGL_PIPELINE_FILTER_LINEAR); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + sampler_state = + _cogl_sampler_cache_update_filters (ctx->sampler_cache, + authority->sampler_cache_entry, + min_filter, + mag_filter); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); +} + +const CoglSamplerCacheEntry * +_cogl_pipeline_layer_get_sampler_state (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); + + return authority->sampler_cache_entry; +} + +void +_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + int unit = authority->unit_index; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &unit, sizeof (unit)); +} + +void +_cogl_pipeline_layer_hash_texture_type_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglTextureType texture_type = authority->texture_type; + + state->hash = _cogl_util_one_at_a_time_hash (state->hash, + &texture_type, + sizeof (texture_type)); +} + +void +_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + GLuint gl_handle; + + cogl_texture_get_gl_texture (authority->texture, &gl_handle, NULL); + + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &gl_handle, sizeof (gl_handle)); +} + +void +_cogl_pipeline_layer_hash_sampler_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, + &authority->sampler_cache_entry, + sizeof (authority->sampler_cache_entry)); +} + +void +_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + unsigned int hash = state->hash; + CoglPipelineLayerBigState *b = authority->big_state; + int n_args; + int i; + + hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_func, + sizeof (b->texture_combine_rgb_func)); + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_src[i], + sizeof (b->texture_combine_rgb_src[i])); + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_op[i], + sizeof (b->texture_combine_rgb_op[i])); + } + + hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_func, + sizeof (b->texture_combine_alpha_func)); + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_src[i], + sizeof (b->texture_combine_alpha_src[i])); + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_op[i], + sizeof (b->texture_combine_alpha_op[i])); + } + + state->hash = hash; +} + +void +_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *b = authority->big_state; + CoglBool need_hash = FALSE; + int n_args; + int i; + + /* XXX: If the user also asked to hash the ALPHA_FUNC_STATE then it + * would be nice if we could combine the n_args loops in this + * function and _cogl_pipeline_layer_hash_combine_state. + */ + + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + if (b->texture_combine_rgb_src[i] == + COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) + { + /* XXX: should we be careful to only hash the alpha + * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ + need_hash = TRUE; + goto done; + } + } + + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + if (b->texture_combine_alpha_src[i] == + COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) + { + /* XXX: should we be careful to only hash the alpha + * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ + need_hash = TRUE; + goto done; + } + } + +done: + if (need_hash) + { + float *constant = b->texture_combine_constant; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, constant, + sizeof (float) * 4); + } +} + +void +_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *big_state = authority->big_state; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->matrix, + sizeof (float) * 16); +} + +void +_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *big_state = authority->big_state; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &big_state->point_sprite_coords, + sizeof (big_state->point_sprite_coords)); +} + +void +_cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + _cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets, + &state->hash); +} + +void +_cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + _cogl_pipeline_snippet_list_hash (&authority->big_state->fragment_snippets, + &state->hash); +} diff --git a/cogl/cogl/cogl-pipeline-layer-state.h b/cogl/cogl/cogl-pipeline-layer-state.h new file mode 100644 index 000000000..4be7d398f --- /dev/null +++ b/cogl/cogl/cogl-pipeline-layer-state.h @@ -0,0 +1,620 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PIPELINE_LAYER_STATE_H__ +#define __COGL_PIPELINE_LAYER_STATE_H__ + +#include +#include +#include +#include + +COGL_BEGIN_DECLS + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * CoglPipelineFilter: + * @COGL_PIPELINE_FILTER_NEAREST: Measuring in manhatten distance from the, + * current pixel center, use the nearest texture texel + * @COGL_PIPELINE_FILTER_LINEAR: Use the weighted average of the 4 texels + * nearest the current pixel center + * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose + * texel size most closely matches the current pixel, and use the + * %COGL_PIPELINE_FILTER_NEAREST criterion + * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose + * texel size most closely matches the current pixel, and use the + * %COGL_PIPELINE_FILTER_LINEAR criterion + * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels + * whose texel size most closely matches the current pixel, use + * the %COGL_PIPELINE_FILTER_NEAREST criterion on each one and take + * their weighted average + * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels + * whose texel size most closely matches the current pixel, use + * the %COGL_PIPELINE_FILTER_LINEAR criterion on each one and take + * their weighted average + * + * Texture filtering is used whenever the current pixel maps either to more + * than one texture element (texel) or less than one. These filter enums + * correspond to different strategies used to come up with a pixel color, by + * possibly referring to multiple neighbouring texels and taking a weighted + * average or simply using the nearest texel. + */ +typedef enum { + COGL_PIPELINE_FILTER_NEAREST = 0x2600, + COGL_PIPELINE_FILTER_LINEAR = 0x2601, + COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700, + COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701, + COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702, + COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703 +} CoglPipelineFilter; +/* NB: these values come from the equivalents in gl.h */ + +/** + * CoglPipelineWrapMode: + * @COGL_PIPELINE_WRAP_MODE_REPEAT: The texture will be repeated. This + * is useful for example to draw a tiled background. + * @COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the + * range 0→1 will sample copies of the edge pixels of the + * texture. This is useful to avoid artifacts if only one copy of + * the texture is being rendered. + * @COGL_PIPELINE_WRAP_MODE_AUTOMATIC: Cogl will try to automatically + * decide which of the above two to use. For cogl_rectangle(), it + * will use repeat mode if any of the texture coordinates are + * outside the range 0→1, otherwise it will use clamp to edge. For + * cogl_polygon() it will always use repeat mode. For + * cogl_vertex_buffer_draw() it will use repeat mode except for + * layers that have point sprite coordinate generation enabled. This + * is the default value. + * + * The wrap mode specifies what happens when texture coordinates + * outside the range 0→1 are used. Note that if the filter mode is + * anything but %COGL_PIPELINE_FILTER_NEAREST then texels outside the + * range 0→1 might be used even when the coordinate is exactly 0 or 1 + * because OpenGL will try to sample neighbouring pixels. For example + * if you are trying to render the full texture then you may get + * artifacts around the edges when the pixels from the other side are + * merged in if the wrap mode is set to repeat. + * + * Since: 2.0 + */ +/* GL_ALWAYS is just used here as a value that is known not to clash + * with any valid GL wrap modes + * + * XXX: keep the values in sync with the CoglPipelineWrapModeInternal + * enum so no conversion is actually needed. + */ +typedef enum { + COGL_PIPELINE_WRAP_MODE_REPEAT = 0x2901, + COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT = 0x8370, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE = 0x812F, + COGL_PIPELINE_WRAP_MODE_AUTOMATIC = 0x0207 /* GL_ALWAYS */ +} CoglPipelineWrapMode; +/* NB: these values come from the equivalents in gl.h */ + +/** + * cogl_pipeline_set_layer: + * @pipeline: A #CoglPipeline object + * @layer_index: the index of the layer + * @texture: a #CoglTexture for the layer object + * + * In addition to the standard OpenGL lighting model a Cogl pipeline may have + * one or more layers comprised of textures that can be blended together in + * order, with a number of different texture combine modes. This function + * defines a new texture layer. + * + * The index values of multiple layers do not have to be consecutive; it is + * only their relative order that is important. + * + * The @texture parameter can also be %NULL in which case the pipeline + * will use a default white texture. The type of the default texture + * will be the same as whatever texture was last used for the pipeline + * or %COGL_TEXTURE_TYPE_2D if none has been specified yet. To + * explicitly specify the type of default texture required, use + * cogl_pipeline_set_layer_null_texture() instead. + * + * In the future, we may define other types of pipeline layers, such + * as purely GLSL based layers. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_texture (CoglPipeline *pipeline, + int layer_index, + CoglTexture *texture); + +/** + * cogl_pipeline_set_layer_null_texture: + * @pipeline: A #CoglPipeline + * @layer_index: The layer number to modify + * @texture_type: The type of the default texture to use + * + * Sets the texture for this layer to be the default texture for the + * given type. This is equivalent to calling + * cogl_pipeline_set_layer_texture() with %NULL for the texture + * argument except that you can also specify the type of default + * texture to use. The default texture is a 1x1 pixel white texture. + * + * This function is mostly useful if you want to create a base + * pipeline that you want to create multiple copies from using + * cogl_pipeline_copy(). In that case this function can be used to + * specify the texture type so that any pipeline copies can share the + * internal texture type state for efficiency. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_null_texture (CoglPipeline *pipeline, + int layer_index, + CoglTextureType texture_type); + +/** + * cogl_pipeline_get_layer_texture: + * @pipeline: A #CoglPipeline object + * @layer_index: the index of the layer + * + * Return value: (transfer none): the texture that was set for the + * given layer of the pipeline or %NULL if no texture was set. + * Stability: unstable + * Since: 1.10 + */ +CoglTexture * +cogl_pipeline_get_layer_texture (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_remove_layer: + * @pipeline: A #CoglPipeline object + * @layer_index: Specifies the layer you want to remove + * + * This function removes a layer from your pipeline + * Since: 1.10 + * Stability: unstable + */ +void +cogl_pipeline_remove_layer (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_set_layer_combine: + * @pipeline: A #CoglPipeline object + * @layer_index: Specifies the layer you want define a combine function for + * @blend_string: A Cogl blend string + * describing the desired texture combine function. + * @error: A #CoglError that may report parse errors or lack of GPU/driver + * support. May be %NULL, in which case a warning will be printed out if an + * error is encountered. + * + * If not already familiar; you can refer + * here for an overview of what blend + * strings are and there syntax. + * + * These are all the functions available for texture combining: + * + * REPLACE(arg0) = arg0 + * MODULATE(arg0, arg1) = arg0 x arg1 + * ADD(arg0, arg1) = arg0 + arg1 + * ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5 + * INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2) + * SUBTRACT(arg0, arg1) = arg0 - arg1 + * + * + * DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * + * + * DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * + * + * Refer to the + * color-source syntax for + * describing the arguments. The valid source names for texture combining + * are: + * + * + * TEXTURE + * Use the color from the current texture layer + * + * + * TEXTURE_0, TEXTURE_1, etc + * Use the color from the specified texture layer + * + * + * CONSTANT + * Use the color from the constant given with + * cogl_pipeline_set_layer_combine_constant() + * + * + * PRIMARY + * Use the color of the pipeline as set with + * cogl_pipeline_set_color() + * + * + * PREVIOUS + * Either use the texture color from the previous layer, or + * if this is layer 0, use the color of the pipeline as set with + * cogl_pipeline_set_color() + * + * + * + * + * Layer Combine Examples + * This is effectively what the default blending is: + * + * RGBA = MODULATE (PREVIOUS, TEXTURE) + * + * This could be used to cross-fade between two images, using + * the alpha component of a constant as the interpolator. The constant + * color is given by calling + * cogl_pipeline_set_layer_combine_constant(). + * + * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A]) + * + * + * + * You can't give a multiplication factor for arguments as you can + * with blending. + * + * Return value: %TRUE if the blend string was successfully parsed, and the + * described texture combining is supported by the underlying driver and + * or hardware. On failure, %FALSE is returned and @error is set + * + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_pipeline_set_layer_combine (CoglPipeline *pipeline, + int layer_index, + const char *blend_string, + CoglError **error); + +/** + * cogl_pipeline_set_layer_combine_constant: + * @pipeline: A #CoglPipeline object + * @layer_index: Specifies the layer you want to specify a constant used + * for texture combining + * @constant: The constant color you want + * + * When you are using the 'CONSTANT' color source in a layer combine + * description then you can use this function to define its value. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + const CoglColor *constant); + +/** + * cogl_pipeline_set_layer_matrix: + * @pipeline: A #CoglPipeline object + * @layer_index: the index for the layer inside @pipeline + * @matrix: the transformation matrix for the layer + * + * This function lets you set a matrix that can be used to e.g. translate + * and rotate a single layer of a pipeline used to fill your geometry. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline, + int layer_index, + const CoglMatrix *matrix); + +/** + * cogl_pipeline_get_n_layers: + * @pipeline: A #CoglPipeline object + * + * Retrieves the number of layers defined for the given @pipeline + * + * Return value: the number of layers + * + * Since: 2.0 + * Stability: unstable + */ +int +cogl_pipeline_get_n_layers (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_layer_filters: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @min_filter: the filter used when scaling a texture down. + * @mag_filter: the filter used when magnifying a texture. + * + * Changes the decimation and interpolation filters used when a texture is + * drawn at other scales than 100%. + * + * It is an error to pass anything other than + * %COGL_PIPELINE_FILTER_NEAREST or %COGL_PIPELINE_FILTER_LINEAR as + * magnification filters since magnification doesn't ever need to + * reference values stored in the mipmap chain. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter min_filter, + CoglPipelineFilter mag_filter); + +/** + * cogl_pipeline_get_layer_min_filter: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * + * Retrieves the currently set minification #CoglPipelineFilter set on + * the specified layer. The miniifcation filter determines how the + * layer should be sampled when down-scaled. + * + * The default filter is %COGL_PIPELINE_FILTER_LINEAR but this can be + * changed using cogl_pipeline_set_layer_filters(). + * + * Return value: The minification #CoglPipelineFilter for the + * specified layer. + * Since: 1.10 + * Stability: unstable + */ +CoglPipelineFilter +cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_get_layer_mag_filter: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * + * Retrieves the currently set magnification #CoglPipelineFilter set on + * the specified layer. The magnification filter determines how the + * layer should be sampled when up-scaled. + * + * The default filter is %COGL_PIPELINE_FILTER_LINEAR but this can be + * changed using cogl_pipeline_set_layer_filters(). + * + * Return value: The magnification #CoglPipelineFilter for the + * specified layer. + * Since: 1.10 + * Stability: unstable + */ +CoglPipelineFilter +cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_set_layer_point_sprite_coords_enabled: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @enable: whether to enable point sprite coord generation. + * @error: A return location for a CoglError, or NULL to ignore errors. + * + * When rendering points, if @enable is %TRUE then the texture + * coordinates for this layer will be replaced with coordinates that + * vary from 0.0 to 1.0 across the primitive. The top left of the + * point will have the coordinates 0.0,0.0 and the bottom right will + * have 1.0,1.0. If @enable is %FALSE then the coordinates will be + * fixed for the entire point. + * + * This function will only work if %COGL_FEATURE_ID_POINT_SPRITE is + * available. If the feature is not available then the function will + * return %FALSE and set @error. + * + * Return value: %TRUE if the function succeeds, %FALSE otherwise. + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index, + CoglBool enable, + CoglError **error); + +/** + * cogl_pipeline_get_layer_point_sprite_coords_enabled: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to check. + * + * Gets whether point sprite coordinate generation is enabled for this + * texture layer. + * + * Return value: whether the texture coordinates will be replaced with + * point sprite coordinates. + * + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_get_layer_wrap_mode_s: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 's' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 's' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Stability: unstable + */ +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_set_layer_wrap_mode_s: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 's' coordinate of texture lookups on this layer. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode); + +/** + * cogl_pipeline_get_layer_wrap_mode_t: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 't' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 't' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Stability: unstable + */ +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, + int layer_index); + + +/** + * cogl_pipeline_set_layer_wrap_mode_t: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 't' coordinate of texture lookups on this layer. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode); + +/** + * cogl_pipeline_get_layer_wrap_mode_p: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 'p' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 'p' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Stability: unstable + */ +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, + int layer_index); + +/** + * cogl_pipeline_set_layer_wrap_mode_p: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 'p' coordinate of texture lookups on + * this layer. 'p' is the third coordinate. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode); + +/** + * cogl_pipeline_set_layer_wrap_mode: + * @pipeline: A #CoglPipeline object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for all three coordinates of texture lookups on + * this layer. This is equivalent to calling + * cogl_pipeline_set_layer_wrap_mode_s(), + * cogl_pipeline_set_layer_wrap_mode_t() and + * cogl_pipeline_set_layer_wrap_mode_p() separately. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode); + +/** + * cogl_pipeline_add_layer_snippet: + * @pipeline: A #CoglPipeline + * @layer: The layer to hook the snippet to + * @snippet: A #CoglSnippet + * + * Adds a shader snippet that will hook on to the given layer of the + * pipeline. The exact part of the pipeline that the snippet wraps + * around depends on the hook that is given to + * cogl_snippet_new(). Note that some hooks can't be used with a layer + * and need to be added with cogl_pipeline_add_snippet() instead. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_pipeline_add_layer_snippet (CoglPipeline *pipeline, + int layer, + CoglSnippet *snippet); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +#endif /* __COGL_PIPELINE_LAYER_STATE_H__ */ diff --git a/cogl/cogl/cogl-pipeline-layer.c b/cogl/cogl/cogl-pipeline-layer.c new file mode 100644 index 000000000..37a512000 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-layer.c @@ -0,0 +1,942 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-texture-private.h" + +#include "cogl-pipeline.h" +#include "cogl-pipeline-layer-private.h" +#include "cogl-pipeline-layer-state-private.h" +#include "cogl-pipeline-layer-state.h" +#include "cogl-node-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-context-private.h" +#include "cogl-texture-private.h" + +#include + +static void +_cogl_pipeline_layer_free (CoglPipelineLayer *layer); + +/* This type was made deprecated before the cogl_is_pipeline_layer + function was ever exposed in the public headers so there's no need + to make the cogl_is_pipeline_layer function public. We use INTERNAL + so that the cogl_is_* function won't get defined */ +COGL_OBJECT_INTERNAL_DEFINE (PipelineLayer, pipeline_layer); + + +CoglPipelineLayer * +_cogl_pipeline_layer_get_authority (CoglPipelineLayer *layer, + unsigned long difference) +{ + CoglPipelineLayer *authority = layer; + while (!(authority->differences & difference)) + authority = _cogl_pipeline_layer_get_parent (authority); + return authority; +} + +int +_cogl_pipeline_layer_get_unit_index (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_UNIT); + return authority->unit_index; +} + +CoglBool +_cogl_pipeline_layer_has_alpha (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *combine_authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_COMBINE); + CoglPipelineLayerBigState *big_state = combine_authority->big_state; + CoglPipelineLayer *tex_authority; + CoglPipelineLayer *snippets_authority; + + /* has_alpha maintains the alpha status for the GL_PREVIOUS layer */ + + /* For anything but the default texture combine we currently just + * assume it may result in an alpha value < 1 + * + * FIXME: we could do better than this. */ + if (big_state->texture_combine_alpha_func != + COGL_PIPELINE_COMBINE_FUNC_MODULATE || + big_state->texture_combine_alpha_src[0] != + COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS || + big_state->texture_combine_alpha_op[0] != + COGL_PIPELINE_COMBINE_OP_SRC_ALPHA || + big_state->texture_combine_alpha_src[1] != + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE || + big_state->texture_combine_alpha_op[1] != + COGL_PIPELINE_COMBINE_OP_SRC_ALPHA) + { + return TRUE; + } + + /* NB: A layer may have a combine mode set on it but not yet + * have an associated texture which would mean we'd fallback + * to the default texture which doesn't have an alpha component + */ + tex_authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); + if (tex_authority->texture && + _cogl_texture_get_format (tex_authority->texture) & COGL_A_BIT) + { + return TRUE; + } + + /* All bets are off if the layer contains any snippets */ + snippets_authority = _cogl_pipeline_layer_get_authority + (layer, COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS); + if (snippets_authority->big_state->vertex_snippets.entries != NULL) + return TRUE; + snippets_authority = _cogl_pipeline_layer_get_authority + (layer, COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS); + if (snippets_authority->big_state->fragment_snippets.entries != NULL) + return TRUE; + + return FALSE; +} + +unsigned int +_cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func) +{ + switch (func) + { + case COGL_PIPELINE_COMBINE_FUNC_REPLACE: + return 1; + case COGL_PIPELINE_COMBINE_FUNC_MODULATE: + case COGL_PIPELINE_COMBINE_FUNC_ADD: + case COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED: + case COGL_PIPELINE_COMBINE_FUNC_SUBTRACT: + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB: + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA: + return 2; + case COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE: + return 3; + } + return 0; +} + +void +_cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest, + CoglPipelineLayer *src, + unsigned long differences) +{ + CoglPipelineLayerBigState *big_dest, *big_src; + + if ((differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) && + !dest->has_big_state) + { + dest->big_state = g_slice_new (CoglPipelineLayerBigState); + dest->has_big_state = TRUE; + } + + big_dest = dest->big_state; + big_src = src->big_state; + + dest->differences |= differences; + + while (differences) + { + int index = _cogl_util_ffs (differences) - 1; + + differences &= ~(1 << index); + + /* This convoluted switch statement is just here so that we'll + * get a warning if a new state is added without handling it + * here */ + switch (index) + { + case COGL_PIPELINE_LAYER_STATE_COUNT: + case COGL_PIPELINE_LAYER_STATE_UNIT_INDEX: + g_warn_if_reached (); + break; + + case COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX: + dest->texture_type = src->texture_type; + break; + + case COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX: + dest->texture = src->texture; + if (dest->texture) + cogl_object_ref (dest->texture); + break; + + case COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX: + dest->sampler_cache_entry = src->sampler_cache_entry; + break; + + case COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX: + { + CoglPipelineCombineFunc func; + int n_args, i; + + func = big_src->texture_combine_rgb_func; + big_dest->texture_combine_rgb_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + big_dest->texture_combine_rgb_src[i] = + big_src->texture_combine_rgb_src[i]; + big_dest->texture_combine_rgb_op[i] = + big_src->texture_combine_rgb_op[i]; + } + + func = big_src->texture_combine_alpha_func; + big_dest->texture_combine_alpha_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + big_dest->texture_combine_alpha_src[i] = + big_src->texture_combine_alpha_src[i]; + big_dest->texture_combine_alpha_op[i] = + big_src->texture_combine_alpha_op[i]; + } + } + break; + + case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX: + memcpy (big_dest->texture_combine_constant, + big_src->texture_combine_constant, + sizeof (big_dest->texture_combine_constant)); + break; + + case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX: + big_dest->point_sprite_coords = big_src->point_sprite_coords; + break; + + case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX: + _cogl_pipeline_snippet_list_copy (&big_dest->vertex_snippets, + &big_src->vertex_snippets); + break; + + case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX: + _cogl_pipeline_snippet_list_copy (&big_dest->fragment_snippets, + &big_src->fragment_snippets); + break; + } + } +} + +static void +_cogl_pipeline_layer_init_multi_property_sparse_state ( + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + CoglPipelineLayer *authority; + + /* Nothing to initialize in these cases since they are all comprised + * of one member which we expect to immediately be overwritten. */ + if (!(change & COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY)) + return; + + authority = _cogl_pipeline_layer_get_authority (layer, change); + + switch (change) + { + /* XXX: avoid using a default: label so we get a warning if we + * don't explicitly handle a newly defined state-group here. */ + case COGL_PIPELINE_LAYER_STATE_UNIT: + case COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE: + case COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA: + case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS: + case COGL_PIPELINE_LAYER_STATE_USER_MATRIX: + case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT: + case COGL_PIPELINE_LAYER_STATE_SAMPLER: + g_return_if_reached (); + + /* XXX: technically we could probably even consider these as + * single property state-groups from the pov that currently the + * corresponding property setters always update all of the values + * at the same time. */ + case COGL_PIPELINE_LAYER_STATE_COMBINE: + { + int n_args; + int i; + CoglPipelineLayerBigState *src_big_state = authority->big_state; + CoglPipelineLayerBigState *dest_big_state = layer->big_state; + GLint func = src_big_state->texture_combine_rgb_func; + + dest_big_state->texture_combine_rgb_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + dest_big_state->texture_combine_rgb_src[i] = + src_big_state->texture_combine_rgb_src[i]; + dest_big_state->texture_combine_rgb_op[i] = + src_big_state->texture_combine_rgb_op[i]; + } + + func = src_big_state->texture_combine_alpha_func; + dest_big_state->texture_combine_alpha_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + dest_big_state->texture_combine_alpha_src[i] = + src_big_state->texture_combine_alpha_src[i]; + dest_big_state->texture_combine_alpha_op[i] = + src_big_state->texture_combine_alpha_op[i]; + } + break; + } + case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS: + _cogl_pipeline_snippet_list_copy (&layer->big_state->vertex_snippets, + &authority->big_state-> + vertex_snippets); + break; + case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS: + _cogl_pipeline_snippet_list_copy (&layer->big_state->fragment_snippets, + &authority->big_state-> + fragment_snippets); + break; + } +} + +/* NB: If a layer has descendants we can't modify the layer + * NB: If the layer is owned and the owner has descendants we can't + * modify the layer. + * + * This function will allocate a new derived layer if you are trying + * to change the state of a layer with dependants (as described above) + * so you must always check the return value. + * + * If a new layer is returned it will be owned by required_owner. + * (NB: a layer is always modified with respect to a pipeline - the + * "required_owner") + * + * required_owner can only by NULL for new, currently unowned layers + * with no dependants. + */ +CoglPipelineLayer * +_cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + CoglTextureUnit *unit; + + /* Identify the case where the layer is new with no owner or + * dependants and so we don't need to do anything. */ + if (_cogl_list_empty (&COGL_NODE (layer)->children) && + layer->owner == NULL) + goto init_layer_state; + + /* We only allow a NULL required_owner for new layers */ + _COGL_RETURN_VAL_IF_FAIL (required_owner != NULL, layer); + + /* Chain up: + * A modification of a layer is indirectly also a modification of + * its owner so first make sure to flush the journal of any + * references to the current owner state and if necessary perform + * a copy-on-write for the required_owner if it has dependants. + */ + _cogl_pipeline_pre_change_notify (required_owner, + COGL_PIPELINE_STATE_LAYERS, + NULL, + TRUE); + + /* Unlike pipelines; layers are simply considered immutable once + * they have dependants - either direct children, or another + * pipeline as an owner. + */ + if (!_cogl_list_empty (&COGL_NODE (layer)->children) || + layer->owner != required_owner) + { + CoglPipelineLayer *new = _cogl_pipeline_layer_copy (layer); + if (layer->owner == required_owner) + _cogl_pipeline_remove_layer_difference (required_owner, layer, FALSE); + _cogl_pipeline_add_layer_difference (required_owner, new, FALSE); + cogl_object_unref (new); + layer = new; + goto init_layer_state; + } + + /* Note: At this point we know there is only one pipeline dependant on + * this layer (required_owner), and there are no other layers + * dependant on this layer so it's ok to modify it. */ + + /* NB: Although layers can have private state associated with them + * by multiple backends we know that a layer can't be *changed* if + * it has multiple dependants so if we reach here we know we only + * have a single owner and can only be associated with a single + * backend that needs to be notified of the layer change... + */ + if (required_owner->progend != COGL_PIPELINE_PROGEND_UNDEFINED) + { + const CoglPipelineProgend *progend = + _cogl_pipeline_progends[required_owner->progend]; + const CoglPipelineFragend *fragend = + _cogl_pipeline_fragends[progend->fragend]; + const CoglPipelineVertend *vertend = + _cogl_pipeline_vertends[progend->vertend]; + + if (fragend->layer_pre_change_notify) + fragend->layer_pre_change_notify (required_owner, layer, change); + if (vertend->layer_pre_change_notify) + vertend->layer_pre_change_notify (required_owner, layer, change); + if (progend->layer_pre_change_notify) + progend->layer_pre_change_notify (required_owner, layer, change); + } + + /* If the layer being changed is the same as the last layer we + * flushed to the corresponding texture unit then we keep a track of + * the changes so we can try to minimize redundant OpenGL calls if + * the same layer is flushed again. + */ + unit = _cogl_get_texture_unit (_cogl_pipeline_layer_get_unit_index (layer)); + if (unit->layer == layer) + unit->layer_changes_since_flush |= change; + +init_layer_state: + + if (required_owner) + required_owner->age++; + + if (change & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE && + !layer->has_big_state) + { + layer->big_state = g_slice_new (CoglPipelineLayerBigState); + layer->has_big_state = TRUE; + } + + /* Note: conceptually we have just been notified that a single + * property value is about to change, but since some state-groups + * contain multiple properties and 'layer' is about to take over + * being the authority for the property's corresponding state-group + * we need to maintain the integrity of the other property values + * too. + * + * To ensure this we handle multi-property state-groups by copying + * all the values from the old-authority to the new... + * + * We don't have to worry about non-sparse property groups since + * we never take over being an authority for such properties so + * they automatically maintain integrity. + */ + if (change & COGL_PIPELINE_LAYER_STATE_ALL_SPARSE && + !(layer->differences & change)) + { + _cogl_pipeline_layer_init_multi_property_sparse_state (layer, change); + layer->differences |= change; + } + + return layer; +} + +static void +_cogl_pipeline_layer_unparent (CoglNode *layer) +{ + /* Chain up */ + _cogl_pipeline_node_unparent_real (layer); +} + +static void +_cogl_pipeline_layer_set_parent (CoglPipelineLayer *layer, + CoglPipelineLayer *parent) +{ + /* Chain up */ + _cogl_pipeline_node_set_parent_real (COGL_NODE (layer), + COGL_NODE (parent), + _cogl_pipeline_layer_unparent, + TRUE); +} + +CoglPipelineLayer * +_cogl_pipeline_layer_copy (CoglPipelineLayer *src) +{ + CoglPipelineLayer *layer = g_slice_new (CoglPipelineLayer); + + _cogl_pipeline_node_init (COGL_NODE (layer)); + + layer->owner = NULL; + layer->index = src->index; + layer->differences = 0; + layer->has_big_state = FALSE; + + _cogl_pipeline_layer_set_parent (layer, src); + + return _cogl_pipeline_layer_object_new (layer); +} + +/* XXX: This is duplicated logic; the same as for + * _cogl_pipeline_prune_redundant_ancestry it would be nice to find a + * way to consolidate these functions! */ +void +_cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *new_parent = _cogl_pipeline_layer_get_parent (layer); + + /* walk up past ancestors that are now redundant and potentially + * reparent the layer. */ + while (_cogl_pipeline_layer_get_parent (new_parent) && + (new_parent->differences | layer->differences) == + layer->differences) + new_parent = _cogl_pipeline_layer_get_parent (new_parent); + + _cogl_pipeline_layer_set_parent (layer, new_parent); +} + +/* Determine the mask of differences between two layers. + * + * XXX: If layers and pipelines could both be cast to a common Tree + * type of some kind then we could have a unified + * compare_differences() function. + */ +unsigned long +_cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0, + CoglPipelineLayer *layer1) +{ + GSList *head0 = NULL; + GSList *head1 = NULL; + CoglPipelineLayer *node0; + CoglPipelineLayer *node1; + int len0 = 0; + int len1 = 0; + int count; + GSList *common_ancestor0; + GSList *common_ancestor1; + unsigned long layers_difference = 0; + + /* Algorithm: + * + * 1) Walk the ancestors of each layer to the root node, adding a + * pointer to each ancester node to two linked lists + * + * 2) Compare the lists to find the nodes where they start to + * differ marking the common_ancestor node for each list. + * + * 3) For each list now iterate starting after the common_ancestor + * nodes ORing each nodes ->difference mask into the final + * differences mask. + */ + + for (node0 = layer0; node0; node0 = _cogl_pipeline_layer_get_parent (node0)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head0; + link->data = node0; + head0 = link; + len0++; + } + for (node1 = layer1; node1; node1 = _cogl_pipeline_layer_get_parent (node1)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head1; + link->data = node1; + head1 = link; + len1++; + } + + /* NB: There's no point looking at the head entries since we know both layers + * must have the same default layer as their root node. */ + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + count = MIN (len0, len1) - 1; + while (count--) + { + if (head0->data != head1->data) + break; + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + } + + for (head0 = common_ancestor0->next; head0; head0 = head0->next) + { + node0 = head0->data; + layers_difference |= node0->differences; + } + for (head1 = common_ancestor1->next; head1; head1 = head1->next) + { + node1 = head1->data; + layers_difference |= node1->differences; + } + + return layers_difference; +} + +static CoglBool +layer_state_equal (CoglPipelineLayerStateIndex state_index, + CoglPipelineLayer **authorities0, + CoglPipelineLayer **authorities1, + CoglPipelineLayerStateComparitor comparitor) +{ + return comparitor (authorities0[state_index], authorities1[state_index]); +} + +void +_cogl_pipeline_layer_resolve_authorities (CoglPipelineLayer *layer, + unsigned long differences, + CoglPipelineLayer **authorities) +{ + unsigned long remaining = differences; + CoglPipelineLayer *authority = layer; + + do + { + unsigned long found = authority->differences & remaining; + int i; + + if (found == 0) + continue; + + for (i = 0; TRUE; i++) + { + unsigned long state = (1L< found) + break; + } + + remaining &= ~found; + if (remaining == 0) + return; + } + while ((authority = _cogl_pipeline_layer_get_parent (authority))); + + g_assert (remaining == 0); +} + +CoglBool +_cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, + CoglPipelineLayer *layer1, + unsigned long differences_mask, + CoglPipelineEvalFlags flags) +{ + unsigned long layers_difference; + CoglPipelineLayer *authorities0[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; + CoglPipelineLayer *authorities1[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; + + if (layer0 == layer1) + return TRUE; + + layers_difference = + _cogl_pipeline_layer_compare_differences (layer0, layer1); + + /* Only compare the sparse state groups requested by the caller... */ + layers_difference &= differences_mask; + + _cogl_pipeline_layer_resolve_authorities (layer0, + layers_difference, + authorities0); + _cogl_pipeline_layer_resolve_authorities (layer1, + layers_difference, + authorities1); + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE) + { + CoglPipelineLayerStateIndex state_index = + COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX; + if (!_cogl_pipeline_layer_texture_type_equal (authorities0[state_index], + authorities1[state_index], + flags)) + return FALSE; + } + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) + { + CoglPipelineLayerStateIndex state_index = + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX; + if (!_cogl_pipeline_layer_texture_data_equal (authorities0[state_index], + authorities1[state_index], + flags)) + return FALSE; + } + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_combine_state_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_combine_constant_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_sampler_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_user_matrix_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_point_sprite_coords_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_vertex_snippets_equal)) + return FALSE; + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX, + authorities0, authorities1, + _cogl_pipeline_layer_fragment_snippets_equal)) + return FALSE; + + return TRUE; +} + +static void +_cogl_pipeline_layer_free (CoglPipelineLayer *layer) +{ + _cogl_pipeline_layer_unparent (COGL_NODE (layer)); + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA && + layer->texture != NULL) + cogl_object_unref (layer->texture); + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS) + _cogl_pipeline_snippet_list_free (&layer->big_state->vertex_snippets); + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS) + _cogl_pipeline_snippet_list_free (&layer->big_state->fragment_snippets); + + if (layer->differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) + g_slice_free (CoglPipelineLayerBigState, layer->big_state); + + g_slice_free (CoglPipelineLayer, layer); +} + +void +_cogl_pipeline_init_default_layers (void) +{ + CoglPipelineLayer *layer = g_slice_new0 (CoglPipelineLayer); + CoglPipelineLayerBigState *big_state = + g_slice_new0 (CoglPipelineLayerBigState); + CoglPipelineLayer *new; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_pipeline_node_init (COGL_NODE (layer)); + + layer->index = 0; + + layer->differences = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; + + layer->unit_index = 0; + + layer->texture = NULL; + layer->texture_type = COGL_TEXTURE_TYPE_2D; + + layer->sampler_cache_entry = + _cogl_sampler_cache_get_default_entry (ctx->sampler_cache); + + layer->big_state = big_state; + layer->has_big_state = TRUE; + + /* Choose the same default combine mode as OpenGL: + * RGBA = MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */ + big_state->texture_combine_rgb_func = + COGL_PIPELINE_COMBINE_FUNC_MODULATE; + big_state->texture_combine_rgb_src[0] = + COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; + big_state->texture_combine_rgb_src[1] = + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + big_state->texture_combine_rgb_op[0] = + COGL_PIPELINE_COMBINE_OP_SRC_COLOR; + big_state->texture_combine_rgb_op[1] = + COGL_PIPELINE_COMBINE_OP_SRC_COLOR; + big_state->texture_combine_alpha_func = + COGL_PIPELINE_COMBINE_FUNC_MODULATE; + big_state->texture_combine_alpha_src[0] = + COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; + big_state->texture_combine_alpha_src[1] = + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + big_state->texture_combine_alpha_op[0] = + COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; + big_state->texture_combine_alpha_op[1] = + COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; + + big_state->point_sprite_coords = FALSE; + + cogl_matrix_init_identity (&big_state->matrix); + + ctx->default_layer_0 = _cogl_pipeline_layer_object_new (layer); + + /* TODO: we should make default_layer_n comprise of two + * descendants of default_layer_0: + * - the first descendant should change the texture combine + * to what we expect is most commonly used for multitexturing + * - the second should revert the above change. + * + * why? the documentation for how a new layer is initialized + * doesn't say that layers > 0 have different defaults so unless + * we change the documentation we can't use different defaults, + * but if the user does what we expect and changes the + * texture combine then we can revert the authority to the + * first descendant which means we can maximize the number + * of layers with a common ancestor. + * + * The main problem will be that we'll need to disable the + * optimizations for flattening the ancestry when we make + * the second descendant which reverts the state. + */ + ctx->default_layer_n = _cogl_pipeline_layer_copy (layer); + new = _cogl_pipeline_set_layer_unit (NULL, ctx->default_layer_n, 1); + g_assert (new == ctx->default_layer_n); + /* Since we passed a newly allocated layer we don't expect that + * _set_layer_unit() will have to allocate *another* layer. */ + + /* Finally we create a dummy dependant for ->default_layer_n which + * effectively ensures that ->default_layer_n and ->default_layer_0 + * remain immutable. + */ + ctx->dummy_layer_dependant = + _cogl_pipeline_layer_copy (ctx->default_layer_n); +} + +void +_cogl_pipeline_layer_pre_paint (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *texture_authority; + + texture_authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); + + if (texture_authority->texture != NULL) + { + CoglTexturePrePaintFlags flags = 0; + CoglPipelineFilter min_filter; + CoglPipelineFilter mag_filter; + + _cogl_pipeline_layer_get_filters (layer, &min_filter, &mag_filter); + + if (min_filter == COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST + || min_filter == COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST + || min_filter == COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR + || min_filter == COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR) + flags |= COGL_TEXTURE_NEEDS_MIPMAP; + + _cogl_texture_pre_paint (texture_authority->texture, flags); + } +} + +/* Determines if we need to handle the RGB and A texture combining + * separately or is the same function used for both channel masks and + * with the same arguments... + */ +CoglBool +_cogl_pipeline_layer_needs_combine_separate + (CoglPipelineLayer *combine_authority) +{ + CoglPipelineLayerBigState *big_state = combine_authority->big_state; + int n_args; + int i; + + if (big_state->texture_combine_rgb_func != + big_state->texture_combine_alpha_func) + return TRUE; + + n_args = _cogl_get_n_args_for_combine_func (big_state->texture_combine_rgb_func); + + for (i = 0; i < n_args; i++) + { + if (big_state->texture_combine_rgb_src[i] != + big_state->texture_combine_alpha_src[i]) + return TRUE; + + /* + * We can allow some variation of the source operands without + * needing a separation... + * + * "A = REPLACE (CONSTANT[A])" + either of the following... + * "RGB = REPLACE (CONSTANT[RGB])" + * "RGB = REPLACE (CONSTANT[A])" + * + * can be combined as: + * "RGBA = REPLACE (CONSTANT)" or + * "RGBA = REPLACE (CONSTANT[A])" or + * + * And "A = REPLACE (1-CONSTANT[A])" + either of the following... + * "RGB = REPLACE (1-CONSTANT)" or + * "RGB = REPLACE (1-CONSTANT[A])" + * + * can be combined as: + * "RGBA = REPLACE (1-CONSTANT)" or + * "RGBA = REPLACE (1-CONSTANT[A])" + */ + switch (big_state->texture_combine_alpha_op[i]) + { + case GL_SRC_ALPHA: + switch (big_state->texture_combine_rgb_op[i]) + { + case GL_SRC_COLOR: + case GL_SRC_ALPHA: + break; + default: + return FALSE; + } + break; + case GL_ONE_MINUS_SRC_ALPHA: + switch (big_state->texture_combine_rgb_op[i]) + { + case GL_ONE_MINUS_SRC_COLOR: + case GL_ONE_MINUS_SRC_ALPHA: + break; + default: + return FALSE; + } + break; + default: + return FALSE; /* impossible */ + } + } + + return FALSE; +} + + diff --git a/cogl/cogl/cogl-pipeline-private.h b/cogl/cogl/cogl-pipeline-private.h new file mode 100644 index 000000000..845fdd866 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-private.h @@ -0,0 +1,998 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_PRIVATE_H +#define __COGL_PIPELINE_PRIVATE_H + +#include "cogl-node-private.h" +#include "cogl-pipeline-layer-private.h" +#include "cogl-pipeline.h" +#include "cogl-matrix.h" +#include "cogl-object-private.h" +#include "cogl-profile.h" +#include "cogl-list.h" +#include "cogl-boxed-value.h" +#include "cogl-pipeline-snippet-private.h" +#include "cogl-pipeline-state.h" +#include "cogl-framebuffer.h" +#include "cogl-bitmask.h" + +#include + +#ifdef HAVE_COGL_GL + +#define COGL_PIPELINE_PROGEND_FIXED_ARBFP 0 +#define COGL_PIPELINE_PROGEND_FIXED 1 +#define COGL_PIPELINE_PROGEND_GLSL 2 +#define COGL_PIPELINE_N_PROGENDS 3 + +#define COGL_PIPELINE_VERTEND_FIXED 0 +#define COGL_PIPELINE_VERTEND_GLSL 1 +#define COGL_PIPELINE_N_VERTENDS 2 + +#define COGL_PIPELINE_FRAGEND_ARBFP 0 +#define COGL_PIPELINE_FRAGEND_FIXED 1 +#define COGL_PIPELINE_FRAGEND_GLSL 2 +#define COGL_PIPELINE_N_FRAGENDS 3 + +#else /* HAVE_COGL_GL */ + +#ifdef HAVE_COGL_GLES2 + +#define COGL_PIPELINE_PROGEND_GLSL 0 +#define COGL_PIPELINE_VERTEND_GLSL 0 +#define COGL_PIPELINE_FRAGEND_GLSL 0 + +#ifdef HAVE_COGL_GLES +#define COGL_PIPELINE_PROGEND_FIXED 1 +#define COGL_PIPELINE_VERTEND_FIXED 1 +#define COGL_PIPELINE_FRAGEND_FIXED 1 + +#define COGL_PIPELINE_N_PROGENDS 2 +#define COGL_PIPELINE_N_VERTENDS 2 +#define COGL_PIPELINE_N_FRAGENDS 2 +#else +#define COGL_PIPELINE_N_PROGENDS 1 +#define COGL_PIPELINE_N_VERTENDS 1 +#define COGL_PIPELINE_N_FRAGENDS 1 +#endif + +#else /* HAVE_COGL_GLES2 */ + +#ifdef HAVE_COGL_GLES +#define COGL_PIPELINE_PROGEND_FIXED 0 +#define COGL_PIPELINE_VERTEND_FIXED 0 +#define COGL_PIPELINE_FRAGEND_FIXED 0 +#define COGL_PIPELINE_N_PROGENDS 1 +#define COGL_PIPELINE_N_VERTENDS 1 +#define COGL_PIPELINE_N_FRAGENDS 1 +#else +#error No drivers defined +#endif + +#endif /* HAVE_COGL_GLES2 */ + +#endif /* HAVE_COGL_GL */ + +#define COGL_PIPELINE_PROGEND_DEFAULT 0 +#define COGL_PIPELINE_PROGEND_UNDEFINED 3 + +/* XXX: should I rename these as + * COGL_PIPELINE_STATE_INDEX_XYZ... ? + */ +typedef enum +{ + /* sparse state */ + COGL_PIPELINE_STATE_COLOR_INDEX, + COGL_PIPELINE_STATE_BLEND_ENABLE_INDEX, + COGL_PIPELINE_STATE_LAYERS_INDEX, + COGL_PIPELINE_STATE_LIGHTING_INDEX, + COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX, + COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX, + COGL_PIPELINE_STATE_BLEND_INDEX, + COGL_PIPELINE_STATE_USER_SHADER_INDEX, + COGL_PIPELINE_STATE_DEPTH_INDEX, + COGL_PIPELINE_STATE_FOG_INDEX, + COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX, + COGL_PIPELINE_STATE_POINT_SIZE_INDEX, + COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX, + COGL_PIPELINE_STATE_LOGIC_OPS_INDEX, + COGL_PIPELINE_STATE_CULL_FACE_INDEX, + COGL_PIPELINE_STATE_UNIFORMS_INDEX, + COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX, + COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX, + + /* non-sparse */ + COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX, + + COGL_PIPELINE_STATE_COUNT +} CoglPipelineStateIndex; + +#define COGL_PIPELINE_STATE_SPARSE_COUNT (COGL_PIPELINE_STATE_COUNT - 1) + +/* Used in pipeline->differences masks and for notifying pipeline + * state changes. + * + * XXX: If you add or remove state groups here you may need to update + * some of the state masks following this enum too! + * + * FIXME: perhaps it would be better to rename this enum to + * CoglPipelineStateGroup to better convey the fact that a single enum + * here can map to multiple properties. + */ +typedef enum _CoglPipelineState +{ + COGL_PIPELINE_STATE_COLOR = + 1L<big_state. + */ + + /* Layers represent their state in a tree structure where some of + * the state relating to a given pipeline or layer may actually be + * owned by one if is ancestors in the tree. We have a common data + * type to track the tree heirachy so we can share code... */ + CoglNode _parent; + + /* When weak pipelines are destroyed the user is notified via this + * callback */ + CoglPipelineDestroyCallback destroy_callback; + + /* When notifying that a weak pipeline has been destroyed this + * private data is passed to the above callback */ + void *destroy_data; + + /* We need to track if a pipeline is referenced in the journal + * because we can't allow modification to these pipelines without + * flushing the journal first */ + unsigned int journal_ref_count; + + /* A mask of which sparse state groups are different in this + * pipeline in comparison to its parent. */ + unsigned int differences; + + /* Whenever a pipeline is modified we increment the age. There's no + * guarantee that it won't wrap but it can nevertheless be a + * convenient mechanism to determine when a pipeline has been + * changed to you can invalidate some some associated cache that + * depends on the old state. */ + unsigned int age; + + /* This is the primary color of the pipeline. + * + * This is a sparse property, ref COGL_PIPELINE_STATE_COLOR */ + CoglColor color; + + /* A pipeline may be made up with multiple layers used to combine + * textures together. + * + * This is sparse state, ref COGL_PIPELINE_STATE_LAYERS */ + unsigned int n_layers; + GList *layer_differences; + + /* As a basic way to reduce memory usage we divide the pipeline + * state into two groups; the minimal state modified in 90% of + * all pipelines and the rest, so that the second group can + * be allocated dynamically when required... */ + CoglPipelineBigState *big_state; + +#ifdef COGL_DEBUG_ENABLED + /* For debugging purposes it's possible to associate a static const + * string with a pipeline which can be an aid when trying to trace + * where the pipeline originates from */ + const char *static_breadcrumb; +#endif + + /* Cached state... */ + + /* A cached, complete list of the layers this pipeline depends + * on sorted by layer->unit_index. */ + CoglPipelineLayer **layers_cache; + /* To avoid a separate ->layers_cache allocation for common + * pipelines with only a few layers... */ + CoglPipelineLayer *short_layers_cache[3]; + + /* The deprecated cogl_pipeline_get_layers() API returns a + * const GList of layers, which we track here... */ + GList *deprecated_get_layers_list; + + /* XXX: consider adding an authorities cache to speed up sparse + * property value lookups: + * CoglPipeline *authorities_cache[COGL_PIPELINE_N_SPARSE_PROPERTIES]; + * and corresponding authorities_cache_dirty:1 bitfield + */ + + /* bitfields */ + + /* Weak pipelines don't count as dependants on their parents which + * means that the parent pipeline can be modified without + * considering how the modifications may affect the weak pipeline. + */ + unsigned int is_weak:1; + + /* Determines if pipeline->big_state is valid */ + unsigned int has_big_state:1; + + /* By default blending is enabled automatically depending on the + * unlit color, the lighting colors or the texture format. The user + * can override this to explicitly enable or disable blending. + * + * This is a sparse property */ + unsigned int blend_enable:3; + + /* There are many factors that can determine if we need to enable + * blending, this holds our final decision */ + unsigned int real_blend_enable:1; + + /* Since the code for deciding if blending really needs to be + * enabled for a particular pipeline is quite expensive we update + * the real_blend_enable flag lazily when flushing a pipeline if + * this dirty flag has been set. */ + unsigned int dirty_real_blend_enable:1; + + /* Whenever a pipeline is flushed we keep track of whether the + * pipeline was used with a color attribute where we don't know + * whether the colors are opaque. The real_blend_enable state + * depends on this, and must be updated whenever this changes (even + * if dirty_real_blend_enable isn't set) */ + unsigned int unknown_color_alpha:1; + + unsigned int layers_cache_dirty:1; + unsigned int deprecated_get_layers_list_dirty:1; + +#ifdef COGL_DEBUG_ENABLED + /* For debugging purposes it's possible to associate a static const + * string with a pipeline which can be an aid when trying to trace + * where the pipeline originates from */ + unsigned int has_static_breadcrumb:1; +#endif + + /* There are multiple fragment and vertex processing backends for + * CoglPipeline, glsl, arbfp and fixed that are bundled under a + * "progend". This identifies the backend being used for the + * pipeline. */ + unsigned int progend:3; +}; + +typedef struct _CoglPipelineFragend +{ + void (*start) (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference); + CoglBool (*add_layer) (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference); + CoglBool (*passthrough) (CoglPipeline *pipeline); + CoglBool (*end) (CoglPipeline *pipeline, + unsigned long pipelines_difference); + + void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color); + void (*pipeline_set_parent_notify) (CoglPipeline *pipeline); + void (*layer_pre_change_notify) (CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change); +} CoglPipelineFragend; + +typedef struct _CoglPipelineVertend +{ + void (*start) (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference); + CoglBool (*add_layer) (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference, + CoglFramebuffer *framebuffer); + CoglBool (*end) (CoglPipeline *pipeline, + unsigned long pipelines_difference); + + void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color); + void (*layer_pre_change_notify) (CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change); +} CoglPipelineVertend; + +typedef struct +{ + int vertend; + int fragend; + CoglBool (*start) (CoglPipeline *pipeline); + void (*end) (CoglPipeline *pipeline, + unsigned long pipelines_difference); + void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color); + void (*layer_pre_change_notify) (CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change); + /* This is called after all of the other functions whenever the + pipeline is flushed, even if the pipeline hasn't changed since + the last flush */ + void (* pre_paint) (CoglPipeline *pipeline, CoglFramebuffer *framebuffer); +} CoglPipelineProgend; + +typedef enum +{ + COGL_PIPELINE_PROGRAM_TYPE_GLSL = 1, + COGL_PIPELINE_PROGRAM_TYPE_ARBFP, + COGL_PIPELINE_PROGRAM_TYPE_FIXED +} CoglPipelineProgramType; + +extern const CoglPipelineFragend * +_cogl_pipeline_fragends[COGL_PIPELINE_N_FRAGENDS]; +extern const CoglPipelineVertend * +_cogl_pipeline_vertends[COGL_PIPELINE_N_VERTENDS]; +extern const CoglPipelineProgend * +_cogl_pipeline_progends[]; + +void +_cogl_pipeline_init_default_pipeline (void); + +static inline CoglPipeline * +_cogl_pipeline_get_parent (CoglPipeline *pipeline) +{ + CoglNode *parent_node = COGL_NODE (pipeline)->parent; + return COGL_PIPELINE (parent_node); +} + +static inline CoglPipeline * +_cogl_pipeline_get_authority (CoglPipeline *pipeline, + unsigned long difference) +{ + CoglPipeline *authority = pipeline; + while (!(authority->differences & difference)) + authority = _cogl_pipeline_get_parent (authority); + return authority; +} + +typedef CoglBool (*CoglPipelineStateComparitor) (CoglPipeline *authority0, + CoglPipeline *authority1); + +void +_cogl_pipeline_update_authority (CoglPipeline *pipeline, + CoglPipeline *authority, + CoglPipelineState state, + CoglPipelineStateComparitor comparitor); + +void +_cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color, + CoglBool from_layer_change); + +void +_cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline); + +void +_cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline, + CoglBool unknown_color_alpha); + +typedef enum +{ + COGL_PIPELINE_GET_LAYER_NO_CREATE = 1<<0 +} CoglPipelineGetLayerFlags; + +CoglPipelineLayer * +_cogl_pipeline_get_layer_with_flags (CoglPipeline *pipeline, + int layer_index, + CoglPipelineGetLayerFlags flags); + +#define _cogl_pipeline_get_layer(p, l) \ + _cogl_pipeline_get_layer_with_flags (p, l, 0) + +CoglBool +_cogl_is_pipeline_layer (void *object); + +void +_cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority, + CoglPipelineLayer *layer); + +/* + * SECTION:cogl-pipeline-internals + * @short_description: Functions for creating custom primitives that make use + * of Cogl pipelines for filling. + * + * Normally you shouldn't need to use this API directly, but if you need to + * developing a custom/specialised primitive - probably using raw OpenGL - then + * this API aims to expose enough of the pipeline internals to support being + * able to fill your geometry according to a given Cogl pipeline. + */ + +CoglBool +_cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline); + +/* + * Calls the pre_paint method on the layer texture if there is + * one. This will determine whether mipmaps are needed based on the + * filter settings. + */ +void +_cogl_pipeline_pre_paint_for_layer (CoglPipeline *pipeline, + int layer_id); + +/* + * CoglPipelineFlushFlag: + * @COGL_PIPELINE_FLUSH_FALLBACK_MASK: The fallback_layers member is set to + * a uint32_t mask of the layers that can't be supported with the user + * supplied texture and need to be replaced with fallback textures. (1 = + * fallback, and the least significant bit = layer 0) + * @COGL_PIPELINE_FLUSH_DISABLE_MASK: The disable_layers member is set to + * a uint32_t mask of the layers that you want to completly disable + * texturing for (1 = fallback, and the least significant bit = layer 0) + * @COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE: The layer0_override_texture member is + * set to a GLuint OpenGL texture name to override the texture used for + * layer 0 of the pipeline. This is intended for dealing with sliced + * textures where you will need to point to each of the texture slices in + * turn when drawing your geometry. Passing a value of 0 is the same as + * not passing the option at all. + * @COGL_PIPELINE_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the + * pipeline don't call glColor. + */ +typedef enum _CoglPipelineFlushFlag +{ + COGL_PIPELINE_FLUSH_FALLBACK_MASK = 1L<<0, + COGL_PIPELINE_FLUSH_DISABLE_MASK = 1L<<1, + COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE = 1L<<2, + COGL_PIPELINE_FLUSH_SKIP_GL_COLOR = 1L<<3 +} CoglPipelineFlushFlag; + +/* + * CoglPipelineFlushOptions: + * + */ +typedef struct _CoglPipelineFlushOptions +{ + CoglPipelineFlushFlag flags; + + uint32_t fallback_layers; + uint32_t disable_layers; + CoglTexture *layer0_override_texture; +} CoglPipelineFlushOptions; + +void +_cogl_use_fragment_program (GLuint gl_program, CoglPipelineProgramType type); + +void +_cogl_use_vertex_program (GLuint gl_program, CoglPipelineProgramType type); + +unsigned int +_cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func); + +/* + * _cogl_pipeline_weak_copy: + * @pipeline: A #CoglPipeline object + * @callback: A callback to notify when your weak pipeline is destroyed + * @user_data: Private data to pass to your given callback. + * + * Returns a weak copy of the given source @pipeline. Unlike a normal + * copy no internal reference is taken on the source @pipeline and you + * can expect that later modifications of the source pipeline (or in + * fact any other pipeline) can result in the weak pipeline being + * destroyed. + * + * To understand this better its good to know a bit about the internal + * design of #CoglPipeline... + * + * Internally #CoglPipelines are represented as a graph of + * property diff's, where each node is a diff of properties that gets + * applied on top of its parent. Copying a pipeline creates an empty + * diff and a child->parent relationship between the empty diff and + * the source @pipeline, parent. + * + * Because of this internal graph design a single #CoglPipeline may + * indirectly depend on a chain of ancestors to fully define all of + * its properties. Because a node depends on its ancestors it normally + * owns a reference to its parent to stop it from being freed. Also if + * you try to modify a pipeline with children we internally use a + * copy-on-write mechanism to ensure that you don't indirectly change + * the properties those children. + * + * Weak pipelines avoid the use of copy-on-write to preserve the + * integrity of weak dependants and instead weak dependants are + * simply destroyed allowing the parent to be modified directly. Also + * because weak pipelines don't own a reference to their parent they + * won't stop the source @pipeline from being freed when the user + * releases their reference on it. + * + * Because weak pipelines don't own a reference on their parent they + * are the recommended mechanism for creating derived pipelines that you + * want to cache as a private property of the original pipeline + * because they won't result in a circular dependency. + * + * An example use case: + * + * Consider for example you are implementing a custom primitive that is + * not compatible with certain source pipelines. To handle this you + * implement a validation stage that given an arbitrary pipeline as + * input will create a derived pipeline that is suitable for drawing + * your primitive. + * + * Because you don't want to have to repeat this validation every time + * the same incompatible pipeline is given as input you want to cache + * the result as a private property of the original pipeline. If the + * derived pipeline were created using cogl_pipeline_copy that would + * create a circular dependency so the original pipeline can never be + * freed. + * + * If you instead create a weak copy you won't stop the original pipeline + * from being freed if it's no longer needed, and you will instead simply + * be notified that your weak pipeline has been destroyed. + * + * This is the recommended coding pattern for validating an input + * pipeline and caching a derived result: + * |[ + * static CoglUserDataKey _cogl_my_cache_key; + * + * typedef struct { + * CoglPipeline *validated_source; + * } MyValidatedMaterialCache; + * + * static void + * destroy_cache_cb (CoglObject *object, void *user_data) + * { + * g_slice_free (MyValidatedMaterialCache, user_data); + * } + * + * static void + * invalidate_cache_cb (CoglPipeline *destroyed, void *user_data) + * { + * MyValidatedMaterialCache *cache = user_data; + * cogl_object_unref (cache->validated_source); + * cache->validated_source = NULL; + * } + * + * static CoglPipeline * + * get_validated_pipeline (CoglPipeline *source) + * { + * MyValidatedMaterialCache *cache = + * cogl_object_get_user_data (COGL_OBJECT (source), + * &_cogl_my_cache_key); + * if (G_UNLIKELY (cache == NULL)) + * { + * cache = g_slice_new (MyValidatedMaterialCache); + * cogl_object_set_user_data (COGL_OBJECT (source), + * &_cogl_my_cache_key, + * cache, destroy_cache_cb); + * cache->validated_source = source; + * } + * + * if (G_UNLIKELY (cache->validated_source == NULL)) + * { + * cache->validated_source = source; + * + * / * Start validating source... * / + * + * / * If you find you need to change something... * / + * if (cache->validated_source == source) + * cache->validated_source = + * cogl_pipeline_weak_copy (source, + * invalidate_cache_cb, + * cache); + * + * / * Modify cache->validated_source * / + * } + * + * return cache->validated_source; + * } + * ]| + */ +CoglPipeline * +_cogl_pipeline_weak_copy (CoglPipeline *pipeline, + CoglPipelineDestroyCallback callback, + void *user_data); + +void +_cogl_pipeline_set_progend (CoglPipeline *pipeline, int progend); + +CoglPipeline * +_cogl_pipeline_get_parent (CoglPipeline *pipeline); + +void +_cogl_pipeline_get_colorubv (CoglPipeline *pipeline, + uint8_t *color); + +/* XXX: At some point it could be good for this to accept a mask of + * the state groups we are interested in comparing since we can + * probably use that information in a number situations to reduce + * the work we do. */ +unsigned long +_cogl_pipeline_compare_differences (CoglPipeline *pipeline0, + CoglPipeline *pipeline1); + +CoglBool +_cogl_pipeline_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1, + unsigned int differences, + unsigned long layer_differences, + CoglPipelineEvalFlags flags); + +unsigned int +_cogl_pipeline_hash (CoglPipeline *pipeline, + unsigned int differences, + unsigned long layer_differences, + CoglPipelineEvalFlags flags); + +/* Makes a copy of the given pipeline that is a child of the root + * pipeline rather than a child of the source pipeline. That way the + * new pipeline won't hold a reference to the source pipeline. The + * differences specified in @differences and @layer_differences are + * copied across and all other state is left with the default + * values. */ +CoglPipeline * +_cogl_pipeline_deep_copy (CoglPipeline *pipeline, + unsigned long differences, + unsigned long layer_differences); + +CoglPipeline * +_cogl_pipeline_journal_ref (CoglPipeline *pipeline); + +void +_cogl_pipeline_journal_unref (CoglPipeline *pipeline); + +const CoglMatrix * +_cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, + int layer_index); + +void +_cogl_pipeline_texture_storage_change_notify (CoglTexture *texture); + +void +_cogl_pipeline_apply_legacy_state (CoglPipeline *pipeline); + +void +_cogl_pipeline_apply_overrides (CoglPipeline *pipeline, + CoglPipelineFlushOptions *options); + +CoglPipelineBlendEnable +_cogl_pipeline_get_blend_enabled (CoglPipeline *pipeline); + +void +_cogl_pipeline_set_blend_enabled (CoglPipeline *pipeline, + CoglPipelineBlendEnable enable); + +CoglBool +_cogl_pipeline_get_fog_enabled (CoglPipeline *pipeline); + +#ifdef COGL_DEBUG_ENABLED +void +_cogl_pipeline_set_static_breadcrumb (CoglPipeline *pipeline, + const char *breadcrumb); +#endif + +unsigned long +_cogl_pipeline_get_age (CoglPipeline *pipeline); + +CoglPipeline * +_cogl_pipeline_get_authority (CoglPipeline *pipeline, + unsigned long difference); + +void +_cogl_pipeline_add_layer_difference (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglBool inc_n_layers); + +void +_cogl_pipeline_remove_layer_difference (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglBool dec_n_layers); + +CoglPipeline * +_cogl_pipeline_find_equivalent_parent (CoglPipeline *pipeline, + CoglPipelineState pipeline_state, + CoglPipelineLayerState layer_state); + +void +_cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + float *constant); + +void +_cogl_pipeline_prune_to_n_layers (CoglPipeline *pipeline, int n); + + +/* + * API to support the deprecate cogl_pipeline_layer_xyz functions... + */ + +const GList * +_cogl_pipeline_get_layers (CoglPipeline *pipeline); + +typedef CoglBool (*CoglPipelineInternalLayerCallback) (CoglPipelineLayer *layer, + void *user_data); + +void +_cogl_pipeline_foreach_layer_internal (CoglPipeline *pipeline, + CoglPipelineInternalLayerCallback callback, + void *user_data); + +CoglBool +_cogl_pipeline_layer_numbers_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1); + +CoglBool +_cogl_pipeline_layer_and_unit_numbers_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1); + +CoglBool +_cogl_pipeline_need_texture_combine_separate + (CoglPipelineLayer *combine_authority); + +void +_cogl_pipeline_init_state_hash_functions (void); + +void +_cogl_pipeline_init_layer_state_hash_functions (void); + +CoglPipelineState +_cogl_pipeline_get_state_for_vertex_codegen (CoglContext *context); + +CoglPipelineLayerState +_cogl_pipeline_get_layer_state_for_fragment_codegen (CoglContext *context); + +CoglPipelineState +_cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context); + +#endif /* __COGL_PIPELINE_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-pipeline-snippet-private.h b/cogl/cogl/cogl-pipeline-snippet-private.h new file mode 100644 index 000000000..7a9d233c8 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-snippet-private.h @@ -0,0 +1,116 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_SNIPPET_PRIVATE_H +#define __COGL_PIPELINE_SNIPPET_PRIVATE_H + +#include + +#include "cogl-snippet.h" + +typedef struct +{ + GList *entries; +} CoglPipelineSnippetList; + +/* Arguments to pass to _cogl_pipeline_snippet_generate_code() */ +typedef struct +{ + CoglPipelineSnippetList *snippets; + + /* Only snippets at this hook point will be used */ + CoglSnippetHook hook; + + /* The final function to chain on to after all of the snippets code + has been run */ + const char *chain_function; + + /* The name of the final generated function */ + const char *final_name; + + /* A prefix to insert before each generate function name */ + const char *function_prefix; + + /* The return type of all of the functions, or NULL to use void */ + const char *return_type; + + /* A variable to return from the functions. The snippets are + expected to modify this variable. Ignored if return_type is + NULL */ + const char *return_variable; + + /* If this is TRUE then it won't allocate a separate variable for + the return value. Instead it is expected that the snippet will + modify one of the argument variables directly and that will be + returned */ + CoglBool return_variable_is_argument; + + /* The argument names or NULL if there are none */ + const char *arguments; + + /* The argument types or NULL */ + const char *argument_declarations; + + /* The string to generate the source into */ + GString *source_buf; +} CoglPipelineSnippetData; + +void +_cogl_pipeline_snippet_generate_code (const CoglPipelineSnippetData *data); + +void +_cogl_pipeline_snippet_generate_declarations (GString *declarations_buf, + CoglSnippetHook hook, + CoglPipelineSnippetList *list); + +void +_cogl_pipeline_snippet_list_free (CoglPipelineSnippetList *list); + +void +_cogl_pipeline_snippet_list_add (CoglPipelineSnippetList *list, + CoglSnippet *snippet); + +void +_cogl_pipeline_snippet_list_copy (CoglPipelineSnippetList *dst, + const CoglPipelineSnippetList *src); + +void +_cogl_pipeline_snippet_list_hash (CoglPipelineSnippetList *list, + unsigned int *hash); + +CoglBool +_cogl_pipeline_snippet_list_equal (CoglPipelineSnippetList *list0, + CoglPipelineSnippetList *list1); + +#endif /* __COGL_PIPELINE_SNIPPET_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-pipeline-snippet.c b/cogl/cogl/cogl-pipeline-snippet.c new file mode 100644 index 000000000..59f85b3ec --- /dev/null +++ b/cogl/cogl/cogl-pipeline-snippet.c @@ -0,0 +1,286 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-types.h" +#include "cogl-pipeline-snippet-private.h" +#include "cogl-snippet-private.h" +#include "cogl-util.h" + +/* Helper functions that are used by both GLSL pipeline backends */ + +void +_cogl_pipeline_snippet_generate_code (const CoglPipelineSnippetData *data) +{ + GList *first_snippet, *l; + CoglSnippet *snippet; + int snippet_num = 0; + int n_snippets = 0; + + first_snippet = data->snippets->entries; + + /* First count the number of snippets so we can easily tell when + we're at the last one */ + for (l = data->snippets->entries; l; l = l->next) + { + snippet = l->data; + + if (snippet->hook == data->hook) + { + /* Don't bother processing any previous snippets if we reach + one that has a replacement */ + if (snippet->replace) + { + n_snippets = 1; + first_snippet = l; + } + else + n_snippets++; + } + } + + /* If there weren't any snippets then generate a stub function with + the final name */ + if (n_snippets == 0) + { + if (data->return_type) + g_string_append_printf (data->source_buf, + "\n" + "%s\n" + "%s (%s)\n" + "{\n" + " return %s (%s);\n" + "}\n", + data->return_type, + data->final_name, + data->argument_declarations ? + data->argument_declarations : "", + data->chain_function, + data->arguments ? data->arguments : ""); + else + g_string_append_printf (data->source_buf, + "\n" + "void\n" + "%s (%s)\n" + "{\n" + " %s (%s);\n" + "}\n", + data->final_name, + data->argument_declarations ? + data->argument_declarations : "", + data->chain_function, + data->arguments ? data->arguments : ""); + + return; + } + + for (l = first_snippet; snippet_num < n_snippets; l = l->next) + { + snippet = l->data; + + if (snippet->hook == data->hook) + { + const char *source; + + if ((source = cogl_snippet_get_declarations (snippet))) + g_string_append (data->source_buf, source); + + g_string_append_printf (data->source_buf, + "\n" + "%s\n", + data->return_type ? + data->return_type : + "void"); + + if (snippet_num + 1 < n_snippets) + g_string_append_printf (data->source_buf, + "%s_%i", + data->function_prefix, + snippet_num); + else + g_string_append (data->source_buf, data->final_name); + + g_string_append (data->source_buf, " ("); + + if (data->argument_declarations) + g_string_append (data->source_buf, data->argument_declarations); + + g_string_append (data->source_buf, + ")\n" + "{\n"); + + if (data->return_type && !data->return_variable_is_argument) + g_string_append_printf (data->source_buf, + " %s %s;\n" + "\n", + data->return_type, + data->return_variable); + + if ((source = cogl_snippet_get_pre (snippet))) + g_string_append (data->source_buf, source); + + /* Chain on to the next function, or bypass it if there is + a replace string */ + if ((source = cogl_snippet_get_replace (snippet))) + g_string_append (data->source_buf, source); + else + { + g_string_append (data->source_buf, " "); + + if (data->return_type) + g_string_append_printf (data->source_buf, + "%s = ", + data->return_variable); + + if (snippet_num > 0) + g_string_append_printf (data->source_buf, + "%s_%i", + data->function_prefix, + snippet_num - 1); + else + g_string_append (data->source_buf, data->chain_function); + + g_string_append (data->source_buf, " ("); + + if (data->arguments) + g_string_append (data->source_buf, data->arguments); + + g_string_append (data->source_buf, ");\n"); + } + + if ((source = cogl_snippet_get_post (snippet))) + g_string_append (data->source_buf, source); + + if (data->return_type) + g_string_append_printf (data->source_buf, + " return %s;\n", + data->return_variable); + + g_string_append (data->source_buf, "}\n"); + snippet_num++; + } + } +} + +void +_cogl_pipeline_snippet_generate_declarations (GString *declarations_buf, + CoglSnippetHook hook, + CoglPipelineSnippetList *snippets) +{ + GList *l; + + for (l = snippets->entries; l; l = l->next) + { + CoglSnippet *snippet = l->data; + + if (snippet->hook == hook) + { + const char *source; + + if ((source = cogl_snippet_get_declarations (snippet))) + g_string_append (declarations_buf, source); + } + } +} + +void +_cogl_pipeline_snippet_list_free (CoglPipelineSnippetList *list) +{ + GList *l, *tmp; + + for (l = list->entries; l; l = tmp) + { + tmp = l->next; + + cogl_object_unref (l->data); + g_list_free_1 (l); + } +} + +void +_cogl_pipeline_snippet_list_add (CoglPipelineSnippetList *list, + CoglSnippet *snippet) +{ + list->entries = g_list_append (list->entries, cogl_object_ref (snippet)); + + _cogl_snippet_make_immutable (snippet); +} + +void +_cogl_pipeline_snippet_list_copy (CoglPipelineSnippetList *dst, + const CoglPipelineSnippetList *src) +{ + GQueue queue = G_QUEUE_INIT; + const GList *l; + + for (l = src->entries; l; l = l->next) + g_queue_push_tail (&queue, cogl_object_ref (l->data)); + + dst->entries = queue.head; +} + +void +_cogl_pipeline_snippet_list_hash (CoglPipelineSnippetList *list, + unsigned int *hash) +{ + GList *l; + + for (l = list->entries; l; l = l->next) + { + CoglSnippet *snippet = l->data; + + *hash = _cogl_util_one_at_a_time_hash (*hash, + &snippet, + sizeof (CoglSnippet *)); + } +} + +CoglBool +_cogl_pipeline_snippet_list_equal (CoglPipelineSnippetList *list0, + CoglPipelineSnippetList *list1) +{ + GList *l0, *l1; + + for (l0 = list0->entries, l1 = list1->entries; + l0 && l1; + l0 = l0->next, l1 = l1->next) + if (l0->data != l1->data) + return FALSE; + + return l0 == NULL && l1 == NULL; +} diff --git a/cogl/cogl/cogl-pipeline-state-private.h b/cogl/cogl/cogl-pipeline-state-private.h new file mode 100644 index 000000000..366683ec4 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-state-private.h @@ -0,0 +1,196 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_STATE_PRIVATE_H +#define __COGL_PIPELINE_STATE_PRIVATE_H + +CoglPipeline * +_cogl_pipeline_get_user_program (CoglPipeline *pipeline); + +CoglBool +_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline); + +CoglBool +_cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline); + +CoglBool +_cogl_pipeline_has_non_layer_vertex_snippets (CoglPipeline *pipeline); + +CoglBool +_cogl_pipeline_has_non_layer_fragment_snippets (CoglPipeline *pipeline); + +void +_cogl_pipeline_set_fog_state (CoglPipeline *pipeline, + const CoglPipelineFogState *fog_state); + +CoglBool +_cogl_pipeline_color_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_lighting_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_alpha_func_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_alpha_func_reference_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_blend_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_depth_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_fog_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_non_zero_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1); +CoglBool +_cogl_pipeline_per_vertex_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_logic_ops_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_user_shader_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_cull_face_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +CoglBool +_cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1); + +void +_cogl_pipeline_hash_color_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_blend_enable_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_layers_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_lighting_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_alpha_func_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_alpha_func_reference_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_blend_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_user_shader_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_depth_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_fog_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_non_zero_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_per_vertex_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_logic_ops_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_cull_face_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_uniforms_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority, + CoglPipelineHashState *state); + +void +_cogl_pipeline_compare_uniform_differences (unsigned long *differences, + CoglPipeline *pipeline0, + CoglPipeline *pipeline1); + +#endif /* __COGL_PIPELINE_STATE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-pipeline-state.c b/cogl/cogl/cogl-pipeline-state.c new file mode 100644 index 000000000..04c76f8a7 --- /dev/null +++ b/cogl/cogl/cogl-pipeline-state.c @@ -0,0 +1,2171 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-color-private.h" +#include "cogl-blend-string.h" +#include "cogl-util.h" +#include "cogl-depth-state-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-snippet-private.h" +#include "cogl-error-private.h" + +#include + +#include "string.h" + +#ifndef GL_FUNC_ADD +#define GL_FUNC_ADD 0x8006 +#endif + +CoglPipeline * +_cogl_pipeline_get_user_program (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), NULL); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_USER_SHADER); + + return authority->big_state->user_program; +} + +CoglBool +_cogl_pipeline_color_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return cogl_color_equal (&authority0->color, &authority1->color); +} + +CoglBool +_cogl_pipeline_lighting_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineLightingState *state0 = &authority0->big_state->lighting_state; + CoglPipelineLightingState *state1 = &authority1->big_state->lighting_state; + + if (memcmp (state0->ambient, state1->ambient, sizeof (float) * 4) != 0) + return FALSE; + if (memcmp (state0->diffuse, state1->diffuse, sizeof (float) * 4) != 0) + return FALSE; + if (memcmp (state0->specular, state1->specular, sizeof (float) * 4) != 0) + return FALSE; + if (memcmp (state0->emission, state1->emission, sizeof (float) * 4) != 0) + return FALSE; + if (state0->shininess != state1->shininess) + return FALSE; + + return TRUE; +} + +CoglBool +_cogl_pipeline_alpha_func_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineAlphaFuncState *alpha_state0 = + &authority0->big_state->alpha_state; + CoglPipelineAlphaFuncState *alpha_state1 = + &authority1->big_state->alpha_state; + + return alpha_state0->alpha_func == alpha_state1->alpha_func; +} + +CoglBool +_cogl_pipeline_alpha_func_reference_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineAlphaFuncState *alpha_state0 = + &authority0->big_state->alpha_state; + CoglPipelineAlphaFuncState *alpha_state1 = + &authority1->big_state->alpha_state; + + return (alpha_state0->alpha_func_reference == + alpha_state1->alpha_func_reference); +} + +CoglBool +_cogl_pipeline_blend_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineBlendState *blend_state0 = &authority0->big_state->blend_state; + CoglPipelineBlendState *blend_state1 = &authority1->big_state->blend_state; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (blend_state0->blend_equation_rgb != blend_state1->blend_equation_rgb) + return FALSE; + + if (blend_state0->blend_equation_alpha != + blend_state1->blend_equation_alpha) + return FALSE; + if (blend_state0->blend_src_factor_alpha != + blend_state1->blend_src_factor_alpha) + return FALSE; + if (blend_state0->blend_dst_factor_alpha != + blend_state1->blend_dst_factor_alpha) + return FALSE; + + if (blend_state0->blend_src_factor_rgb != + blend_state1->blend_src_factor_rgb) + return FALSE; + if (blend_state0->blend_dst_factor_rgb != + blend_state1->blend_dst_factor_rgb) + return FALSE; + + if (blend_state0->blend_src_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state0->blend_src_factor_rgb == GL_CONSTANT_COLOR || + blend_state0->blend_dst_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state0->blend_dst_factor_rgb == GL_CONSTANT_COLOR) + { + if (!cogl_color_equal (&blend_state0->blend_constant, + &blend_state1->blend_constant)) + return FALSE; + } + + return TRUE; +} + +CoglBool +_cogl_pipeline_depth_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + if (authority0->big_state->depth_state.test_enabled == FALSE && + authority1->big_state->depth_state.test_enabled == FALSE) + return TRUE; + else + { + CoglDepthState *s0 = &authority0->big_state->depth_state; + CoglDepthState *s1 = &authority1->big_state->depth_state; + return s0->test_enabled == s1->test_enabled && + s0->test_function == s1->test_function && + s0->write_enabled == s1->write_enabled && + s0->range_near == s1->range_near && + s0->range_far == s1->range_far; + } +} + +CoglBool +_cogl_pipeline_fog_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineFogState *fog_state0 = &authority0->big_state->fog_state; + CoglPipelineFogState *fog_state1 = &authority1->big_state->fog_state; + + if (fog_state0->enabled == fog_state1->enabled && + cogl_color_equal (&fog_state0->color, &fog_state1->color) && + fog_state0->mode == fog_state1->mode && + fog_state0->density == fog_state1->density && + fog_state0->z_near == fog_state1->z_near && + fog_state0->z_far == fog_state1->z_far) + return TRUE; + else + return FALSE; +} + +CoglBool +_cogl_pipeline_non_zero_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return (authority0->big_state->non_zero_point_size == + authority1->big_state->non_zero_point_size); +} + +CoglBool +_cogl_pipeline_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return authority0->big_state->point_size == authority1->big_state->point_size; +} + +CoglBool +_cogl_pipeline_per_vertex_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return (authority0->big_state->per_vertex_point_size == + authority1->big_state->per_vertex_point_size); +} + +CoglBool +_cogl_pipeline_logic_ops_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineLogicOpsState *logic_ops_state0 = &authority0->big_state->logic_ops_state; + CoglPipelineLogicOpsState *logic_ops_state1 = &authority1->big_state->logic_ops_state; + + return logic_ops_state0->color_mask == logic_ops_state1->color_mask; +} + +CoglBool +_cogl_pipeline_cull_face_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + CoglPipelineCullFaceState *cull_face_state0 + = &authority0->big_state->cull_face_state; + CoglPipelineCullFaceState *cull_face_state1 + = &authority1->big_state->cull_face_state; + + /* The cull face state is considered equal if two pipelines are both + set to no culling. If the front winding property is ever used for + anything else or the comparison is used not just for drawing then + this would have to change */ + + if (cull_face_state0->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) + return cull_face_state1->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE; + + return (cull_face_state0->mode == cull_face_state1->mode && + cull_face_state0->front_winding == cull_face_state1->front_winding); +} + +CoglBool +_cogl_pipeline_user_shader_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return (authority0->big_state->user_program == + authority1->big_state->user_program); +} + +typedef struct +{ + const CoglBoxedValue **dst_values; + const CoglBoxedValue *src_values; + int override_count; +} GetUniformsClosure; + +static CoglBool +get_uniforms_cb (int uniform_num, void *user_data) +{ + GetUniformsClosure *data = user_data; + + if (data->dst_values[uniform_num] == NULL) + data->dst_values[uniform_num] = data->src_values + data->override_count; + + data->override_count++; + + return TRUE; +} + +static void +_cogl_pipeline_get_all_uniform_values (CoglPipeline *pipeline, + const CoglBoxedValue **values) +{ + GetUniformsClosure data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + memset (values, 0, + sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); + + data.dst_values = values; + + do + { + if ((pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)) + { + const CoglPipelineUniformsState *uniforms_state = + &pipeline->big_state->uniforms_state; + + data.override_count = 0; + data.src_values = uniforms_state->override_values; + + _cogl_bitmask_foreach (&uniforms_state->override_mask, + get_uniforms_cb, + &data); + } + pipeline = _cogl_pipeline_get_parent (pipeline); + } + while (pipeline); +} + +CoglBool +_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + unsigned long *differences; + const CoglBoxedValue **values0, **values1; + int n_longs; + int i; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (authority0 == authority1) + return TRUE; + + values0 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); + values1 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); + + n_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names); + differences = g_alloca (n_longs * sizeof (unsigned long)); + memset (differences, 0, sizeof (unsigned long) * n_longs); + _cogl_pipeline_compare_uniform_differences (differences, + authority0, + authority1); + + _cogl_pipeline_get_all_uniform_values (authority0, values0); + _cogl_pipeline_get_all_uniform_values (authority1, values1); + + COGL_FLAGS_FOREACH_START (differences, n_longs, i) + { + const CoglBoxedValue *value0 = values0[i]; + const CoglBoxedValue *value1 = values1[i]; + + if (value0 == NULL) + { + if (value1 != NULL && value1->type != COGL_BOXED_NONE) + return FALSE; + } + else if (value1 == NULL) + { + if (value0 != NULL && value0->type != COGL_BOXED_NONE) + return FALSE; + } + else if (!_cogl_boxed_value_equal (value0, value1)) + return FALSE; + } + COGL_FLAGS_FOREACH_END; + + return TRUE; +} + +CoglBool +_cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> + vertex_snippets, + &authority1->big_state-> + vertex_snippets); +} + +CoglBool +_cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> + fragment_snippets, + &authority1->big_state-> + fragment_snippets); +} + +void +cogl_pipeline_get_color (CoglPipeline *pipeline, + CoglColor *color) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); + + *color = authority->color; +} + +/* This is used heavily by the cogl journal when logging quads */ +void +_cogl_pipeline_get_colorubv (CoglPipeline *pipeline, + uint8_t *color) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); + + _cogl_color_get_rgba_4ubv (&authority->color, color); +} + +void +cogl_pipeline_set_color (CoglPipeline *pipeline, + const CoglColor *color) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_COLOR; + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + if (cogl_color_equal (color, &authority->color)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, color, FALSE); + + pipeline->color = *color; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_color_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +cogl_pipeline_set_color4ub (CoglPipeline *pipeline, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + CoglColor color; + cogl_color_init_from_4ub (&color, red, green, blue, alpha); + cogl_pipeline_set_color (pipeline, &color); +} + +void +cogl_pipeline_set_color4f (CoglPipeline *pipeline, + float red, + float green, + float blue, + float alpha) +{ + CoglColor color; + cogl_color_init_from_4f (&color, red, green, blue, alpha); + cogl_pipeline_set_color (pipeline, &color); +} + +CoglPipelineBlendEnable +_cogl_pipeline_get_blend_enabled (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND_ENABLE); + return authority->blend_enable; +} + +static CoglBool +_cogl_pipeline_blend_enable_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return authority0->blend_enable == authority1->blend_enable ? TRUE : FALSE; +} + +void +_cogl_pipeline_set_blend_enabled (CoglPipeline *pipeline, + CoglPipelineBlendEnable enable) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_BLEND_ENABLE; + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (enable > 1 && + "don't pass TRUE or FALSE to _set_blend_enabled!"); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + if (authority->blend_enable == enable) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->blend_enable = enable; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_blend_enable_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +cogl_pipeline_get_ambient (CoglPipeline *pipeline, + CoglColor *ambient) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + + cogl_color_init_from_4fv (ambient, + authority->big_state->lighting_state.ambient); +} + +void +cogl_pipeline_set_ambient (CoglPipeline *pipeline, + const CoglColor *ambient) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_LIGHTING; + CoglPipeline *authority; + CoglPipelineLightingState *lighting_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + lighting_state = &authority->big_state->lighting_state; + if (cogl_color_equal (ambient, &lighting_state->ambient)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + lighting_state = &pipeline->big_state->lighting_state; + lighting_state->ambient[0] = cogl_color_get_red_float (ambient); + lighting_state->ambient[1] = cogl_color_get_green_float (ambient); + lighting_state->ambient[2] = cogl_color_get_blue_float (ambient); + lighting_state->ambient[3] = cogl_color_get_alpha_float (ambient); + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_lighting_state_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +cogl_pipeline_get_diffuse (CoglPipeline *pipeline, + CoglColor *diffuse) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + + cogl_color_init_from_4fv (diffuse, + authority->big_state->lighting_state.diffuse); +} + +void +cogl_pipeline_set_diffuse (CoglPipeline *pipeline, + const CoglColor *diffuse) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_LIGHTING; + CoglPipeline *authority; + CoglPipelineLightingState *lighting_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + lighting_state = &authority->big_state->lighting_state; + if (cogl_color_equal (diffuse, &lighting_state->diffuse)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + lighting_state = &pipeline->big_state->lighting_state; + lighting_state->diffuse[0] = cogl_color_get_red_float (diffuse); + lighting_state->diffuse[1] = cogl_color_get_green_float (diffuse); + lighting_state->diffuse[2] = cogl_color_get_blue_float (diffuse); + lighting_state->diffuse[3] = cogl_color_get_alpha_float (diffuse); + + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_lighting_state_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +void +cogl_pipeline_set_ambient_and_diffuse (CoglPipeline *pipeline, + const CoglColor *color) +{ + cogl_pipeline_set_ambient (pipeline, color); + cogl_pipeline_set_diffuse (pipeline, color); +} + +void +cogl_pipeline_get_specular (CoglPipeline *pipeline, + CoglColor *specular) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + + cogl_color_init_from_4fv (specular, + authority->big_state->lighting_state.specular); +} + +void +cogl_pipeline_set_specular (CoglPipeline *pipeline, const CoglColor *specular) +{ + CoglPipeline *authority; + CoglPipelineState state = COGL_PIPELINE_STATE_LIGHTING; + CoglPipelineLightingState *lighting_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + lighting_state = &authority->big_state->lighting_state; + if (cogl_color_equal (specular, &lighting_state->specular)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + lighting_state = &pipeline->big_state->lighting_state; + lighting_state->specular[0] = cogl_color_get_red_float (specular); + lighting_state->specular[1] = cogl_color_get_green_float (specular); + lighting_state->specular[2] = cogl_color_get_blue_float (specular); + lighting_state->specular[3] = cogl_color_get_alpha_float (specular); + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_lighting_state_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +float +cogl_pipeline_get_shininess (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + + return authority->big_state->lighting_state.shininess; +} + +void +cogl_pipeline_set_shininess (CoglPipeline *pipeline, + float shininess) +{ + CoglPipeline *authority; + CoglPipelineState state = COGL_PIPELINE_STATE_LIGHTING; + CoglPipelineLightingState *lighting_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + if (shininess < 0.0) + { + g_warning ("Out of range shininess %f supplied for pipeline\n", + shininess); + return; + } + + authority = _cogl_pipeline_get_authority (pipeline, state); + + lighting_state = &authority->big_state->lighting_state; + + if (lighting_state->shininess == shininess) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + lighting_state = &pipeline->big_state->lighting_state; + lighting_state->shininess = shininess; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_lighting_state_equal); +} + +void +cogl_pipeline_get_emission (CoglPipeline *pipeline, + CoglColor *emission) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + + cogl_color_init_from_4fv (emission, + authority->big_state->lighting_state.emission); +} + +void +cogl_pipeline_set_emission (CoglPipeline *pipeline, const CoglColor *emission) +{ + CoglPipeline *authority; + CoglPipelineState state = COGL_PIPELINE_STATE_LIGHTING; + CoglPipelineLightingState *lighting_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + lighting_state = &authority->big_state->lighting_state; + if (cogl_color_equal (emission, &lighting_state->emission)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + lighting_state = &pipeline->big_state->lighting_state; + lighting_state->emission[0] = cogl_color_get_red_float (emission); + lighting_state->emission[1] = cogl_color_get_green_float (emission); + lighting_state->emission[2] = cogl_color_get_blue_float (emission); + lighting_state->emission[3] = cogl_color_get_alpha_float (emission); + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_lighting_state_equal); + + pipeline->dirty_real_blend_enable = TRUE; +} + +static void +_cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, + CoglPipelineAlphaFunc alpha_func) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_ALPHA_FUNC; + CoglPipeline *authority; + CoglPipelineAlphaFuncState *alpha_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + alpha_state = &authority->big_state->alpha_state; + if (alpha_state->alpha_func == alpha_func) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + alpha_state = &pipeline->big_state->alpha_state; + alpha_state->alpha_func = alpha_func; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_alpha_func_state_equal); +} + +static void +_cogl_pipeline_set_alpha_test_function_reference (CoglPipeline *pipeline, + float alpha_reference) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE; + CoglPipeline *authority; + CoglPipelineAlphaFuncState *alpha_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + alpha_state = &authority->big_state->alpha_state; + if (alpha_state->alpha_func_reference == alpha_reference) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + alpha_state = &pipeline->big_state->alpha_state; + alpha_state->alpha_func_reference = alpha_reference; + + _cogl_pipeline_update_authority + (pipeline, authority, state, + _cogl_pipeline_alpha_func_reference_state_equal); +} + +void +cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, + CoglPipelineAlphaFunc alpha_func, + float alpha_reference) +{ + _cogl_pipeline_set_alpha_test_function (pipeline, alpha_func); + _cogl_pipeline_set_alpha_test_function_reference (pipeline, alpha_reference); +} + +CoglPipelineAlphaFunc +cogl_pipeline_get_alpha_test_function (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_ALPHA_FUNC); + + return authority->big_state->alpha_state.alpha_func; +} + +float +cogl_pipeline_get_alpha_test_reference (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0.0f); + + authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE); + + return authority->big_state->alpha_state.alpha_func_reference; +} + +static GLenum +arg_to_gl_blend_factor (CoglBlendStringArgument *arg) +{ + if (arg->source.is_zero) + return GL_ZERO; + if (arg->factor.is_one) + return GL_ONE; + else if (arg->factor.is_src_alpha_saturate) + return GL_SRC_ALPHA_SATURATE; + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) + { + if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_SRC_COLOR; + else + return GL_SRC_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_SRC_ALPHA; + else + return GL_SRC_ALPHA; + } + } + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR) + { + if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_DST_COLOR; + else + return GL_DST_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_DST_ALPHA; + else + return GL_DST_ALPHA; + } + } +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT) + { + if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_CONSTANT_COLOR; + else + return GL_CONSTANT_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_CONSTANT_ALPHA; + else + return GL_CONSTANT_ALPHA; + } + } +#endif + + g_warning ("Unable to determine valid blend factor from blend string\n"); + return GL_ONE; +} + +static void +setup_blend_state (CoglBlendStringStatement *statement, + GLenum *blend_equation, + GLint *blend_src_factor, + GLint *blend_dst_factor) +{ + switch (statement->function->type) + { + case COGL_BLEND_STRING_FUNCTION_ADD: + *blend_equation = GL_FUNC_ADD; + break; + /* TODO - add more */ + default: + g_warning ("Unsupported blend function given"); + *blend_equation = GL_FUNC_ADD; + } + + *blend_src_factor = arg_to_gl_blend_factor (&statement->args[0]); + *blend_dst_factor = arg_to_gl_blend_factor (&statement->args[1]); +} + +CoglBool +cogl_pipeline_set_blend (CoglPipeline *pipeline, + const char *blend_description, + CoglError **error) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_BLEND; + CoglPipeline *authority; + CoglBlendStringStatement statements[2]; + CoglBlendStringStatement *rgb; + CoglBlendStringStatement *a; + int count; + CoglPipelineBlendState *blend_state; + + _COGL_GET_CONTEXT (ctx, FALSE); + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + count = + _cogl_blend_string_compile (blend_description, + COGL_BLEND_STRING_CONTEXT_BLENDING, + statements, + error); + if (!count) + return FALSE; + + if (count == 1) + rgb = a = statements; + else + { + rgb = &statements[0]; + a = &statements[1]; + } + + authority = + _cogl_pipeline_get_authority (pipeline, state); + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + blend_state = &pipeline->big_state->blend_state; + + setup_blend_state (rgb, + &blend_state->blend_equation_rgb, + &blend_state->blend_src_factor_rgb, + &blend_state->blend_dst_factor_rgb); + setup_blend_state (a, + &blend_state->blend_equation_alpha, + &blend_state->blend_src_factor_alpha, + &blend_state->blend_dst_factor_alpha); + + /* If we are the current authority see if we can revert to one of our + * ancestors being the authority */ + if (pipeline == authority && + _cogl_pipeline_get_parent (authority) != NULL) + { + CoglPipeline *parent = _cogl_pipeline_get_parent (authority); + CoglPipeline *old_authority = + _cogl_pipeline_get_authority (parent, state); + + if (_cogl_pipeline_blend_state_equal (authority, old_authority)) + pipeline->differences &= ~state; + } + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (pipeline != authority) + { + pipeline->differences |= state; + _cogl_pipeline_prune_redundant_ancestry (pipeline); + } + + pipeline->dirty_real_blend_enable = TRUE; + + return TRUE; +} + +void +cogl_pipeline_set_blend_constant (CoglPipeline *pipeline, + const CoglColor *constant_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLEND_CONSTANT)) + return; + +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + { + CoglPipelineState state = COGL_PIPELINE_STATE_BLEND; + CoglPipeline *authority; + CoglPipelineBlendState *blend_state; + + authority = _cogl_pipeline_get_authority (pipeline, state); + + blend_state = &authority->big_state->blend_state; + if (cogl_color_equal (constant_color, &blend_state->blend_constant)) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + blend_state = &pipeline->big_state->blend_state; + blend_state->blend_constant = *constant_color; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_blend_state_equal); + + pipeline->dirty_real_blend_enable = TRUE; + } +#endif +} + +CoglHandle +cogl_pipeline_get_user_program (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), COGL_INVALID_HANDLE); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_USER_SHADER); + + return authority->big_state->user_program; +} + +/* XXX: for now we don't mind if the program has vertex shaders + * attached but if we ever make a similar API public we should only + * allow attaching of programs containing fragment shaders. Eventually + * we will have a CoglPipeline abstraction to also cover vertex + * processing. + */ +void +cogl_pipeline_set_user_program (CoglPipeline *pipeline, + CoglHandle program) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_USER_SHADER; + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + if (authority->big_state->user_program == program) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + if (program != COGL_INVALID_HANDLE) + _cogl_pipeline_set_progend (pipeline, COGL_PIPELINE_PROGEND_UNDEFINED); + + /* If we are the current authority see if we can revert to one of our + * ancestors being the authority */ + if (pipeline == authority && + _cogl_pipeline_get_parent (authority) != NULL) + { + CoglPipeline *parent = _cogl_pipeline_get_parent (authority); + CoglPipeline *old_authority = + _cogl_pipeline_get_authority (parent, state); + + if (old_authority->big_state->user_program == program) + pipeline->differences &= ~state; + } + else if (pipeline != authority) + { + /* If we weren't previously the authority on this state then we + * need to extended our differences mask and so it's possible + * that some of our ancestry will now become redundant, so we + * aim to reparent ourselves if that's true... */ + pipeline->differences |= state; + _cogl_pipeline_prune_redundant_ancestry (pipeline); + } + + if (program != COGL_INVALID_HANDLE) + cogl_handle_ref (program); + if (authority == pipeline && + pipeline->big_state->user_program != COGL_INVALID_HANDLE) + cogl_handle_unref (pipeline->big_state->user_program); + pipeline->big_state->user_program = program; + + pipeline->dirty_real_blend_enable = TRUE; +} + +CoglBool +cogl_pipeline_set_depth_state (CoglPipeline *pipeline, + const CoglDepthState *depth_state, + CoglError **error) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_DEPTH; + CoglPipeline *authority; + CoglDepthState *orig_state; + + _COGL_GET_CONTEXT (ctx, FALSE); + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + _COGL_RETURN_VAL_IF_FAIL (depth_state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + orig_state = &authority->big_state->depth_state; + if (orig_state->test_enabled == depth_state->test_enabled && + orig_state->write_enabled == depth_state->write_enabled && + orig_state->test_function == depth_state->test_function && + orig_state->range_near == depth_state->range_near && + orig_state->range_far == depth_state->range_far) + return TRUE; + + if (ctx->driver == COGL_DRIVER_GLES1 && + (depth_state->range_near != 0 || + depth_state->range_far != 1)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "glDepthRange not available on GLES 1"); + return FALSE; + } + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->depth_state = *depth_state; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_depth_state_equal); + + return TRUE; +} + +void +cogl_pipeline_get_depth_state (CoglPipeline *pipeline, + CoglDepthState *state) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_DEPTH); + *state = authority->big_state->depth_state; +} + +CoglColorMask +cogl_pipeline_get_color_mask (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LOGIC_OPS); + + return authority->big_state->logic_ops_state.color_mask; +} + +void +cogl_pipeline_set_color_mask (CoglPipeline *pipeline, + CoglColorMask color_mask) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_LOGIC_OPS; + CoglPipeline *authority; + CoglPipelineLogicOpsState *logic_ops_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + logic_ops_state = &authority->big_state->logic_ops_state; + if (logic_ops_state->color_mask == color_mask) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + logic_ops_state = &pipeline->big_state->logic_ops_state; + logic_ops_state->color_mask = color_mask; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_logic_ops_state_equal); +} + +void +_cogl_pipeline_set_fog_state (CoglPipeline *pipeline, + const CoglPipelineFogState *fog_state) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_FOG; + CoglPipeline *authority; + CoglPipelineFogState *current_fog_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + current_fog_state = &authority->big_state->fog_state; + + if (current_fog_state->enabled == fog_state->enabled && + cogl_color_equal (¤t_fog_state->color, &fog_state->color) && + current_fog_state->mode == fog_state->mode && + current_fog_state->density == fog_state->density && + current_fog_state->z_near == fog_state->z_near && + current_fog_state->z_far == fog_state->z_far) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->fog_state = *fog_state; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_fog_state_equal); +} + +void +cogl_pipeline_set_cull_face_mode (CoglPipeline *pipeline, + CoglPipelineCullFaceMode cull_face_mode) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; + CoglPipeline *authority; + CoglPipelineCullFaceState *cull_face_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + cull_face_state = &authority->big_state->cull_face_state; + + if (cull_face_state->mode == cull_face_mode) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->cull_face_state.mode = cull_face_mode; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_cull_face_state_equal); +} + +void +cogl_pipeline_set_front_face_winding (CoglPipeline *pipeline, + CoglWinding front_winding) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; + CoglPipeline *authority; + CoglPipelineCullFaceState *cull_face_state; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + cull_face_state = &authority->big_state->cull_face_state; + + if (cull_face_state->front_winding == front_winding) + return; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->cull_face_state.front_winding = front_winding; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_cull_face_state_equal); +} + +CoglPipelineCullFaceMode +cogl_pipeline_get_cull_face_mode (CoglPipeline *pipeline) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), + COGL_PIPELINE_CULL_FACE_MODE_NONE); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + return authority->big_state->cull_face_state.mode; +} + +CoglWinding +cogl_pipeline_get_front_face_winding (CoglPipeline *pipeline) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), + COGL_PIPELINE_CULL_FACE_MODE_NONE); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + return authority->big_state->cull_face_state.front_winding; +} + +float +cogl_pipeline_get_point_size (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_POINT_SIZE); + + return authority->big_state->point_size; +} + +static void +_cogl_pipeline_set_non_zero_point_size (CoglPipeline *pipeline, + CoglBool value) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE; + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->non_zero_point_size = !!value; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_non_zero_point_size_equal); +} + +void +cogl_pipeline_set_point_size (CoglPipeline *pipeline, + float point_size) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_POINT_SIZE; + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + if (authority->big_state->point_size == point_size) + return; + + /* Changing the point size may additionally modify + * COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE. */ + + if ((authority->big_state->point_size > 0.0f) != (point_size > 0.0f)) + _cogl_pipeline_set_non_zero_point_size (pipeline, point_size > 0.0f); + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->point_size = point_size; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_point_size_equal); +} + +CoglBool +cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, + CoglBool enable, + CoglError **error) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE; + CoglPipeline *authority; + + _COGL_GET_CONTEXT (ctx, FALSE); + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + enable = !!enable; + + if (authority->big_state->per_vertex_point_size == enable) + return TRUE; + + if (enable && !cogl_has_feature (ctx, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Per-vertex point size is not supported"); + + return FALSE; + } + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->per_vertex_point_size = enable; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_point_size_equal); + + return TRUE; +} + +CoglBool +cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE); + + return authority->big_state->per_vertex_point_size; +} + +static CoglBoxedValue * +_cogl_pipeline_override_uniform (CoglPipeline *pipeline, + int location) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_UNIFORMS; + CoglPipelineUniformsState *uniforms_state; + int override_index; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); + g_return_val_if_fail (location >= 0, NULL); + g_return_val_if_fail (location < ctx->n_uniform_names, NULL); + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + uniforms_state = &pipeline->big_state->uniforms_state; + + /* Count the number of bits that are set below this location. That + should give us the position where our new value should lie */ + override_index = _cogl_bitmask_popcount_upto (&uniforms_state->override_mask, + location); + + _cogl_bitmask_set (&uniforms_state->changed_mask, location, TRUE); + + /* If this pipeline already has an override for this value then we + can just use it directly */ + if (_cogl_bitmask_get (&uniforms_state->override_mask, location)) + return uniforms_state->override_values + override_index; + + /* We need to create a new override value in the right position + within the array. This is pretty inefficient but the hope is that + it will be much more common to modify an existing uniform rather + than modify a new one so it is more important to optimise the + former case. */ + + if (uniforms_state->override_values == NULL) + { + g_assert (override_index == 0); + uniforms_state->override_values = g_new (CoglBoxedValue, 1); + } + else + { + /* We need to grow the array and copy in the old values */ + CoglBoxedValue *old_values = uniforms_state->override_values; + int old_size = _cogl_bitmask_popcount (&uniforms_state->override_mask); + + uniforms_state->override_values = g_new (CoglBoxedValue, old_size + 1); + + /* Copy in the old values leaving a gap for the new value */ + memcpy (uniforms_state->override_values, + old_values, + sizeof (CoglBoxedValue) * override_index); + memcpy (uniforms_state->override_values + override_index + 1, + old_values + override_index, + sizeof (CoglBoxedValue) * (old_size - override_index)); + + g_free (old_values); + } + + _cogl_boxed_value_init (uniforms_state->override_values + override_index); + + _cogl_bitmask_set (&uniforms_state->override_mask, location, TRUE); + + return uniforms_state->override_values + override_index; +} + +void +cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline, + int uniform_location, + float value) +{ + CoglBoxedValue *boxed_value; + + boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); + + _cogl_boxed_value_set_1f (boxed_value, value); +} + +void +cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline, + int uniform_location, + int value) +{ + CoglBoxedValue *boxed_value; + + boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); + + _cogl_boxed_value_set_1i (boxed_value, value); +} + +void +cogl_pipeline_set_uniform_float (CoglPipeline *pipeline, + int uniform_location, + int n_components, + int count, + const float *value) +{ + CoglBoxedValue *boxed_value; + + boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); + + _cogl_boxed_value_set_float (boxed_value, n_components, count, value); +} + +void +cogl_pipeline_set_uniform_int (CoglPipeline *pipeline, + int uniform_location, + int n_components, + int count, + const int *value) +{ + CoglBoxedValue *boxed_value; + + boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); + + _cogl_boxed_value_set_int (boxed_value, n_components, count, value); +} + +void +cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline, + int uniform_location, + int dimensions, + int count, + CoglBool transpose, + const float *value) +{ + CoglBoxedValue *boxed_value; + + boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); + + _cogl_boxed_value_set_matrix (boxed_value, + dimensions, + count, + transpose, + value); +} + +static void +_cogl_pipeline_add_vertex_snippet (CoglPipeline *pipeline, + CoglSnippet *snippet) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_VERTEX_SNIPPETS; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + _cogl_pipeline_snippet_list_add (&pipeline->big_state->vertex_snippets, + snippet); +} + +static void +_cogl_pipeline_add_fragment_snippet (CoglPipeline *pipeline, + CoglSnippet *snippet) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS; + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + _cogl_pipeline_snippet_list_add (&pipeline->big_state->fragment_snippets, + snippet); +} + +void +cogl_pipeline_add_snippet (CoglPipeline *pipeline, + CoglSnippet *snippet) +{ + g_return_if_fail (cogl_is_pipeline (pipeline)); + g_return_if_fail (cogl_is_snippet (snippet)); + g_return_if_fail (snippet->hook < COGL_SNIPPET_FIRST_LAYER_HOOK); + + if (snippet->hook < COGL_SNIPPET_FIRST_PIPELINE_FRAGMENT_HOOK) + _cogl_pipeline_add_vertex_snippet (pipeline, snippet); + else + _cogl_pipeline_add_fragment_snippet (pipeline, snippet); +} + +CoglBool +_cogl_pipeline_has_non_layer_vertex_snippets (CoglPipeline *pipeline) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_VERTEX_SNIPPETS); + + return authority->big_state->vertex_snippets.entries != NULL; +} + +static CoglBool +check_layer_has_vertex_snippet (CoglPipelineLayer *layer, + void *user_data) +{ + unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, state); + CoglBool *found_vertex_snippet = user_data; + + if (authority->big_state->vertex_snippets.entries) + { + *found_vertex_snippet = TRUE; + return FALSE; + } + + return TRUE; +} + +CoglBool +_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline) +{ + CoglBool found_vertex_snippet = FALSE; + + if (_cogl_pipeline_has_non_layer_vertex_snippets (pipeline)) + return TRUE; + + _cogl_pipeline_foreach_layer_internal (pipeline, + check_layer_has_vertex_snippet, + &found_vertex_snippet); + + return found_vertex_snippet; +} + +CoglBool +_cogl_pipeline_has_non_layer_fragment_snippets (CoglPipeline *pipeline) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS); + + return authority->big_state->fragment_snippets.entries != NULL; +} + +static CoglBool +check_layer_has_fragment_snippet (CoglPipelineLayer *layer, + void *user_data) +{ + unsigned long state = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, state); + CoglBool *found_fragment_snippet = user_data; + + if (authority->big_state->fragment_snippets.entries) + { + *found_fragment_snippet = TRUE; + return FALSE; + } + + return TRUE; +} + +CoglBool +_cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline) +{ + CoglBool found_fragment_snippet = FALSE; + + if (_cogl_pipeline_has_non_layer_fragment_snippets (pipeline)) + return TRUE; + + _cogl_pipeline_foreach_layer_internal (pipeline, + check_layer_has_fragment_snippet, + &found_fragment_snippet); + + return found_fragment_snippet; +} + +void +_cogl_pipeline_hash_color_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &authority->color, + _COGL_COLOR_DATA_SIZE); +} + +void +_cogl_pipeline_hash_blend_enable_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + uint8_t blend_enable = authority->blend_enable; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &blend_enable, 1); +} + +void +_cogl_pipeline_hash_lighting_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineLightingState *lighting_state = + &authority->big_state->lighting_state; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, lighting_state, + sizeof (CoglPipelineLightingState)); +} + +void +_cogl_pipeline_hash_alpha_func_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineAlphaFuncState *alpha_state = &authority->big_state->alpha_state; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &alpha_state->alpha_func, + sizeof (alpha_state->alpha_func)); +} + +void +_cogl_pipeline_hash_alpha_func_reference_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineAlphaFuncState *alpha_state = &authority->big_state->alpha_state; + float ref = alpha_state->alpha_func_reference; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &ref, sizeof (float)); +} + +void +_cogl_pipeline_hash_blend_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineBlendState *blend_state = &authority->big_state->blend_state; + unsigned int hash; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!authority->real_blend_enable) + return; + + hash = state->hash; + + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_equation_rgb, + sizeof (blend_state->blend_equation_rgb)); + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_equation_alpha, + sizeof (blend_state->blend_equation_alpha)); + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_src_factor_alpha, + sizeof (blend_state->blend_src_factor_alpha)); + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_dst_factor_alpha, + sizeof (blend_state->blend_dst_factor_alpha)); + + if (blend_state->blend_src_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state->blend_src_factor_rgb == GL_CONSTANT_COLOR || + blend_state->blend_dst_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state->blend_dst_factor_rgb == GL_CONSTANT_COLOR) + { + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_constant, + sizeof (blend_state->blend_constant)); + } + + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_src_factor_rgb, + sizeof (blend_state->blend_src_factor_rgb)); + hash = + _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_dst_factor_rgb, + sizeof (blend_state->blend_dst_factor_rgb)); + + state->hash = hash; +} + +void +_cogl_pipeline_hash_user_shader_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglHandle user_program = authority->big_state->user_program; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &user_program, + sizeof (user_program)); +} + +void +_cogl_pipeline_hash_depth_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglDepthState *depth_state = &authority->big_state->depth_state; + unsigned int hash = state->hash; + + if (depth_state->test_enabled) + { + uint8_t enabled = depth_state->test_enabled; + CoglDepthTestFunction function = depth_state->test_function; + hash = _cogl_util_one_at_a_time_hash (hash, &enabled, sizeof (enabled)); + hash = _cogl_util_one_at_a_time_hash (hash, &function, sizeof (function)); + } + + if (depth_state->write_enabled) + { + uint8_t enabled = depth_state->write_enabled; + float near_val = depth_state->range_near; + float far_val = depth_state->range_far; + hash = _cogl_util_one_at_a_time_hash (hash, &enabled, sizeof (enabled)); + hash = _cogl_util_one_at_a_time_hash (hash, &near_val, sizeof (near_val)); + hash = _cogl_util_one_at_a_time_hash (hash, &far_val, sizeof (far_val)); + } + + state->hash = hash; +} + +void +_cogl_pipeline_hash_fog_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineFogState *fog_state = &authority->big_state->fog_state; + unsigned long hash = state->hash; + + if (!fog_state->enabled) + hash = _cogl_util_one_at_a_time_hash (hash, &fog_state->enabled, + sizeof (fog_state->enabled)); + else + hash = _cogl_util_one_at_a_time_hash (hash, &fog_state, + sizeof (CoglPipelineFogState)); + + state->hash = hash; +} + +void +_cogl_pipeline_hash_non_zero_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglBool non_zero_point_size = authority->big_state->non_zero_point_size; + + state->hash = _cogl_util_one_at_a_time_hash (state->hash, + &non_zero_point_size, + sizeof (non_zero_point_size)); +} + +void +_cogl_pipeline_hash_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + float point_size = authority->big_state->point_size; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &point_size, + sizeof (point_size)); +} + +void +_cogl_pipeline_hash_per_vertex_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglBool per_vertex_point_size = authority->big_state->per_vertex_point_size; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, + &per_vertex_point_size, + sizeof (per_vertex_point_size)); +} + +void +_cogl_pipeline_hash_logic_ops_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineLogicOpsState *logic_ops_state = &authority->big_state->logic_ops_state; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &logic_ops_state->color_mask, + sizeof (CoglColorMask)); +} + +void +_cogl_pipeline_hash_cull_face_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglPipelineCullFaceState *cull_face_state + = &authority->big_state->cull_face_state; + + /* The cull face state is considered equal if two pipelines are both + set to no culling. If the front winding property is ever used for + anything else or the hashing is used not just for drawing then + this would have to change */ + if (cull_face_state->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, + &cull_face_state->mode, + sizeof (CoglPipelineCullFaceMode)); + else + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, + cull_face_state, + sizeof (CoglPipelineCullFaceState)); +} + +void +_cogl_pipeline_hash_uniforms_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + /* This isn't used anywhere yet because the uniform state doesn't + affect program generation. It's quite a hassle to implement so + let's just leave it until something actually needs it */ + g_warn_if_reached (); +} + +void +_cogl_pipeline_compare_uniform_differences (unsigned long *differences, + CoglPipeline *pipeline0, + CoglPipeline *pipeline1) +{ + GSList *head0 = NULL; + GSList *head1 = NULL; + CoglPipeline *node0; + CoglPipeline *node1; + int len0 = 0; + int len1 = 0; + int count; + GSList *common_ancestor0; + GSList *common_ancestor1; + + /* This algorithm is copied from + _cogl_pipeline_compare_differences(). It might be nice to share + the code more */ + + for (node0 = pipeline0; node0; node0 = _cogl_pipeline_get_parent (node0)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head0; + link->data = node0; + head0 = link; + len0++; + } + for (node1 = pipeline1; node1; node1 = _cogl_pipeline_get_parent (node1)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head1; + link->data = node1; + head1 = link; + len1++; + } + + /* NB: There's no point looking at the head entries since we know both + * pipelines must have the same default pipeline as their root node. */ + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + count = MIN (len0, len1) - 1; + while (count--) + { + if (head0->data != head1->data) + break; + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + } + + for (head0 = common_ancestor0->next; head0; head0 = head0->next) + { + node0 = head0->data; + if ((node0->differences & COGL_PIPELINE_STATE_UNIFORMS)) + { + const CoglPipelineUniformsState *uniforms_state = + &node0->big_state->uniforms_state; + _cogl_bitmask_set_flags (&uniforms_state->override_mask, + differences); + } + } + for (head1 = common_ancestor1->next; head1; head1 = head1->next) + { + node1 = head1->data; + if ((node1->differences & COGL_PIPELINE_STATE_UNIFORMS)) + { + const CoglPipelineUniformsState *uniforms_state = + &node1->big_state->uniforms_state; + _cogl_bitmask_set_flags (&uniforms_state->override_mask, + differences); + } + } +} + +void +_cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + _cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets, + &state->hash); +} + +void +_cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + _cogl_pipeline_snippet_list_hash (&authority->big_state->fragment_snippets, + &state->hash); +} + +UNIT_TEST (check_blend_constant_ancestry, + 0 /* no requirements */, + 0 /* no known failures */) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglNode *node; + int pipeline_length = 0; + int i; + + /* Repeatedly making a copy of a pipeline and changing the same + * state (in this case the blend constant) shouldn't cause a long + * chain of pipelines to be created because the redundant ancestry + * should be pruned. */ + + for (i = 0; i < 20; i++) + { + CoglColor color; + CoglPipeline *tmp_pipeline; + + cogl_color_init_from_4f (&color, i / 20.0f, 0.0f, 0.0f, 1.0f); + + tmp_pipeline = cogl_pipeline_copy (pipeline); + cogl_object_unref (pipeline); + pipeline = tmp_pipeline; + + cogl_pipeline_set_blend_constant (pipeline, &color); + } + + for (node = (CoglNode *) pipeline; node; node = node->parent) + pipeline_length++; + + g_assert_cmpint (pipeline_length, <=, 2); + + cogl_object_unref (pipeline); +} + +UNIT_TEST (check_uniform_ancestry, + 0 /* no requirements */, + TEST_KNOWN_FAILURE) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglNode *node; + int pipeline_length = 0; + int i; + + /* Repeatedly making a copy of a pipeline and changing a uniform + * shouldn't cause a long chain of pipelines to be created */ + + for (i = 0; i < 20; i++) + { + CoglPipeline *tmp_pipeline; + int uniform_location; + + tmp_pipeline = cogl_pipeline_copy (pipeline); + cogl_object_unref (pipeline); + pipeline = tmp_pipeline; + + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "a_uniform"); + + cogl_pipeline_set_uniform_1i (pipeline, uniform_location, i); + } + + for (node = (CoglNode *) pipeline; node; node = node->parent) + pipeline_length++; + + g_assert_cmpint (pipeline_length, <=, 2); + + cogl_object_unref (pipeline); +} diff --git a/cogl/cogl/cogl-pipeline-state.h b/cogl/cogl/cogl-pipeline-state.h new file mode 100644 index 000000000..6acbb6dbf --- /dev/null +++ b/cogl/cogl/cogl-pipeline-state.h @@ -0,0 +1,980 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PIPELINE_STATE_H__ +#define __COGL_PIPELINE_STATE_H__ + +#include +#include +#include + +COGL_BEGIN_DECLS + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * cogl_pipeline_set_color: + * @pipeline: A #CoglPipeline object + * @color: The components of the color + * + * Sets the basic color of the pipeline, used when no lighting is enabled. + * + * Note that if you don't add any layers to the pipeline then the color + * will be blended unmodified with the destination; the default blend + * expects premultiplied colors: for example, use (0.5, 0.0, 0.0, 0.5) for + * semi-transparent red. See cogl_color_premultiply(). + * + * The default value is (1.0, 1.0, 1.0, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_color (CoglPipeline *pipeline, + const CoglColor *color); + +/** + * cogl_pipeline_set_color4ub: + * @pipeline: A #CoglPipeline object + * @red: The red component + * @green: The green component + * @blue: The blue component + * @alpha: The alpha component + * + * Sets the basic color of the pipeline, used when no lighting is enabled. + * + * The default value is (0xff, 0xff, 0xff, 0xff) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_color4ub (CoglPipeline *pipeline, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); + +/** + * cogl_pipeline_set_color4f: + * @pipeline: A #CoglPipeline object + * @red: The red component + * @green: The green component + * @blue: The blue component + * @alpha: The alpha component + * + * Sets the basic color of the pipeline, used when no lighting is enabled. + * + * The default value is (1.0, 1.0, 1.0, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_color4f (CoglPipeline *pipeline, + float red, + float green, + float blue, + float alpha); + +/** + * cogl_pipeline_get_color: + * @pipeline: A #CoglPipeline object + * @color: (out): The location to store the color + * + * Retrieves the current pipeline color. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_color (CoglPipeline *pipeline, + CoglColor *color); + +/** + * cogl_pipeline_set_ambient: + * @pipeline: A #CoglPipeline object + * @ambient: The components of the desired ambient color + * + * Sets the pipeline's ambient color, in the standard OpenGL lighting + * model. The ambient color affects the overall color of the object. + * + * Since the diffuse color will be intense when the light hits the surface + * directly, the ambient will be most apparent where the light hits at a + * slant. + * + * The default value is (0.2, 0.2, 0.2, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_ambient (CoglPipeline *pipeline, + const CoglColor *ambient); + +/** + * cogl_pipeline_get_ambient: + * @pipeline: A #CoglPipeline object + * @ambient: The location to store the ambient color + * + * Retrieves the current ambient color for @pipeline + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_ambient (CoglPipeline *pipeline, + CoglColor *ambient); + +/** + * cogl_pipeline_set_diffuse: + * @pipeline: A #CoglPipeline object + * @diffuse: The components of the desired diffuse color + * + * Sets the pipeline's diffuse color, in the standard OpenGL lighting + * model. The diffuse color is most intense where the light hits the + * surface directly - perpendicular to the surface. + * + * The default value is (0.8, 0.8, 0.8, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_diffuse (CoglPipeline *pipeline, + const CoglColor *diffuse); + +/** + * cogl_pipeline_get_diffuse: + * @pipeline: A #CoglPipeline object + * @diffuse: The location to store the diffuse color + * + * Retrieves the current diffuse color for @pipeline + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_diffuse (CoglPipeline *pipeline, + CoglColor *diffuse); + +/** + * cogl_pipeline_set_ambient_and_diffuse: + * @pipeline: A #CoglPipeline object + * @color: The components of the desired ambient and diffuse colors + * + * Conveniently sets the diffuse and ambient color of @pipeline at the same + * time. See cogl_pipeline_set_ambient() and cogl_pipeline_set_diffuse(). + * + * The default ambient color is (0.2, 0.2, 0.2, 1.0) + * + * The default diffuse color is (0.8, 0.8, 0.8, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_ambient_and_diffuse (CoglPipeline *pipeline, + const CoglColor *color); + +/** + * cogl_pipeline_set_specular: + * @pipeline: A #CoglPipeline object + * @specular: The components of the desired specular color + * + * Sets the pipeline's specular color, in the standard OpenGL lighting + * model. The intensity of the specular color depends on the viewport + * position, and is brightest along the lines of reflection. + * + * The default value is (0.0, 0.0, 0.0, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_specular (CoglPipeline *pipeline, + const CoglColor *specular); + +/** + * cogl_pipeline_get_specular: + * @pipeline: A #CoglPipeline object + * @specular: The location to store the specular color + * + * Retrieves the pipelines current specular color. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_specular (CoglPipeline *pipeline, + CoglColor *specular); + +/** + * cogl_pipeline_set_shininess: + * @pipeline: A #CoglPipeline object + * @shininess: The desired shininess; must be >= 0.0 + * + * Sets the shininess of the pipeline, in the standard OpenGL lighting + * model, which determines the size of the specular highlights. A + * higher @shininess will produce smaller highlights which makes the + * object appear more shiny. + * + * The default value is 0.0 + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_shininess (CoglPipeline *pipeline, + float shininess); + +/** + * cogl_pipeline_get_shininess: + * @pipeline: A #CoglPipeline object + * + * Retrieves the pipelines current emission color. + * + * Return value: The pipelines current shininess value + * + * Since: 2.0 + * Stability: Unstable + */ +float +cogl_pipeline_get_shininess (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_emission: + * @pipeline: A #CoglPipeline object + * @emission: The components of the desired emissive color + * + * Sets the pipeline's emissive color, in the standard OpenGL lighting + * model. It will look like the surface is a light source emitting this + * color. + * + * The default value is (0.0, 0.0, 0.0, 1.0) + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_emission (CoglPipeline *pipeline, + const CoglColor *emission); + +/** + * cogl_pipeline_get_emission: + * @pipeline: A #CoglPipeline object + * @emission: The location to store the emission color + * + * Retrieves the pipelines current emission color. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_emission (CoglPipeline *pipeline, + CoglColor *emission); + +/** + * CoglPipelineAlphaFunc: + * @COGL_PIPELINE_ALPHA_FUNC_NEVER: Never let the fragment through. + * @COGL_PIPELINE_ALPHA_FUNC_LESS: Let the fragment through if the incoming + * alpha value is less than the reference alpha value + * @COGL_PIPELINE_ALPHA_FUNC_EQUAL: Let the fragment through if the incoming + * alpha value equals the reference alpha value + * @COGL_PIPELINE_ALPHA_FUNC_LEQUAL: Let the fragment through if the incoming + * alpha value is less than or equal to the reference alpha value + * @COGL_PIPELINE_ALPHA_FUNC_GREATER: Let the fragment through if the incoming + * alpha value is greater than the reference alpha value + * @COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL: Let the fragment through if the incoming + * alpha value does not equal the reference alpha value + * @COGL_PIPELINE_ALPHA_FUNC_GEQUAL: Let the fragment through if the incoming + * alpha value is greater than or equal to the reference alpha value. + * @COGL_PIPELINE_ALPHA_FUNC_ALWAYS: Always let the fragment through. + * + * Alpha testing happens before blending primitives with the framebuffer and + * gives an opportunity to discard fragments based on a comparison with the + * incoming alpha value and a reference alpha value. The #CoglPipelineAlphaFunc + * determines how the comparison is done. + */ +typedef enum { + COGL_PIPELINE_ALPHA_FUNC_NEVER = 0x0200, + COGL_PIPELINE_ALPHA_FUNC_LESS = 0x0201, + COGL_PIPELINE_ALPHA_FUNC_EQUAL = 0x0202, + COGL_PIPELINE_ALPHA_FUNC_LEQUAL = 0x0203, + COGL_PIPELINE_ALPHA_FUNC_GREATER = 0x0204, + COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL = 0x0205, + COGL_PIPELINE_ALPHA_FUNC_GEQUAL = 0x0206, + COGL_PIPELINE_ALPHA_FUNC_ALWAYS = 0x0207 +} CoglPipelineAlphaFunc; +/* NB: these values come from the equivalents in gl.h */ + +/** + * cogl_pipeline_set_alpha_test_function: + * @pipeline: A #CoglPipeline object + * @alpha_func: A @CoglPipelineAlphaFunc constant + * @alpha_reference: A reference point that the chosen alpha function uses + * to compare incoming fragments to. + * + * Before a primitive is blended with the framebuffer, it goes through an + * alpha test stage which lets you discard fragments based on the current + * alpha value. This function lets you change the function used to evaluate + * the alpha channel, and thus determine which fragments are discarded + * and which continue on to the blending stage. + * + * The default is %COGL_PIPELINE_ALPHA_FUNC_ALWAYS + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, + CoglPipelineAlphaFunc alpha_func, + float alpha_reference); + +/** + * cogl_pipeline_get_alpha_test_function: + * @pipeline: A #CoglPipeline object + * + * Return value: The alpha test function of @pipeline. + * + * Since: 2.0 + * Stability: Unstable + */ +CoglPipelineAlphaFunc +cogl_pipeline_get_alpha_test_function (CoglPipeline *pipeline); + +/** + * cogl_pipeline_get_alpha_test_reference: + * @pipeline: A #CoglPipeline object + * + * Return value: The alpha test reference value of @pipeline. + * + * Since: 2.0 + * Stability: Unstable + */ +float +cogl_pipeline_get_alpha_test_reference (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_blend: + * @pipeline: A #CoglPipeline object + * @blend_string: A Cogl blend string + * describing the desired blend function. + * @error: return location for a #CoglError that may report lack of driver + * support if you give separate blend string statements for the alpha + * channel and RGB channels since some drivers, or backends such as + * GLES 1.1, don't support this feature. May be %NULL, in which case a + * warning will be printed out using GLib's logging facilities if an + * error is encountered. + * + * If not already familiar; please refer here + * for an overview of what blend strings are, and their syntax. + * + * Blending occurs after the alpha test function, and combines fragments with + * the framebuffer. + + * Currently the only blend function Cogl exposes is ADD(). So any valid + * blend statements will be of the form: + * + * |[ + * <channel-mask>=ADD(SRC_COLOR*(<factor>), DST_COLOR*(<factor>)) + * ]| + * + * This is the list of source-names usable as blend factors: + * + * SRC_COLOR: The color of the in comming fragment + * DST_COLOR: The color of the framebuffer + * CONSTANT: The constant set via cogl_pipeline_set_blend_constant() + * + * + * The source names can be used according to the + * color-source and factor syntax, + * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would + * "(CONSTANT[RGB])" + * + * These can also be used as factors: + * + * 0: (0, 0, 0, 0) + * 1: (1, 1, 1, 1) + * SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A]) + * + * + * Remember; all color components are normalized to the range [0, 1] + * before computing the result of blending. + * + * + * Blend Strings/1 + * Blend a non-premultiplied source over a destination with + * premultiplied alpha: + * + * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))" + * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + * + * + * + * Blend Strings/2 + * Blend a premultiplied source over a destination with + * premultiplied alpha + * + * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + * + * + * The default blend string is: + * |[ + * RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A])) + * ]| + * + * That gives normal alpha-blending when the calculated color for the pipeline + * is in premultiplied form. + * + * Return value: %TRUE if the blend string was successfully parsed, and the + * described blending is supported by the underlying driver/hardware. If + * there was an error, %FALSE is returned and @error is set accordingly (if + * present). + * + * Since: 2.0 + * Stability: Unstable + */ +CoglBool +cogl_pipeline_set_blend (CoglPipeline *pipeline, + const char *blend_string, + CoglError **error); + +/** + * cogl_pipeline_set_blend_constant: + * @pipeline: A #CoglPipeline object + * @constant_color: The constant color you want + * + * When blending is setup to reference a CONSTANT blend factor then + * blending will depend on the constant set with this function. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_blend_constant (CoglPipeline *pipeline, + const CoglColor *constant_color); + +/** + * cogl_pipeline_set_point_size: + * @pipeline: a #CoglPipeline pointer + * @point_size: the new point size. + * + * Changes the size of points drawn when %COGL_VERTICES_MODE_POINTS is + * used with the attribute buffer API. Note that typically the GPU + * will only support a limited minimum and maximum range of point + * sizes. If the chosen point size is outside that range then the + * nearest value within that range will be used instead. The size of a + * point is in screen space so it will be the same regardless of any + * transformations. + * + * If the point size is set to 0.0 then drawing points with the + * pipeline will have undefined results. This is the default value so + * if an application wants to draw points it must make sure to use a + * pipeline that has an explicit point size set on it. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_point_size (CoglPipeline *pipeline, + float point_size); + +/** + * cogl_pipeline_get_point_size: + * @pipeline: a #CoglPipeline pointer + * + * Get the size of points drawn when %COGL_VERTICES_MODE_POINTS is + * used with the vertex buffer API. + * + * Return value: the point size of the @pipeline. + * + * Since: 2.0 + * Stability: Unstable + */ +float +cogl_pipeline_get_point_size (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_per_vertex_point_size: + * @pipeline: a #CoglPipeline pointer + * @enable: whether to enable per-vertex point size + * @error: a location to store a #CoglError if the change failed + * + * Sets whether to use a per-vertex point size or to use the value set + * by cogl_pipeline_set_point_size(). If per-vertex point size is + * enabled then the point size can be set for an individual point + * either by drawing with a #CoglAttribute with the name + * ‘cogl_point_size_in’ or by writing to the GLSL builtin + * ‘cogl_point_size_out’ from a vertex shader snippet. + * + * If per-vertex point size is enabled and this attribute is not used + * and cogl_point_size_out is not written to then the results are + * undefined. + * + * Note that enabling this will only work if the + * %COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE feature is available. If + * this is not available then the function will return %FALSE and set + * a #CoglError. + * + * Since: 2.0 + * Stability: Unstable + * Return value: %TRUE if the change suceeded or %FALSE otherwise + */ +CoglBool +cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, + CoglBool enable, + CoglError **error); + +/** + * cogl_pipeline_get_per_vertex_point_size: + * @pipeline: a #CoglPipeline pointer + * + * Since: 2.0 + * Stability: Unstable + * Return value: %TRUE if the pipeline has per-vertex point size + * enabled or %FALSE otherwise. The per-vertex point size can be + * enabled with cogl_pipeline_set_per_vertex_point_size(). + */ +CoglBool +cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline); + +/** + * cogl_pipeline_get_color_mask: + * @pipeline: a #CoglPipeline object. + * + * Gets the current #CoglColorMask of which channels would be written to the + * current framebuffer. Each bit set in the mask means that the + * corresponding color would be written. + * + * Returns: A #CoglColorMask + * Since: 1.8 + * Stability: unstable + */ +CoglColorMask +cogl_pipeline_get_color_mask (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_color_mask: + * @pipeline: a #CoglPipeline object. + * @color_mask: A #CoglColorMask of which color channels to write to + * the current framebuffer. + * + * Defines a bit mask of which color channels should be written to the + * current framebuffer. If a bit is set in @color_mask that means that + * color will be written. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_pipeline_set_color_mask (CoglPipeline *pipeline, + CoglColorMask color_mask); + +/** + * cogl_pipeline_get_user_program: + * @pipeline: a #CoglPipeline object. + * + * Queries what user program has been associated with the given + * @pipeline using cogl_pipeline_set_user_program(). + * + * Return value: (transfer none): The current user program or %COGL_INVALID_HANDLE. + * + * Since: 2.0 + * Stability: Unstable + */ +CoglHandle +cogl_pipeline_get_user_program (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_user_program: + * @pipeline: a #CoglPipeline object. + * @program: A #CoglHandle to a linked CoglProgram + * + * Associates a linked CoglProgram with the given pipeline so that the + * program can take full control of vertex and/or fragment processing. + * + * This is an example of how it can be used to associate an ARBfp + * program with a #CoglPipeline: + * |[ + * CoglHandle shader; + * CoglHandle program; + * CoglPipeline *pipeline; + * + * shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); + * cogl_shader_source (shader, + * "!!ARBfp1.0\n" + * "MOV result.color,fragment.color;\n" + * "END\n"); + * cogl_shader_compile (shader); + * + * program = cogl_create_program (); + * cogl_program_attach_shader (program, shader); + * cogl_program_link (program); + * + * pipeline = cogl_pipeline_new (); + * cogl_pipeline_set_user_program (pipeline, program); + * + * cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + * cogl_rectangle (0, 0, 100, 100); + * ]| + * + * It is possibly worth keeping in mind that this API is not part of + * the long term design for how we want to expose shaders to Cogl + * developers (We are planning on deprecating the cogl_program and + * cogl_shader APIs in favour of a "snippet" framework) but in the + * meantime we hope this will handle most practical GLSL and ARBfp + * requirements. + * + * Also remember you need to check for either the + * %COGL_FEATURE_SHADERS_GLSL or %COGL_FEATURE_SHADERS_ARBFP before + * using the cogl_program or cogl_shader API. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_user_program (CoglPipeline *pipeline, + CoglHandle program); + +/** + * cogl_pipeline_set_depth_state: + * @pipeline: A #CoglPipeline object + * @state: A #CoglDepthState struct + * @error: A #CoglError to report failures to setup the given @state. + * + * This commits all the depth state configured in @state struct to the + * given @pipeline. The configuration values are copied into the + * pipeline so there is no requirement to keep the #CoglDepthState + * struct around if you don't need it any more. + * + * Note: Since some platforms do not support the depth range feature + * it is possible for this function to fail and report an @error. + * + * Returns: TRUE if the GPU supports all the given @state else %FALSE + * and returns an @error. + * + * Since: 2.0 + * Stability: Unstable + */ +CoglBool +cogl_pipeline_set_depth_state (CoglPipeline *pipeline, + const CoglDepthState *state, + CoglError **error); + +/** + * cogl_pipeline_get_depth_state: + * @pipeline: A #CoglPipeline object + * @state_out: (out): A destination #CoglDepthState struct + * + * Retrieves the current depth state configuration for the given + * @pipeline as previously set using cogl_pipeline_set_depth_state(). + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_get_depth_state (CoglPipeline *pipeline, + CoglDepthState *state_out); + +/** + * CoglPipelineCullFaceMode: + * @COGL_PIPELINE_CULL_FACE_MODE_NONE: Neither face will be + * culled. This is the default. + * @COGL_PIPELINE_CULL_FACE_MODE_FRONT: Front faces will be culled. + * @COGL_PIPELINE_CULL_FACE_MODE_BACK: Back faces will be culled. + * @COGL_PIPELINE_CULL_FACE_MODE_BOTH: All faces will be culled. + * + * Specifies which faces should be culled. This can be set on a + * pipeline using cogl_pipeline_set_cull_face_mode(). + */ +typedef enum +{ + COGL_PIPELINE_CULL_FACE_MODE_NONE, + COGL_PIPELINE_CULL_FACE_MODE_FRONT, + COGL_PIPELINE_CULL_FACE_MODE_BACK, + COGL_PIPELINE_CULL_FACE_MODE_BOTH +} CoglPipelineCullFaceMode; + +/** + * cogl_pipeline_set_cull_face_mode: + * @pipeline: A #CoglPipeline + * @cull_face_mode: The new mode to set + * + * Sets which faces will be culled when drawing. Face culling can be + * used to increase efficiency by avoiding drawing faces that would + * get overridden. For example, if a model has gaps so that it is + * impossible to see the inside then faces which are facing away from + * the screen will never be seen so there is no point in drawing + * them. This can be acheived by setting the cull face mode to + * %COGL_PIPELINE_CULL_FACE_MODE_BACK. + * + * Face culling relies on the primitives being drawn with a specific + * order to represent which faces are facing inside and outside the + * model. This order can be specified by calling + * cogl_pipeline_set_front_face_winding(). + * + * Status: Unstable + * Since: 2.0 + */ +void +cogl_pipeline_set_cull_face_mode (CoglPipeline *pipeline, + CoglPipelineCullFaceMode cull_face_mode); + +/** + * cogl_pipeline_get_cull_face_mode: + * + * Return value: the cull face mode that was previously set with + * cogl_pipeline_set_cull_face_mode(). + * + * Status: Unstable + * Since: 2.0 + */ +CoglPipelineCullFaceMode +cogl_pipeline_get_cull_face_mode (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_front_face_winding: + * @pipeline: a #CoglPipeline + * @front_winding: the winding order + * + * The order of the vertices within a primitive specifies whether it + * is considered to be front or back facing. This function specifies + * which order is considered to be the front + * faces. %COGL_WINDING_COUNTER_CLOCKWISE sets the front faces to + * primitives with vertices in a counter-clockwise order and + * %COGL_WINDING_CLOCKWISE sets them to be clockwise. The default is + * %COGL_WINDING_COUNTER_CLOCKWISE. + * + * Status: Unstable + * Since: 2.0 + */ +void +cogl_pipeline_set_front_face_winding (CoglPipeline *pipeline, + CoglWinding front_winding); + +/** + * cogl_pipeline_get_front_face_winding: + * @pipeline: a #CoglPipeline + * + * The order of the vertices within a primitive specifies whether it + * is considered to be front or back facing. This function specifies + * which order is considered to be the front + * faces. %COGL_WINDING_COUNTER_CLOCKWISE sets the front faces to + * primitives with vertices in a counter-clockwise order and + * %COGL_WINDING_CLOCKWISE sets them to be clockwise. The default is + * %COGL_WINDING_COUNTER_CLOCKWISE. + * + * Returns: The @pipeline front face winding + * + * Status: Unstable + * Since: 2.0 + */ +CoglWinding +cogl_pipeline_get_front_face_winding (CoglPipeline *pipeline); + +/** + * cogl_pipeline_set_uniform_1f: + * @pipeline: A #CoglPipeline object + * @uniform_location: The uniform's location identifier + * @value: The new value for the uniform + * + * Sets a new value for the uniform at @uniform_location. If this + * pipeline has a user program attached and is later used as a source + * for drawing, the given value will be assigned to the uniform which + * can be accessed from the shader's source. The value for + * @uniform_location should be retrieved from the string name of the + * uniform by calling cogl_pipeline_get_uniform_location(). + * + * This function should be used to set uniforms that are of type + * float. It can also be used to set a single member of a float array + * uniform. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline, + int uniform_location, + float value); + +/** + * cogl_pipeline_set_uniform_1i: + * @pipeline: A #CoglPipeline object + * @uniform_location: The uniform's location identifier + * @value: The new value for the uniform + * + * Sets a new value for the uniform at @uniform_location. If this + * pipeline has a user program attached and is later used as a source + * for drawing, the given value will be assigned to the uniform which + * can be accessed from the shader's source. The value for + * @uniform_location should be retrieved from the string name of the + * uniform by calling cogl_pipeline_get_uniform_location(). + * + * This function should be used to set uniforms that are of type + * int. It can also be used to set a single member of a int array + * uniform or a sampler uniform. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline, + int uniform_location, + int value); + +/** + * cogl_pipeline_set_uniform_float: + * @pipeline: A #CoglPipeline object + * @uniform_location: The uniform's location identifier + * @n_components: The number of components in the corresponding uniform's type + * @count: The number of values to set + * @value: Pointer to the new values to set + * + * Sets new values for the uniform at @uniform_location. If this + * pipeline has a user program attached and is later used as a source + * for drawing, the given values will be assigned to the uniform which + * can be accessed from the shader's source. The value for + * @uniform_location should be retrieved from the string name of the + * uniform by calling cogl_pipeline_get_uniform_location(). + * + * This function can be used to set any floating point type uniform, + * including float arrays and float vectors. For example, to set a + * single vec4 uniform you would use 4 for @n_components and 1 for + * @count. To set an array of 8 float values, you could use 1 for + * @n_components and 8 for @count. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_uniform_float (CoglPipeline *pipeline, + int uniform_location, + int n_components, + int count, + const float *value); + +/** + * cogl_pipeline_set_uniform_int: + * @pipeline: A #CoglPipeline object + * @uniform_location: The uniform's location identifier + * @n_components: The number of components in the corresponding uniform's type + * @count: The number of values to set + * @value: Pointer to the new values to set + * + * Sets new values for the uniform at @uniform_location. If this + * pipeline has a user program attached and is later used as a source + * for drawing, the given values will be assigned to the uniform which + * can be accessed from the shader's source. The value for + * @uniform_location should be retrieved from the string name of the + * uniform by calling cogl_pipeline_get_uniform_location(). + * + * This function can be used to set any integer type uniform, + * including int arrays and int vectors. For example, to set a single + * ivec4 uniform you would use 4 for @n_components and 1 for + * @count. To set an array of 8 int values, you could use 1 for + * @n_components and 8 for @count. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_uniform_int (CoglPipeline *pipeline, + int uniform_location, + int n_components, + int count, + const int *value); + +/** + * cogl_pipeline_set_uniform_matrix: + * @pipeline: A #CoglPipeline object + * @uniform_location: The uniform's location identifier + * @dimensions: The size of the matrix + * @count: The number of values to set + * @transpose: Whether to transpose the matrix + * @value: Pointer to the new values to set + * + * Sets new values for the uniform at @uniform_location. If this + * pipeline has a user program attached and is later used as a source + * for drawing, the given values will be assigned to the uniform which + * can be accessed from the shader's source. The value for + * @uniform_location should be retrieved from the string name of the + * uniform by calling cogl_pipeline_get_uniform_location(). + * + * This function can be used to set any matrix type uniform, including + * matrix arrays. For example, to set a single mat4 uniform you would + * use 4 for @dimensions and 1 for @count. To set an array of 8 + * mat3 values, you could use 3 for @dimensions and 8 for @count. + * + * If @transpose is %FALSE then the matrix is expected to be in + * column-major order or if it is %TRUE then the matrix is in + * row-major order. You can pass a #CoglMatrix by calling by passing + * the result of cogl_matrix_get_array() in @value and setting + * @transpose to %FALSE. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline, + int uniform_location, + int dimensions, + int count, + CoglBool transpose, + const float *value); + +/** + * cogl_pipeline_add_snippet: + * @pipeline: A #CoglPipeline + * @snippet: The #CoglSnippet to add to the vertex processing hook + * + * Adds a shader snippet to @pipeline. The snippet will wrap around or + * replace some part of the pipeline as defined by the hook point in + * @snippet. Note that some hook points are specific to a layer and + * must be added with cogl_pipeline_add_layer_snippet() instead. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_pipeline_add_snippet (CoglPipeline *pipeline, + CoglSnippet *snippet); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +#endif /* __COGL_PIPELINE_STATE_H__ */ diff --git a/cogl/cogl/cogl-pipeline.c b/cogl/cogl/cogl-pipeline.c new file mode 100644 index 000000000..b2fee10d4 --- /dev/null +++ b/cogl/cogl/cogl-pipeline.c @@ -0,0 +1,3174 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-context-private.h" +#include "cogl-object.h" + +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-pipeline-layer-state-private.h" +#include "cogl-texture-private.h" +#include "cogl-blend-string.h" +#include "cogl-journal-private.h" +#include "cogl-color-private.h" +#include "cogl-util.h" +#include "cogl-profile.h" +#include "cogl-depth-state-private.h" +#include "cogl1-context.h" +#include "cogl-gtype-private.h" + +#include +#include +#include + +static void _cogl_pipeline_free (CoglPipeline *tex); +static void recursively_free_layer_caches (CoglPipeline *pipeline); +static CoglBool _cogl_pipeline_is_weak (CoglPipeline *pipeline); + +const CoglPipelineFragend *_cogl_pipeline_fragends[COGL_PIPELINE_N_FRAGENDS]; +const CoglPipelineVertend *_cogl_pipeline_vertends[COGL_PIPELINE_N_VERTENDS]; +/* The 'MAX' here is so that we don't define an empty array when there + are no progends */ +const CoglPipelineProgend * +_cogl_pipeline_progends[MAX (COGL_PIPELINE_N_PROGENDS, 1)]; + +#ifdef COGL_PIPELINE_FRAGEND_GLSL +#include "cogl-pipeline-fragend-glsl-private.h" +#endif +#ifdef COGL_PIPELINE_FRAGEND_ARBFP +#include "cogl-pipeline-fragend-arbfp-private.h" +#endif +#ifdef COGL_PIPELINE_FRAGEND_FIXED +#include "cogl-pipeline-fragend-fixed-private.h" +#endif + +#ifdef COGL_PIPELINE_VERTEND_GLSL +#include "cogl-pipeline-vertend-glsl-private.h" +#endif +#ifdef COGL_PIPELINE_VERTEND_FIXED +#include "cogl-pipeline-vertend-fixed-private.h" +#endif + +#ifdef COGL_PIPELINE_PROGEND_FIXED_ARBFP +#include "cogl-pipeline-progend-fixed-arbfp-private.h" +#endif +#ifdef COGL_PIPELINE_PROGEND_FIXED +#include "cogl-pipeline-progend-fixed-private.h" +#endif +#ifdef COGL_PIPELINE_PROGEND_GLSL +#include "cogl-pipeline-progend-glsl-private.h" +#endif + +COGL_OBJECT_DEFINE (Pipeline, pipeline); +COGL_GTYPE_DEFINE_CLASS (Pipeline, pipeline); + +/* + * This initializes the first pipeline owned by the Cogl context. All + * subsequently instantiated pipelines created via the cogl_pipeline_new() + * API will initially be a copy of this pipeline. + * + * The default pipeline is the topmost ancester for all pipelines. + */ +void +_cogl_pipeline_init_default_pipeline (void) +{ + /* Create new - blank - pipeline */ + CoglPipeline *pipeline = g_slice_new0 (CoglPipeline); + /* XXX: NB: It's important that we zero this to avoid polluting + * pipeline hash values with un-initialized data */ + CoglPipelineBigState *big_state = g_slice_new0 (CoglPipelineBigState); + CoglPipelineLightingState *lighting_state = &big_state->lighting_state; + CoglPipelineAlphaFuncState *alpha_state = &big_state->alpha_state; + CoglPipelineBlendState *blend_state = &big_state->blend_state; + CoglPipelineLogicOpsState *logic_ops_state = &big_state->logic_ops_state; + CoglPipelineCullFaceState *cull_face_state = &big_state->cull_face_state; + CoglPipelineUniformsState *uniforms_state = &big_state->uniforms_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Take this opportunity to setup the backends... */ +#ifdef COGL_PIPELINE_FRAGEND_GLSL + _cogl_pipeline_fragends[COGL_PIPELINE_FRAGEND_GLSL] = + &_cogl_pipeline_glsl_fragend; +#endif +#ifdef COGL_PIPELINE_FRAGEND_ARBFP + _cogl_pipeline_fragends[COGL_PIPELINE_FRAGEND_ARBFP] = + &_cogl_pipeline_arbfp_fragend; +#endif +#ifdef COGL_PIPELINE_FRAGEND_FIXED + _cogl_pipeline_fragends[COGL_PIPELINE_FRAGEND_FIXED] = + &_cogl_pipeline_fixed_fragend; +#endif +#ifdef COGL_PIPELINE_PROGEND_FIXED + _cogl_pipeline_progends[COGL_PIPELINE_PROGEND_FIXED_ARBFP] = + &_cogl_pipeline_fixed_arbfp_progend; +#endif +#ifdef COGL_PIPELINE_PROGEND_FIXED + _cogl_pipeline_progends[COGL_PIPELINE_PROGEND_FIXED] = + &_cogl_pipeline_fixed_progend; +#endif +#ifdef COGL_PIPELINE_PROGEND_GLSL + _cogl_pipeline_progends[COGL_PIPELINE_PROGEND_GLSL] = + &_cogl_pipeline_glsl_progend; +#endif + +#ifdef COGL_PIPELINE_VERTEND_GLSL + _cogl_pipeline_vertends[COGL_PIPELINE_VERTEND_GLSL] = + &_cogl_pipeline_glsl_vertend; +#endif +#ifdef COGL_PIPELINE_VERTEND_FIXED + _cogl_pipeline_vertends[COGL_PIPELINE_VERTEND_FIXED] = + &_cogl_pipeline_fixed_vertend; +#endif + + _cogl_pipeline_node_init (COGL_NODE (pipeline)); + + pipeline->is_weak = FALSE; + pipeline->journal_ref_count = 0; + pipeline->progend = COGL_PIPELINE_PROGEND_UNDEFINED; + pipeline->differences = COGL_PIPELINE_STATE_ALL_SPARSE; + + pipeline->real_blend_enable = FALSE; + + pipeline->blend_enable = COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC; + pipeline->layer_differences = NULL; + pipeline->n_layers = 0; + + pipeline->big_state = big_state; + pipeline->has_big_state = TRUE; + + pipeline->static_breadcrumb = "default pipeline"; + pipeline->has_static_breadcrumb = TRUE; + + pipeline->age = 0; + + /* Use the same defaults as the GL spec... */ + cogl_color_init_from_4ub (&pipeline->color, 0xff, 0xff, 0xff, 0xff); + + /* Use the same defaults as the GL spec... */ + lighting_state->ambient[0] = 0.2; + lighting_state->ambient[1] = 0.2; + lighting_state->ambient[2] = 0.2; + lighting_state->ambient[3] = 1.0; + + lighting_state->diffuse[0] = 0.8; + lighting_state->diffuse[1] = 0.8; + lighting_state->diffuse[2] = 0.8; + lighting_state->diffuse[3] = 1.0; + + lighting_state->specular[0] = 0; + lighting_state->specular[1] = 0; + lighting_state->specular[2] = 0; + lighting_state->specular[3] = 1.0; + + lighting_state->emission[0] = 0; + lighting_state->emission[1] = 0; + lighting_state->emission[2] = 0; + lighting_state->emission[3] = 1.0; + + lighting_state->shininess = 0.0f; + + /* Use the same defaults as the GL spec... */ + alpha_state->alpha_func = COGL_PIPELINE_ALPHA_FUNC_ALWAYS; + alpha_state->alpha_func_reference = 0.0; + + /* Not the same as the GL default, but seems saner... */ +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + blend_state->blend_equation_rgb = GL_FUNC_ADD; + blend_state->blend_equation_alpha = GL_FUNC_ADD; + blend_state->blend_src_factor_alpha = GL_ONE; + blend_state->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA; + cogl_color_init_from_4ub (&blend_state->blend_constant, + 0x00, 0x00, 0x00, 0x00); +#endif + blend_state->blend_src_factor_rgb = GL_ONE; + blend_state->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA; + + big_state->user_program = COGL_INVALID_HANDLE; + + cogl_depth_state_init (&big_state->depth_state); + + big_state->point_size = 0.0f; + + logic_ops_state->color_mask = COGL_COLOR_MASK_ALL; + + cull_face_state->mode = COGL_PIPELINE_CULL_FACE_MODE_NONE; + cull_face_state->front_winding = COGL_WINDING_COUNTER_CLOCKWISE; + + _cogl_bitmask_init (&uniforms_state->override_mask); + _cogl_bitmask_init (&uniforms_state->changed_mask); + uniforms_state->override_values = NULL; + + ctx->default_pipeline = _cogl_pipeline_object_new (pipeline); +} + +static void +_cogl_pipeline_unparent (CoglNode *pipeline) +{ + /* Chain up */ + _cogl_pipeline_node_unparent_real (pipeline); +} + +static CoglBool +recursively_free_layer_caches_cb (CoglNode *node, + void *user_data) +{ + recursively_free_layer_caches (COGL_PIPELINE (node)); + return TRUE; +} + +/* This recursively frees the layers_cache of a pipeline and all of + * its descendants. + * + * For instance if we change a pipelines ->layer_differences list + * then that pipeline and all of its descendants may now have + * incorrect layer caches. */ +static void +recursively_free_layer_caches (CoglPipeline *pipeline) +{ + /* Note: we maintain the invariable that if a pipeline already has a + * dirty layers_cache then so do all of its descendants. */ + if (pipeline->layers_cache_dirty) + return; + + if (G_UNLIKELY (pipeline->layers_cache != pipeline->short_layers_cache)) + g_slice_free1 (sizeof (CoglPipelineLayer *) * pipeline->n_layers, + pipeline->layers_cache); + pipeline->layers_cache_dirty = TRUE; + + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + recursively_free_layer_caches_cb, + NULL); +} + +static void +_cogl_pipeline_set_parent (CoglPipeline *pipeline, + CoglPipeline *parent, + CoglBool take_strong_reference) +{ + /* Chain up */ + _cogl_pipeline_node_set_parent_real (COGL_NODE (pipeline), + COGL_NODE (parent), + _cogl_pipeline_unparent, + take_strong_reference); + + /* Since we just changed the ancestry of the pipeline its cache of + * layers could now be invalid so free it... */ + if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) + recursively_free_layer_caches (pipeline); + + /* If the backends are also caching state along with the pipeline + * that depends on the pipeline's ancestry then it may be notified + * here... + */ + if (pipeline->progend != COGL_PIPELINE_PROGEND_UNDEFINED) + { + const CoglPipelineProgend *progend = + _cogl_pipeline_progends[pipeline->progend]; + const CoglPipelineFragend *fragend = + _cogl_pipeline_fragends[progend->fragend]; + + /* Currently only the fragends ever care about reparenting of + * pipelines... */ + if (fragend->pipeline_set_parent_notify) + fragend->pipeline_set_parent_notify (pipeline); + } +} + +static void +_cogl_pipeline_promote_weak_ancestors (CoglPipeline *strong) +{ + CoglNode *n; + + _COGL_RETURN_IF_FAIL (!strong->is_weak); + + /* If the parent of strong is weak, then we want to promote it by + taking a reference on strong's grandparent. We don't need to take + a reference on strong's direct parent */ + + if (COGL_NODE (strong)->parent == NULL) + return; + + for (n = COGL_NODE (strong)->parent; + /* We can assume that all weak pipelines have a parent */ + COGL_PIPELINE (n)->is_weak; + n = n->parent) + /* 'n' is weak so we take a reference on its parent */ + cogl_object_ref (n->parent); +} + +static void +_cogl_pipeline_revert_weak_ancestors (CoglPipeline *strong) +{ + CoglNode *n; + + _COGL_RETURN_IF_FAIL (!strong->is_weak); + + /* This reverts the effect of calling + _cogl_pipeline_promote_weak_ancestors */ + + if (COGL_NODE (strong)->parent == NULL) + return; + + for (n = COGL_NODE (strong)->parent; + /* We can assume that all weak pipelines have a parent */ + COGL_PIPELINE (n)->is_weak; + n = n->parent) + /* 'n' is weak so we unref its parent */ + cogl_object_unref (n->parent); +} + +/* XXX: Always have an eye out for opportunities to lower the cost of + * cogl_pipeline_copy. */ +static CoglPipeline * +_cogl_pipeline_copy (CoglPipeline *src, CoglBool is_weak) +{ + CoglPipeline *pipeline = g_slice_new (CoglPipeline); + + _cogl_pipeline_node_init (COGL_NODE (pipeline)); + + pipeline->is_weak = is_weak; + + pipeline->journal_ref_count = 0; + + pipeline->differences = 0; + + pipeline->has_big_state = FALSE; + + /* NB: real_blend_enable isn't a sparse property, it's valid for + * every pipeline node so we have fast access to it. */ + pipeline->real_blend_enable = src->real_blend_enable; + pipeline->dirty_real_blend_enable = src->dirty_real_blend_enable; + pipeline->unknown_color_alpha = src->unknown_color_alpha; + + /* XXX: + * consider generalizing the idea of "cached" properties. These + * would still have an authority like other sparse properties but + * you wouldn't have to walk up the ancestry to find the authority + * because the value would be cached directly in each pipeline. + */ + + pipeline->layers_cache_dirty = TRUE; + pipeline->deprecated_get_layers_list = NULL; + pipeline->deprecated_get_layers_list_dirty = TRUE; + + pipeline->progend = src->progend; + + pipeline->has_static_breadcrumb = FALSE; + + pipeline->age = 0; + + _cogl_pipeline_set_parent (pipeline, src, !is_weak); + + /* The semantics for copying a weak pipeline are that we promote all + * weak ancestors to temporarily become strong pipelines until the + * copy is freed. */ + if (!is_weak) + _cogl_pipeline_promote_weak_ancestors (pipeline); + + return _cogl_pipeline_object_new (pipeline); +} + +CoglPipeline * +cogl_pipeline_copy (CoglPipeline *src) +{ + return _cogl_pipeline_copy (src, FALSE); +} + +CoglPipeline * +_cogl_pipeline_weak_copy (CoglPipeline *pipeline, + CoglPipelineDestroyCallback callback, + void *user_data) +{ + CoglPipeline *copy; + CoglPipeline *copy_pipeline; + + copy = _cogl_pipeline_copy (pipeline, TRUE); + copy_pipeline = COGL_PIPELINE (copy); + copy_pipeline->destroy_callback = callback; + copy_pipeline->destroy_data = user_data; + + return copy; +} + +CoglPipeline * +cogl_pipeline_new (CoglContext *context) +{ + CoglPipeline *new; + + new = cogl_pipeline_copy (context->default_pipeline); +#ifdef COGL_DEBUG_ENABLED + _cogl_pipeline_set_static_breadcrumb (new, "new"); +#endif + return new; +} + +static CoglBool +destroy_weak_children_cb (CoglNode *node, + void *user_data) +{ + CoglPipeline *pipeline = COGL_PIPELINE (node); + + if (_cogl_pipeline_is_weak (pipeline)) + { + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + destroy_weak_children_cb, + NULL); + + pipeline->destroy_callback (pipeline, pipeline->destroy_data); + _cogl_pipeline_unparent (COGL_NODE (pipeline)); + } + + return TRUE; +} + +static void +_cogl_pipeline_free (CoglPipeline *pipeline) +{ + if (!pipeline->is_weak) + _cogl_pipeline_revert_weak_ancestors (pipeline); + + /* Weak pipelines don't take a reference on their parent */ + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + destroy_weak_children_cb, + NULL); + + g_assert (_cogl_list_empty (&COGL_NODE (pipeline)->children)); + + _cogl_pipeline_unparent (COGL_NODE (pipeline)); + + if (pipeline->differences & COGL_PIPELINE_STATE_USER_SHADER && + pipeline->big_state->user_program) + cogl_handle_unref (pipeline->big_state->user_program); + + if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) + { + CoglPipelineUniformsState *uniforms_state + = &pipeline->big_state->uniforms_state; + int n_overrides = _cogl_bitmask_popcount (&uniforms_state->override_mask); + int i; + + for (i = 0; i < n_overrides; i++) + _cogl_boxed_value_destroy (uniforms_state->override_values + i); + g_free (uniforms_state->override_values); + + _cogl_bitmask_destroy (&uniforms_state->override_mask); + _cogl_bitmask_destroy (&uniforms_state->changed_mask); + } + + if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) + g_slice_free (CoglPipelineBigState, pipeline->big_state); + + if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) + { + g_list_foreach (pipeline->layer_differences, + (GFunc)cogl_object_unref, NULL); + g_list_free (pipeline->layer_differences); + } + + if (pipeline->differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) + _cogl_pipeline_snippet_list_free (&pipeline->big_state->vertex_snippets); + + if (pipeline->differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) + _cogl_pipeline_snippet_list_free (&pipeline->big_state->fragment_snippets); + + g_list_free (pipeline->deprecated_get_layers_list); + + recursively_free_layer_caches (pipeline); + + g_slice_free (CoglPipeline, pipeline); +} + +CoglBool +_cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + return pipeline->real_blend_enable; +} + +static void +_cogl_pipeline_update_layers_cache (CoglPipeline *pipeline) +{ + /* Note: we assume this pipeline is a _LAYERS authority */ + int n_layers; + CoglPipeline *current; + int layers_found; + + if (G_LIKELY (!pipeline->layers_cache_dirty) || + pipeline->n_layers == 0) + return; + + pipeline->layers_cache_dirty = FALSE; + + n_layers = pipeline->n_layers; + if (G_LIKELY (n_layers < G_N_ELEMENTS (pipeline->short_layers_cache))) + { + pipeline->layers_cache = pipeline->short_layers_cache; + memset (pipeline->layers_cache, 0, + sizeof (CoglPipelineLayer *) * + G_N_ELEMENTS (pipeline->short_layers_cache)); + } + else + { + pipeline->layers_cache = + g_slice_alloc0 (sizeof (CoglPipelineLayer *) * n_layers); + } + + /* Notes: + * + * Each pipeline doesn't have to contain a complete list of the layers + * it depends on, some of them are indirectly referenced through the + * pipeline's ancestors. + * + * pipeline->layer_differences only contains a list of layers that + * have changed in relation to its parent. + * + * pipeline->layer_differences is not maintained sorted, but it + * won't contain multiple layers corresponding to a particular + * ->unit_index. + * + * Some of the ancestor pipelines may reference layers with + * ->unit_index values >= n_layers so we ignore them. + * + * As we ascend through the ancestors we are searching for any + * CoglPipelineLayers corresponding to the texture ->unit_index + * values in the range [0,n_layers-1]. As soon as a pointer is found + * we ignore layers of further ancestors with the same ->unit_index + * values. + */ + + layers_found = 0; + for (current = pipeline; + _cogl_pipeline_get_parent (current); + current = _cogl_pipeline_get_parent (current)) + { + GList *l; + + if (!(current->differences & COGL_PIPELINE_STATE_LAYERS)) + continue; + + for (l = current->layer_differences; l; l = l->next) + { + CoglPipelineLayer *layer = l->data; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + + if (unit_index < n_layers && !pipeline->layers_cache[unit_index]) + { + pipeline->layers_cache[unit_index] = layer; + layers_found++; + if (layers_found == n_layers) + return; + } + } + } + + g_warn_if_reached (); +} + +/* XXX: Be carefull when using this API that the callback given doesn't result + * in the layer cache being invalidated during the iteration! */ +void +_cogl_pipeline_foreach_layer_internal (CoglPipeline *pipeline, + CoglPipelineInternalLayerCallback callback, + void *user_data) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + int n_layers; + int i; + CoglBool cont; + + n_layers = authority->n_layers; + if (n_layers == 0) + return; + + _cogl_pipeline_update_layers_cache (authority); + + for (i = 0, cont = TRUE; i < n_layers && cont == TRUE; i++) + { + _COGL_RETURN_IF_FAIL (authority->layers_cache_dirty == FALSE); + cont = callback (authority->layers_cache[i], user_data); + } +} + +CoglBool +_cogl_pipeline_layer_numbers_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1) +{ + CoglPipeline *authority0 = + _cogl_pipeline_get_authority (pipeline0, COGL_PIPELINE_STATE_LAYERS); + CoglPipeline *authority1 = + _cogl_pipeline_get_authority (pipeline1, COGL_PIPELINE_STATE_LAYERS); + int n_layers = authority0->n_layers; + int i; + + if (authority1->n_layers != n_layers) + return FALSE; + + _cogl_pipeline_update_layers_cache (authority0); + _cogl_pipeline_update_layers_cache (authority1); + + for (i = 0; i < n_layers; i++) + { + CoglPipelineLayer *layer0 = authority0->layers_cache[i]; + CoglPipelineLayer *layer1 = authority1->layers_cache[i]; + + if (layer0->index != layer1->index) + return FALSE; + } + + return TRUE; +} + +CoglBool +_cogl_pipeline_layer_and_unit_numbers_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1) +{ + CoglPipeline *authority0 = + _cogl_pipeline_get_authority (pipeline0, COGL_PIPELINE_STATE_LAYERS); + CoglPipeline *authority1 = + _cogl_pipeline_get_authority (pipeline1, COGL_PIPELINE_STATE_LAYERS); + int n_layers = authority0->n_layers; + int i; + + if (authority1->n_layers != n_layers) + return FALSE; + + _cogl_pipeline_update_layers_cache (authority0); + _cogl_pipeline_update_layers_cache (authority1); + + for (i = 0; i < n_layers; i++) + { + CoglPipelineLayer *layer0 = authority0->layers_cache[i]; + CoglPipelineLayer *layer1 = authority1->layers_cache[i]; + int unit0, unit1; + + if (layer0->index != layer1->index) + return FALSE; + + unit0 = _cogl_pipeline_layer_get_unit_index (layer0); + unit1 = _cogl_pipeline_layer_get_unit_index (layer1); + if (unit0 != unit1) + return FALSE; + } + + return TRUE; +} + +typedef struct +{ + int i; + int *indices; +} AppendLayerIndexState; + +static CoglBool +append_layer_index_cb (CoglPipelineLayer *layer, + void *user_data) +{ + AppendLayerIndexState *state = user_data; + state->indices[state->i++] = layer->index; + return TRUE; +} + +void +cogl_pipeline_foreach_layer (CoglPipeline *pipeline, + CoglPipelineLayerCallback callback, + void *user_data) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + AppendLayerIndexState state; + CoglBool cont; + int i; + + /* XXX: We don't know what the user is going to want to do to the layers + * but any modification of layers can result in the layer graph changing + * which could confuse _cogl_pipeline_foreach_layer_internal(). We first + * get a list of layer indices which will remain valid so long as the + * user doesn't remove layers. */ + + state.i = 0; + state.indices = g_alloca (authority->n_layers * sizeof (int)); + + _cogl_pipeline_foreach_layer_internal (pipeline, + append_layer_index_cb, + &state); + + for (i = 0, cont = TRUE; i < authority->n_layers && cont; i++) + cont = callback (pipeline, state.indices[i], user_data); +} + +static CoglBool +layer_has_alpha_cb (CoglPipelineLayer *layer, void *data) +{ + CoglBool *has_alpha = data; + *has_alpha = _cogl_pipeline_layer_has_alpha (layer); + + /* return FALSE to stop iterating layers if we find any layer + * has alpha ... + * + * FIXME: actually we should never be bailing out because it's + * always possible that a later layer could discard any previous + * alpha! + */ + + return !(*has_alpha); +} + +/* NB: If this pipeline returns FALSE that doesn't mean that the + * pipeline is definitely opaque, it just means that that the + * given changes dont imply transparency. + * + * If you want to find out of the pipeline is opaque then assuming + * this returns FALSE for a set of changes then you can follow + * up + */ +static CoglBool +_cogl_pipeline_change_implies_transparency (CoglPipeline *pipeline, + unsigned int changes, + const CoglColor *override_color, + CoglBool unknown_color_alpha) +{ + /* In the case of a layer state change we need to check everything + * else first since they contribute to the has_alpha status of the + * "PREVIOUS" layer. */ + if (changes & COGL_PIPELINE_STATE_LAYERS) + changes = COGL_PIPELINE_STATE_AFFECTS_BLENDING; + + if (unknown_color_alpha) + return TRUE; + + if ((override_color && cogl_color_get_alpha_byte (override_color) != 0xff)) + return TRUE; + + if (changes & COGL_PIPELINE_STATE_COLOR) + { + CoglColor tmp; + cogl_pipeline_get_color (pipeline, &tmp); + if (cogl_color_get_alpha_byte (&tmp) != 0xff) + return TRUE; + } + + if (changes & COGL_PIPELINE_STATE_USER_SHADER) + { + /* We can't make any assumptions about the alpha channel if the user + * is using an unknown fragment shader. + * + * TODO: check that it isn't just a vertex shader! + */ + if (_cogl_pipeline_get_user_program (pipeline) != COGL_INVALID_HANDLE) + return TRUE; + } + + if (changes & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) + { + if (_cogl_pipeline_has_non_layer_fragment_snippets (pipeline)) + return TRUE; + } + + if (changes & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) + { + if (_cogl_pipeline_has_non_layer_vertex_snippets (pipeline)) + return TRUE; + } + + /* XXX: we should only need to look at these if lighting is enabled + */ + if (changes & COGL_PIPELINE_STATE_LIGHTING) + { + /* XXX: This stuff is showing up in sysprof reports which is + * silly because lighting isn't currently actually supported + * by Cogl except for these token properties. When we actually + * expose lighting support we can avoid these checks when + * lighting is disabled. */ +#if 0 + CoglColor tmp; + cogl_pipeline_get_ambient (pipeline, &tmp); + if (cogl_color_get_alpha_byte (&tmp) != 0xff) + return TRUE; + cogl_pipeline_get_diffuse (pipeline, &tmp); + if (cogl_color_get_alpha_byte (&tmp) != 0xff) + return TRUE; + cogl_pipeline_get_specular (pipeline, &tmp); + if (cogl_color_get_alpha_byte (&tmp) != 0xff) + return TRUE; + cogl_pipeline_get_emission (pipeline, &tmp); + if (cogl_color_get_alpha_byte (&tmp) != 0xff) + return TRUE; +#endif + } + + if (changes & COGL_PIPELINE_STATE_LAYERS) + { + /* has_alpha tracks the alpha status of the GL_PREVIOUS layer. + * To start with that's defined by the pipeline color which + * must be fully opaque if we got this far. */ + CoglBool has_alpha = FALSE; + _cogl_pipeline_foreach_layer_internal (pipeline, + layer_has_alpha_cb, + &has_alpha); + if (has_alpha) + return TRUE; + } + + return FALSE; +} + +static CoglBool +_cogl_pipeline_needs_blending_enabled (CoglPipeline *pipeline, + unsigned int changes, + const CoglColor *override_color, + CoglBool unknown_color_alpha) +{ + CoglPipeline *enable_authority; + CoglPipeline *blend_authority; + CoglPipelineBlendState *blend_state; + CoglPipelineBlendEnable enabled; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BLENDING))) + return FALSE; + + /* We unconditionally check the _BLEND_ENABLE state first because + * all the other changes are irrelevent if blend_enable != _AUTOMATIC + */ + enable_authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND_ENABLE); + + enabled = enable_authority->blend_enable; + if (enabled != COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC) + return enabled == COGL_PIPELINE_BLEND_ENABLE_ENABLED ? TRUE : FALSE; + + blend_authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND); + + blend_state = &blend_authority->big_state->blend_state; + + /* We are trying to identify some cases that are equivalent to + * blending being disable, where the output is simply GL_SRC_COLOR. + * + * Note: we currently only consider a few cases that can be + * optimized but there could be opportunities to special case more + * blend functions later. + */ + + /* As the most common way that we currently use to effectively + * disable blending is to use an equation of + * "RGBA=ADD(SRC_COLOR, 0)" that's the first thing we check + * for... */ + if (blend_state->blend_equation_rgb == GL_FUNC_ADD && + blend_state->blend_equation_alpha == GL_FUNC_ADD && + blend_state->blend_src_factor_alpha == GL_ONE && + blend_state->blend_dst_factor_alpha == GL_ZERO) + { + return FALSE; + } + + /* NB: The default blending equation for Cogl is + * "RGBA=ADD(SRC_COLOR, DST_COLOR * (1-SRC_COLOR[A]))" + * + * Next we check if the default blending equation is being used. If + * so then we follow that by looking for cases where SRC_COLOR[A] == + * 1 since that simplifies "DST_COLOR * (1-SRC_COLOR[A])" to 0 which + * also effectively requires no blending. + */ + + if (blend_state->blend_equation_rgb != GL_FUNC_ADD || + blend_state->blend_equation_alpha != GL_FUNC_ADD) + return TRUE; + + if (blend_state->blend_src_factor_alpha != GL_ONE || + blend_state->blend_dst_factor_alpha != GL_ONE_MINUS_SRC_ALPHA) + return TRUE; + + if (blend_state->blend_src_factor_rgb != GL_ONE || + blend_state->blend_dst_factor_rgb != GL_ONE_MINUS_SRC_ALPHA) + return TRUE; + + /* Given the above constraints, it's now a case of finding any + * SRC_ALPHA that != 1 */ + + if (_cogl_pipeline_change_implies_transparency (pipeline, changes, + override_color, + unknown_color_alpha)) + return TRUE; + + /* At this point, considering just the state that has changed it + * looks like blending isn't needed. If blending was previously + * enabled though it could be that some other state still requires + * that we have blending enabled because it implies transparency. + * In this case we still need to go and check the other state... + * + * XXX: We could explicitly keep track of the mask of state groups + * that are currently causing blending to be enabled so that we + * never have to resort to checking *all* the state and can instead + * always limit the check to those in the mask. + */ + if (pipeline->real_blend_enable) + { + unsigned int other_state = + COGL_PIPELINE_STATE_AFFECTS_BLENDING & ~changes; + if (other_state && + _cogl_pipeline_change_implies_transparency (pipeline, other_state, NULL, FALSE)) + return TRUE; + } + + return FALSE; +} + +void +_cogl_pipeline_set_progend (CoglPipeline *pipeline, int progend) +{ + pipeline->progend = progend; +} + +static void +_cogl_pipeline_copy_differences (CoglPipeline *dest, + CoglPipeline *src, + unsigned long differences) +{ + CoglPipelineBigState *big_state; + + if (differences & COGL_PIPELINE_STATE_COLOR) + dest->color = src->color; + + if (differences & COGL_PIPELINE_STATE_BLEND_ENABLE) + dest->blend_enable = src->blend_enable; + + if (differences & COGL_PIPELINE_STATE_LAYERS) + { + GList *l; + + if (dest->differences & COGL_PIPELINE_STATE_LAYERS && + dest->layer_differences) + { + g_list_foreach (dest->layer_differences, + (GFunc)cogl_object_unref, + NULL); + g_list_free (dest->layer_differences); + } + + for (l = src->layer_differences; l; l = l->next) + { + /* NB: a layer can't have more than one ->owner so we can't + * simply take a references on each of the original + * layer_differences, we have to derive new layers from the + * originals instead. */ + CoglPipelineLayer *copy = _cogl_pipeline_layer_copy (l->data); + _cogl_pipeline_add_layer_difference (dest, copy, FALSE); + cogl_object_unref (copy); + } + + /* Note: we initialize n_layers after adding the layer differences + * since the act of adding the layers will initialize n_layers to 0 + * because dest isn't initially a STATE_LAYERS authority. */ + dest->n_layers = src->n_layers; + } + + if (differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) + { + if (!dest->has_big_state) + { + dest->big_state = g_slice_new (CoglPipelineBigState); + dest->has_big_state = TRUE; + } + big_state = dest->big_state; + } + else + goto check_for_blending_change; + + if (differences & COGL_PIPELINE_STATE_LIGHTING) + { + memcpy (&big_state->lighting_state, + &src->big_state->lighting_state, + sizeof (CoglPipelineLightingState)); + } + + if (differences & COGL_PIPELINE_STATE_ALPHA_FUNC) + big_state->alpha_state.alpha_func = + src->big_state->alpha_state.alpha_func; + + if (differences & COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE) + big_state->alpha_state.alpha_func_reference = + src->big_state->alpha_state.alpha_func_reference; + + if (differences & COGL_PIPELINE_STATE_BLEND) + { + memcpy (&big_state->blend_state, + &src->big_state->blend_state, + sizeof (CoglPipelineBlendState)); + } + + if (differences & COGL_PIPELINE_STATE_USER_SHADER) + { + if (src->big_state->user_program) + big_state->user_program = + cogl_handle_ref (src->big_state->user_program); + else + big_state->user_program = COGL_INVALID_HANDLE; + } + + if (differences & COGL_PIPELINE_STATE_DEPTH) + { + memcpy (&big_state->depth_state, + &src->big_state->depth_state, + sizeof (CoglDepthState)); + } + + if (differences & COGL_PIPELINE_STATE_FOG) + { + memcpy (&big_state->fog_state, + &src->big_state->fog_state, + sizeof (CoglPipelineFogState)); + } + + if (differences & COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE) + big_state->non_zero_point_size = src->big_state->non_zero_point_size; + + if (differences & COGL_PIPELINE_STATE_POINT_SIZE) + big_state->point_size = src->big_state->point_size; + + if (differences & COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE) + big_state->per_vertex_point_size = src->big_state->per_vertex_point_size; + + if (differences & COGL_PIPELINE_STATE_LOGIC_OPS) + { + memcpy (&big_state->logic_ops_state, + &src->big_state->logic_ops_state, + sizeof (CoglPipelineLogicOpsState)); + } + + if (differences & COGL_PIPELINE_STATE_CULL_FACE) + { + memcpy (&big_state->cull_face_state, + &src->big_state->cull_face_state, + sizeof (CoglPipelineCullFaceState)); + } + + if (differences & COGL_PIPELINE_STATE_UNIFORMS) + { + int n_overrides = + _cogl_bitmask_popcount (&src->big_state->uniforms_state.override_mask); + int i; + + big_state->uniforms_state.override_values = + g_malloc (n_overrides * sizeof (CoglBoxedValue)); + + for (i = 0; i < n_overrides; i++) + { + CoglBoxedValue *dst_bv = + big_state->uniforms_state.override_values + i; + const CoglBoxedValue *src_bv = + src->big_state->uniforms_state.override_values + i; + + _cogl_boxed_value_copy (dst_bv, src_bv); + } + + _cogl_bitmask_init (&big_state->uniforms_state.override_mask); + _cogl_bitmask_set_bits (&big_state->uniforms_state.override_mask, + &src->big_state->uniforms_state.override_mask); + + _cogl_bitmask_init (&big_state->uniforms_state.changed_mask); + } + + if (differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) + _cogl_pipeline_snippet_list_copy (&big_state->vertex_snippets, + &src->big_state->vertex_snippets); + + if (differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) + _cogl_pipeline_snippet_list_copy (&big_state->fragment_snippets, + &src->big_state->fragment_snippets); + + /* XXX: we shouldn't bother doing this in most cases since + * _copy_differences is typically used to initialize pipeline state + * by copying it from the current authority, so it's not actually + * *changing* anything. + */ +check_for_blending_change: + if (differences & COGL_PIPELINE_STATE_AFFECTS_BLENDING) + dest->dirty_real_blend_enable = TRUE; + + dest->differences |= differences; +} + +static void +_cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline, + CoglPipelineState change) +{ + CoglPipeline *authority; + + _COGL_RETURN_IF_FAIL (change & COGL_PIPELINE_STATE_ALL_SPARSE); + + if (!(change & COGL_PIPELINE_STATE_MULTI_PROPERTY)) + return; + + authority = _cogl_pipeline_get_authority (pipeline, change); + + switch (change) + { + /* XXX: avoid using a default: label so we get a warning if we + * don't explicitly handle a newly defined state-group here. */ + case COGL_PIPELINE_STATE_COLOR: + case COGL_PIPELINE_STATE_BLEND_ENABLE: + case COGL_PIPELINE_STATE_ALPHA_FUNC: + case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE: + case COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE: + case COGL_PIPELINE_STATE_POINT_SIZE: + case COGL_PIPELINE_STATE_USER_SHADER: + case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE: + case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE: + g_return_if_reached (); + + case COGL_PIPELINE_STATE_LAYERS: + pipeline->n_layers = authority->n_layers; + pipeline->layer_differences = NULL; + break; + case COGL_PIPELINE_STATE_LIGHTING: + { + memcpy (&pipeline->big_state->lighting_state, + &authority->big_state->lighting_state, + sizeof (CoglPipelineLightingState)); + break; + } + case COGL_PIPELINE_STATE_BLEND: + { + memcpy (&pipeline->big_state->blend_state, + &authority->big_state->blend_state, + sizeof (CoglPipelineBlendState)); + break; + } + case COGL_PIPELINE_STATE_DEPTH: + { + memcpy (&pipeline->big_state->depth_state, + &authority->big_state->depth_state, + sizeof (CoglDepthState)); + break; + } + case COGL_PIPELINE_STATE_FOG: + { + memcpy (&pipeline->big_state->fog_state, + &authority->big_state->fog_state, + sizeof (CoglPipelineFogState)); + break; + } + case COGL_PIPELINE_STATE_LOGIC_OPS: + { + memcpy (&pipeline->big_state->logic_ops_state, + &authority->big_state->logic_ops_state, + sizeof (CoglPipelineLogicOpsState)); + break; + } + case COGL_PIPELINE_STATE_CULL_FACE: + { + memcpy (&pipeline->big_state->cull_face_state, + &authority->big_state->cull_face_state, + sizeof (CoglPipelineCullFaceState)); + break; + } + case COGL_PIPELINE_STATE_UNIFORMS: + { + CoglPipelineUniformsState *uniforms_state = + &pipeline->big_state->uniforms_state; + _cogl_bitmask_init (&uniforms_state->override_mask); + _cogl_bitmask_init (&uniforms_state->changed_mask); + uniforms_state->override_values = NULL; + break; + } + case COGL_PIPELINE_STATE_VERTEX_SNIPPETS: + _cogl_pipeline_snippet_list_copy (&pipeline->big_state->vertex_snippets, + &authority->big_state->vertex_snippets); + break; + + case COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS: + _cogl_pipeline_snippet_list_copy (&pipeline->big_state->fragment_snippets, + &authority->big_state-> + fragment_snippets); + break; + } +} + +static CoglBool +check_if_strong_cb (CoglNode *node, void *user_data) +{ + CoglPipeline *pipeline = COGL_PIPELINE (node); + CoglBool *has_strong_child = user_data; + + if (!_cogl_pipeline_is_weak (pipeline)) + { + *has_strong_child = TRUE; + return FALSE; + } + + return TRUE; +} + +static CoglBool +has_strong_children (CoglPipeline *pipeline) +{ + CoglBool has_strong_child = FALSE; + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + check_if_strong_cb, + &has_strong_child); + return has_strong_child; +} + +static CoglBool +_cogl_pipeline_is_weak (CoglPipeline *pipeline) +{ + if (pipeline->is_weak && !has_strong_children (pipeline)) + return TRUE; + else + return FALSE; +} + +static CoglBool +reparent_children_cb (CoglNode *node, + void *user_data) +{ + CoglPipeline *pipeline = COGL_PIPELINE (node); + CoglPipeline *parent = user_data; + + _cogl_pipeline_set_parent (pipeline, parent, TRUE); + + return TRUE; +} + +void +_cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color, + CoglBool from_layer_change) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If primitives have been logged in the journal referencing the + * current state of this pipeline we need to flush the journal + * before we can modify it... */ + if (pipeline->journal_ref_count) + { + CoglBool skip_journal_flush = FALSE; + + /* XXX: We don't usually need to flush the journal just due to + * color changes since pipeline colors are logged in the + * journal's vertex buffer. The exception is when the change in + * color enables or disables the need for blending. */ + if (change == COGL_PIPELINE_STATE_COLOR) + { + CoglBool will_need_blending = + _cogl_pipeline_needs_blending_enabled (pipeline, + change, + new_color, + FALSE); + CoglBool blend_enable = pipeline->real_blend_enable ? TRUE : FALSE; + + if (will_need_blending == blend_enable) + skip_journal_flush = TRUE; + } + + if (!skip_journal_flush) + { + /* XXX: note we use cogl_flush() not _cogl_flush_journal() so + * we will flush *all* known journals that might reference the + * current pipeline. */ + cogl_flush (); + } + } + + /* XXX: + * To simplify things for the vertex, fragment and program backends + * we are careful about how we report STATE_LAYERS changes. + * + * All STATE_LAYERS change notifications with the exception of + * ->n_layers will also result in layer_pre_change_notifications. + * + * For backends that perform code generation for fragment processing + * they typically need to understand the details of how layers get + * changed to determine if they need to repeat codegen. It doesn't + * help them to report a pipeline STATE_LAYERS change for all layer + * changes since it's so broad, they really need to wait for the + * specific layer change to be notified. What does help though is + * to report a STATE_LAYERS change for a change in ->n_layers + * because they typically do need to repeat codegen in that case. + * + * Here we ensure that change notifications against a pipeline or + * against a layer are mutually exclusive as far as fragment, vertex + * and program backends are concerned. + * + * NB: A pipeline can potentially have private state from multiple + * backends associated with it because descendants may cache state + * with an ancestor to maximize the chance that it can later be + * re-used by other descendants and a descendent can require a + * different backend to an ancestor. + */ + if (!from_layer_change) + { + int i; + + for (i = 0; i < COGL_PIPELINE_N_PROGENDS; i++) + { + const CoglPipelineProgend *progend = _cogl_pipeline_progends[i]; + const CoglPipelineVertend *vertend = + _cogl_pipeline_vertends[progend->vertend]; + const CoglPipelineFragend *fragend = + _cogl_pipeline_fragends[progend->fragend]; + + if (vertend->pipeline_pre_change_notify) + vertend->pipeline_pre_change_notify (pipeline, change, new_color); + + /* TODO: make the vertend and fragend implementation details + * of the progend */ + + if (fragend->pipeline_pre_change_notify) + fragend->pipeline_pre_change_notify (pipeline, change, new_color); + + if (progend->pipeline_pre_change_notify) + progend->pipeline_pre_change_notify (pipeline, change, new_color); + } + } + + /* There may be an arbitrary tree of descendants of this pipeline; + * any of which may indirectly depend on this pipeline as the + * authority for some set of properties. (Meaning for example that + * one of its descendants derives its color or blending state from + * this pipeline.) + * + * We can't modify any property that this pipeline is the authority + * for unless we create another pipeline to take its place first and + * make sure descendants reference this new pipeline instead. + */ + + /* The simplest descendants to handle are weak pipelines; we simply + * destroy them if we are modifying a pipeline they depend on. This + * means weak pipelines never cause us to do a copy-on-write. */ + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + destroy_weak_children_cb, + NULL); + + /* If there are still children remaining though we'll need to + * perform a copy-on-write and reparent the dependants as children + * of the copy. */ + if (!_cogl_list_empty (&COGL_NODE (pipeline)->children)) + { + CoglPipeline *new_authority; + + COGL_STATIC_COUNTER (pipeline_copy_on_write_counter, + "pipeline copy on write counter", + "Increments each time a pipeline " + "must be copied to allow modification", + 0 /* no application private data */); + + COGL_COUNTER_INC (_cogl_uprof_context, pipeline_copy_on_write_counter); + + new_authority = + cogl_pipeline_copy (_cogl_pipeline_get_parent (pipeline)); +#ifdef COGL_DEBUG_ENABLED + _cogl_pipeline_set_static_breadcrumb (new_authority, + "pre_change_notify:copy-on-write"); +#endif + + /* We could explicitly walk the descendants, OR together the set + * of differences that we determine this pipeline is the + * authority on and only copy those differences copied across. + * + * Or, if we don't explicitly walk the descendants we at least + * know that pipeline->differences represents the largest set of + * differences that this pipeline could possibly be an authority + * on. + * + * We do the later just because it's simplest, but we might need + * to come back to this later... + */ + _cogl_pipeline_copy_differences (new_authority, pipeline, + pipeline->differences); + + /* Reparent the dependants of pipeline to be children of + * new_authority instead... */ + _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), + reparent_children_cb, + new_authority); + + /* The children will keep the new authority alive so drop the + * reference we got when copying... */ + cogl_object_unref (new_authority); + } + + /* At this point we know we have a pipeline with no strong + * dependants (though we may have some weak children) so we are now + * free to modify the pipeline. */ + + pipeline->age++; + + if (change & COGL_PIPELINE_STATE_NEEDS_BIG_STATE && + !pipeline->has_big_state) + { + pipeline->big_state = g_slice_new (CoglPipelineBigState); + pipeline->has_big_state = TRUE; + } + + /* Note: conceptually we have just been notified that a single + * property value is about to change, but since some state-groups + * contain multiple properties and 'pipeline' is about to take over + * being the authority for the property's corresponding state-group + * we need to maintain the integrity of the other property values + * too. + * + * To ensure this we handle multi-property state-groups by copying + * all the values from the old-authority to the new... + * + * We don't have to worry about non-sparse property groups since + * we never take over being an authority for such properties so + * they automatically maintain integrity. + */ + if (change & COGL_PIPELINE_STATE_ALL_SPARSE && + !(pipeline->differences & change)) + { + _cogl_pipeline_init_multi_property_sparse_state (pipeline, change); + pipeline->differences |= change; + } + + /* Each pipeline has a sorted cache of the layers it depends on + * which will need updating via _cogl_pipeline_update_layers_cache + * if a pipeline's layers are changed. */ + if (change == COGL_PIPELINE_STATE_LAYERS) + recursively_free_layer_caches (pipeline); + + /* If the pipeline being changed is the same as the last pipeline we + * flushed then we keep a track of the changes so we can try to + * minimize redundant OpenGL calls if the same pipeline is flushed + * again. + */ + if (ctx->current_pipeline == pipeline) + ctx->current_pipeline_changes_since_flush |= change; +} + + +void +_cogl_pipeline_add_layer_difference (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglBool inc_n_layers) +{ + _COGL_RETURN_IF_FAIL (layer->owner == NULL); + + layer->owner = pipeline; + cogl_object_ref (layer); + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + /* Note: the last argument to _cogl_pipeline_pre_change_notify is + * needed to differentiate STATE_LAYER changes which don't affect + * the number of layers from those that do. NB: Layer change + * notifications that don't change the number of layers don't get + * forwarded to the fragend. */ + _cogl_pipeline_pre_change_notify (pipeline, + COGL_PIPELINE_STATE_LAYERS, + NULL, + !inc_n_layers); + + pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; + + pipeline->layer_differences = + g_list_prepend (pipeline->layer_differences, layer); + + if (inc_n_layers) + pipeline->n_layers++; + + /* Adding a layer difference may mean this pipeline now overrides + * all of the layers of its parent which might make the parent + * redundant so we should try to prune the hierarchy */ + _cogl_pipeline_prune_redundant_ancestry (pipeline); +} + +void +_cogl_pipeline_remove_layer_difference (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglBool dec_n_layers) +{ + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + /* Note: the last argument to _cogl_pipeline_pre_change_notify is + * needed to differentiate STATE_LAYER changes which don't affect + * the number of layers from those that do. NB: Layer change + * notifications that don't change the number of layers don't get + * forwarded to the fragend. */ + _cogl_pipeline_pre_change_notify (pipeline, + COGL_PIPELINE_STATE_LAYERS, + NULL, + !dec_n_layers); + + /* We only need to remove the layer difference if the pipeline is + * currently the owner. If it is not the owner then one of two + * things will happen to make sure this layer is replaced. If it is + * the last layer being removed then decrementing n_layers will + * ensure that the last layer is skipped. If it is any other layer + * then the subsequent layers will have been shifted down and cause + * it be replaced */ + if (layer->owner == pipeline) + { + layer->owner = NULL; + cogl_object_unref (layer); + + pipeline->layer_differences = + g_list_remove (pipeline->layer_differences, layer); + } + + pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; + + if (dec_n_layers) + pipeline->n_layers--; +} + +static void +_cogl_pipeline_try_reverting_layers_authority (CoglPipeline *authority, + CoglPipeline *old_authority) +{ + if (authority->layer_differences == NULL && + _cogl_pipeline_get_parent (authority)) + { + /* If the previous _STATE_LAYERS authority has the same + * ->n_layers then we can revert to that being the authority + * again. */ + if (!old_authority) + { + old_authority = + _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority), + COGL_PIPELINE_STATE_LAYERS); + } + + if (old_authority->n_layers == authority->n_layers) + authority->differences &= ~COGL_PIPELINE_STATE_LAYERS; + } +} + +void +_cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline, + CoglBool unknown_color_alpha) +{ + CoglPipeline *parent; + unsigned int differences; + + if (pipeline->dirty_real_blend_enable == FALSE && + pipeline->unknown_color_alpha == unknown_color_alpha) + return; + + if (pipeline->dirty_real_blend_enable) + { + differences = pipeline->differences; + + parent = _cogl_pipeline_get_parent (pipeline); + while (parent->dirty_real_blend_enable) + { + differences |= parent->differences; + parent = _cogl_pipeline_get_parent (parent); + } + + /* We initialize the pipeline's real_blend_enable with a known + * reference value from its nearest ancestor with clean state so + * we can then potentially reduce the work involved in checking + * if the pipeline really needs blending itself because we can + * just look at the things that differ between the ancestor and + * this pipeline. + */ + pipeline->real_blend_enable = parent->real_blend_enable; + } + else /* pipeline->unknown_color_alpha != unknown_color_alpha */ + differences = 0; + + /* Note we don't call _cogl_pipeline_pre_change_notify() for this + * state change because ->real_blend_enable is lazily derived from + * other state while flushing the pipeline and we'd need to avoid + * recursion problems in cases where _pre_change_notify() flushes + * the journal if the pipeline is referenced by a journal. + */ + pipeline->real_blend_enable = + _cogl_pipeline_needs_blending_enabled (pipeline, differences, + NULL, unknown_color_alpha); + pipeline->dirty_real_blend_enable = FALSE; + pipeline->unknown_color_alpha = unknown_color_alpha; +} + +typedef struct +{ + int keep_n; + int current_pos; + int first_index_to_prune; +} CoglPipelinePruneLayersInfo; + +static CoglBool +update_prune_layers_info_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglPipelinePruneLayersInfo *state = user_data; + + if (state->current_pos == state->keep_n) + { + state->first_index_to_prune = layer->index; + return FALSE; + } + state->current_pos++; + return TRUE; +} + +void +_cogl_pipeline_prune_to_n_layers (CoglPipeline *pipeline, int n) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + CoglPipelinePruneLayersInfo state; + GList *l; + GList *next; + + if (authority->n_layers <= n) + return; + + /* This call to foreach_layer_internal needs to be done before + * calling pre_change_notify because it recreates the layer cache. + * We are relying on pre_change_notify to clear the layer cache + * before we change the number of layers */ + state.keep_n = n; + state.current_pos = 0; + _cogl_pipeline_foreach_layer_internal (pipeline, + update_prune_layers_info_cb, + &state); + + _cogl_pipeline_pre_change_notify (pipeline, + COGL_PIPELINE_STATE_LAYERS, + NULL, + FALSE); + + pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; + pipeline->n_layers = n; + + /* It's possible that this pipeline owns some of the layers being + * discarded, so we'll need to unlink them... */ + for (l = pipeline->layer_differences; l; l = next) + { + CoglPipelineLayer *layer = l->data; + next = l->next; /* we're modifying the list we're iterating */ + + if (layer->index >= state.first_index_to_prune) + _cogl_pipeline_remove_layer_difference (pipeline, layer, FALSE); + } + + pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; +} + +typedef struct +{ + /* The layer we are trying to find */ + int layer_index; + + /* The layer we find or untouched if not found */ + CoglPipelineLayer *layer; + + /* If the layer can't be found then a new layer should be + * inserted after this texture unit index... */ + int insert_after; + + /* When adding a layer we need the list of layers to shift up + * to a new texture unit. When removing we need the list of + * layers to shift down. + * + * Note: the list isn't sorted */ + CoglPipelineLayer **layers_to_shift; + int n_layers_to_shift; + + /* When adding a layer we don't need a complete list of + * layers_to_shift if we find a layer already corresponding to the + * layer_index. */ + CoglBool ignore_shift_layers_if_found; + +} CoglPipelineLayerInfo; + +/* Returns TRUE once we know there is nothing more to update */ +static CoglBool +update_layer_info (CoglPipelineLayer *layer, + CoglPipelineLayerInfo *layer_info) +{ + if (layer->index == layer_info->layer_index) + { + layer_info->layer = layer; + if (layer_info->ignore_shift_layers_if_found) + return TRUE; + } + else if (layer->index < layer_info->layer_index) + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + layer_info->insert_after = unit_index; + } + else + layer_info->layers_to_shift[layer_info->n_layers_to_shift++] = + layer; + + return FALSE; +} + +/* Returns FALSE to break out of a _foreach_layer () iteration */ +static CoglBool +update_layer_info_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineLayerInfo *layer_info = user_data; + + if (update_layer_info (layer, layer_info)) + return FALSE; /* break */ + else + return TRUE; /* continue */ +} + +static void +_cogl_pipeline_get_layer_info (CoglPipeline *pipeline, + CoglPipelineLayerInfo *layer_info) +{ + /* Note: we are assuming this pipeline is a _STATE_LAYERS authority */ + int n_layers = pipeline->n_layers; + int i; + + /* FIXME: _cogl_pipeline_foreach_layer_internal now calls + * _cogl_pipeline_update_layers_cache anyway so this codepath is + * pointless! */ + if (layer_info->ignore_shift_layers_if_found && + pipeline->layers_cache_dirty) + { + /* The expectation is that callers of + * _cogl_pipeline_get_layer_info are likely to be modifying the + * list of layers associated with a pipeline so in this case + * where we don't have a cache of the layers and we don't + * necessarily have to iterate all the layers of the pipeline we + * use a foreach_layer callback instead of updating the cache + * and iterating that as below. */ + _cogl_pipeline_foreach_layer_internal (pipeline, + update_layer_info_cb, + layer_info); + return; + } + + _cogl_pipeline_update_layers_cache (pipeline); + for (i = 0; i < n_layers; i++) + { + CoglPipelineLayer *layer = pipeline->layers_cache[i]; + + if (update_layer_info (layer, layer_info)) + return; + } +} + +CoglPipelineLayer * +_cogl_pipeline_get_layer_with_flags (CoglPipeline *pipeline, + int layer_index, + CoglPipelineGetLayerFlags flags) +{ + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + CoglPipelineLayerInfo layer_info; + CoglPipelineLayer *layer; + int unit_index; + int i; + CoglContext *ctx; + + /* The layer index of the layer we want info about */ + layer_info.layer_index = layer_index; + + /* If a layer already exists with the given index this will be + * updated. */ + layer_info.layer = NULL; + + /* If a layer isn't found for the given index we'll need to know + * where to insert a new layer. */ + layer_info.insert_after = -1; + + /* If a layer can't be found then we'll need to insert a new layer + * and bump up the texture unit for all layers with an index + * > layer_index. */ + layer_info.layers_to_shift = + g_alloca (sizeof (CoglPipelineLayer *) * authority->n_layers); + layer_info.n_layers_to_shift = 0; + + /* If an exact match is found though we don't need a complete + * list of layers with indices > layer_index... */ + layer_info.ignore_shift_layers_if_found = TRUE; + + _cogl_pipeline_get_layer_info (authority, &layer_info); + + if (layer_info.layer || (flags & COGL_PIPELINE_GET_LAYER_NO_CREATE)) + return layer_info.layer; + + ctx = _cogl_context_get_default (); + + unit_index = layer_info.insert_after + 1; + if (unit_index == 0) + layer = _cogl_pipeline_layer_copy (ctx->default_layer_0); + else + { + CoglPipelineLayer *new; + layer = _cogl_pipeline_layer_copy (ctx->default_layer_n); + new = _cogl_pipeline_set_layer_unit (NULL, layer, unit_index); + /* Since we passed a newly allocated layer we wouldn't expect + * _set_layer_unit() to have to allocate *another* layer. */ + g_assert (new == layer); + } + layer->index = layer_index; + + for (i = 0; i < layer_info.n_layers_to_shift; i++) + { + CoglPipelineLayer *shift_layer = layer_info.layers_to_shift[i]; + + unit_index = _cogl_pipeline_layer_get_unit_index (shift_layer); + _cogl_pipeline_set_layer_unit (pipeline, shift_layer, unit_index + 1); + /* NB: shift_layer may not be writeable so _set_layer_unit() + * will allocate a derived layer internally which will become + * owned by pipeline. Check the return value if we need to do + * anything else with this layer. */ + } + + _cogl_pipeline_add_layer_difference (pipeline, layer, TRUE); + + cogl_object_unref (layer); + + return layer; +} + +void +_cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority, + CoglPipelineLayer *layer) +{ + /* Find the GList link that references the empty layer */ + GList *link = g_list_find (layers_authority->layer_differences, layer); + /* No pipeline directly owns the root node layer so this is safe... */ + CoglPipelineLayer *layer_parent = _cogl_pipeline_layer_get_parent (layer); + CoglPipelineLayerInfo layer_info; + CoglPipeline *old_layers_authority; + + _COGL_RETURN_IF_FAIL (link != NULL); + + /* If the layer's parent doesn't have an owner then we can simply + * take ownership ourselves and drop our reference on the empty + * layer. We don't want to take ownership of the root node layer so + * we also need to verify that the parent has a parent + */ + if (layer_parent->index == layer->index && layer_parent->owner == NULL && + _cogl_pipeline_layer_get_parent (layer_parent) != NULL) + { + cogl_object_ref (layer_parent); + layer_parent->owner = layers_authority; + link->data = layer_parent; + cogl_object_unref (layer); + recursively_free_layer_caches (layers_authority); + return; + } + + /* Now we want to find the layer that would become the authority for + * layer->index if we were to remove layer from + * layers_authority->layer_differences + */ + + /* The layer index of the layer we want info about */ + layer_info.layer_index = layer->index; + + /* If a layer already exists with the given index this will be + * updated. */ + layer_info.layer = NULL; + + /* If a layer can't be found then we'll need to insert a new layer + * and bump up the texture unit for all layers with an index + * > layer_index. */ + layer_info.layers_to_shift = + g_alloca (sizeof (CoglPipelineLayer *) * layers_authority->n_layers); + layer_info.n_layers_to_shift = 0; + + /* If an exact match is found though we don't need a complete + * list of layers with indices > layer_index... */ + layer_info.ignore_shift_layers_if_found = TRUE; + + /* We know the default/root pipeline isn't a LAYERS authority so it's + * safe to use the result of _cogl_pipeline_get_parent (layers_authority) + * without checking it. + */ + old_layers_authority = + _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (layers_authority), + COGL_PIPELINE_STATE_LAYERS); + + _cogl_pipeline_get_layer_info (old_layers_authority, &layer_info); + + /* If layer is the defining layer for the corresponding ->index then + * we can't get rid of it. */ + if (!layer_info.layer) + return; + + /* If the layer that would become the authority for layer->index is + * _cogl_pipeline_layer_get_parent (layer) then we can simply remove the + * layer difference. */ + if (layer_info.layer == _cogl_pipeline_layer_get_parent (layer)) + { + _cogl_pipeline_remove_layer_difference (layers_authority, layer, FALSE); + _cogl_pipeline_try_reverting_layers_authority (layers_authority, + old_layers_authority); + } +} + +typedef struct +{ + int i; + CoglPipeline *pipeline; + unsigned long fallback_layers; +} CoglPipelineFallbackState; + +static CoglBool +fallback_layer_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglPipelineFallbackState *state = user_data; + CoglPipeline *pipeline = state->pipeline; + CoglTextureType texture_type = _cogl_pipeline_layer_get_texture_type (layer); + CoglTexture *texture = NULL; + COGL_STATIC_COUNTER (layer_fallback_counter, + "layer fallback counter", + "Increments each time a layer's texture is " + "forced to a fallback texture", + 0 /* no application private data */); + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (!(state->fallback_layers & 1<i)) + return TRUE; + + COGL_COUNTER_INC (_cogl_uprof_context, layer_fallback_counter); + + switch (texture_type) + { + case COGL_TEXTURE_TYPE_2D: + texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); + break; + + case COGL_TEXTURE_TYPE_3D: + texture = COGL_TEXTURE (ctx->default_gl_texture_3d_tex); + break; + + case COGL_TEXTURE_TYPE_RECTANGLE: + texture = COGL_TEXTURE (ctx->default_gl_texture_rect_tex); + break; + } + + if (texture == NULL) + { + g_warning ("We don't have a fallback texture we can use to fill " + "in for an invalid pipeline layer, since it was " + "using an unsupported texture target "); + /* might get away with this... */ + texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); + } + + cogl_pipeline_set_layer_texture (pipeline, layer->index, texture); + + state->i++; + + return TRUE; +} + +typedef struct +{ + CoglPipeline *pipeline; + CoglTexture *texture; +} CoglPipelineOverrideLayerState; + +static CoglBool +override_layer_texture_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglPipelineOverrideLayerState *state = user_data; + + cogl_pipeline_set_layer_texture (state->pipeline, + layer->index, + state->texture); + + return TRUE; +} + +void +_cogl_pipeline_apply_overrides (CoglPipeline *pipeline, + CoglPipelineFlushOptions *options) +{ + COGL_STATIC_COUNTER (apply_overrides_counter, + "pipeline overrides counter", + "Increments each time we have to apply " + "override options to a pipeline", + 0 /* no application private data */); + + COGL_COUNTER_INC (_cogl_uprof_context, apply_overrides_counter); + + if (options->flags & COGL_PIPELINE_FLUSH_DISABLE_MASK) + { + int i; + + /* NB: we can assume that once we see one bit to disable + * a layer, all subsequent layers are also disabled. */ + for (i = 0; i < 32 && options->disable_layers & (1<flags & COGL_PIPELINE_FLUSH_FALLBACK_MASK) + { + CoglPipelineFallbackState state; + + state.i = 0; + state.pipeline = pipeline; + state.fallback_layers = options->fallback_layers; + + _cogl_pipeline_foreach_layer_internal (pipeline, + fallback_layer_cb, + &state); + } + + if (options->flags & COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE) + { + CoglPipelineOverrideLayerState state; + + _cogl_pipeline_prune_to_n_layers (pipeline, 1); + + /* NB: we are overriding the first layer, but we don't know + * the user's given layer_index, which is why we use + * _cogl_pipeline_foreach_layer_internal() here even though we know + * there's only one layer. */ + state.pipeline = pipeline; + state.texture = options->layer0_override_texture; + _cogl_pipeline_foreach_layer_internal (pipeline, + override_layer_texture_cb, + &state); + } +} + +static CoglBool +_cogl_pipeline_layers_equal (CoglPipeline *authority0, + CoglPipeline *authority1, + unsigned long differences, + CoglPipelineEvalFlags flags) +{ + int i; + + if (authority0->n_layers != authority1->n_layers) + return FALSE; + + _cogl_pipeline_update_layers_cache (authority0); + _cogl_pipeline_update_layers_cache (authority1); + + for (i = 0; i < authority0->n_layers; i++) + { + if (!_cogl_pipeline_layer_equal (authority0->layers_cache[i], + authority1->layers_cache[i], + differences, + flags)) + return FALSE; + } + return TRUE; +} + +/* Determine the mask of differences between two pipelines */ +unsigned long +_cogl_pipeline_compare_differences (CoglPipeline *pipeline0, + CoglPipeline *pipeline1) +{ + GSList *head0 = NULL; + GSList *head1 = NULL; + CoglPipeline *node0; + CoglPipeline *node1; + int len0 = 0; + int len1 = 0; + int count; + GSList *common_ancestor0; + GSList *common_ancestor1; + unsigned long pipelines_difference = 0; + + /* Algorithm: + * + * 1) Walk the ancestors of each pipeline to the root node, adding a + * pointer to each ancester node to two linked lists + * + * 2) Compare the lists to find the nodes where they start to + * differ marking the common_ancestor node for each list. + * + * 3) For each list now iterate starting after the common_ancestor + * nodes ORing each nodes ->difference mask into the final + * differences mask. + */ + + for (node0 = pipeline0; node0; node0 = _cogl_pipeline_get_parent (node0)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head0; + link->data = node0; + head0 = link; + len0++; + } + for (node1 = pipeline1; node1; node1 = _cogl_pipeline_get_parent (node1)) + { + GSList *link = alloca (sizeof (GSList)); + link->next = head1; + link->data = node1; + head1 = link; + len1++; + } + + /* NB: There's no point looking at the head entries since we know both + * pipelines must have the same default pipeline as their root node. */ + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + count = MIN (len0, len1) - 1; + while (count--) + { + if (head0->data != head1->data) + break; + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + } + + for (head0 = common_ancestor0->next; head0; head0 = head0->next) + { + node0 = head0->data; + pipelines_difference |= node0->differences; + } + for (head1 = common_ancestor1->next; head1; head1 = head1->next) + { + node1 = head1->data; + pipelines_difference |= node1->differences; + } + + return pipelines_difference; +} + +static void +_cogl_pipeline_resolve_authorities (CoglPipeline *pipeline, + unsigned long differences, + CoglPipeline **authorities) +{ + unsigned long remaining = differences; + CoglPipeline *authority = pipeline; + + do + { + unsigned long found = authority->differences & remaining; + int i; + + if (found == 0) + continue; + + for (i = 0; TRUE; i++) + { + unsigned long state = (1L< found) + break; + } + + remaining &= ~found; + if (remaining == 0) + return; + } + while ((authority = _cogl_pipeline_get_parent (authority))); + + g_assert (remaining == 0); +} + +/* Comparison of two arbitrary pipelines is done by: + * 1) walking up the parents of each pipeline until a common + * ancestor is found, and at each step ORing together the + * difference masks. + * + * 2) using the final difference mask to determine which state + * groups to compare. + * + * This is used, for example, by the Cogl journal to compare pipelines so that + * it can split up geometry that needs different OpenGL state. + * + * XXX: When comparing texture layers, _cogl_pipeline_equal will actually + * compare the underlying GL texture handle that the Cogl texture uses so that + * atlas textures and sub textures will be considered equal if they point to + * the same texture. This is useful for comparing pipelines in the journal but + * it means that _cogl_pipeline_equal doesn't strictly compare whether the + * pipelines are the same. If we needed those semantics we could perhaps add + * another function or some flags to control the behaviour. + * + * XXX: Similarly when comparing the wrap modes, + * COGL_PIPELINE_WRAP_MODE_AUTOMATIC is considered to be the same as + * COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE because once they get to the + * journal stage they act exactly the same. + */ +CoglBool +_cogl_pipeline_equal (CoglPipeline *pipeline0, + CoglPipeline *pipeline1, + unsigned int differences, + unsigned long layer_differences, + CoglPipelineEvalFlags flags) +{ + unsigned long pipelines_difference; + CoglPipeline *authorities0[COGL_PIPELINE_STATE_SPARSE_COUNT]; + CoglPipeline *authorities1[COGL_PIPELINE_STATE_SPARSE_COUNT]; + int bit; + CoglBool ret; + + COGL_STATIC_TIMER (pipeline_equal_timer, + "Mainloop", /* parent */ + "_cogl_pipeline_equal", + "The time spent comparing cogl pipelines", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, pipeline_equal_timer); + + if (pipeline0 == pipeline1) + { + ret = TRUE; + goto done; + } + + ret = FALSE; + + _cogl_pipeline_update_real_blend_enable (pipeline0, FALSE); + _cogl_pipeline_update_real_blend_enable (pipeline1, FALSE); + + /* First check non-sparse properties */ + + if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE && + pipeline0->real_blend_enable != pipeline1->real_blend_enable) + goto done; + + /* Then check sparse properties */ + + pipelines_difference = + _cogl_pipeline_compare_differences (pipeline0, pipeline1); + + /* Only compare the sparse state groups requested by the caller... */ + pipelines_difference &= differences; + + _cogl_pipeline_resolve_authorities (pipeline0, + pipelines_difference, + authorities0); + _cogl_pipeline_resolve_authorities (pipeline1, + pipelines_difference, + authorities1); + + COGL_FLAGS_FOREACH_START (&pipelines_difference, 1, bit) + { + /* XXX: We considered having an array of callbacks for each state index + * that we'd call here but decided that this way the compiler is more + * likely going to be able to in-line the comparison functions and use + * the index to jump straight to the required code. */ + switch ((CoglPipelineStateIndex)bit) + { + case COGL_PIPELINE_STATE_COLOR_INDEX: + if (!cogl_color_equal (&authorities0[bit]->color, + &authorities1[bit]->color)) + goto done; + break; + case COGL_PIPELINE_STATE_LIGHTING_INDEX: + if (!_cogl_pipeline_lighting_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX: + if (!_cogl_pipeline_alpha_func_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX: + if (!_cogl_pipeline_alpha_func_reference_state_equal ( + authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_BLEND_INDEX: + /* We don't need to compare the detailed blending state if we know + * blending is disabled for both pipelines. */ + if (pipeline0->real_blend_enable) + { + if (!_cogl_pipeline_blend_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + } + break; + case COGL_PIPELINE_STATE_DEPTH_INDEX: + if (!_cogl_pipeline_depth_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_FOG_INDEX: + if (!_cogl_pipeline_fog_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_CULL_FACE_INDEX: + if (!_cogl_pipeline_cull_face_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX: + if (!_cogl_pipeline_non_zero_point_size_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_POINT_SIZE_INDEX: + if (!_cogl_pipeline_point_size_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX: + if (!_cogl_pipeline_per_vertex_point_size_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_LOGIC_OPS_INDEX: + if (!_cogl_pipeline_logic_ops_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_USER_SHADER_INDEX: + if (!_cogl_pipeline_user_shader_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_UNIFORMS_INDEX: + if (!_cogl_pipeline_uniforms_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX: + if (!_cogl_pipeline_vertex_snippets_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX: + if (!_cogl_pipeline_fragment_snippets_state_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; + case COGL_PIPELINE_STATE_LAYERS_INDEX: + { + if (!_cogl_pipeline_layers_equal (authorities0[bit], + authorities1[bit], + layer_differences, + flags)) + goto done; + break; + } + + case COGL_PIPELINE_STATE_BLEND_ENABLE_INDEX: + case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX: + case COGL_PIPELINE_STATE_COUNT: + g_warn_if_reached (); + } + } + COGL_FLAGS_FOREACH_END; + + ret = TRUE; +done: + COGL_TIMER_STOP (_cogl_uprof_context, pipeline_equal_timer); + return ret; +} + +void +_cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline) +{ + CoglPipeline *new_parent = _cogl_pipeline_get_parent (pipeline); + + /* Before considering pruning redundant ancestry we check if this + * pipeline is an authority for layer state and if so only consider + * reparenting if it *owns* all the layers it depends on. NB: A + * pipeline can be be a STATE_LAYERS authority but it may still + * defer to its ancestors to define the state for some of its + * layers. + * + * For example a pipeline that derives from a parent with 5 layers + * can become a STATE_LAYERS authority by simply changing it's + * ->n_layers count to 4 and in that case it can still defer to its + * ancestors to define the state of those 4 layers. + * + * If a pipeline depends on any ancestors for layer state then we + * immediatly bail out. + */ + if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) + { + if (pipeline->n_layers != g_list_length (pipeline->layer_differences)) + return; + } + + /* walk up past ancestors that are now redundant and potentially + * reparent the pipeline. */ + while (_cogl_pipeline_get_parent (new_parent) && + (new_parent->differences | pipeline->differences) == + pipeline->differences) + new_parent = _cogl_pipeline_get_parent (new_parent); + + if (new_parent != _cogl_pipeline_get_parent (pipeline)) + { + CoglBool is_weak = _cogl_pipeline_is_weak (pipeline); + _cogl_pipeline_set_parent (pipeline, new_parent, is_weak ? FALSE : TRUE); + } +} + +void +_cogl_pipeline_update_authority (CoglPipeline *pipeline, + CoglPipeline *authority, + CoglPipelineState state, + CoglPipelineStateComparitor comparitor) +{ + /* If we are the current authority see if we can revert to one of + * our ancestors being the authority */ + if (pipeline == authority && + _cogl_pipeline_get_parent (authority) != NULL) + { + CoglPipeline *parent = _cogl_pipeline_get_parent (authority); + CoglPipeline *old_authority = + _cogl_pipeline_get_authority (parent, state); + + if (comparitor (authority, old_authority)) + pipeline->differences &= ~state; + } + else if (pipeline != authority) + { + /* If we weren't previously the authority on this state then we + * need to extended our differences mask and so it's possible + * that some of our ancestry will now become redundant, so we + * aim to reparent ourselves if that's true... */ + pipeline->differences |= state; + _cogl_pipeline_prune_redundant_ancestry (pipeline); + } +} + +CoglBool +_cogl_pipeline_get_fog_enabled (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_FOG); + return authority->big_state->fog_state.enabled; +} + +unsigned long +_cogl_pipeline_get_age (CoglPipeline *pipeline) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0); + + return pipeline->age; +} + +void +cogl_pipeline_remove_layer (CoglPipeline *pipeline, int layer_index) +{ + CoglPipeline *authority; + CoglPipelineLayerInfo layer_info; + int i; + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + + /* The layer index of the layer we want info about */ + layer_info.layer_index = layer_index; + + /* This will be updated with a reference to the layer being removed + * if it can be found. */ + layer_info.layer = NULL; + + /* This will be filled in with a list of layers that need to be + * dropped down to a lower texture unit to fill the gap of the + * removed layer. */ + layer_info.layers_to_shift = + g_alloca (sizeof (CoglPipelineLayer *) * authority->n_layers); + layer_info.n_layers_to_shift = 0; + + /* Unlike when we query layer info when adding a layer we must + * always have a complete layers_to_shift list... */ + layer_info.ignore_shift_layers_if_found = FALSE; + + _cogl_pipeline_get_layer_info (authority, &layer_info); + + if (layer_info.layer == NULL) + return; + + for (i = 0; i < layer_info.n_layers_to_shift; i++) + { + CoglPipelineLayer *shift_layer = layer_info.layers_to_shift[i]; + int unit_index = _cogl_pipeline_layer_get_unit_index (shift_layer); + _cogl_pipeline_set_layer_unit (pipeline, shift_layer, unit_index - 1); + /* NB: shift_layer may not be writeable so _set_layer_unit() + * will allocate a derived layer internally which will become + * owned by pipeline. Check the return value if we need to do + * anything else with this layer. */ + } + + _cogl_pipeline_remove_layer_difference (pipeline, layer_info.layer, TRUE); + _cogl_pipeline_try_reverting_layers_authority (pipeline, NULL); + + pipeline->dirty_real_blend_enable = TRUE; +} + +static CoglBool +prepend_layer_to_list_cb (CoglPipelineLayer *layer, + void *user_data) +{ + GList **layers = user_data; + + *layers = g_list_prepend (*layers, layer); + return TRUE; +} + +/* TODO: deprecate this API and replace it with + * cogl_pipeline_foreach_layer + * TODO: update the docs to note that if the user modifies any layers + * then the list may become invalid. + */ +const GList * +_cogl_pipeline_get_layers (CoglPipeline *pipeline) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), NULL); + + if (!pipeline->deprecated_get_layers_list_dirty) + g_list_free (pipeline->deprecated_get_layers_list); + + pipeline->deprecated_get_layers_list = NULL; + + _cogl_pipeline_foreach_layer_internal (pipeline, + prepend_layer_to_list_cb, + &pipeline->deprecated_get_layers_list); + pipeline->deprecated_get_layers_list = + g_list_reverse (pipeline->deprecated_get_layers_list); + + pipeline->deprecated_get_layers_list_dirty = 0; + + return pipeline->deprecated_get_layers_list; +} + +int +cogl_pipeline_get_n_layers (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), 0); + + authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); + + return authority->n_layers; +} + +void +_cogl_pipeline_pre_paint_for_layer (CoglPipeline *pipeline, + int layer_id) +{ + CoglPipelineLayer *layer = _cogl_pipeline_get_layer (pipeline, layer_id); + _cogl_pipeline_layer_pre_paint (layer); +} + +/* While a pipeline is referenced by the Cogl journal we can not allow + * modifications, so this gives us a mechanism to track journal + * references separately */ +CoglPipeline * +_cogl_pipeline_journal_ref (CoglPipeline *pipeline) +{ + pipeline->journal_ref_count++; + return cogl_object_ref (pipeline); +} + +void +_cogl_pipeline_journal_unref (CoglPipeline *pipeline) +{ + pipeline->journal_ref_count--; + cogl_object_unref (pipeline); +} + +#ifdef COGL_DEBUG_ENABLED +void +_cogl_pipeline_apply_legacy_state (CoglPipeline *pipeline) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* It was a mistake that we ever copied the OpenGL style API for + * associating these things directly with the context when we + * originally wrote Cogl. Until the corresponding deprecated APIs + * can be removed though we now shoehorn the state changes through + * the cogl_pipeline API instead. + */ + + /* A program explicitly set on the pipeline has higher precedence than + * one associated with the context using cogl_program_use() */ + if (ctx->current_program && + cogl_pipeline_get_user_program (pipeline) == COGL_INVALID_HANDLE) + cogl_pipeline_set_user_program (pipeline, ctx->current_program); + + if (ctx->legacy_depth_test_enabled) + { + CoglDepthState depth_state; + cogl_depth_state_init (&depth_state); + cogl_depth_state_set_test_enabled (&depth_state, TRUE); + cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL); + } + + if (ctx->legacy_fog_state.enabled) + _cogl_pipeline_set_fog_state (pipeline, &ctx->legacy_fog_state); + + if (ctx->legacy_backface_culling_enabled) + cogl_pipeline_set_cull_face_mode (pipeline, + COGL_PIPELINE_CULL_FACE_MODE_BACK); +} + +void +_cogl_pipeline_set_static_breadcrumb (CoglPipeline *pipeline, + const char *breadcrumb) +{ + pipeline->has_static_breadcrumb = TRUE; + pipeline->static_breadcrumb = breadcrumb; +} +#endif + +typedef void (*LayerStateHashFunction) (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state); + +static LayerStateHashFunction +layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; + +/* XXX: We don't statically initialize the array of hash functions, so + * we won't get caught out by later re-indexing the groups for some + * reason. */ +void +_cogl_pipeline_init_layer_state_hash_functions (void) +{ + CoglPipelineLayerStateIndex _index; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_UNIT_INDEX] = + _cogl_pipeline_layer_hash_unit_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX] = + _cogl_pipeline_layer_hash_texture_type_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX] = + _cogl_pipeline_layer_hash_texture_data_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX] = + _cogl_pipeline_layer_hash_sampler_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX] = + _cogl_pipeline_layer_hash_combine_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX] = + _cogl_pipeline_layer_hash_combine_constant_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX] = + _cogl_pipeline_layer_hash_user_matrix_state; + _index = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX; + layer_state_hash_functions[_index] = + _cogl_pipeline_layer_hash_point_sprite_state; + _index = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX; + layer_state_hash_functions[_index] = + _cogl_pipeline_layer_hash_point_sprite_state; + _index = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX; + layer_state_hash_functions[_index] = + _cogl_pipeline_layer_hash_fragment_snippets_state; + + { + /* So we get a big error if we forget to update this code! */ + _COGL_STATIC_ASSERT (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 10, + "Don't forget to install a hash function for new " + "pipeline state and update assert at end of " + "_cogl_pipeline_init_state_hash_functions"); + } +} + +static CoglBool +_cogl_pipeline_hash_layer_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineHashState *state = user_data; + unsigned long differences = state->layer_differences; + CoglPipelineLayer *authorities[COGL_PIPELINE_LAYER_STATE_COUNT]; + unsigned long mask; + int i; + + /* Theoretically we would hash non-sparse layer state here but + * currently layers don't have any. */ + + /* XXX: we resolve all the authorities here - not just those + * corresponding to hash_state->layer_differences - because + * the hashing of some state groups actually depends on the values + * in other groups. For example we don't hash layer combine + * constants if they are aren't referenced by the current layer + * combine function. + */ + mask = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; + _cogl_pipeline_layer_resolve_authorities (layer, + mask, + authorities); + + /* So we go right ahead and hash the sparse state... */ + for (i = 0; i < COGL_PIPELINE_LAYER_STATE_COUNT; i++) + { + unsigned long current_state = (1L< differences) + break; + } + + return TRUE; +} + +void +_cogl_pipeline_hash_layers_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &authority->n_layers, + sizeof (authority->n_layers)); + _cogl_pipeline_foreach_layer_internal (authority, + _cogl_pipeline_hash_layer_cb, + state); +} + +typedef void (*StateHashFunction) (CoglPipeline *authority, CoglPipelineHashState *state); + +static StateHashFunction +state_hash_functions[COGL_PIPELINE_STATE_SPARSE_COUNT]; + +/* We don't statically initialize the array of hash functions + * so we won't get caught out by later re-indexing the groups for + * some reason. */ +void +_cogl_pipeline_init_state_hash_functions (void) +{ + state_hash_functions[COGL_PIPELINE_STATE_COLOR_INDEX] = + _cogl_pipeline_hash_color_state; + state_hash_functions[COGL_PIPELINE_STATE_BLEND_ENABLE_INDEX] = + _cogl_pipeline_hash_blend_enable_state; + state_hash_functions[COGL_PIPELINE_STATE_LAYERS_INDEX] = + _cogl_pipeline_hash_layers_state; + state_hash_functions[COGL_PIPELINE_STATE_LIGHTING_INDEX] = + _cogl_pipeline_hash_lighting_state; + state_hash_functions[COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX] = + _cogl_pipeline_hash_alpha_func_state; + state_hash_functions[COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX] = + _cogl_pipeline_hash_alpha_func_reference_state; + state_hash_functions[COGL_PIPELINE_STATE_BLEND_INDEX] = + _cogl_pipeline_hash_blend_state; + state_hash_functions[COGL_PIPELINE_STATE_USER_SHADER_INDEX] = + _cogl_pipeline_hash_user_shader_state; + state_hash_functions[COGL_PIPELINE_STATE_DEPTH_INDEX] = + _cogl_pipeline_hash_depth_state; + state_hash_functions[COGL_PIPELINE_STATE_FOG_INDEX] = + _cogl_pipeline_hash_fog_state; + state_hash_functions[COGL_PIPELINE_STATE_CULL_FACE_INDEX] = + _cogl_pipeline_hash_cull_face_state; + state_hash_functions[COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX] = + _cogl_pipeline_hash_non_zero_point_size_state; + state_hash_functions[COGL_PIPELINE_STATE_POINT_SIZE_INDEX] = + _cogl_pipeline_hash_point_size_state; + state_hash_functions[COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX] = + _cogl_pipeline_hash_per_vertex_point_size_state; + state_hash_functions[COGL_PIPELINE_STATE_LOGIC_OPS_INDEX] = + _cogl_pipeline_hash_logic_ops_state; + state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] = + _cogl_pipeline_hash_uniforms_state; + state_hash_functions[COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX] = + _cogl_pipeline_hash_vertex_snippets_state; + state_hash_functions[COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX] = + _cogl_pipeline_hash_fragment_snippets_state; + + { + /* So we get a big error if we forget to update this code! */ + _COGL_STATIC_ASSERT (COGL_PIPELINE_STATE_SPARSE_COUNT == 18, + "Make sure to install a hash function for " + "newly added pipeline state and update assert " + "in _cogl_pipeline_init_state_hash_functions"); + } +} + +unsigned int +_cogl_pipeline_hash (CoglPipeline *pipeline, + unsigned int differences, + unsigned long layer_differences, + CoglPipelineEvalFlags flags) +{ + CoglPipeline *authorities[COGL_PIPELINE_STATE_SPARSE_COUNT]; + unsigned int mask; + int i; + CoglPipelineHashState state; + unsigned int final_hash = 0; + + state.hash = 0; + state.layer_differences = layer_differences; + state.flags = flags; + + _cogl_pipeline_update_real_blend_enable (pipeline, FALSE); + + /* hash non-sparse state */ + + if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE) + { + CoglBool enable = pipeline->real_blend_enable; + state.hash = + _cogl_util_one_at_a_time_hash (state.hash, &enable, sizeof (enable)); + } + + /* hash sparse state */ + + mask = differences & COGL_PIPELINE_STATE_ALL_SPARSE; + _cogl_pipeline_resolve_authorities (pipeline, mask, authorities); + + for (i = 0; i < COGL_PIPELINE_STATE_SPARSE_COUNT; i++) + { + unsigned int current_state = (1< differences) + break; + } + + return _cogl_util_one_at_a_time_mix (final_hash); +} + +typedef struct +{ + CoglContext *context; + CoglPipeline *src_pipeline; + CoglPipeline *dst_pipeline; + unsigned int layer_differences; +} DeepCopyData; + +static CoglBool +deep_copy_layer_cb (CoglPipelineLayer *src_layer, + void *user_data) +{ + DeepCopyData *data = user_data; + CoglPipelineLayer *dst_layer; + unsigned int differences = data->layer_differences; + + dst_layer = _cogl_pipeline_get_layer (data->dst_pipeline, src_layer->index); + + while (src_layer != data->context->default_layer_n && + src_layer != data->context->default_layer_0 && + differences) + { + unsigned long to_copy = differences & src_layer->differences; + + if (to_copy) + { + _cogl_pipeline_layer_copy_differences (dst_layer, src_layer, to_copy); + differences ^= to_copy; + } + + src_layer = COGL_PIPELINE_LAYER (COGL_NODE (src_layer)->parent); + } + + return TRUE; +} + +CoglPipeline * +_cogl_pipeline_deep_copy (CoglPipeline *pipeline, + unsigned long differences, + unsigned long layer_differences) +{ + CoglPipeline *new, *authority; + CoglBool copy_layer_state; + + _COGL_GET_CONTEXT (ctx, NULL); + + if ((differences & COGL_PIPELINE_STATE_LAYERS)) + { + copy_layer_state = TRUE; + differences &= ~COGL_PIPELINE_STATE_LAYERS; + } + else + copy_layer_state = FALSE; + + new = cogl_pipeline_new (ctx); + + for (authority = pipeline; + authority != ctx->default_pipeline && differences; + authority = COGL_PIPELINE (COGL_NODE (authority)->parent)) + { + unsigned long to_copy = differences & authority->differences; + + if (to_copy) + { + _cogl_pipeline_copy_differences (new, authority, to_copy); + differences ^= to_copy; + } + } + + if (copy_layer_state) + { + DeepCopyData data; + + /* The unit index doesn't need to be copied because it should + * end up with the same values anyway because the new pipeline + * will have the same indices as the source pipeline */ + layer_differences &= ~COGL_PIPELINE_LAYER_STATE_UNIT; + + data.context = ctx; + data.src_pipeline = pipeline; + data.dst_pipeline = new; + data.layer_differences = layer_differences; + + _cogl_pipeline_foreach_layer_internal (pipeline, + deep_copy_layer_cb, + &data); + } + + return new; +} + +typedef struct +{ + int i; + CoglPipelineLayer **layers; +} AddLayersToArrayState; + +static CoglBool +add_layer_to_array_cb (CoglPipelineLayer *layer, + void *user_data) +{ + AddLayersToArrayState *state = user_data; + state->layers[state->i++] = layer; + return TRUE; +} + +/* This tries to find the oldest ancestor whose pipeline and layer + state matches the given flags. This is mostly used to detect code + gen authorities so that we can reduce the numer of programs + generated */ +CoglPipeline * +_cogl_pipeline_find_equivalent_parent (CoglPipeline *pipeline, + CoglPipelineState pipeline_state, + CoglPipelineLayerState layer_state) +{ + CoglPipeline *authority0; + CoglPipeline *authority1; + int n_layers; + CoglPipelineLayer **authority0_layers; + CoglPipelineLayer **authority1_layers; + + /* Find the first pipeline that modifies state that affects the + * state or any layer state... */ + authority0 = _cogl_pipeline_get_authority (pipeline, + pipeline_state | + COGL_PIPELINE_STATE_LAYERS); + + /* Find the next ancestor after that, that also modifies the + * state... */ + if (_cogl_pipeline_get_parent (authority0)) + { + authority1 = + _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority0), + pipeline_state | + COGL_PIPELINE_STATE_LAYERS); + } + else + return authority0; + + n_layers = cogl_pipeline_get_n_layers (authority0); + + for (;;) + { + AddLayersToArrayState state; + int i; + + if (n_layers != cogl_pipeline_get_n_layers (authority1)) + return authority0; + + /* If the programs differ by anything that isn't part of the + layer state then we can't continue */ + if (pipeline_state && + (_cogl_pipeline_compare_differences (authority0, authority1) & + pipeline_state)) + return authority0; + + authority0_layers = + g_alloca (sizeof (CoglPipelineLayer *) * n_layers); + state.i = 0; + state.layers = authority0_layers; + _cogl_pipeline_foreach_layer_internal (authority0, + add_layer_to_array_cb, + &state); + + authority1_layers = + g_alloca (sizeof (CoglPipelineLayer *) * n_layers); + state.i = 0; + state.layers = authority1_layers; + _cogl_pipeline_foreach_layer_internal (authority1, + add_layer_to_array_cb, + &state); + + for (i = 0; i < n_layers; i++) + { + unsigned long layer_differences; + + if (authority0_layers[i] == authority1_layers[i]) + continue; + + layer_differences = + _cogl_pipeline_layer_compare_differences (authority0_layers[i], + authority1_layers[i]); + + if (layer_differences & layer_state) + return authority0; + } + + /* Find the next ancestor after that, that also modifies state + * affecting codegen... */ + + if (!_cogl_pipeline_get_parent (authority1)) + break; + + authority0 = authority1; + authority1 = + _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority1), + pipeline_state | + COGL_PIPELINE_STATE_LAYERS); + if (authority1 == authority0) + break; + } + + return authority1; +} + +CoglPipelineState +_cogl_pipeline_get_state_for_vertex_codegen (CoglContext *context) +{ + CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS | + COGL_PIPELINE_STATE_USER_SHADER | + COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE | + COGL_PIPELINE_STATE_VERTEX_SNIPPETS); + + /* If we don't have the builtin point size uniform then we'll add + * one in the GLSL but we'll only do this if the point size is + * non-zero. Whether or not the point size is zero is represented by + * COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE */ + if (!_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) + state |= COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE; + + return state; +} + +CoglPipelineLayerState +_cogl_pipeline_get_layer_state_for_fragment_codegen (CoglContext *context) +{ + CoglPipelineLayerState state = + (COGL_PIPELINE_LAYER_STATE_COMBINE | + COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE | + COGL_PIPELINE_LAYER_STATE_UNIT | + COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS); + + /* If the driver supports GLSL then we might be using gl_PointCoord + * to implement the sprite coords. In that case the generated code + * depends on the point sprite state */ + if (cogl_has_feature (context, COGL_FEATURE_ID_GLSL)) + state |= COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + + return state; +} + +CoglPipelineState +_cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context) +{ + CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS | + COGL_PIPELINE_STATE_USER_SHADER | + COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS); + + if (!_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_ALPHA_TEST)) + state |= COGL_PIPELINE_STATE_ALPHA_FUNC; + + return state; +} + +int +cogl_pipeline_get_uniform_location (CoglPipeline *pipeline, + const char *uniform_name) +{ + void *location_ptr; + char *uniform_name_copy; + + _COGL_GET_CONTEXT (ctx, -1); + + /* This API is designed as if the uniform locations are specific to + a pipeline but they are actually unique across a whole + CoglContext. Potentially this could just be + cogl_context_get_uniform_location but it seems to make sense to + keep the API this way so that we can change the internals if need + be. */ + + /* Look for an existing uniform with this name */ + if (g_hash_table_lookup_extended (ctx->uniform_name_hash, + uniform_name, + NULL, + &location_ptr)) + return GPOINTER_TO_INT (location_ptr); + + uniform_name_copy = g_strdup (uniform_name); + g_ptr_array_add (ctx->uniform_names, uniform_name_copy); + g_hash_table_insert (ctx->uniform_name_hash, + uniform_name_copy, + GINT_TO_POINTER (ctx->n_uniform_names)); + + return ctx->n_uniform_names++; +} diff --git a/cogl/cogl/cogl-pipeline.h b/cogl/cogl/cogl-pipeline.h new file mode 100644 index 000000000..08ae3f69c --- /dev/null +++ b/cogl/cogl/cogl-pipeline.h @@ -0,0 +1,194 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PIPELINE_H__ +#define __COGL_PIPELINE_H__ + +/* We forward declare the CoglPipeline type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglPipeline CoglPipeline; + +#include +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * SECTION:cogl-pipeline + * @short_description: Functions for creating and manipulating the GPU + * pipeline + * + * Cogl allows creating and manipulating objects representing the full + * configuration of the GPU pipeline. In simplified terms the GPU + * pipeline takes primitive geometry as the input, it first performs + * vertex processing, allowing you to deform your geometry, then + * rasterizes that (turning it from pure geometry into fragments) then + * performs fragment processing including depth testing and texture + * mapping. Finally it blends the result with the framebuffer. + */ + +#define COGL_PIPELINE(OBJECT) ((CoglPipeline *)OBJECT) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_pipeline_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_pipeline_get_gtype (void); +#endif + +/** + * cogl_pipeline_new: + * @context: a #CoglContext + * + * Allocates and initializes a default simple pipeline that will color + * a primitive white. + * + * Return value: (transfer full): a pointer to a new #CoglPipeline + * + * Since: 2.0 + * Stability: Unstable + */ +CoglPipeline * +cogl_pipeline_new (CoglContext *context); + +/** + * cogl_pipeline_copy: + * @source: a #CoglPipeline object to copy + * + * Creates a new pipeline with the configuration copied from the + * source pipeline. + * + * We would strongly advise developers to always aim to use + * cogl_pipeline_copy() instead of cogl_pipeline_new() whenever there will + * be any similarity between two pipelines. Copying a pipeline helps Cogl + * keep track of a pipelines ancestry which we may use to help minimize GPU + * state changes. + * + * Return value: (transfer full): a pointer to the newly allocated #CoglPipeline + * + * Since: 2.0 + * Stability: Unstable + */ +CoglPipeline * +cogl_pipeline_copy (CoglPipeline *source); + +/** + * cogl_is_pipeline: + * @object: A #CoglObject + * + * Gets whether the given @object references an existing pipeline object. + * + * Return value: %TRUE if the @object references a #CoglPipeline, + * %FALSE otherwise + * + * Since: 2.0 + * Stability: Unstable + */ +CoglBool +cogl_is_pipeline (void *object); + +/** + * CoglPipelineLayerCallback: + * @pipeline: The #CoglPipeline whos layers are being iterated + * @layer_index: The current layer index + * @user_data: The private data passed to cogl_pipeline_foreach_layer() + * + * The callback prototype used with cogl_pipeline_foreach_layer() for + * iterating all the layers of a @pipeline. + * + * Since: 2.0 + * Stability: Unstable + */ +typedef CoglBool (*CoglPipelineLayerCallback) (CoglPipeline *pipeline, + int layer_index, + void *user_data); + +/** + * cogl_pipeline_foreach_layer: + * @pipeline: A #CoglPipeline object + * @callback: (scope call): A #CoglPipelineLayerCallback to be + * called for each layer index + * @user_data: (closure): Private data that will be passed to the + * callback + * + * Iterates all the layer indices of the given @pipeline. + * + * Since: 2.0 + * Stability: Unstable + */ +void +cogl_pipeline_foreach_layer (CoglPipeline *pipeline, + CoglPipelineLayerCallback callback, + void *user_data); + +/** + * cogl_pipeline_get_uniform_location: + * @pipeline: A #CoglPipeline object + * @uniform_name: The name of a uniform + * + * This is used to get an integer representing the uniform with the + * name @uniform_name. The integer can be passed to functions such as + * cogl_pipeline_set_uniform_1f() to set the value of a uniform. + * + * This function will always return a valid integer. Ie, unlike + * OpenGL, it does not return -1 if the uniform is not available in + * this pipeline so it can not be used to test whether uniforms are + * present. It is not necessary to set the program on the pipeline + * before calling this function. + * + * Return value: A integer representing the location of the given uniform. + * + * Since: 2.0 + * Stability: Unstable + */ +int +cogl_pipeline_get_uniform_location (CoglPipeline *pipeline, + const char *uniform_name); + + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +#endif /* __COGL_PIPELINE_H__ */ diff --git a/cogl/cogl/cogl-pixel-buffer-private.h b/cogl/cogl/cogl-pixel-buffer-private.h new file mode 100644 index 000000000..2735277e0 --- /dev/null +++ b/cogl/cogl/cogl-pixel-buffer-private.h @@ -0,0 +1,52 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +#ifndef __COGL_PIXEL_BUFFER_PRIVATE_H__ +#define __COGL_PIXEL_BUFFER_PRIVATE_H__ + +#include "cogl-object-private.h" +#include "cogl-buffer-private.h" + +#include + +COGL_BEGIN_DECLS + +struct _CoglPixelBuffer +{ + CoglBuffer _parent; +}; + +COGL_END_DECLS + +#endif /* __COGL_PIXEL_BUFFER_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-pixel-buffer.c b/cogl/cogl/cogl-pixel-buffer.c new file mode 100644 index 000000000..e2d6565fb --- /dev/null +++ b/cogl/cogl/cogl-pixel-buffer.c @@ -0,0 +1,134 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +/* For an overview of the functionality implemented here, please see + * cogl-buffer-array.h, which contains the gtk-doc section overview for the + * Pixel Buffers API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-object.h" +#include "cogl-pixel-buffer-private.h" +#include "cogl-pixel-buffer.h" +#include "cogl-gtype-private.h" + +/* + * GL/GLES compatibility defines for the buffer API: + */ + +#if defined (HAVE_COGL_GL) + +#ifndef GL_PIXEL_UNPACK_BUFFER +#define GL_PIXEL_UNPACK_BUFFER GL_PIXEL_UNPACK_BUFFER_ARB +#endif + +#ifndef GL_PIXEL_PACK_BUFFER +#define GL_PIXEL_PACK_BUFFER GL_PIXEL_PACK_BUFFER_ARB +#endif + +#endif + +static void +_cogl_pixel_buffer_free (CoglPixelBuffer *buffer); + +COGL_BUFFER_DEFINE (PixelBuffer, pixel_buffer) +COGL_GTYPE_DEFINE_CLASS (PixelBuffer, pixel_buffer) + +static CoglPixelBuffer * +_cogl_pixel_buffer_new (CoglContext *context, + size_t size, + const void *data, + CoglError **error) +{ + CoglPixelBuffer *pixel_buffer = g_slice_new0 (CoglPixelBuffer); + CoglBuffer *buffer = COGL_BUFFER (pixel_buffer); + + /* parent's constructor */ + _cogl_buffer_initialize (buffer, + context, + size, + COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, + COGL_BUFFER_USAGE_HINT_TEXTURE, + COGL_BUFFER_UPDATE_HINT_STATIC); + + _cogl_pixel_buffer_object_new (pixel_buffer); + + if (data) + { + if (!_cogl_buffer_set_data (COGL_BUFFER (pixel_buffer), + 0, + data, + size, + error)) + { + cogl_object_unref (pixel_buffer); + return NULL; + } + } + + return pixel_buffer; +} + +CoglPixelBuffer * +cogl_pixel_buffer_new (CoglContext *context, + size_t size, + const void *data) +{ + CoglError *ignore_error = NULL; + CoglPixelBuffer *buffer = + _cogl_pixel_buffer_new (context, size, data, &ignore_error); + if (!buffer) + cogl_error_free (ignore_error); + return buffer; +} + +static void +_cogl_pixel_buffer_free (CoglPixelBuffer *buffer) +{ + /* parent's destructor */ + _cogl_buffer_fini (COGL_BUFFER (buffer)); + + g_slice_free (CoglPixelBuffer, buffer); +} + diff --git a/cogl/cogl/cogl-pixel-buffer.h b/cogl/cogl/cogl-pixel-buffer.h new file mode 100644 index 000000000..8ae616483 --- /dev/null +++ b/cogl/cogl/cogl-pixel-buffer.h @@ -0,0 +1,138 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PIXEL_BUFFER_H__ +#define __COGL_PIXEL_BUFFER_H__ + +/* XXX: We forward declare CoglPixelBuffer here to allow for circular + * dependencies between some headers */ +typedef struct _CoglPixelBuffer CoglPixelBuffer; + +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#define COGL_PIXEL_BUFFER(buffer) ((CoglPixelBuffer *)(buffer)) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_pixel_buffer_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_pixel_buffer_get_gtype (void); +#endif + +/** + * cogl_pixel_buffer_new: + * @context: A #CoglContext + * @size: The number of bytes to allocate for the pixel data. + * @data: An optional pointer to vertex data to upload immediately + * + * Declares a new #CoglPixelBuffer of @size bytes to contain arrays of + * pixels. Once declared, data can be set using cogl_buffer_set_data() + * or by mapping it into the application's address space using + * cogl_buffer_map(). + * + * If @data isn't %NULL then @size bytes will be read from @data and + * immediately copied into the new buffer. + * + * Return value: (transfer full): a newly allocated #CoglPixelBuffer + * + * Since: 1.10 + * Stability: unstable + */ +CoglPixelBuffer * +cogl_pixel_buffer_new (CoglContext *context, + size_t size, + const void *data); + +/** + * cogl_is_pixel_buffer: + * @object: a #CoglObject to test + * + * Checks whether @object is a pixel buffer. + * + * Return value: %TRUE if the @object is a pixel buffer, and %FALSE + * otherwise + * + * Since: 1.2 + * Stability: Unstable + */ +CoglBool +cogl_is_pixel_buffer (void *object); + +#if 0 +/* + * cogl_pixel_buffer_set_region: + * @buffer: A #CoglPixelBuffer object + * @data: pixel data to upload to @array + * @src_width: width in pixels of the region to update + * @src_height: height in pixels of the region to update + * @src_rowstride: row stride in bytes of the source array + * @dst_x: upper left destination horizontal coordinate + * @dst_y: upper left destination vertical coordinate + * + * Uploads new data into a pixel array. The source data pointed by @data can + * have a different stride than @array in which case the function will do the + * right thing for you. For performance reasons, it is recommended for the + * source data to have the same stride than @array. + * + * Return value: %TRUE if the upload succeeded, %FALSE otherwise + * + * Since: 1.2 + * Stability: Unstable + */ +CoglBool +cogl_pixel_buffer_set_region (CoglPixelBuffer *buffer, + uint8_t *data, + unsigned int src_width, + unsigned int src_height, + unsigned int src_rowstride, + unsigned int dst_x, + unsigned int dst_y); +#endif + +COGL_END_DECLS + +#endif /* __COGL_PIXEL_BUFFER_H__ */ diff --git a/cogl/cogl/cogl-point-in-poly-private.h b/cogl/cogl/cogl-point-in-poly-private.h new file mode 100644 index 000000000..1f3f7014c --- /dev/null +++ b/cogl/cogl/cogl-point-in-poly-private.h @@ -0,0 +1,46 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_POINT_INT_POLYGON_PRIVATE_H +#define __COGL_POINT_INT_POLYGON_PRIVATE_H + +#include + +COGL_BEGIN_DECLS + +int +_cogl_util_point_in_screen_poly (float point_x, + float point_y, + void *vertices, + size_t stride, + int n_vertices); + +COGL_END_DECLS + +#endif /* __COGL_POINT_INT_POLYGON_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-point-in-poly.c b/cogl/cogl/cogl-point-in-poly.c new file mode 100644 index 000000000..cf87b9334 --- /dev/null +++ b/cogl/cogl/cogl-point-in-poly.c @@ -0,0 +1,101 @@ +/* + * Point Inclusion in Polygon Test + * + * Copyright (c) 1970-2003, Wm. Randolph Franklin + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * 2. Redistributions in binary form must reproduce the above + * copyright notice in the documentation and/or other materials + * provided with the distribution. + * 3. The name of W. Randolph Franklin may not be used to endorse or + * promote products derived from this Software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: + * The algorithm for this point_in_poly() function was learnt from: + * http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-point-in-poly-private.h" + +#include + +/* We've made a notable change to the original algorithm referenced + * above to make sure we have reliable results for screen aligned + * rectangles even though there may be some numerical in-precision in + * how the vertices of the polygon were calculated. + * + * We've avoided introducing an epsilon factor to the comparisons + * since we feel there's a risk of changing some semantics in ways that + * might not be desirable. One of those is that if you transform two + * polygons which share an edge and test a point close to that edge + * then this algorithm will currently give a positive result for only + * one polygon. + * + * Another concern is the way this algorithm resolves the corner case + * where the horizontal ray being cast to count edge crossings may + * cross directly through a vertex. The solution is based on the "idea + * of Simulation of Simplicity" and "pretends to shift the ray + * infinitesimally down so that it either clearly intersects, or + * clearly doesn't touch". I'm not familiar with the idea myself so I + * expect a misplaced epsilon is likely to break that aspect of the + * algorithm. + * + * The simple solution we've gone for is to pixel align the polygon + * vertices which should eradicate most noise due to in-precision. + */ +int +_cogl_util_point_in_screen_poly (float point_x, + float point_y, + void *vertices, + size_t stride, + int n_vertices) +{ + int i, j, c = 0; + + for (i = 0, j = n_vertices - 1; i < n_vertices; j = i++) + { + float vert_xi = *(float *)((uint8_t *)vertices + i * stride); + float vert_xj = *(float *)((uint8_t *)vertices + j * stride); + float vert_yi = *(float *)((uint8_t *)vertices + i * stride + + sizeof (float)); + float vert_yj = *(float *)((uint8_t *)vertices + j * stride + + sizeof (float)); + + vert_xi = COGL_UTIL_NEARBYINT (vert_xi); + vert_xj = COGL_UTIL_NEARBYINT (vert_xj); + vert_yi = COGL_UTIL_NEARBYINT (vert_yi); + vert_yj = COGL_UTIL_NEARBYINT (vert_yj); + + if (((vert_yi > point_y) != (vert_yj > point_y)) && + (point_x < (vert_xj - vert_xi) * (point_y - vert_yi) / + (vert_yj - vert_yi) + vert_xi) ) + c = !c; + } + + return c; +} + diff --git a/cogl/cogl/cogl-poll-private.h b/cogl/cogl/cogl-poll-private.h new file mode 100644 index 000000000..bdc9e6d80 --- /dev/null +++ b/cogl/cogl/cogl-poll-private.h @@ -0,0 +1,77 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_POLL_PRIVATE_H__ +#define __COGL_POLL_PRIVATE_H__ + +#include "cogl-poll.h" +#include "cogl-renderer.h" +#include "cogl-closure-list-private.h" + +void +_cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd); + +typedef int64_t (*CoglPollPrepareCallback) (void *user_data); +typedef void (*CoglPollDispatchCallback) (void *user_data, int revents); + +void +_cogl_poll_renderer_add_fd (CoglRenderer *renderer, + int fd, + CoglPollFDEvent events, + CoglPollPrepareCallback prepare, + CoglPollDispatchCallback dispatch, + void *user_data); + +void +_cogl_poll_renderer_modify_fd (CoglRenderer *renderer, + int fd, + CoglPollFDEvent events); + +typedef struct _CoglPollSource CoglPollSource; + +CoglPollSource * +_cogl_poll_renderer_add_source (CoglRenderer *renderer, + CoglPollPrepareCallback prepare, + CoglPollDispatchCallback dispatch, + void *user_data); + +void +_cogl_poll_renderer_remove_source (CoglRenderer *renderer, + CoglPollSource *source); + +typedef void (*CoglIdleCallback) (void *user_data); + +CoglClosure * +_cogl_poll_renderer_add_idle (CoglRenderer *renderer, + CoglIdleCallback idle_cb, + void *user_data, + CoglUserDataDestroyCallback destroy_cb); + +#endif /* __COGL_POLL_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-poll.c b/cogl/cogl/cogl-poll.c new file mode 100644 index 000000000..1ce62f67c --- /dev/null +++ b/cogl/cogl/cogl-poll.c @@ -0,0 +1,267 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-poll.h" +#include "cogl-poll-private.h" +#include "cogl-winsys-private.h" +#include "cogl-renderer-private.h" + +struct _CoglPollSource +{ + int fd; + CoglPollPrepareCallback prepare; + CoglPollDispatchCallback dispatch; + void *user_data; +}; + +int +cogl_poll_renderer_get_info (CoglRenderer *renderer, + CoglPollFD **poll_fds, + int *n_poll_fds, + int64_t *timeout) +{ + GList *l, *next; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), 0); + _COGL_RETURN_VAL_IF_FAIL (poll_fds != NULL, 0); + _COGL_RETURN_VAL_IF_FAIL (n_poll_fds != NULL, 0); + _COGL_RETURN_VAL_IF_FAIL (timeout != NULL, 0); + + *timeout = -1; + + if (!_cogl_list_empty (&renderer->idle_closures)) + *timeout = 0; + + /* This loop needs to cope with the prepare callback removing its + * own fd */ + for (l = renderer->poll_sources; l; l = next) + { + CoglPollSource *source = l->data; + + next = l->next; + + if (source->prepare) + { + int64_t source_timeout = source->prepare (source->user_data); + if (source_timeout >= 0 && + (*timeout == -1 || *timeout > source_timeout)) + *timeout = source_timeout; + } + } + + /* This is deliberately set after calling the prepare callbacks in + * case one of them removes its fd */ + *poll_fds = (void *)renderer->poll_fds->data; + *n_poll_fds = renderer->poll_fds->len; + + return renderer->poll_fds_age; +} + +void +cogl_poll_renderer_dispatch (CoglRenderer *renderer, + const CoglPollFD *poll_fds, + int n_poll_fds) +{ + GList *l, *next; + + _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer)); + + _cogl_closure_list_invoke_no_args (&renderer->idle_closures); + + /* This loop needs to cope with the dispatch callback removing its + * own fd */ + for (l = renderer->poll_sources; l; l = next) + { + CoglPollSource *source = l->data; + int i; + + next = l->next; + + if (source->fd == -1) + { + source->dispatch (source->user_data, 0); + continue; + } + + for (i = 0; i < n_poll_fds; i++) + { + const CoglPollFD *pollfd = &poll_fds[i]; + + if (pollfd->fd == source->fd) + { + source->dispatch (source->user_data, pollfd->revents); + break; + } + } + } +} + +static int +find_pollfd (CoglRenderer *renderer, int fd) +{ + int i; + + for (i = 0; i < renderer->poll_fds->len; i++) + { + CoglPollFD *pollfd = &g_array_index (renderer->poll_fds, CoglPollFD, i); + + if (pollfd->fd == fd) + return i; + } + + return -1; +} + +void +_cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd) +{ + int i = find_pollfd (renderer, fd); + GList *l; + + if (i < 0) + return; + + g_array_remove_index_fast (renderer->poll_fds, i); + renderer->poll_fds_age++; + + for (l = renderer->poll_sources; l; l = l->next) + { + CoglPollSource *source = l->data; + if (source->fd == fd) + { + renderer->poll_sources = + g_list_delete_link (renderer->poll_sources, l); + g_slice_free (CoglPollSource, source); + break; + } + } +} + +void +_cogl_poll_renderer_modify_fd (CoglRenderer *renderer, + int fd, + CoglPollFDEvent events) +{ + int fd_index = find_pollfd (renderer, fd); + + if (fd_index == -1) + g_warn_if_reached (); + else + { + CoglPollFD *pollfd = + &g_array_index (renderer->poll_sources, CoglPollFD, fd_index); + + pollfd->events = events; + renderer->poll_fds_age++; + } +} + +void +_cogl_poll_renderer_add_fd (CoglRenderer *renderer, + int fd, + CoglPollFDEvent events, + CoglPollPrepareCallback prepare, + CoglPollDispatchCallback dispatch, + void *user_data) +{ + CoglPollFD pollfd = { + fd, + events + }; + CoglPollSource *source; + + _cogl_poll_renderer_remove_fd (renderer, fd); + + source = g_slice_new0 (CoglPollSource); + source->fd = fd; + source->prepare = prepare; + source->dispatch = dispatch; + source->user_data = user_data; + + renderer->poll_sources = g_list_prepend (renderer->poll_sources, source); + + g_array_append_val (renderer->poll_fds, pollfd); + renderer->poll_fds_age++; +} + +CoglPollSource * +_cogl_poll_renderer_add_source (CoglRenderer *renderer, + CoglPollPrepareCallback prepare, + CoglPollDispatchCallback dispatch, + void *user_data) +{ + CoglPollSource *source; + + source = g_slice_new0 (CoglPollSource); + source->fd = -1; + source->prepare = prepare; + source->dispatch = dispatch; + source->user_data = user_data; + + renderer->poll_sources = g_list_prepend (renderer->poll_sources, source); + + return source; +} + +void +_cogl_poll_renderer_remove_source (CoglRenderer *renderer, + CoglPollSource *source) +{ + GList *l; + + for (l = renderer->poll_sources; l; l = l->next) + { + if (l->data == source) + { + renderer->poll_sources = + g_list_delete_link (renderer->poll_sources, l); + g_slice_free (CoglPollSource, source); + break; + } + } +} + +CoglClosure * +_cogl_poll_renderer_add_idle (CoglRenderer *renderer, + CoglIdleCallback idle_cb, + void *user_data, + CoglUserDataDestroyCallback destroy_cb) +{ + return _cogl_closure_list_add (&renderer->idle_closures, + idle_cb, + user_data, + destroy_cb); +} diff --git a/cogl/cogl/cogl-poll.h b/cogl/cogl/cogl-poll.h new file mode 100644 index 000000000..849e95c92 --- /dev/null +++ b/cogl/cogl/cogl-poll.h @@ -0,0 +1,195 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_POLL_H__ +#define __COGL_POLL_H__ + +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-poll + * @short_description: Functions for integrating Cogl with an + * application's main loop + * + * Cogl needs to integrate with the application's main loop so that it + * can internally handle some events from the driver. All Cogl + * applications must use these functions. They provide enough + * information to describe the state that Cogl will need to wake up + * on. An application using the GLib main loop can instead use + * cogl_glib_source_new() which provides a #GSource ready to be added + * to the main loop. + */ + +/** + * CoglPollFDEvent: + * @COGL_POLL_FD_EVENT_IN: there is data to read + * @COGL_POLL_FD_EVENT_PRI: data can be written (without blocking) + * @COGL_POLL_FD_EVENT_OUT: there is urgent data to read. + * @COGL_POLL_FD_EVENT_ERR: error condition + * @COGL_POLL_FD_EVENT_HUP: hung up (the connection has been broken, usually + * for pipes and sockets). + * @COGL_POLL_FD_EVENT_NVAL: invalid request. The file descriptor is not open. + * + * A bitmask of events that Cogl may need to wake on for a file + * descriptor. Note that these all have the same values as the + * corresponding defines for the poll function call on Unix so they + * may be directly passed to poll. + * + * Since: 1.10 + * Stability: unstable + */ +typedef enum +{ + COGL_POLL_FD_EVENT_IN = COGL_SYSDEF_POLLIN, + COGL_POLL_FD_EVENT_PRI = COGL_SYSDEF_POLLPRI, + COGL_POLL_FD_EVENT_OUT = COGL_SYSDEF_POLLOUT, + COGL_POLL_FD_EVENT_ERR = COGL_SYSDEF_POLLERR, + COGL_POLL_FD_EVENT_HUP = COGL_SYSDEF_POLLHUP, + COGL_POLL_FD_EVENT_NVAL = COGL_SYSDEF_POLLNVAL +} CoglPollFDEvent; + +/** + * CoglPollFD: + * @fd: The file descriptor to block on + * @events: A bitmask of events to block on + * @revents: A bitmask of returned events + * + * A struct for describing the state of a file descriptor that Cogl + * needs to block on. The @events field contains a bitmask of + * #CoglPollFDEvents that should cause the application to wake + * up. After the application is woken up from idle it should pass back + * an array of #CoglPollFDs to Cogl and update the @revents + * mask to the actual events that occurred on the file descriptor. + * + * Note that CoglPollFD is deliberately exactly the same as struct + * pollfd on Unix so that it can simply be cast when calling poll. + * + * Since: 1.10 + * Stability: unstable + */ +typedef struct { + int fd; + short int events; + short int revents; +} CoglPollFD; + +/** + * cogl_poll_renderer_get_info: + * @renderer: A #CoglRenderer + * @poll_fds: A return location for a pointer to an array + * of #CoglPollFDs + * @n_poll_fds: A return location for the number of entries in *@poll_fds + * @timeout: A return location for the maximum length of time to wait + * in microseconds, or -1 to wait indefinitely. + * + * Is used to integrate Cogl with an application mainloop that is based + * on the unix poll(2) api (or select() or something equivalent). This + * api should be called whenever an application is about to go idle so + * that Cogl has a chance to describe what file descriptor events it + * needs to be woken up for. + * + * If your application is using the Glib mainloop then you + * should jump to the cogl_glib_source_new() api as a more convenient + * way of integrating Cogl with the mainloop. + * + * After the function is called *@poll_fds will contain a pointer to + * an array of #CoglPollFD structs describing the file descriptors + * that Cogl expects. The fd and events members will be updated + * accordingly. After the application has completed its idle it is + * expected to either update the revents members directly in this + * array or to create a copy of the array and update them + * there. + * + * When the application mainloop returns from calling poll(2) (or its + * equivalent) then it should call cogl_poll_renderer_dispatch() + * passing a pointer the array of CoglPollFDs with updated + * revent values. + * + * @timeout will contain a maximum amount of time to wait in + * microseconds before the application should wake up or -1 if the + * application should wait indefinitely. This can also be 0 if + * Cogl needs to be woken up immediately. + * + * Return value: A "poll fd state age" that changes whenever the set + * of poll_fds has changed. If this API is being used to + * integrate with another system mainloop api then + * knowing if the set of file descriptors and events has + * really changed can help avoid redundant work + * depending the api. The age isn't guaranteed to change + * when the timeout changes. + * + * Stability: unstable + * Since: 1.16 + */ +int +cogl_poll_renderer_get_info (CoglRenderer *renderer, + CoglPollFD **poll_fds, + int *n_poll_fds, + int64_t *timeout); + +/** + * cogl_poll_renderer_dispatch: + * @renderer: A #CoglRenderer + * @poll_fds: An array of #CoglPollFDs describing the events + * that have occurred since the application went idle. + * @n_poll_fds: The length of the @poll_fds array. + * + * This should be called whenever an application is woken up from + * going idle in its main loop. The @poll_fds array should contain a + * list of file descriptors matched with the events that occurred in + * revents. The events field is ignored. It is safe to pass in extra + * file descriptors that Cogl didn't request when calling + * cogl_poll_renderer_get_info() or a shorter array missing some file + * descriptors that Cogl requested. + * + * If your application didn't originally create a #CoglRenderer + * manually then you can easily get a #CoglRenderer pointer by calling + * cogl_get_renderer(). + * + * Stability: unstable + * Since: 1.16 + */ +void +cogl_poll_renderer_dispatch (CoglRenderer *renderer, + const CoglPollFD *poll_fds, + int n_poll_fds); + +COGL_END_DECLS + +#endif /* __COGL_POLL_H__ */ diff --git a/cogl/cogl/cogl-primitive-private.h b/cogl/cogl/cogl-primitive-private.h new file mode 100644 index 000000000..78627295a --- /dev/null +++ b/cogl/cogl/cogl-primitive-private.h @@ -0,0 +1,73 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PRIMITIVE_PRIVATE_H +#define __COGL_PRIMITIVE_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-attribute-buffer-private.h" +#include "cogl-attribute-private.h" +#include "cogl-framebuffer.h" + +struct _CoglPrimitive +{ + CoglObject _parent; + + CoglIndices *indices; + CoglVerticesMode mode; + int first_vertex; + int n_vertices; + + int immutable_ref; + + CoglAttribute **attributes; + int n_attributes; + + int n_embedded_attributes; + CoglAttribute *embedded_attribute; +}; + +CoglPrimitive * +_cogl_primitive_immutable_ref (CoglPrimitive *primitive); + +void +_cogl_primitive_immutable_unref (CoglPrimitive *primitive); + +void +_cogl_primitive_draw (CoglPrimitive *primitive, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags); + +#endif /* __COGL_PRIMITIVE_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-primitive-texture.c b/cogl/cogl/cogl-primitive-texture.c new file mode 100644 index 000000000..fce97238b --- /dev/null +++ b/cogl/cogl/cogl-primitive-texture.c @@ -0,0 +1,60 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-primitive-texture.h" +#include "cogl-texture-private.h" + +CoglBool +cogl_is_primitive_texture (void *object) +{ + return (cogl_is_texture (object) && + COGL_TEXTURE (object)->vtable->is_primitive); +} + +void +cogl_primitive_texture_set_auto_mipmap (CoglPrimitiveTexture *primitive_texture, + CoglBool value) +{ + CoglTexture *texture; + + _COGL_RETURN_IF_FAIL (cogl_is_primitive_texture (primitive_texture)); + + texture = COGL_TEXTURE (primitive_texture); + + g_assert (texture->vtable->set_auto_mipmap != NULL); + + texture->vtable->set_auto_mipmap (texture, value); +} diff --git a/cogl/cogl/cogl-primitive-texture.h b/cogl/cogl/cogl-primitive-texture.h new file mode 100644 index 000000000..effaac35b --- /dev/null +++ b/cogl/cogl/cogl-primitive-texture.h @@ -0,0 +1,111 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PRIMITIVE_TEXTURE_H__ +#define __COGL_PRIMITIVE_TEXTURE_H__ + +#include "cogl-types.h" + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-primitive-texture + * @short_description: Interface for low-level textures like + * #CoglTexture2D and #CoglTexture3D. + * + * A #CoglPrimitiveTexture is a texture that is directly represented + * by a single texture on the GPU. For example these could be a + * #CoglTexture2D, #CoglTexture3D or #CoglTextureRectangle. This is + * opposed to high level meta textures which may be composed of + * multiple primitive textures or a sub-region of another texture such + * as #CoglAtlasTexture and #CoglTexture2DSliced. + * + * A texture that implements this interface can be directly used with + * the low level cogl_primitive_draw() API. Other types of textures + * need to be first resolved to primitive textures using the + * #CoglMetaTexture interface. + * + * Most developers won't need to use this interface directly but + * still it is worth understanding the distinction between high-level + * and primitive textures because you may find other references in the + * documentation that detail limitations of using + * primitive textures. + */ + +#ifdef __COGL_H_INSIDE__ +/* For the public C api we typedef interface types as void to avoid needing + * lots of casting in code and instead we will rely on runtime type checking + * for these objects. */ +typedef void CoglPrimitiveTexture; +#else +typedef struct _CoglPrimitiveTexture CoglPrimitiveTexture; +#define COGL_PRIMITIVE_TEXTURE(X) ((CoglPrimitiveTexture *)X) +#endif + +/** + * cogl_is_primitive_texture: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a primitive texture object. + * + * Return value: %TRUE if the pointer references a primitive texture, and + * %FALSE otherwise + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_is_primitive_texture (void *object); + +/** + * cogl_primitive_texture_set_auto_mipmap: + * @primitive_texture: A #CoglPrimitiveTexture + * @value: The new value for whether to auto mipmap + * + * Sets whether the texture will automatically update the smaller + * mipmap levels after any part of level 0 is updated. The update will + * only occur whenever the texture is used for drawing with a texture + * filter that requires the lower mipmap levels. An application should + * disable this if it wants to upload its own data for the other + * levels. By default auto mipmapping is enabled. + * + * Since: 2.0 + * Stability: unstable + */ +void +cogl_primitive_texture_set_auto_mipmap (CoglPrimitiveTexture *primitive_texture, + CoglBool value); + +COGL_END_DECLS + +#endif /* __COGL_PRIMITIVE_TEXTURE_H__ */ diff --git a/cogl/cogl/cogl-primitive.c b/cogl/cogl/cogl-primitive.c new file mode 100644 index 000000000..b31a25a86 --- /dev/null +++ b/cogl/cogl/cogl-primitive.c @@ -0,0 +1,645 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-object-private.h" +#include "cogl-primitive.h" +#include "cogl-primitive-private.h" +#include "cogl-attribute-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +static void _cogl_primitive_free (CoglPrimitive *primitive); + +COGL_OBJECT_DEFINE (Primitive, primitive); +COGL_GTYPE_DEFINE_CLASS (Primitive, primitive); + +CoglPrimitive * +cogl_primitive_new_with_attributes (CoglVerticesMode mode, + int n_vertices, + CoglAttribute **attributes, + int n_attributes) +{ + CoglPrimitive *primitive; + int i; + + primitive = g_slice_alloc (sizeof (CoglPrimitive) + + sizeof (CoglAttribute *) * (n_attributes - 1)); + primitive->mode = mode; + primitive->first_vertex = 0; + primitive->n_vertices = n_vertices; + primitive->indices = NULL; + primitive->immutable_ref = 0; + + primitive->n_attributes = n_attributes; + primitive->n_embedded_attributes = n_attributes; + primitive->attributes = &primitive->embedded_attribute; + for (i = 0; i < n_attributes; i++) + { + CoglAttribute *attribute = attributes[i]; + cogl_object_ref (attribute); + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL); + + primitive->attributes[i] = attribute; + } + + return _cogl_primitive_object_new (primitive); +} + +/* This is just an internal convenience wrapper around + new_with_attributes that also unrefs the attributes. It is just + used for the builtin struct constructors */ +static CoglPrimitive * +_cogl_primitive_new_with_attributes_unref (CoglVerticesMode mode, + int n_vertices, + CoglAttribute **attributes, + int n_attributes) +{ + CoglPrimitive *primitive; + int i; + + primitive = cogl_primitive_new_with_attributes (mode, + n_vertices, + attributes, + n_attributes); + + for (i = 0; i < n_attributes; i++) + cogl_object_unref (attributes[i]); + + return primitive; +} + +CoglPrimitive * +cogl_primitive_new (CoglVerticesMode mode, + int n_vertices, + ...) +{ + va_list ap; + int n_attributes; + CoglAttribute **attributes; + int i; + CoglAttribute *attribute; + + va_start (ap, n_vertices); + for (n_attributes = 0; va_arg (ap, CoglAttribute *); n_attributes++) + ; + va_end (ap); + + attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); + + va_start (ap, n_vertices); + for (i = 0; (attribute = va_arg (ap, CoglAttribute *)); i++) + attributes[i] = attribute; + va_end (ap); + + return cogl_primitive_new_with_attributes (mode, n_vertices, + attributes, + i); +} + +CoglPrimitive * +cogl_primitive_new_p2 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2), data); + CoglAttribute *attributes[1]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP2), + offsetof (CoglVertexP2, x), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 1); +} + +CoglPrimitive * +cogl_primitive_new_p3 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3), data); + CoglAttribute *attributes[1]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP3), + offsetof (CoglVertexP3, x), + 3, + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 1); +} + +CoglPrimitive * +cogl_primitive_new_p2c4 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2C4 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2C4), data); + CoglAttribute *attributes[2]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP2C4), + offsetof (CoglVertexP2C4, x), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_color_in", + sizeof (CoglVertexP2C4), + offsetof (CoglVertexP2C4, r), + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 2); +} + +CoglPrimitive * +cogl_primitive_new_p3c4 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3C4 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3C4), data); + CoglAttribute *attributes[2]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP3C4), + offsetof (CoglVertexP3C4, x), + 3, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_color_in", + sizeof (CoglVertexP3C4), + offsetof (CoglVertexP3C4, r), + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 2); +} + +CoglPrimitive * +cogl_primitive_new_p2t2 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2T2 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2T2), data); + CoglAttribute *attributes[2]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP2T2), + offsetof (CoglVertexP2T2, x), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP2T2), + offsetof (CoglVertexP2T2, s), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 2); +} + +CoglPrimitive * +cogl_primitive_new_p3t2 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3T2 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3T2), data); + CoglAttribute *attributes[2]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP3T2), + offsetof (CoglVertexP3T2, x), + 3, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP3T2), + offsetof (CoglVertexP3T2, s), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 2); +} + +CoglPrimitive * +cogl_primitive_new_p2t2c4 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2T2C4 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, + n_vertices * sizeof (CoglVertexP2T2C4), data); + CoglAttribute *attributes[3]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP2T2C4), + offsetof (CoglVertexP2T2C4, x), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP2T2C4), + offsetof (CoglVertexP2T2C4, s), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[2] = cogl_attribute_new (attribute_buffer, + "cogl_color_in", + sizeof (CoglVertexP2T2C4), + offsetof (CoglVertexP2T2C4, r), + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 3); +} + +CoglPrimitive * +cogl_primitive_new_p3t2c4 (CoglContext *ctx, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3T2C4 *data) +{ + CoglAttributeBuffer *attribute_buffer = + cogl_attribute_buffer_new (ctx, + n_vertices * sizeof (CoglVertexP3T2C4), data); + CoglAttribute *attributes[3]; + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (CoglVertexP3T2C4), + offsetof (CoglVertexP3T2C4, x), + 3, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP3T2C4), + offsetof (CoglVertexP3T2C4, s), + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[2] = cogl_attribute_new (attribute_buffer, + "cogl_color_in", + sizeof (CoglVertexP3T2C4), + offsetof (CoglVertexP3T2C4, r), + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + cogl_object_unref (attribute_buffer); + + return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, + attributes, + 3); +} + +static void +_cogl_primitive_free (CoglPrimitive *primitive) +{ + int i; + + for (i = 0; i < primitive->n_attributes; i++) + cogl_object_unref (primitive->attributes[i]); + + if (primitive->attributes != &primitive->embedded_attribute) + g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, + primitive->attributes); + + if (primitive->indices) + cogl_object_unref (primitive->indices); + + g_slice_free1 (sizeof (CoglPrimitive) + + sizeof (CoglAttribute *) * + (primitive->n_embedded_attributes - 1), primitive); +} + +static void +warn_about_midscene_changes (void) +{ + static CoglBool seen = FALSE; + if (!seen) + { + g_warning ("Mid-scene modification of primitives has " + "undefined results\n"); + seen = TRUE; + } +} + +void +cogl_primitive_set_attributes (CoglPrimitive *primitive, + CoglAttribute **attributes, + int n_attributes) +{ + int i; + + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + + if (G_UNLIKELY (primitive->immutable_ref)) + { + warn_about_midscene_changes (); + return; + } + + /* NB: we don't unref the previous attributes before refing the new + * in case we would end up releasing the last reference for an + * attribute thats actually in the new list too. */ + for (i = 0; i < n_attributes; i++) + { + _COGL_RETURN_IF_FAIL (cogl_is_attribute (attributes[i])); + cogl_object_ref (attributes[i]); + } + + for (i = 0; i < primitive->n_attributes; i++) + cogl_object_unref (primitive->attributes[i]); + + /* First try to use the embedded storage assocated with the + * primitive, else fallback to slice allocating separate storage for + * the attribute pointers... */ + + if (n_attributes <= primitive->n_embedded_attributes) + { + if (primitive->attributes != &primitive->embedded_attribute) + g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, + primitive->attributes); + primitive->attributes = &primitive->embedded_attribute; + } + else + { + if (primitive->attributes != &primitive->embedded_attribute) + g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, + primitive->attributes); + primitive->attributes = + g_slice_alloc (sizeof (CoglAttribute *) * n_attributes); + } + + memcpy (primitive->attributes, attributes, + sizeof (CoglAttribute *) * n_attributes); + + primitive->n_attributes = n_attributes; +} + +int +cogl_primitive_get_first_vertex (CoglPrimitive *primitive) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_primitive (primitive), 0); + + return primitive->first_vertex; +} + +void +cogl_primitive_set_first_vertex (CoglPrimitive *primitive, + int first_vertex) +{ + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + + if (G_UNLIKELY (primitive->immutable_ref)) + { + warn_about_midscene_changes (); + return; + } + + primitive->first_vertex = first_vertex; +} + +int +cogl_primitive_get_n_vertices (CoglPrimitive *primitive) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_primitive (primitive), 0); + + return primitive->n_vertices; +} + +void +cogl_primitive_set_n_vertices (CoglPrimitive *primitive, + int n_vertices) +{ + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + + primitive->n_vertices = n_vertices; +} + +CoglVerticesMode +cogl_primitive_get_mode (CoglPrimitive *primitive) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_primitive (primitive), 0); + + return primitive->mode; +} + +void +cogl_primitive_set_mode (CoglPrimitive *primitive, + CoglVerticesMode mode) +{ + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + + if (G_UNLIKELY (primitive->immutable_ref)) + { + warn_about_midscene_changes (); + return; + } + + primitive->mode = mode; +} + +void +cogl_primitive_set_indices (CoglPrimitive *primitive, + CoglIndices *indices, + int n_indices) +{ + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + + if (G_UNLIKELY (primitive->immutable_ref)) + { + warn_about_midscene_changes (); + return; + } + + if (indices) + cogl_object_ref (indices); + if (primitive->indices) + cogl_object_unref (primitive->indices); + primitive->indices = indices; + primitive->n_vertices = n_indices; +} + +CoglIndices * +cogl_primitive_get_indices (CoglPrimitive *primitive) +{ + return primitive->indices; +} + +CoglPrimitive * +cogl_primitive_copy (CoglPrimitive *primitive) +{ + CoglPrimitive *copy; + + copy = cogl_primitive_new_with_attributes (primitive->mode, + primitive->n_vertices, + primitive->attributes, + primitive->n_attributes); + + cogl_primitive_set_indices (copy, primitive->indices, primitive->n_vertices); + cogl_primitive_set_first_vertex (copy, primitive->first_vertex); + + return copy; +} + +CoglPrimitive * +_cogl_primitive_immutable_ref (CoglPrimitive *primitive) +{ + int i; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_primitive (primitive), NULL); + + primitive->immutable_ref++; + + for (i = 0; i < primitive->n_attributes; i++) + _cogl_attribute_immutable_ref (primitive->attributes[i]); + + return primitive; +} + +void +_cogl_primitive_immutable_unref (CoglPrimitive *primitive) +{ + int i; + + _COGL_RETURN_IF_FAIL (cogl_is_primitive (primitive)); + _COGL_RETURN_IF_FAIL (primitive->immutable_ref > 0); + + primitive->immutable_ref--; + + for (i = 0; i < primitive->n_attributes; i++) + _cogl_attribute_immutable_unref (primitive->attributes[i]); +} + +void +cogl_primitive_foreach_attribute (CoglPrimitive *primitive, + CoglPrimitiveAttributeCallback callback, + void *user_data) +{ + int i; + + for (i = 0; i < primitive->n_attributes; i++) + if (!callback (primitive, primitive->attributes[i], user_data)) + break; +} + +void +_cogl_primitive_draw (CoglPrimitive *primitive, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags) +{ + if (primitive->indices) + _cogl_framebuffer_draw_indexed_attributes (framebuffer, + pipeline, + primitive->mode, + primitive->first_vertex, + primitive->n_vertices, + primitive->indices, + primitive->attributes, + primitive->n_attributes, + flags); + else + _cogl_framebuffer_draw_attributes (framebuffer, + pipeline, + primitive->mode, + primitive->first_vertex, + primitive->n_vertices, + primitive->attributes, + primitive->n_attributes, + flags); +} + +void +cogl_primitive_draw (CoglPrimitive *primitive, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline) +{ + _cogl_primitive_draw (primitive, framebuffer, pipeline, 0 /* flags */); +} diff --git a/cogl/cogl/cogl-primitive.h b/cogl/cogl/cogl-primitive.h new file mode 100644 index 000000000..0f20bb58b --- /dev/null +++ b/cogl/cogl/cogl-primitive.h @@ -0,0 +1,942 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_PRIMITIVE_H__ +#define __COGL_PRIMITIVE_H__ + +/* We forward declare the CoglPrimitive type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglPrimitive CoglPrimitive; + +#include /* for CoglVerticesMode */ +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-primitive + * @short_description: Functions for creating, manipulating and drawing + * primitives + * + * FIXME + */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_primitive_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_primitive_get_gtype (void); +#endif + +/** + * CoglVertexP2: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p2(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y; +} CoglVertexP2; + +/** + * CoglVertexP3: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @z: The z component of a position attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p3(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y, z; +} CoglVertexP3; + +/** + * CoglVertexP2C4: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @r: The red component of a color attribute + * @b: The green component of a color attribute + * @g: The blue component of a color attribute + * @a: The alpha component of a color attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p2c4(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y; + uint8_t r, g, b, a; +} CoglVertexP2C4; + +/** + * CoglVertexP3C4: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @z: The z component of a position attribute + * @r: The red component of a color attribute + * @b: The green component of a color attribute + * @g: The blue component of a color attribute + * @a: The alpha component of a color attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p3c4(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y, z; + uint8_t r, g, b, a; +} CoglVertexP3C4; + +/** + * CoglVertexP2T2: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @s: The s component of a texture coordinate attribute + * @t: The t component of a texture coordinate attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p2t2(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y; + float s, t; +} CoglVertexP2T2; + +/** + * CoglVertexP3T2: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @z: The z component of a position attribute + * @s: The s component of a texture coordinate attribute + * @t: The t component of a texture coordinate attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p3t2(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y, z; + float s, t; +} CoglVertexP3T2; + + +/** + * CoglVertexP2T2C4: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @s: The s component of a texture coordinate attribute + * @t: The t component of a texture coordinate attribute + * @r: The red component of a color attribute + * @b: The green component of a color attribute + * @g: The blue component of a color attribute + * @a: The alpha component of a color attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p3t2c4(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y; + float s, t; + uint8_t r, g, b, a; +} CoglVertexP2T2C4; + +/** + * CoglVertexP3T2C4: + * @x: The x component of a position attribute + * @y: The y component of a position attribute + * @z: The z component of a position attribute + * @s: The s component of a texture coordinate attribute + * @t: The t component of a texture coordinate attribute + * @r: The red component of a color attribute + * @b: The green component of a color attribute + * @g: The blue component of a color attribute + * @a: The alpha component of a color attribute + * + * A convenience vertex definition that can be used with + * cogl_primitive_new_p3t2c4(). + * + * Since: 1.6 + * Stability: Unstable + */ +typedef struct { + float x, y, z; + float s, t; + uint8_t r, g, b, a; +} CoglVertexP3T2C4; + +/** + * cogl_primitive_new: + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to process when drawing + * @...: A %NULL terminated list of attributes + * + * Combines a set of #CoglAttributes with a specific draw @mode + * and defines a vertex count so a #CoglPrimitive object can be retained and + * drawn later with no addition information required. + * + * The value passed as @n_vertices will simply update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + * + * Return value: (transfer full): A newly allocated #CoglPrimitive object + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new (CoglVerticesMode mode, + int n_vertices, + ...); + +/** + * cogl_primitive_new_with_attributes: + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to process when drawing + * @attributes: An array of CoglAttribute + * @n_attributes: The number of attributes + * + * Combines a set of #CoglAttributes with a specific draw @mode + * and defines a vertex count so a #CoglPrimitive object can be retained and + * drawn later with no addition information required. + * + * The value passed as @n_vertices will simply update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + * + * Return value: (transfer full): A newly allocated #CoglPrimitive object + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_with_attributes (CoglVerticesMode mode, + int n_vertices, + CoglAttribute **attributes, + int n_attributes); + +/** + * cogl_primitive_new_p2: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP2): An array + * of #CoglVertexP2 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position + * attribute with a #CoglAttribute and upload your data. + * + * For example to draw a convex polygon you can do: + * |[ + * CoglVertexP2 triangle[] = + * { + * { 0, 300 }, + * { 150, 0, }, + * { 300, 300 } + * }; + * prim = cogl_primitive_new_p2 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p2 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2 *data); + +/** + * cogl_primitive_new_p3: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP3): An array of + * #CoglVertexP3 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position + * attribute with a #CoglAttribute and upload your data. + * + * For example to draw a convex polygon you can do: + * |[ + * CoglVertexP3 triangle[] = + * { + * { 0, 300, 0 }, + * { 150, 0, 0 }, + * { 300, 300, 0 } + * }; + * prim = cogl_primitive_new_p3 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p3 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3 *data); + +/** + * cogl_primitive_new_p2c4: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP2C4): An array + * of #CoglVertexP2C4 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position + * and color attributes with #CoglAttributes and upload + * your data. + * + * For example to draw a convex polygon with a linear gradient you + * can do: + * |[ + * CoglVertexP2C4 triangle[] = + * { + * { 0, 300, 0xff, 0x00, 0x00, 0xff }, + * { 150, 0, 0x00, 0xff, 0x00, 0xff }, + * { 300, 300, 0xff, 0x00, 0x00, 0xff } + * }; + * prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p2c4 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2C4 *data); + +/** + * cogl_primitive_new_p3c4: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP3C4): An array + * of #CoglVertexP3C4 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position + * and color attributes with #CoglAttributes and upload + * your data. + * + * For example to draw a convex polygon with a linear gradient you + * can do: + * |[ + * CoglVertexP3C4 triangle[] = + * { + * { 0, 300, 0, 0xff, 0x00, 0x00, 0xff }, + * { 150, 0, 0, 0x00, 0xff, 0x00, 0xff }, + * { 300, 300, 0, 0xff, 0x00, 0x00, 0xff } + * }; + * prim = cogl_primitive_new_p3c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p3c4 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3C4 *data); + +/** + * cogl_primitive_new_p2t2: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP2T2): An array + * of #CoglVertexP2T2 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position and + * texture coordinate attributes with #CoglAttributes and + * upload your data. + * + * For example to draw a convex polygon with texture mapping you can + * do: + * |[ + * CoglVertexP2T2 triangle[] = + * { + * { 0, 300, 0.0, 1.0}, + * { 150, 0, 0.5, 0.0}, + * { 300, 300, 1.0, 1.0} + * }; + * prim = cogl_primitive_new_p2t2 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p2t2 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2T2 *data); + +/** + * cogl_primitive_new_p3t2: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP3T2): An array + * of #CoglVertexP3T2 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position and + * texture coordinate attributes with #CoglAttributes and + * upload your data. + * + * For example to draw a convex polygon with texture mapping you can + * do: + * |[ + * CoglVertexP3T2 triangle[] = + * { + * { 0, 300, 0, 0.0, 1.0}, + * { 150, 0, 0, 0.5, 0.0}, + * { 300, 300, 0, 1.0, 1.0} + * }; + * prim = cogl_primitive_new_p3t2 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p3t2 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3T2 *data); + +/** + * cogl_primitive_new_p2t2c4: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP2T2C4): An + * array of #CoglVertexP2T2C4 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position, texture + * coordinate and color attributes with #CoglAttributes and + * upload your data. + * + * For example to draw a convex polygon with texture mapping and a + * linear gradient you can do: + * |[ + * CoglVertexP2T2C4 triangle[] = + * { + * { 0, 300, 0.0, 1.0, 0xff, 0x00, 0x00, 0xff}, + * { 150, 0, 0.5, 0.0, 0x00, 0xff, 0x00, 0xff}, + * { 300, 300, 1.0, 1.0, 0xff, 0x00, 0x00, 0xff} + * }; + * prim = cogl_primitive_new_p2t2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p2t2c4 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP2T2C4 *data); + +/** + * cogl_primitive_new_p3t2c4: + * @context: A #CoglContext + * @mode: A #CoglVerticesMode defining how to draw the vertices + * @n_vertices: The number of vertices to read from @data and also + * the number of vertices to read when later drawing. + * @data: (array length=n_vertices): (type Cogl.VertexP3T2C4): An + * array of #CoglVertexP3T2C4 vertices + * + * Provides a convenient way to describe a primitive, such as a single + * triangle strip or a triangle fan, that will internally allocate the + * necessary #CoglAttributeBuffer storage, describe the position, texture + * coordinate and color attributes with #CoglAttributes and + * upload your data. + * + * For example to draw a convex polygon with texture mapping and a + * linear gradient you can do: + * |[ + * CoglVertexP3T2C4 triangle[] = + * { + * { 0, 300, 0, 0.0, 1.0, 0xff, 0x00, 0x00, 0xff}, + * { 150, 0, 0, 0.5, 0.0, 0x00, 0xff, 0x00, 0xff}, + * { 300, 300, 0, 1.0, 1.0, 0xff, 0x00, 0x00, 0xff} + * }; + * prim = cogl_primitive_new_p3t2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, + * 3, triangle); + * cogl_primitive_draw (prim); + * ]| + * + * The value passed as @n_vertices is initially used to determine how + * much can be read from @data but it will also be used to update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to read when drawing. + + * The primitive API doesn't support drawing with sliced + * textures (since switching between slices implies changing state and + * so that implies multiple primitives need to be submitted). You + * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that + * might be used while drawing with this API. If your hardware doesn't + * support non-power of two textures (For example you are using GLES + * 1.1) then you will need to make sure your assets are resized to a + * power-of-two size (though they don't have to be square) + * + * Return value: (transfer full): A newly allocated #CoglPrimitive + * with a reference of 1. This can be freed using cogl_object_unref(). + * + * Since: 1.6 + * Stability: Unstable + */ +CoglPrimitive * +cogl_primitive_new_p3t2c4 (CoglContext *context, + CoglVerticesMode mode, + int n_vertices, + const CoglVertexP3T2C4 *data); +int +cogl_primitive_get_first_vertex (CoglPrimitive *primitive); + +void +cogl_primitive_set_first_vertex (CoglPrimitive *primitive, + int first_vertex); + +/** + * cogl_primitive_get_n_vertices: + * @primitive: A #CoglPrimitive object + * + * Queries the number of vertices to read when drawing the given + * @primitive. Usually this value is implicitly set when associating + * vertex data or indices with a #CoglPrimitive. + * + * If cogl_primitive_set_indices() has been used to associate a + * sequence of #CoglIndices with the given @primitive then the + * number of vertices to read can also be phrased as the number + * of indices to read. + * + * To be clear; it doesn't refer to the number of vertices - in + * terms of data - associated with the primitive it's just the number + * of vertices to read and draw. + * + * Returns: The number of vertices to read when drawing. + * + * Since: 1.8 + * Stability: unstable + */ +int +cogl_primitive_get_n_vertices (CoglPrimitive *primitive); + +/** + * cogl_primitive_set_n_vertices: + * @primitive: A #CoglPrimitive object + * @n_vertices: The number of vertices to read when drawing. + * + * Specifies how many vertices should be read when drawing the given + * @primitive. + * + * Usually this value is set implicitly when associating vertex data + * or indices with a #CoglPrimitive. + * + * To be clear; it doesn't refer to the number of vertices - in + * terms of data - associated with the primitive it's just the number + * of vertices to read and draw. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_primitive_set_n_vertices (CoglPrimitive *primitive, + int n_vertices); + +CoglVerticesMode +cogl_primitive_get_mode (CoglPrimitive *primitive); + +void +cogl_primitive_set_mode (CoglPrimitive *primitive, + CoglVerticesMode mode); + +/** + * cogl_primitive_set_attributes: + * @primitive: A #CoglPrimitive object + * @attributes: an array of #CoglAttribute pointers + * @n_attributes: the number of elements in @attributes + * + * Replaces all the attributes of the given #CoglPrimitive object. + * + * Since: 1.6 + * Stability: Unstable + */ +void +cogl_primitive_set_attributes (CoglPrimitive *primitive, + CoglAttribute **attributes, + int n_attributes); + +/** + * cogl_primitive_set_indices: + * @primitive: A #CoglPrimitive + * @indices: A #CoglIndices array + * @n_indices: The number of indices to reference when drawing + * + * Associates a sequence of #CoglIndices with the given @primitive. + * + * #CoglIndices provide a way to virtualize your real vertex data by + * providing a sequence of indices that index into your real vertex + * data. The GPU will walk though the index values to indirectly + * lookup the data for each vertex instead of sequentially walking + * through the data directly. This lets you save memory by indexing + * shared data multiple times instead of duplicating the data. + * + * The value passed as @n_indices will simply update the + * #CoglPrimitive n_vertices property as if + * cogl_primitive_set_n_vertices() were called. This property defines + * the number of vertices to draw or, put another way, how many + * indices should be read from @indices when drawing. + * + * The #CoglPrimitive first_vertex property + * also affects drawing with indices by defining the first entry of the + * indices to start drawing from. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_primitive_set_indices (CoglPrimitive *primitive, + CoglIndices *indices, + int n_indices); + +/** + * cogl_primitive_get_indices: + * @primitive: A #CoglPrimitive + * + * Return value: (transfer none): the indices that were set with + * cogl_primitive_set_indices() or %NULL if no indices were set. + * + * Since: 1.10 + * Stability: unstable + */ +CoglIndices * +cogl_primitive_get_indices (CoglPrimitive *primitive); + +/** + * cogl_primitive_copy: + * @primitive: A primitive copy + * + * Makes a copy of an existing #CoglPrimitive. Note that the primitive + * is a shallow copy which means it will use the same attributes and + * attribute buffers as the original primitive. + * + * Return value: (transfer full): the new primitive + * Since: 1.10 + * Stability: unstable + */ +CoglPrimitive * +cogl_primitive_copy (CoglPrimitive *primitive); + +/** + * cogl_is_primitive: + * @object: A #CoglObject + * + * Gets whether the given object references a #CoglPrimitive. + * + * Returns: %TRUE if the @object references a #CoglPrimitive, + * %FALSE otherwise + * + * Since: 1.6 + * Stability: Unstable + */ +CoglBool +cogl_is_primitive (void *object); + +/** + * CoglPrimitiveAttributeCallback: + * @primitive: The #CoglPrimitive whose attributes are being iterated + * @attribute: The #CoglAttribute + * @user_data: The private data passed to cogl_primitive_foreach_attribute() + * + * The callback prototype used with cogl_primitive_foreach_attribute() + * for iterating all the attributes of a #CoglPrimitive. + * + * The function should return TRUE to continue iteration or FALSE to + * stop. + * + * Since: 1.10 + * Stability: Unstable + */ +typedef CoglBool (* CoglPrimitiveAttributeCallback) (CoglPrimitive *primitive, + CoglAttribute *attribute, + void *user_data); + +/** + * cogl_primitive_foreach_attribute: + * @primitive: A #CoglPrimitive object + * @callback: (scope call): A #CoglPrimitiveAttributeCallback to be + * called for each attribute + * @user_data: (closure): Private data that will be passed to the + * callback + * + * Iterates all the attributes of the given #CoglPrimitive. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_primitive_foreach_attribute (CoglPrimitive *primitive, + CoglPrimitiveAttributeCallback callback, + void *user_data); + +/** + * cogl_primitive_draw: + * @primitive: A #CoglPrimitive geometry object + * @framebuffer: A destination #CoglFramebuffer + * @pipeline: A #CoglPipeline state object + * + * Draws the given @primitive geometry to the specified destination + * @framebuffer using the graphics processing state described by @pipeline. + * + * This drawing api doesn't support high-level meta texture types such + * as #CoglTexture2DSliced so it is the user's responsibility to + * ensure that only low-level textures that can be directly sampled by + * a GPU such as #CoglTexture2D, #CoglTextureRectangle or #CoglTexture3D + * are associated with layers of the given @pipeline. + * + * Stability: unstable + * Since: 1.16 + */ +void +cogl_primitive_draw (CoglPrimitive *primitive, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline); + + +COGL_END_DECLS + +#endif /* __COGL_PRIMITIVE_H__ */ + diff --git a/cogl/cogl/cogl-primitives-private.h b/cogl/cogl/cogl-primitives-private.h new file mode 100644 index 000000000..10db86905 --- /dev/null +++ b/cogl/cogl/cogl-primitives-private.h @@ -0,0 +1,67 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PRIMITIVES_PRIVATE_H +#define __COGL_PRIMITIVES_PRIVATE_H + +#include + +COGL_BEGIN_DECLS + +/* Draws a rectangle without going through the journal so that it will + be flushed immediately. This should only be used in situations + where the code may be called while the journal is already being + flushed. In that case using the journal would go wrong */ +void +_cogl_rectangle_immediate (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2); + +typedef struct _CoglMultiTexturedRect +{ + const float *position; /* x0,y0,x1,y1 */ + const float *tex_coords; /* (tx0,ty0,tx1,ty1)(tx0,ty0,tx1,ty1)(... */ + int tex_coords_len; /* number of floats in tex_coords? */ +} CoglMultiTexturedRect; + +void +_cogl_framebuffer_draw_multitextured_rectangles ( + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglMultiTexturedRect *rects, + int n_rects, + CoglBool disable_legacy_state); + +COGL_END_DECLS + +#endif /* __COGL_PRIMITIVES_PRIVATE_H */ diff --git a/cogl/cogl/cogl-primitives.c b/cogl/cogl/cogl-primitives.c new file mode 100644 index 000000000..bfe773588 --- /dev/null +++ b/cogl/cogl/cogl-primitives.c @@ -0,0 +1,1148 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-context-private.h" +#include "cogl-journal-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-vertex-buffer-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-attribute-private.h" +#include "cogl-private.h" +#include "cogl-meta-texture.h" +#include "cogl-framebuffer-private.h" +#include "cogl1-context.h" +#include "cogl-primitives-private.h" + +#include +#include + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +typedef struct _TextureSlicedQuadState +{ + CoglFramebuffer *framebuffer; + CoglPipeline *pipeline; + CoglTexture *main_texture; + float tex_virtual_origin_x; + float tex_virtual_origin_y; + float quad_origin_x; + float quad_origin_y; + float v_to_q_scale_x; + float v_to_q_scale_y; + float quad_len_x; + float quad_len_y; + CoglBool flipped_x; + CoglBool flipped_y; +} TextureSlicedQuadState; + +typedef struct _TextureSlicedPolygonState +{ + const CoglTextureVertex *vertices; + int n_vertices; + int stride; + CoglAttribute **attributes; +} TextureSlicedPolygonState; + +static void +log_quad_sub_textures_cb (CoglTexture *texture, + const float *subtexture_coords, + const float *virtual_coords, + void *user_data) +{ + TextureSlicedQuadState *state = user_data; + CoglFramebuffer *framebuffer = state->framebuffer; + CoglTexture *texture_override; + float quad_coords[4]; + +#define TEX_VIRTUAL_TO_QUAD(V, Q, AXIS) \ + do { \ + Q = V - state->tex_virtual_origin_##AXIS; \ + Q *= state->v_to_q_scale_##AXIS; \ + if (state->flipped_##AXIS) \ + Q = state->quad_len_##AXIS - Q; \ + Q += state->quad_origin_##AXIS; \ + } while (0); + + TEX_VIRTUAL_TO_QUAD (virtual_coords[0], quad_coords[0], x); + TEX_VIRTUAL_TO_QUAD (virtual_coords[1], quad_coords[1], y); + + TEX_VIRTUAL_TO_QUAD (virtual_coords[2], quad_coords[2], x); + TEX_VIRTUAL_TO_QUAD (virtual_coords[3], quad_coords[3], y); + +#undef TEX_VIRTUAL_TO_QUAD + + COGL_NOTE (DRAW, + "~~~~~ slice\n" + "qx1: %f\t" + "qy1: %f\n" + "qx2: %f\t" + "qy2: %f\n" + "tx1: %f\t" + "ty1: %f\n" + "tx2: %f\t" + "ty2: %f\n", + quad_coords[0], quad_coords[1], + quad_coords[2], quad_coords[3], + subtexture_coords[0], subtexture_coords[1], + subtexture_coords[2], subtexture_coords[3]); + + /* We only need to override the texture if it's different from the + main texture */ + if (texture == state->main_texture) + texture_override = NULL; + else + texture_override = texture; + + _cogl_journal_log_quad (framebuffer->journal, + quad_coords, + state->pipeline, + 1, /* one layer */ + texture_override, /* replace the layer0 texture */ + subtexture_coords, + 4); +} + +typedef struct _ValidateFirstLayerState +{ + CoglPipeline *override_pipeline; +} ValidateFirstLayerState; + +static CoglBool +validate_first_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateFirstLayerState *state = user_data; + CoglPipelineWrapMode clamp_to_edge = + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + CoglPipelineWrapMode wrap_s; + CoglPipelineWrapMode wrap_t; + + /* We can't use hardware repeat so we need to set clamp to edge + * otherwise it might pull in edge pixels from the other side. By + * default WRAP_MODE_AUTOMATIC becomes CLAMP_TO_EDGE so we only need + * to override if the wrap mode isn't already automatic or + * clamp_to_edge. + */ + wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index); + if (wrap_s != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && + wrap_s != COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline, + layer_index, clamp_to_edge); + } + + wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index); + if (wrap_t != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && + wrap_t != COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline, + layer_index, clamp_to_edge); + } + + return FALSE; +} + +/* This path doesn't currently support multitexturing but is used for + * CoglTextures that don't support repeating using the GPU so we need to + * manually emit extra geometry to fake the repeating. This includes: + * + * - CoglTexture2DSliced: when made of > 1 slice or if the users given + * texture coordinates require repeating, + * - CoglTexture2DAtlas: if the users given texture coordinates require + * repeating, + * - CoglTextureRectangle: if the users given texture coordinates require + * repeating, + * - CoglTexturePixmap: if the users given texture coordinates require + * repeating + */ +/* TODO: support multitexturing */ +static void +_cogl_texture_quad_multiple_primitives (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglTexture *texture, + int layer_index, + const float *position, + float tx_1, + float ty_1, + float tx_2, + float ty_2) +{ + TextureSlicedQuadState state; + CoglBool tex_virtual_flipped_x; + CoglBool tex_virtual_flipped_y; + CoglBool quad_flipped_x; + CoglBool quad_flipped_y; + ValidateFirstLayerState validate_first_layer_state; + CoglPipelineWrapMode wrap_s, wrap_t; + + wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index); + wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index); + + validate_first_layer_state.override_pipeline = NULL; + cogl_pipeline_foreach_layer (pipeline, + validate_first_layer_cb, + &validate_first_layer_state); + + state.framebuffer = framebuffer; + state.main_texture = texture; + + if (validate_first_layer_state.override_pipeline) + state.pipeline = validate_first_layer_state.override_pipeline; + else + state.pipeline = pipeline; + + /* Get together the data we need to transform the virtual texture + * coordinates of each slice into quad coordinates... + * + * NB: We need to consider that the quad coordinates and the texture + * coordinates may be inverted along the x or y axis, and must preserve the + * inversions when we emit the final geometry. + */ + +#define X0 0 +#define Y0 1 +#define X1 2 +#define Y1 3 + + tex_virtual_flipped_x = (tx_1 > tx_2) ? TRUE : FALSE; + tex_virtual_flipped_y = (ty_1 > ty_2) ? TRUE : FALSE; + state.tex_virtual_origin_x = tex_virtual_flipped_x ? tx_2 : tx_1; + state.tex_virtual_origin_y = tex_virtual_flipped_y ? ty_2 : ty_1; + + quad_flipped_x = (position[X0] > position[X1]) ? TRUE : FALSE; + quad_flipped_y = (position[Y0] > position[Y1]) ? TRUE : FALSE; + state.quad_origin_x = quad_flipped_x ? position[X1] : position[X0]; + state.quad_origin_y = quad_flipped_y ? position[Y1] : position[Y0]; + + /* flatten the two forms of coordinate inversion into one... */ + state.flipped_x = tex_virtual_flipped_x ^ quad_flipped_x; + state.flipped_y = tex_virtual_flipped_y ^ quad_flipped_y; + + /* We use the _len_AXIS naming here instead of _width and _height because + * log_quad_slice_cb uses a macro with symbol concatenation to handle both + * axis, so this is more convenient... */ + state.quad_len_x = fabs (position[X1] - position[X0]); + state.quad_len_y = fabs (position[Y1] - position[Y0]); + +#undef X0 +#undef Y0 +#undef X1 +#undef Y1 + + state.v_to_q_scale_x = fabs (state.quad_len_x / (tx_2 - tx_1)); + state.v_to_q_scale_y = fabs (state.quad_len_y / (ty_2 - ty_1)); + + /* For backwards compatablity the default wrap mode for cogl_rectangle() is + * _REPEAT... */ + if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT; + if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT; + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture), + tx_1, ty_1, tx_2, ty_2, + wrap_s, + wrap_t, + log_quad_sub_textures_cb, + &state); + + if (validate_first_layer_state.override_pipeline) + cogl_object_unref (validate_first_layer_state.override_pipeline); +} + +typedef struct _ValidateTexCoordsState +{ + int i; + int n_layers; + const float *user_tex_coords; + int user_tex_coords_len; + float *final_tex_coords; + CoglPipeline *override_pipeline; + CoglBool needs_multiple_primitives; +} ValidateTexCoordsState; + +/* + * Validate the texture coordinates for this rectangle. + */ +static CoglBool +validate_tex_coords_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateTexCoordsState *state = user_data; + CoglTexture *texture; + const float *in_tex_coords; + float *out_tex_coords; + float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + CoglTransformResult transform_result; + + state->i++; + + /* FIXME: we should be able to avoid this copying when no + * transform is required by the texture backend and the user + * has supplied enough coordinates for all the layers. + */ + + /* If the user didn't supply texture coordinates for this layer + then use the default coords */ + if (state->i >= state->user_tex_coords_len / 4) + in_tex_coords = default_tex_coords; + else + in_tex_coords = &state->user_tex_coords[state->i * 4]; + + out_tex_coords = &state->final_tex_coords[state->i * 4]; + + memcpy (out_tex_coords, in_tex_coords, sizeof (float) * 4); + + texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); + + /* NB: NULL textures are handled by _cogl_pipeline_flush_gl_state */ + if (!texture) + return TRUE; + + /* Convert the texture coordinates to GL. + */ + transform_result = + _cogl_texture_transform_quad_coords_to_gl (texture, + out_tex_coords); + /* If the texture has waste or we are using GL_TEXTURE_RECT we + * can't handle texture repeating so we can't use the layer if + * repeating is required. + * + * NB: We already know that no texture matrix is being used if the + * texture doesn't support hardware repeat. + */ + if (transform_result == COGL_TRANSFORM_SOFTWARE_REPEAT) + { + if (state->i == 0) + { + if (state->n_layers > 1) + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + g_warning ("Skipping layers 1..n of your material since " + "the first layer doesn't support hardware " + "repeat (e.g. because of waste or use of " + "GL_TEXTURE_RECTANGLE_ARB) and you supplied " + "texture coordinates outside the range [0,1]." + "Falling back to software repeat assuming " + "layer 0 is the most important one keep"); + warning_seen = TRUE; + } + + if (state->override_pipeline) + cogl_object_unref (state->override_pipeline); + state->needs_multiple_primitives = TRUE; + return FALSE; + } + else + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + g_warning ("Skipping layer %d of your material " + "since you have supplied texture coords " + "outside the range [0,1] but the texture " + "doesn't support hardware repeat (e.g. " + "because of waste or use of " + "GL_TEXTURE_RECTANGLE_ARB). This isn't " + "supported with multi-texturing.", state->i); + warning_seen = TRUE; + + cogl_pipeline_set_layer_texture (pipeline, layer_index, NULL); + } + } + + /* By default WRAP_MODE_AUTOMATIC becomes to CLAMP_TO_EDGE. If + the texture coordinates need repeating then we'll override + this to GL_REPEAT. Otherwise we'll leave it at CLAMP_TO_EDGE + so that it won't blend in pixels from the opposite side when + the full texture is drawn with GL_LINEAR filter mode */ + if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT) + { + if (cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline, + layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline, + layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + } + + return TRUE; +} + +/* This path supports multitexturing but only when each of the layers is + * handled with a single GL texture. Also if repeating is necessary then + * _cogl_texture_can_hardware_repeat() must return TRUE. + * This includes layers made from: + * + * - CoglTexture2DSliced: if only comprised of a single slice with optional + * waste, assuming the users given texture coordinates don't require + * repeating. + * - CoglTexture{1D,2D,3D}: always. + * - CoglTexture2DAtlas: assuming the users given texture coordinates don't + * require repeating. + * - CoglTextureRectangle: assuming the users given texture coordinates don't + * require repeating. + * - CoglTexturePixmap: assuming the users given texture coordinates don't + * require repeating. + */ +static CoglBool +_cogl_multitexture_quad_single_primitive (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + const float *position, + const float *user_tex_coords, + int user_tex_coords_len) +{ + int n_layers = cogl_pipeline_get_n_layers (pipeline); + ValidateTexCoordsState state; + float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); + + state.i = -1; + state.n_layers = n_layers; + state.user_tex_coords = user_tex_coords; + state.user_tex_coords_len = user_tex_coords_len; + state.final_tex_coords = final_tex_coords; + state.override_pipeline = NULL; + state.needs_multiple_primitives = FALSE; + + cogl_pipeline_foreach_layer (pipeline, + validate_tex_coords_cb, + &state); + + if (state.needs_multiple_primitives) + return FALSE; + + if (state.override_pipeline) + pipeline = state.override_pipeline; + + _cogl_journal_log_quad (framebuffer->journal, + position, + pipeline, + n_layers, + NULL, /* no texture override */ + final_tex_coords, + n_layers * 4); + + if (state.override_pipeline) + cogl_object_unref (state.override_pipeline); + + return TRUE; +} + +typedef struct _ValidateLayerState +{ + CoglContext *ctx; + int i; + int first_layer; + CoglPipeline *override_source; + CoglBool all_use_sliced_quad_fallback; +} ValidateLayerState; + +static CoglBool +_cogl_rectangles_validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateLayerState *state = user_data; + CoglTexture *texture; + + state->i++; + + /* We need to ensure the mipmaps are ready before deciding + * anything else about the texture because the texture storage + * could completely change if it needs to be migrated out of the + * atlas and will affect how we validate the layer. + * + * FIXME: this needs to be generalized. There could be any + * number of things that might require a shuffling of the + * underlying texture storage. We could add two mechanisms to + * generalize this a bit... + * + * 1) add a _cogl_pipeline_layer_update_storage() function that + * would for instance consider if mipmapping is necessary and + * potentially migrate the texture from an atlas. + * + * 2) allow setting of transient primitive-flags on a pipeline + * that may affect the outcome of _update_storage(). One flag + * could indicate that we expect to sample beyond the bounds of + * the texture border. + * + * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS; + * _cogl_pipeline_layer_assert_primitive_flags (layer, flags) + * _cogl_pipeline_layer_update_storage (layer) + * enqueue primitive in journal + * + * when the primitive is dequeued and drawn we should: + * _cogl_pipeline_flush_gl_state (pipeline) + * draw primitive + * _cogl_pipeline_unassert_primitive_flags (layer, flags); + * + * _cogl_pipeline_layer_update_storage should take into + * consideration all the asserted primitive requirements. (E.g. + * there could be multiple primitives in the journal - or in a + * renderlist in the future - that need mipmaps or that need + * valid contents beyond their borders (for cogl_polygon) + * meaning they can't work with textures in an atas, so + * _cogl_pipeline_layer_update_storage would pass on these + * requirements to the texture atlas backend which would make + * sure the referenced texture is migrated out of the atlas and + * mipmaps are generated.) + */ + _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); + + texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); + + /* NULL textures are handled by + * _cogl_pipeline_flush_gl_state */ + if (texture == NULL) + return TRUE; + + if (state->i == 0) + state->first_layer = layer_index; + + /* XXX: + * For now, if the first layer is sliced then all other layers are + * ignored since we currently don't support multi-texturing with + * sliced textures. If the first layer is not sliced then any other + * layers found to be sliced will be skipped. (with a warning) + * + * TODO: Add support for multi-texturing rectangles with sliced + * textures if no texture matrices are in use. + */ + if (cogl_texture_is_sliced (texture)) + { + if (state->i == 0) + { + if (cogl_pipeline_get_n_layers (pipeline) > 1) + { + static CoglBool warning_seen = FALSE; + + if (!state->override_source) + state->override_source = cogl_pipeline_copy (pipeline); + _cogl_pipeline_prune_to_n_layers (state->override_source, 1); + + if (!warning_seen) + g_warning ("Skipping layers 1..n of your pipeline since " + "the first layer is sliced. We don't currently " + "support any multi-texturing with sliced " + "textures but assume layer 0 is the most " + "important to keep"); + warning_seen = TRUE; + } + + state->all_use_sliced_quad_fallback = TRUE; + + return FALSE; + } + else + { + static CoglBool warning_seen = FALSE; + CoglTexture2D *tex_2d; + + if (!warning_seen) + g_warning ("Skipping layer %d of your pipeline consisting of " + "a sliced texture (unsuported for multi texturing)", + state->i); + warning_seen = TRUE; + + /* Note: currently only 2D textures can be sliced. */ + tex_2d = state->ctx->default_gl_texture_2d_tex; + cogl_pipeline_set_layer_texture (pipeline, layer_index, + COGL_TEXTURE (tex_2d)); + return TRUE; + } + } + +#ifdef COGL_ENABLE_DEBUG + /* If the texture can't be repeated with the GPU (e.g. because it has + * waste or if using GL_TEXTURE_RECTANGLE_ARB) then if a texture matrix + * is also in use we don't know if the result will end up trying + * to texture from the waste area. + * + * Note: we check can_hardware_repeat() first since it's cheaper. + * + * Note: cases where the texture coordinates will require repeating + * will be caught by later validation. + */ + if (!_cogl_texture_can_hardware_repeat (texture) && + _cogl_pipeline_layer_has_user_matrix (pipeline, layer_index)) + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + g_warning ("layer %d of your pipeline uses a custom " + "texture matrix but because the texture doesn't " + "support hardware repeating you may see artefacts " + "due to sampling beyond the texture's bounds.", + state->i); + warning_seen = TRUE; + } +#endif + + return TRUE; +} + +void +_cogl_framebuffer_draw_multitextured_rectangles ( + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglMultiTexturedRect *rects, + int n_rects, + CoglBool disable_legacy_state) +{ + CoglContext *ctx = framebuffer->context; + CoglPipeline *original_pipeline; + ValidateLayerState state; + int i; + + original_pipeline = pipeline; + + /* + * Validate all the layers of the current source pipeline... + */ + state.ctx = ctx; + state.i = -1; + state.first_layer = 0; + state.override_source = NULL; + state.all_use_sliced_quad_fallback = FALSE; + cogl_pipeline_foreach_layer (pipeline, + _cogl_rectangles_validate_layer_cb, + &state); + + if (state.override_source) + pipeline = state.override_source; + + if (!disable_legacy_state) + { + if (G_UNLIKELY (ctx->legacy_state_set) && + _cogl_get_enable_legacy_state ()) + { + /* If we haven't already made a pipeline copy */ + if (pipeline == original_pipeline) + pipeline = cogl_pipeline_copy (pipeline); + _cogl_pipeline_apply_legacy_state (pipeline); + } + } + + /* + * Emit geometry for each of the rectangles... + */ + + for (i = 0; i < n_rects; i++) + { + CoglTexture *texture; + const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + const float *tex_coords; + + if (!state.all_use_sliced_quad_fallback) + { + CoglBool success = + _cogl_multitexture_quad_single_primitive (framebuffer, + pipeline, + rects[i].position, + rects[i].tex_coords, + rects[i].tex_coords_len); + + /* NB: If _cogl_multitexture_quad_single_primitive fails then it + * means the user tried to use texture repeat with a texture that + * can't be repeated by the GPU (e.g. due to waste or use of + * GL_TEXTURE_RECTANGLE_ARB) */ + if (success) + continue; + } + + /* If multitexturing failed or we are drawing with a sliced texture + * then we only support a single layer so we pluck out the texture + * from the first pipeline layer... */ + texture = cogl_pipeline_get_layer_texture (pipeline, state.first_layer); + + if (rects[i].tex_coords) + tex_coords = rects[i].tex_coords; + else + tex_coords = default_tex_coords; + + COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)"); + + _cogl_texture_quad_multiple_primitives (framebuffer, + pipeline, + texture, + state.first_layer, + rects[i].position, + tex_coords[0], + tex_coords[1], + tex_coords[2], + tex_coords[3]); + } + + if (pipeline != original_pipeline) + cogl_object_unref (pipeline); +} + +static void +_cogl_rectangles_with_multitexture_coords ( + CoglMultiTexturedRect *rects, + int n_rects) +{ + _cogl_framebuffer_draw_multitextured_rectangles (cogl_get_draw_framebuffer (), + cogl_get_source (), + rects, + n_rects, + FALSE); +} + +void +cogl_rectangles (const float *verts, + unsigned int n_rects) +{ + CoglMultiTexturedRect *rects; + int i; + + /* XXX: All the cogl_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_rectangles_with_multitexture_coords. + */ + + rects = g_alloca (n_rects * sizeof (CoglMultiTexturedRect)); + + for (i = 0; i < n_rects; i++) + { + rects[i].position = &verts[i * 4]; + rects[i].tex_coords = NULL; + rects[i].tex_coords_len = 0; + } + + _cogl_rectangles_with_multitexture_coords (rects, n_rects); +} + +void +cogl_rectangles_with_texture_coords (const float *verts, + unsigned int n_rects) +{ + CoglMultiTexturedRect *rects; + int i; + + /* XXX: All the cogl_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_rectangles_with_multitexture_coords. + */ + + rects = g_alloca (n_rects * sizeof (CoglMultiTexturedRect)); + + for (i = 0; i < n_rects; i++) + { + rects[i].position = &verts[i * 8]; + rects[i].tex_coords = &verts[i * 8 + 4]; + rects[i].tex_coords_len = 4; + } + + _cogl_rectangles_with_multitexture_coords (rects, n_rects); +} + +void +cogl_rectangle_with_texture_coords (float x_1, + float y_1, + float x_2, + float y_2, + float tx_1, + float ty_1, + float tx_2, + float ty_2) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + const float tex_coords[4] = {tx_1, ty_1, tx_2, ty_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the cogl_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_rectangles_with_multitexture_coords. + */ + + rect.position = position; + rect.tex_coords = tex_coords; + rect.tex_coords_len = 4; + + _cogl_rectangles_with_multitexture_coords (&rect, 1); +} + +void +cogl_rectangle_with_multitexture_coords (float x_1, + float y_1, + float x_2, + float y_2, + const float *user_tex_coords, + int user_tex_coords_len) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the cogl_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_rectangles_with_multitexture_coords. + */ + + rect.position = position; + rect.tex_coords = user_tex_coords; + rect.tex_coords_len = user_tex_coords_len; + + _cogl_rectangles_with_multitexture_coords (&rect, 1); +} + +void +cogl_rectangle (float x_1, + float y_1, + float x_2, + float y_2) +{ + const float position[4] = {x_1, y_1, x_2, y_2}; + CoglMultiTexturedRect rect; + + /* XXX: All the cogl_rectangle* APIs normalize their input into an array of + * CoglMultiTexturedRect rectangles and pass these on to our work horse; + * _cogl_rectangles_with_multitexture_coords. + */ + + rect.position = position; + rect.tex_coords = NULL; + rect.tex_coords_len = 0; + + _cogl_rectangles_with_multitexture_coords (&rect, 1); +} + +void +_cogl_rectangle_immediate (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + float x_1, + float y_1, + float x_2, + float y_2) +{ + /* Draw a rectangle using the vertex array API to avoid going + through the journal. This should only be used in cases where the + code might be called while the journal is already being flushed + such as when flushing the clip state */ + CoglContext *ctx = framebuffer->context; + float vertices[8] = + { + x_1, y_1, + x_1, y_2, + x_2, y_1, + x_2, y_2 + }; + CoglAttributeBuffer *attribute_buffer; + CoglAttribute *attributes[1]; + + attribute_buffer = + cogl_attribute_buffer_new (ctx, sizeof (vertices), vertices); + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (float) * 2, /* stride */ + 0, /* offset */ + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + _cogl_framebuffer_draw_attributes (framebuffer, + pipeline, + COGL_VERTICES_MODE_TRIANGLE_STRIP, + 0, /* first_index */ + 4, /* n_vertices */ + attributes, + 1, + COGL_DRAW_SKIP_JOURNAL_FLUSH | + COGL_DRAW_SKIP_PIPELINE_VALIDATION | + COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH | + COGL_DRAW_SKIP_LEGACY_STATE); + + + cogl_object_unref (attributes[0]); + cogl_object_unref (attribute_buffer); +} + +typedef struct _AppendTexCoordsState +{ + const CoglTextureVertex *vertices_in; + int vertex; + int layer; + float *vertices_out; +} AppendTexCoordsState; + +static CoglBool +append_tex_coord_attributes_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + AppendTexCoordsState *state = user_data; + CoglTexture *texture; + float tx, ty; + float *t; + + tx = state->vertices_in[state->vertex].tx; + ty = state->vertices_in[state->vertex].ty; + + /* NULL textures will be handled in + * _cogl_pipeline_flush_layers_gl_state but there is no need to worry + * about scaling texture coordinates in this case */ + texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); + if (texture != NULL) + _cogl_texture_transform_coords_to_gl (texture, &tx, &ty); + + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + t = state->vertices_out + 3 + 2 * state->layer; + t[0] = tx; + t[1] = ty; + + state->layer++; + + return TRUE; +} + +typedef struct _ValidateState +{ + CoglPipeline *original_pipeline; + CoglPipeline *pipeline; +} ValidateState; + +static CoglBool +_cogl_polygon_validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateState *state = user_data; + + /* By default COGL_PIPELINE_WRAP_MODE_AUTOMATIC becomes + * GL_CLAMP_TO_EDGE but we want the polygon API to use GL_REPEAT to + * maintain compatibility with previous releases + */ + + if (cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (state->original_pipeline == state->pipeline) + state->pipeline = cogl_pipeline_copy (pipeline); + + cogl_pipeline_set_layer_wrap_mode_s (state->pipeline, layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + + if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (state->original_pipeline == state->pipeline) + state->pipeline = cogl_pipeline_copy (pipeline); + + cogl_pipeline_set_layer_wrap_mode_t (state->pipeline, layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + + return TRUE; +} + +void +cogl_polygon (const CoglTextureVertex *vertices, + unsigned int n_vertices, + CoglBool use_color) +{ + CoglPipeline *pipeline; + ValidateState validate_state; + int n_layers; + int n_attributes; + CoglAttribute **attributes; + int i; + unsigned int stride; + size_t stride_bytes; + CoglAttributeBuffer *attribute_buffer; + float *v; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + pipeline = cogl_get_source (); + + validate_state.original_pipeline = pipeline; + validate_state.pipeline = pipeline; + cogl_pipeline_foreach_layer (pipeline, + _cogl_polygon_validate_layer_cb, + &validate_state); + pipeline = validate_state.pipeline; + + n_layers = cogl_pipeline_get_n_layers (pipeline); + + n_attributes = 1 + n_layers + (use_color ? 1 : 0); + attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); + + /* Our data is arranged like: + * [X, Y, Z, TX0, TY0, TX1, TY1..., R, G, B, A,...] */ + stride = 3 + (2 * n_layers) + (use_color ? 1 : 0); + stride_bytes = stride * sizeof (float); + + /* Make sure there is enough space in the global vertex array. This + * is used so we can render the polygon with a single call to OpenGL + * but still support any number of vertices */ + g_array_set_size (ctx->polygon_vertices, n_vertices * stride); + + attribute_buffer = + cogl_attribute_buffer_new (ctx, n_vertices * stride_bytes, NULL); + + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + stride_bytes, + 0, + 3, + COGL_ATTRIBUTE_TYPE_FLOAT); + + for (i = 0; i < n_layers; i++) + { + static const char *names[] = { + "cogl_tex_coord0_in", + "cogl_tex_coord1_in", + "cogl_tex_coord2_in", + "cogl_tex_coord3_in", + "cogl_tex_coord4_in", + "cogl_tex_coord5_in", + "cogl_tex_coord6_in", + "cogl_tex_coord7_in" + }; + char *allocated_name = NULL; + const char *name; + + if (i < 8) + name = names[i]; + else + name = allocated_name = g_strdup_printf ("cogl_tex_coord%d_in", i); + + attributes[i + 1] = cogl_attribute_new (attribute_buffer, + name, + stride_bytes, + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + 12 + 8 * i, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + + g_free (allocated_name); + } + + if (use_color) + { + attributes[n_attributes - 1] = + cogl_attribute_new (attribute_buffer, + "cogl_color_in", + stride_bytes, + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + 12 + 8 * n_layers, + 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + } + + /* Convert the vertices into an array of float vertex attributes */ + v = (float *)ctx->polygon_vertices->data; + for (i = 0; i < n_vertices; i++) + { + AppendTexCoordsState append_tex_coords_state; + uint8_t *c; + + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + v[0] = vertices[i].x; + v[1] = vertices[i].y; + v[2] = vertices[i].z; + + append_tex_coords_state.vertices_in = vertices; + append_tex_coords_state.vertex = i; + append_tex_coords_state.layer = 0; + append_tex_coords_state.vertices_out = v; + cogl_pipeline_foreach_layer (pipeline, + append_tex_coord_attributes_cb, + &append_tex_coords_state); + + if (use_color) + { + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + c = (uint8_t *) (v + 3 + 2 * n_layers); + c[0] = cogl_color_get_red_byte (&vertices[i].color); + c[1] = cogl_color_get_green_byte (&vertices[i].color); + c[2] = cogl_color_get_blue_byte (&vertices[i].color); + c[3] = cogl_color_get_alpha_byte (&vertices[i].color); + } + + v += stride; + } + + v = (float *)ctx->polygon_vertices->data; + cogl_buffer_set_data (COGL_BUFFER (attribute_buffer), + 0, + v, + ctx->polygon_vertices->len * sizeof (float)); + + /* XXX: although this may seem redundant, we need to do this since + * cogl_polygon() can be used with legacy state and its the source stack + * which track whether legacy state is enabled. + * + * (We only have a CoglDrawFlag to disable legacy state not one + * to enable it) */ + cogl_push_source (pipeline); + + _cogl_framebuffer_draw_attributes (cogl_get_draw_framebuffer (), + pipeline, + COGL_VERTICES_MODE_TRIANGLE_FAN, + 0, n_vertices, + attributes, + n_attributes, + 0 /* no draw flags */); + + cogl_pop_source (); + + if (pipeline != validate_state.original_pipeline) + cogl_object_unref (pipeline); + + cogl_object_unref (attribute_buffer); + + for (i = 0; i < n_attributes; i++) + cogl_object_unref (attributes[i]); +} diff --git a/cogl/cogl/cogl-primitives.h b/cogl/cogl/cogl-primitives.h new file mode 100644 index 000000000..0c9211eee --- /dev/null +++ b/cogl/cogl/cogl-primitives.h @@ -0,0 +1,197 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PRIMITIVES_H +#define __COGL_PRIMITIVES_H + +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-primitives + * @short_description: Functions that draw various primitive 3D shapes + * + * The primitives API provides utilities for drawing some + * common 3D shapes in a more convenient way than the CoglVertexBuffer + * API provides. + */ + +/** + * cogl_rectangle: + * @x_1: X coordinate of the top-left corner + * @y_1: Y coordinate of the top-left corner + * @x_2: X coordinate of the bottom-right corner + * @y_2: Y coordinate of the bottom-right corner + * + * Fills a rectangle at the given coordinates with the current source material + **/ +void +cogl_rectangle (float x_1, + float y_1, + float x_2, + float y_2); + +/** + * cogl_rectangle_with_texture_coords: + * @x1: x coordinate upper left on screen. + * @y1: y coordinate upper left on screen. + * @x2: x coordinate lower right on screen. + * @y2: y coordinate lower right on screen. + * @tx1: x part of texture coordinate to use for upper left pixel + * @ty1: y part of texture coordinate to use for upper left pixel + * @tx2: x part of texture coordinate to use for lower right pixel + * @ty2: y part of texture coordinate to use for left pixel + * + * Draw a rectangle using the current material and supply texture coordinates + * to be used for the first texture layer of the material. To draw the entire + * texture pass in @tx1=0.0 @ty1=0.0 @tx2=1.0 @ty2=1.0. + * + * Since: 1.0 + */ +void +cogl_rectangle_with_texture_coords (float x1, + float y1, + float x2, + float y2, + float tx1, + float ty1, + float tx2, + float ty2); + +/** + * cogl_rectangle_with_multitexture_coords: + * @x1: x coordinate upper left on screen. + * @y1: y coordinate upper left on screen. + * @x2: x coordinate lower right on screen. + * @y2: y coordinate lower right on screen. + * @tex_coords: (in) (array) (transfer none): An array containing groups of + * 4 float values: [tx1, ty1, tx2, ty2] that are interpreted as two texture + * coordinates; one for the upper left texel, and one for the lower right + * texel. Each value should be between 0.0 and 1.0, where the coordinate + * (0.0, 0.0) represents the top left of the texture, and (1.0, 1.0) the + * bottom right. + * @tex_coords_len: The length of the tex_coords array. (e.g. for one layer + * and one group of texture coordinates, this would be 4) + * + * This function draws a rectangle using the current source material to + * texture or fill with. As a material may contain multiple texture layers + * this interface lets you supply texture coordinates for each layer of the + * material. + * + * The first pair of coordinates are for the first layer (with the smallest + * layer index) and if you supply less texture coordinates than there are + * layers in the current source material then default texture coordinates + * (0.0, 0.0, 1.0, 1.0) are generated. + * + * Since: 1.0 + */ +void +cogl_rectangle_with_multitexture_coords (float x1, + float y1, + float x2, + float y2, + const float *tex_coords, + int tex_coords_len); + +/** + * cogl_rectangles_with_texture_coords: + * @verts: (in) (array) (transfer none): an array of vertices + * @n_rects: number of rectangles to draw + * + * Draws a series of rectangles in the same way that + * cogl_rectangle_with_texture_coords() does. In some situations it can give a + * significant performance boost to use this function rather than + * calling cogl_rectangle_with_texture_coords() separately for each rectangle. + * + * @verts should point to an array of #floats with + * @n_rects * 8 elements. Each group of 8 values corresponds to the + * parameters x1, y1, x2, y2, tx1, ty1, tx2 and ty2 and have the same + * meaning as in cogl_rectangle_with_texture_coords(). + * + * Since: 0.8.6 + */ +void +cogl_rectangles_with_texture_coords (const float *verts, + unsigned int n_rects); + +/** + * cogl_rectangles: + * @verts: (in) (array) (transfer none): an array of vertices + * @n_rects: number of rectangles to draw + * + * Draws a series of rectangles in the same way that + * cogl_rectangle() does. In some situations it can give a + * significant performance boost to use this function rather than + * calling cogl_rectangle() separately for each rectangle. + * + * @verts should point to an array of #floats with + * @n_rects * 4 elements. Each group of 4 values corresponds to the + * parameters x1, y1, x2, and y2, and have the same + * meaning as in cogl_rectangle(). + * + * Since: 1.0 + */ +void +cogl_rectangles (const float *verts, + unsigned int n_rects); + +/** + * cogl_polygon: + * @vertices: An array of #CoglTextureVertex structs + * @n_vertices: The length of the vertices array + * @use_color: %TRUE if the color member of #CoglTextureVertex should be used + * + * Draws a convex polygon using the current source material to fill / texture + * with according to the texture coordinates passed. + * + * If @use_color is %TRUE then the color will be changed for each vertex using + * the value specified in the color member of #CoglTextureVertex. This can be + * used for example to make the texture fade out by setting the alpha value of + * the color. + * + * All of the texture coordinates must be in the range [0,1] and repeating the + * texture is not supported. + * + * Because of the way this function is implemented it will currently + * only work if either the texture is not sliced or the backend is not + * OpenGL ES and the minifying and magnifying functions are both set + * to COGL_MATERIAL_FILTER_NEAREST. + * + * Since: 1.0 + */ +void +cogl_polygon (const CoglTextureVertex *vertices, + unsigned int n_vertices, + CoglBool use_color); + +COGL_END_DECLS + +#endif /* __COGL_PRIMITIVES_H */ diff --git a/cogl/cogl/cogl-private.h b/cogl/cogl/cogl-private.h new file mode 100644 index 000000000..333955c65 --- /dev/null +++ b/cogl/cogl/cogl-private.h @@ -0,0 +1,170 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PRIVATE_H__ +#define __COGL_PRIVATE_H__ + +#include + +#include "cogl-context.h" +#include "cogl-flags.h" + +COGL_BEGIN_DECLS + +typedef enum +{ + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, + COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, + COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, + COGL_PRIVATE_FEATURE_PBOS, + COGL_PRIVATE_FEATURE_VBOS, + COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL, + COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888, + COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE, + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS, + COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT, + COGL_PRIVATE_FEATURE_ALPHA_TEST, + COGL_PRIVATE_FEATURE_FORMAT_CONVERSION, + COGL_PRIVATE_FEATURE_QUADS, + COGL_PRIVATE_FEATURE_BLEND_CONSTANT, + COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS, + COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM, + COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS, + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE, + COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL, + COGL_PRIVATE_FEATURE_ARBFP, + COGL_PRIVATE_FEATURE_OES_EGL_SYNC, + /* If this is set then the winsys is responsible for queueing dirty + * events. Otherwise a dirty event will be queued when the onscreen + * is first allocated or when it is shown or resized */ + COGL_PRIVATE_FEATURE_DIRTY_EVENTS, + COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE, + /* These features let us avoid conditioning code based on the exact + * driver being used and instead check for broad opengl feature + * sets that can be shared by several GL apis */ + COGL_PRIVATE_FEATURE_ANY_GL, + COGL_PRIVATE_FEATURE_GL_FIXED, + COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, + COGL_PRIVATE_FEATURE_GL_EMBEDDED, + COGL_PRIVATE_FEATURE_GL_WEB, + + COGL_N_PRIVATE_FEATURES +} CoglPrivateFeature; + +/* Sometimes when evaluating pipelines, either during comparisons or + * if calculating a hash value we need to tweak the evaluation + * semantics */ +typedef enum _CoglPipelineEvalFlags +{ + COGL_PIPELINE_EVAL_FLAG_NONE = 0 +} CoglPipelineEvalFlags; + +void +_cogl_transform_point (const CoglMatrix *matrix_mv, + const CoglMatrix *matrix_p, + const float *viewport, + float *x, + float *y); + +CoglBool +_cogl_check_extension (const char *name, char * const *ext); + +void +_cogl_clear (const CoglColor *color, unsigned long buffers); + +void +_cogl_init (void); + +void +_cogl_push_source (CoglPipeline *pipeline, CoglBool enable_legacy); + +CoglBool +_cogl_get_enable_legacy_state (void); + +#define _cogl_has_private_feature(ctx, feature) \ + COGL_FLAGS_GET ((ctx)->private_features, (feature)) + +/* + * _cogl_pixel_format_get_bytes_per_pixel: + * @format: a #CoglPixelFormat + * + * Queries how many bytes a pixel of the given @format takes. + * + * Return value: The number of bytes taken for a pixel of the given + * @format. + */ +int +_cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format); + +/* + * _cogl_pixel_format_has_aligned_components: + * @format: a #CoglPixelFormat + * + * Queries whether the ordering of the components for the given + * @format depend on the endianness of the host CPU or if the + * components can be accessed using bit shifting and bitmasking by + * loading a whole pixel into a word. + * + * XXX: If we ever consider making something like this public we + * should really try to think of a better name and come up with + * much clearer documentation since it really depends on what + * point of view you consider this from whether a format like + * COGL_PIXEL_FORMAT_RGBA_8888 is endian dependent. E.g. If you + * read an RGBA_8888 pixel into a uint32 + * it's endian dependent how you mask out the different channels. + * But If you already have separate color components and you want + * to write them to an RGBA_8888 pixel then the bytes can be + * written sequentially regardless of the endianness. + * + * Return value: %TRUE if you need to consider the host CPU + * endianness when dealing with the given @format + * else %FALSE. + */ +CoglBool +_cogl_pixel_format_is_endian_dependant (CoglPixelFormat format); + +/* + * COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT(format): + * @format: a #CoglPixelFormat + * + * Returns TRUE if the pixel format can take a premult bit. This is + * currently true for all formats that have an alpha channel except + * COGL_PIXEL_FORMAT_A_8 (because that doesn't have any other + * components to multiply by the alpha). + */ +#define COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT(format) \ + (((format) & COGL_A_BIT) && (format) != COGL_PIXEL_FORMAT_A_8) + +COGL_END_DECLS + +#endif /* __COGL_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-profile.c b/cogl/cogl/cogl-profile.c new file mode 100644 index 000000000..766e271ed --- /dev/null +++ b/cogl/cogl/cogl-profile.c @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef COGL_ENABLE_PROFILE + +#include "cogl-profile.h" +#include "cogl-debug.h" +#include "cogl-i18n-private.h" + +#include + +UProfContext *_cogl_uprof_context; + +static CoglBool +debug_option_getter (void *user_data) +{ + unsigned int shift = GPOINTER_TO_UINT (user_data); + return COGL_DEBUG_ENABLED (shift); +} + +static void +debug_option_setter (CoglBool value, void *user_data) +{ + unsigned int shift = GPOINTER_TO_UINT (user_data); + + if (value) + COGL_DEBUG_SET_FLAG (shift); + else + COGL_DEBUG_CLEAR_FLAG (shift); +} + +static void +print_exit_report (void) +{ + if (getenv ("COGL_PROFILE_OUTPUT_REPORT")) + { + UProfContext *mainloop_context; + UProfTimerResult *mainloop_timer; + UProfReport *report; + + /* NB: uprof provides a shared context for mainloop statistics + * which needs to be setup by the application which controls the + * mainloop. + * + * If no "Mainloop" timer has been setup then we print a warning + * since we can't provide a meaningful Cogl report without one. + */ + mainloop_context = uprof_get_mainloop_context (); + mainloop_timer = uprof_context_get_timer_result (mainloop_context, + "Mainloop"); + /* just bail out if the mainloop timer wasn't hit */ + if (!mainloop_timer) + { + g_warning ("\n\n" + "No UProf \"Mainloop\" timer was setup by the " + "application therefore we\ncan't provide a meaningful " + "profile report.\n" + "\n" + "This should be done automatically if you are using Clutter " + "(if\nbuilt with --enable-profile)\n" + "\n" + "If you aren't using Clutter then you can declare a " + "\"Mainloop\" UProf\ntimer in your application like this:\n\n" + " UPROF_STATIC_TIMER (mainloop_timer, \n" + " NULL,\n" + " \"Mainloop\",\n" + " \"Time in glib mainloop\",\n" + " 0);\n" + "\n" + "And start/stop it around your mainloop like this:\n" + "\n" + " UPROF_TIMER_START (uprof_get_mainloop_context (), mainloop_timer);\n" + " g_main_loop_run (loop);\n" + " UPROF_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer);\n"); + return; + } + + report = uprof_report_new ("Cogl report"); + uprof_report_add_context (report, _cogl_uprof_context); + uprof_report_print (report); + uprof_report_unref (report); + } + uprof_context_unref (_cogl_uprof_context); +} + +void +_cogl_uprof_init (void) +{ + _cogl_uprof_context = uprof_context_new ("Cogl"); + uprof_context_link (_cogl_uprof_context, uprof_get_mainloop_context ()); +#define OPT(MASK_NAME, GROUP, NAME, NAME_FORMATTED, DESCRIPTION) \ + G_STMT_START { \ + int shift = COGL_DEBUG_ ## MASK_NAME; \ + uprof_context_add_boolean_option (_cogl_uprof_context, \ + GROUP, \ + NAME, \ + NAME_FORMATTED, \ + DESCRIPTION, \ + debug_option_getter, \ + debug_option_setter, \ + GUINT_TO_POINTER (shift)); \ + } G_STMT_END; + +#include "cogl-debug-options.h" +#undef OPT + + atexit (print_exit_report); +} + +void +_cogl_profile_trace_message (const char *format, ...) +{ + va_list ap; + + va_start (ap, format); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, format, ap); + va_end (ap); + + if (_cogl_uprof_context) + uprof_context_vtrace_message (_cogl_uprof_context, format, ap); +} + +#endif diff --git a/cogl/cogl/cogl-profile.h b/cogl/cogl/cogl-profile.h new file mode 100644 index 000000000..db95647e9 --- /dev/null +++ b/cogl/cogl/cogl-profile.h @@ -0,0 +1,68 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PROFILE_H__ +#define __COGL_PROFILE_H__ + + +#ifdef COGL_ENABLE_PROFILE + +#include + +extern UProfContext *_cogl_uprof_context; + +#define COGL_STATIC_TIMER UPROF_STATIC_TIMER +#define COGL_STATIC_COUNTER UPROF_STATIC_COUNTER +#define COGL_COUNTER_INC UPROF_COUNTER_INC +#define COGL_COUNTER_DEC UPROF_COUNTER_DEC +#define COGL_TIMER_START UPROF_TIMER_START +#define COGL_TIMER_STOP UPROF_TIMER_STOP + +void +_cogl_uprof_init (void); + +void +_cogl_profile_trace_message (const char *format, ...); + +#else + +#define COGL_STATIC_TIMER(A,B,C,D,E) extern void _cogl_dummy_decl (void) +#define COGL_STATIC_COUNTER(A,B,C,D) extern void _cogl_dummy_decl (void) +#define COGL_COUNTER_INC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_COUNTER_DEC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_TIMER_START(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_TIMER_STOP(A,B) G_STMT_START{ (void)0; }G_STMT_END + +#define _cogl_profile_trace_message g_message + +#endif + +#endif /* __COGL_PROFILE_H__ */ + diff --git a/cogl/cogl/cogl-quaternion-private.h b/cogl/cogl/cogl-quaternion-private.h new file mode 100644 index 000000000..eda672ea8 --- /dev/null +++ b/cogl/cogl/cogl-quaternion-private.h @@ -0,0 +1,44 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_QUATERNION_PRIVATE_H__ +#define __COGL_QUATERNION_PRIVATE_H__ + +#include + +/* squared length */ +#define _COGL_QUATERNION_NORM(Q) \ + ((Q)->x*(Q)->x + (Q)->y*(Q)->y + (Q)->z*(Q)->z + (Q)->w*(Q)->w) + +#define _COGL_QUATERNION_DEGREES_TO_RADIANS (G_PI / 180.0f) +#define _COGL_QUATERNION_RADIANS_TO_DEGREES (180.0f / G_PI) + +#endif /* __COGL_QUATERNION_PRIVATE_H__ */ diff --git a/cogl/cogl/cogl-quaternion.c b/cogl/cogl/cogl-quaternion.c new file mode 100644 index 000000000..956744322 --- /dev/null +++ b/cogl/cogl/cogl-quaternion.c @@ -0,0 +1,673 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + * Various references relating to quaternions: + * + * http://www.cs.caltech.edu/courses/cs171/quatut.pdf + * http://mathworld.wolfram.com/Quaternion.html + * http://www.gamedev.net/reference/articles/article1095.asp + * http://www.cprogramming.com/tutorial/3d/quaternions.html + * http://www.isner.com/tutorials/quatSpells/quaternion_spells_12.htm + * http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56 + * 3D Maths Primer for Graphics and Game Development ISBN-10: 1556229119 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include "cogl-gtype-private.h" + +#include +#include + +#define FLOAT_EPSILON 1e-03 + +COGL_GTYPE_DEFINE_BOXED (Quaternion, quaternion, + cogl_quaternion_copy, + cogl_quaternion_free); + +static CoglQuaternion zero_quaternion = +{ + 0.0, 0.0, 0.0, 0.0, +}; + +static CoglQuaternion identity_quaternion = +{ + 1.0, 0.0, 0.0, 0.0, +}; + +/* This function is just here to be called from GDB so we don't really + want to put a declaration in a header and we just add it here to + avoid a warning */ +void +_cogl_quaternion_print (CoglQuaternion *quarternion); + +void +_cogl_quaternion_print (CoglQuaternion *quaternion) +{ + g_print ("[ %6.4f (%6.4f, %6.4f, %6.4f)]\n", + quaternion->w, + quaternion->x, + quaternion->y, + quaternion->z); +} + +void +cogl_quaternion_init (CoglQuaternion *quaternion, + float angle, + float x, + float y, + float z) +{ + float axis[3] = { x, y, z}; + cogl_quaternion_init_from_angle_vector (quaternion, angle, axis); +} + +void +cogl_quaternion_init_from_angle_vector (CoglQuaternion *quaternion, + float angle, + const float *axis3f_in) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + float axis[3]; + float half_angle; + float sin_half_angle; + + /* XXX: Should we make cogl_vector3_normalize have separate in and + * out args? */ + axis[0] = axis3f_in[0]; + axis[1] = axis3f_in[1]; + axis[2] = axis3f_in[2]; + cogl_vector3_normalize (axis); + + half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + sin_half_angle = sinf (half_angle); + + quaternion->w = cosf (half_angle); + + quaternion->x = axis[0] * sin_half_angle; + quaternion->y = axis[1] * sin_half_angle; + quaternion->z = axis[2] * sin_half_angle; + + cogl_quaternion_normalize (quaternion); +} + +void +cogl_quaternion_init_identity (CoglQuaternion *quaternion) +{ + quaternion->w = 1.0; + + quaternion->x = 0.0; + quaternion->y = 0.0; + quaternion->z = 0.0; +} + +void +cogl_quaternion_init_from_array (CoglQuaternion *quaternion, + const float *array) +{ + quaternion->w = array[0]; + quaternion->x = array[1]; + quaternion->y = array[2]; + quaternion->z = array[3]; +} + +void +cogl_quaternion_init_from_x_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = sinf (half_angle); + quaternion->y = 0.0f; + quaternion->z = 0.0f; +} + +void +cogl_quaternion_init_from_y_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = 0.0f; + quaternion->y = sinf (half_angle); + quaternion->z = 0.0f; +} + +void +cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = 0.0f; + quaternion->y = 0.0f; + quaternion->z = sinf (half_angle); +} + +void +cogl_quaternion_init_from_euler (CoglQuaternion *quaternion, + const CoglEuler *euler) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + float sin_heading = + sinf (euler->heading * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + float sin_pitch = + sinf (euler->pitch * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + float sin_roll = + sinf (euler->roll * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + float cos_heading = + cosf (euler->heading * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + float cos_pitch = + cosf (euler->pitch * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + float cos_roll = + cosf (euler->roll * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f); + + quaternion->w = + cos_heading * cos_pitch * cos_roll + + sin_heading * sin_pitch * sin_roll; + + quaternion->x = + cos_heading * sin_pitch * cos_roll + + sin_heading * cos_pitch * sin_roll; + quaternion->y = + sin_heading * cos_pitch * cos_roll - + cos_heading * sin_pitch * sin_roll; + quaternion->z = + cos_heading * cos_pitch * sin_roll - + sin_heading * sin_pitch * cos_roll; +} + +void +cogl_quaternion_init_from_quaternion (CoglQuaternion *quaternion, + CoglQuaternion *src) +{ + memcpy (quaternion, src, sizeof (float) * 4); +} + +/* XXX: it could be nice to make something like this public... */ +/* + * COGL_MATRIX_READ: + * @MATRIX: A 4x4 transformation matrix + * @ROW: The row of the value you want to read + * @COLUMN: The column of the value you want to read + * + * Reads a value from the given matrix using integers to index + * into the matrix. + */ +#define COGL_MATRIX_READ(MATRIX, ROW, COLUMN) \ + (((const float *)matrix)[COLUMN * 4 + ROW]) + +void +cogl_quaternion_init_from_matrix (CoglQuaternion *quaternion, + const CoglMatrix *matrix) +{ + /* Algorithm devised by Ken Shoemake, Ref: + * http://campar.in.tum.de/twiki/pub/Chair/DwarfTutorial/quatut.pdf + */ + + /* 3D maths literature refers to the diagonal of a matrix as the + * "trace" of a matrix... */ + float trace = matrix->xx + matrix->yy + matrix->zz; + float root; + + if (trace > 0.0f) + { + root = sqrtf (trace + 1); + quaternion->w = root * 0.5f; + root = 0.5f / root; + quaternion->x = (matrix->zy - matrix->yz) * root; + quaternion->y = (matrix->xz - matrix->zx) * root; + quaternion->z = (matrix->yx - matrix->xy) * root; + } + else + { +#define X 0 +#define Y 1 +#define Z 2 +#define W 3 + int h = X; + if (matrix->yy > matrix->xx) + h = Y; + if (matrix->zz > COGL_MATRIX_READ (matrix, h, h)) + h = Z; + switch (h) + { +#define CASE_MACRO(i, j, k, I, J, K) \ + case I: \ + root = sqrtf ((COGL_MATRIX_READ (matrix, I, I) - \ + (COGL_MATRIX_READ (matrix, J, J) + \ + COGL_MATRIX_READ (matrix, K, K))) + \ + COGL_MATRIX_READ (matrix, W, W)); \ + quaternion->i = root * 0.5f;\ + root = 0.5f / root;\ + quaternion->j = (COGL_MATRIX_READ (matrix, I, J) + \ + COGL_MATRIX_READ (matrix, J, I)) * root; \ + quaternion->k = (COGL_MATRIX_READ (matrix, K, I) + \ + COGL_MATRIX_READ (matrix, I, K)) * root; \ + quaternion->w = (COGL_MATRIX_READ (matrix, K, J) - \ + COGL_MATRIX_READ (matrix, J, K)) * root;\ + break + CASE_MACRO (x, y, z, X, Y, Z); + CASE_MACRO (y, z, x, Y, Z, X); + CASE_MACRO (z, x, y, Z, X, Y); +#undef CASE_MACRO +#undef X +#undef Y +#undef Z + } + } + + if (matrix->ww != 1.0f) + { + float s = 1.0 / sqrtf (matrix->ww); + quaternion->w *= s; + quaternion->x *= s; + quaternion->y *= s; + quaternion->z *= s; + } +} + +CoglBool +cogl_quaternion_equal (const void *v1, const void *v2) +{ + const CoglQuaternion *a = v1; + const CoglQuaternion *b = v2; + + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + if (v1 == v2) + return TRUE; + + return (a->w == b->w && + a->x == b->x && + a->y == b->y && + a->z == b->z); +} + +CoglQuaternion * +cogl_quaternion_copy (const CoglQuaternion *src) +{ + if (G_LIKELY (src)) + { + CoglQuaternion *new = g_slice_new (CoglQuaternion); + memcpy (new, src, sizeof (float) * 4); + return new; + } + else + return NULL; +} + +void +cogl_quaternion_free (CoglQuaternion *quaternion) +{ + g_slice_free (CoglQuaternion, quaternion); +} + +float +cogl_quaternion_get_rotation_angle (const CoglQuaternion *quaternion) +{ + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + + /* FIXME: clamp [-1, 1] */ + return 2.0f * acosf (quaternion->w) * _COGL_QUATERNION_RADIANS_TO_DEGREES; +} + +void +cogl_quaternion_get_rotation_axis (const CoglQuaternion *quaternion, + float *vector3) +{ + float sin_half_angle_sqr; + float one_over_sin_angle_over_2; + + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + + /* NB: sin²(𝜃) + cos²(𝜃) = 1 */ + + sin_half_angle_sqr = 1.0f - quaternion->w * quaternion->w; + + if (sin_half_angle_sqr <= 0.0f) + { + /* Either an identity quaternion or numerical imprecision. + * Either way we return an arbitrary vector. */ + vector3[0] = 1; + vector3[1] = 0; + vector3[2] = 0; + return; + } + + /* Calculate 1 / sin(𝜃/2) */ + one_over_sin_angle_over_2 = 1.0f / sqrtf (sin_half_angle_sqr); + + vector3[0] = quaternion->x * one_over_sin_angle_over_2; + vector3[1] = quaternion->y * one_over_sin_angle_over_2; + vector3[2] = quaternion->z * one_over_sin_angle_over_2; +} + +void +cogl_quaternion_normalize (CoglQuaternion *quaternion) +{ + float slen = _COGL_QUATERNION_NORM (quaternion); + float factor = 1.0f / sqrtf (slen); + + quaternion->x *= factor; + quaternion->y *= factor; + quaternion->z *= factor; + + quaternion->w *= factor; + + return; +} + +float +cogl_quaternion_dot_product (const CoglQuaternion *a, + const CoglQuaternion *b) +{ + return a->w * b->w + a->x * b->x + a->y * b->y + a->z * b->z; +} + +void +cogl_quaternion_invert (CoglQuaternion *quaternion) +{ + quaternion->x = -quaternion->x; + quaternion->y = -quaternion->y; + quaternion->z = -quaternion->z; +} + +void +cogl_quaternion_multiply (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b) +{ + float w = a->w; + float x = a->x; + float y = a->y; + float z = a->z; + + _COGL_RETURN_IF_FAIL (b != result); + + result->w = w * b->w - x * b->x - y * b->y - z * b->z; + + result->x = w * b->x + x * b->w + y * b->z - z * b->y; + result->y = w * b->y + y * b->w + z * b->x - x * b->z; + result->z = w * b->z + z * b->w + x * b->y - y * b->x; +} + +void +cogl_quaternion_pow (CoglQuaternion *quaternion, float exponent) +{ + float half_angle; + float new_half_angle; + float factor; + + /* Try and identify and nop identity quaternions to avoid + * dividing by zero */ + if (fabs (quaternion->w) > 0.9999f) + return; + + /* NB: We are using quaternions to represent an axis (a), angle (𝜃) pair + * in this form: + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + */ + + /* FIXME: clamp [-1, 1] */ + /* Extract 𝜃/2 from w */ + half_angle = acosf (quaternion->w); + + /* Compute the new 𝜃/2 */ + new_half_angle = half_angle * exponent; + + /* Compute the new w value */ + quaternion->w = cosf (new_half_angle); + + /* And new xyz values */ + factor = sinf (new_half_angle) / sinf (half_angle); + quaternion->x *= factor; + quaternion->y *= factor; + quaternion->z *= factor; +} + +void +cogl_quaternion_slerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t) +{ + float cos_difference; + float qb_w; + float qb_x; + float qb_y; + float qb_z; + float fa; + float fb; + + _COGL_RETURN_IF_FAIL (t >=0 && t <= 1.0f); + + if (t == 0) + { + *result = *a; + return; + } + else if (t == 1) + { + *result = *b; + return; + } + + /* compute the cosine of the angle between the two given quaternions */ + cos_difference = cogl_quaternion_dot_product (a, b); + + /* If negative, use -b. Two quaternions q and -q represent the same angle but + * may produce a different slerp. We choose b or -b to rotate using the acute + * angle. + */ + if (cos_difference < 0.0f) + { + qb_w = -b->w; + qb_x = -b->x; + qb_y = -b->y; + qb_z = -b->z; + cos_difference = -cos_difference; + } + else + { + qb_w = b->w; + qb_x = b->x; + qb_y = b->y; + qb_z = b->z; + } + + /* If we have two unit quaternions the dot should be <= 1.0 */ + g_assert (cos_difference < 1.1f); + + + /* Determine the interpolation factors for each quaternion, simply using + * linear interpolation for quaternions that are nearly exactly the same. + * (this will avoid divisions by zero) + */ + + if (cos_difference > 0.9999f) + { + fa = 1.0f - t; + fb = t; + + /* XXX: should we also normalize() at the end in this case? */ + } + else + { + /* Calculate the sin of the angle between the two quaternions using the + * trig identity: sin²(𝜃) + cos²(𝜃) = 1 + */ + float sin_difference = sqrtf (1.0f - cos_difference * cos_difference); + + float difference = atan2f (sin_difference, cos_difference); + float one_over_sin_difference = 1.0f / sin_difference; + fa = sinf ((1.0f - t) * difference) * one_over_sin_difference; + fb = sinf (t * difference) * one_over_sin_difference; + } + + /* Finally interpolate the two quaternions */ + + result->x = fa * a->x + fb * qb_x; + result->y = fa * a->y + fb * qb_y; + result->z = fa * a->z + fb * qb_z; + result->w = fa * a->w + fb * qb_w; +} + +void +cogl_quaternion_nlerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t) +{ + float cos_difference; + float qb_w; + float qb_x; + float qb_y; + float qb_z; + float fa; + float fb; + + _COGL_RETURN_IF_FAIL (t >=0 && t <= 1.0f); + + if (t == 0) + { + *result = *a; + return; + } + else if (t == 1) + { + *result = *b; + return; + } + + /* compute the cosine of the angle between the two given quaternions */ + cos_difference = cogl_quaternion_dot_product (a, b); + + /* If negative, use -b. Two quaternions q and -q represent the same angle but + * may produce a different slerp. We choose b or -b to rotate using the acute + * angle. + */ + if (cos_difference < 0.0f) + { + qb_w = -b->w; + qb_x = -b->x; + qb_y = -b->y; + qb_z = -b->z; + cos_difference = -cos_difference; + } + else + { + qb_w = b->w; + qb_x = b->x; + qb_y = b->y; + qb_z = b->z; + } + + /* If we have two unit quaternions the dot should be <= 1.0 */ + g_assert (cos_difference < 1.1f); + + fa = 1.0f - t; + fb = t; + + result->x = fa * a->x + fb * qb_x; + result->y = fa * a->y + fb * qb_y; + result->z = fa * a->z + fb * qb_z; + result->w = fa * a->w + fb * qb_w; + + cogl_quaternion_normalize (result); +} + +void +cogl_quaternion_squad (CoglQuaternion *result, + const CoglQuaternion *prev, + const CoglQuaternion *a, + const CoglQuaternion *b, + const CoglQuaternion *next, + float t) +{ + CoglQuaternion slerp0; + CoglQuaternion slerp1; + + cogl_quaternion_slerp (&slerp0, a, b, t); + cogl_quaternion_slerp (&slerp1, prev, next, t); + cogl_quaternion_slerp (result, &slerp0, &slerp1, 2.0f * t * (1.0f - t)); +} + +const CoglQuaternion * +cogl_get_static_identity_quaternion (void) +{ + return &identity_quaternion; +} + +const CoglQuaternion * +cogl_get_static_zero_quaternion (void) +{ + return &zero_quaternion; +} + diff --git a/cogl/cogl/cogl-quaternion.h b/cogl/cogl/cogl-quaternion.h new file mode 100644 index 000000000..c70eccab4 --- /dev/null +++ b/cogl/cogl/cogl-quaternion.h @@ -0,0 +1,564 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_QUATERNION_H__ +#define __COGL_QUATERNION_H__ + +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-quaternion + * @short_description: Functions for initializing and manipulating + * quaternions. + * + * Quaternions have become a standard form for representing 3D + * rotations and have some nice properties when compared with other + * representation such as (roll,pitch,yaw) Euler angles. They can be + * used to interpolate between different rotations and they don't + * suffer from a problem called + * "Gimbal lock" + * where two of the axis of rotation may become aligned and you loose a + * degree of freedom. + * . + */ +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +/** + * CoglQuaternion: + * @w: based on the angle of rotation it is cos(𝜃/2) + * @x: based on the angle of rotation and x component of the axis of + * rotation it is sin(𝜃/2)*axis.x + * @y: based on the angle of rotation and y component of the axis of + * rotation it is sin(𝜃/2)*axis.y + * @z: based on the angle of rotation and z component of the axis of + * rotation it is sin(𝜃/2)*axis.z + * + * A quaternion is comprised of a scalar component and a 3D vector + * component. The scalar component is normally referred to as w and the + * vector might either be referred to as v or a (for axis) or expanded + * with the individual components: (x, y, z) A full quaternion would + * then be written as [w (x, y, z)]. + * + * Quaternions can be considered to represent an axis and angle + * pair although sadly these numbers are buried somewhat under some + * maths... + * + * For the curious you can see here that a given axis (a) and angle (𝜃) + * pair are represented in a quaternion as follows: + * |[ + * [w=cos(𝜃/2) ( x=sin(𝜃/2)*a.x, y=sin(𝜃/2)*a.y, z=sin(𝜃/2)*a.x )] + * ]| + * + * Unit Quaternions: + * When using Quaternions to represent spatial orientations for 3D + * graphics it's always assumed you have a unit quaternion. The + * magnitude of a quaternion is defined as: + * |[ + * sqrt (w² + x² + y² + z²) + * ]| + * and a unit quaternion satisfies this equation: + * |[ + * w² + x² + y² + z² = 1 + * ]| + * + * Thankfully most of the time we don't actually have to worry about + * the maths that goes on behind the scenes but if you are curious to + * learn more here are some external references: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * 3D Maths Primer for Graphics and Game Development ISBN-10: 1556229119 + * + * + * + * + * + * + * + * + * + */ +struct _CoglQuaternion +{ + /*< public >*/ + float w; + + float x; + float y; + float z; + + /*< private >*/ + float padding0; + float padding1; + float padding2; + float padding3; +}; +COGL_STRUCT_SIZE_ASSERT (CoglQuaternion, 32); + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_quaternion_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_quaternion_get_gtype (void); +#endif + +/** + * cogl_quaternion_init: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle you want to rotate around the given axis + * @x: The x component of your axis vector about which you want to + * rotate. + * @y: The y component of your axis vector about which you want to + * rotate. + * @z: The z component of your axis vector about which you want to + * rotate. + * + * Initializes a quaternion that rotates @angle degrees around the + * axis vector (@x, @y, @z). The axis vector does not need to be + * normalized. + * + * Returns: A normalized, unit quaternion representing an orientation + * rotated @angle degrees around the axis vector (@x, @y, @z) + * + * Since: 2.0 + */ +void +cogl_quaternion_init (CoglQuaternion *quaternion, + float angle, + float x, + float y, + float z); + +/** + * cogl_quaternion_init_from_angle_vector: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around @axis3f + * @axis3f: your 3 component axis vector about which you want to rotate. + * + * Initializes a quaternion that rotates @angle degrees around the + * given @axis vector. The axis vector does not need to be + * normalized. + * + * Returns: A normalized, unit quaternion representing an orientation + * rotated @angle degrees around the given @axis vector. + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_angle_vector (CoglQuaternion *quaternion, + float angle, + const float *axis3f); + +/** + * cogl_quaternion_init_identity: + * @quaternion: An uninitialized #CoglQuaternion + * + * Initializes the quaternion with the canonical quaternion identity + * [1 (0, 0, 0)] which represents no rotation. Multiplying a + * quaternion with this identity leaves the quaternion unchanged. + * + * You might also want to consider using + * cogl_get_static_identity_quaternion(). + * + * Since: 2.0 + */ +void +cogl_quaternion_init_identity (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_init_from_array: + * @quaternion: A #CoglQuaternion + * @array: An array of 4 floats w,(x,y,z) + * + * Initializes a [w (x, y,z)] quaternion directly from an array of 4 + * floats: [w,x,y,z]. + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_array (CoglQuaternion *quaternion, + const float *array); + +/** + * cogl_quaternion_init_from_x_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the x axis + * + * XXX: check which direction this rotates + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_x_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_init_from_y_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the y axis + * + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_y_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_init_from_z_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the z axis + * + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_init_from_euler: + * @quaternion: A #CoglQuaternion + * @euler: A #CoglEuler with which to initialize the quaternion + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_euler (CoglQuaternion *quaternion, + const CoglEuler *euler); + +/** + * cogl_quaternion_init_from_quaternion: + * @quaternion: A #CoglQuaternion + * @src: A #CoglQuaternion with which to initialize @quaternion + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_quaternion (CoglQuaternion *quaternion, + CoglQuaternion *src); + +/** + * cogl_quaternion_init_from_matrix: + * @quaternion: A Cogl Quaternion + * @matrix: A rotation matrix with which to initialize the quaternion + * + * Initializes a quaternion from a rotation matrix. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_quaternion_init_from_matrix (CoglQuaternion *quaternion, + const CoglMatrix *matrix); + +/** + * cogl_quaternion_equal: + * @v1: A #CoglQuaternion + * @v2: A #CoglQuaternion + * + * Compares that all the components of quaternions @a and @b are + * equal. + * + * An epsilon value is not used to compare the float components, but + * the == operator is at least used so that 0 and -0 are considered + * equal. + * + * Returns: %TRUE if the quaternions are equal else %FALSE. + * + * Since: 2.0 + */ +CoglBool +cogl_quaternion_equal (const void *v1, const void *v2); + +/** + * cogl_quaternion_copy: + * @src: A #CoglQuaternion + * + * Allocates a new #CoglQuaternion on the stack and initializes it with + * the same values as @src. + * + * Returns: A newly allocated #CoglQuaternion which should be freed + * using cogl_quaternion_free() + * + * Since: 2.0 + */ +CoglQuaternion * +cogl_quaternion_copy (const CoglQuaternion *src); + +/** + * cogl_quaternion_free: + * @quaternion: A #CoglQuaternion + * + * Frees a #CoglQuaternion that was previously allocated via + * cogl_quaternion_copy(). + * + * Since: 2.0 + */ +void +cogl_quaternion_free (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_get_rotation_angle: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +float +cogl_quaternion_get_rotation_angle (const CoglQuaternion *quaternion); + +/** + * cogl_quaternion_get_rotation_axis: + * @quaternion: A #CoglQuaternion + * @vector3: (out): an allocated 3-float array + * + * Since: 2.0 + */ +void +cogl_quaternion_get_rotation_axis (const CoglQuaternion *quaternion, + float *vector3); + +/** + * cogl_quaternion_normalize: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_normalize (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_dot_product: + * @a: A #CoglQuaternion + * @b: A #CoglQuaternion + * + * Since: 2.0 + */ +float +cogl_quaternion_dot_product (const CoglQuaternion *a, + const CoglQuaternion *b); + +/** + * cogl_quaternion_invert: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_invert (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_multiply: + * @result: The destination #CoglQuaternion + * @left: The second #CoglQuaternion rotation to apply + * @right: The first #CoglQuaternion rotation to apply + * + * This combines the rotations of two quaternions into @result. The + * operation is not commutative so the order is important because AxB + * != BxA. Cogl follows the standard convention for quaternions here + * so the rotations are applied @right to @left. This is similar to the + * combining of matrices. + * + * It is possible to multiply the @a quaternion in-place, so + * @result can be equal to @a but can't be equal to @b. + * + * Since: 2.0 + */ +void +cogl_quaternion_multiply (CoglQuaternion *result, + const CoglQuaternion *left, + const CoglQuaternion *right); + +/** + * cogl_quaternion_pow: + * @quaternion: A #CoglQuaternion + * @exponent: the exponent + * + * + * Since: 2.0 + */ +void +cogl_quaternion_pow (CoglQuaternion *quaternion, float exponent); + +/** + * cogl_quaternion_slerp: + * @result: The destination #CoglQuaternion + * @a: The first #CoglQuaternion + * @b: The second #CoglQuaternion + * @t: The factor in the range [0,1] used to interpolate between + * quaternion @a and @b. + * + * Performs a spherical linear interpolation between two quaternions. + * + * Noteable properties: + * + * + * commutative: No + * + * + * constant velocity: Yes + * + * + * torque minimal (travels along the surface of the 4-sphere): Yes + * + * + * more expensive than cogl_quaternion_nlerp() + * + * + */ +void +cogl_quaternion_slerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t); + +/** + * cogl_quaternion_nlerp: + * @result: The destination #CoglQuaternion + * @a: The first #CoglQuaternion + * @b: The second #CoglQuaternion + * @t: The factor in the range [0,1] used to interpolate between + * quaterion @a and @b. + * + * Performs a normalized linear interpolation between two quaternions. + * That is it does a linear interpolation of the quaternion components + * and then normalizes the result. This will follow the shortest arc + * between the two orientations (just like the slerp() function) but + * will not progress at a constant speed. Unlike slerp() nlerp is + * commutative which is useful if you are blending animations + * together. (I.e. nlerp (tmp, a, b) followed by nlerp (result, tmp, + * d) is the same as nlerp (tmp, a, d) followed by nlerp (result, tmp, + * b)). Finally nlerp is cheaper than slerp so it can be a good choice + * if you don't need the constant speed property of the slerp() function. + * + * Notable properties: + * + * + * commutative: Yes + * + * + * constant velocity: No + * + * + * torque minimal (travels along the surface of the 4-sphere): Yes + * + * + * faster than cogl_quaternion_slerp() + * + * + */ +void +cogl_quaternion_nlerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t); +/** + * cogl_quaternion_squad: + * @result: The destination #CoglQuaternion + * @prev: A #CoglQuaternion used before @a + * @a: The first #CoglQuaternion + * @b: The second #CoglQuaternion + * @next: A #CoglQuaternion that will be used after @b + * @t: The factor in the range [0,1] used to interpolate between + * quaternion @a and @b. + * + * + * Since: 2.0 + */ +void +cogl_quaternion_squad (CoglQuaternion *result, + const CoglQuaternion *prev, + const CoglQuaternion *a, + const CoglQuaternion *b, + const CoglQuaternion *next, + float t); + +/** + * cogl_get_static_identity_quaternion: + * + * Returns a pointer to a singleton quaternion constant describing the + * canonical identity [1 (0, 0, 0)] which represents no rotation. + * + * If you multiply a quaternion with the identity quaternion you will + * get back the same value as the original quaternion. + * + * Returns: A pointer to an identity quaternion + * + * Since: 2.0 + */ +const CoglQuaternion * +cogl_get_static_identity_quaternion (void); + +/** + * cogl_get_static_zero_quaternion: + * + * Returns: a pointer to a singleton quaternion constant describing a + * rotation of 180 degrees around a degenerate axis: + * [0 (0, 0, 0)] + * + * Since: 2.0 + */ +const CoglQuaternion * +cogl_get_static_zero_quaternion (void); + +COGL_END_DECLS + +#endif /* __COGL_QUATERNION_H__ */ + diff --git a/cogl/cogl/cogl-rectangle-map.c b/cogl/cogl/cogl-rectangle-map.c new file mode 100644 index 000000000..69368eed6 --- /dev/null +++ b/cogl/cogl/cogl-rectangle-map.c @@ -0,0 +1,764 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-util.h" +#include "cogl-rectangle-map.h" +#include "cogl-debug.h" + +/* Implements a data structure which keeps track of unused + sub-rectangles within a larger rectangle using a binary tree + structure. The algorithm for this is based on the description here: + + http://www.blackpawn.com/texts/lightmaps/default.html +*/ + +#if defined (COGL_ENABLE_DEBUG) && defined (HAVE_CAIRO) + +/* The cairo header is only used for debugging to generate an image of + the atlas */ +#include + +static void _cogl_rectangle_map_dump_image (CoglRectangleMap *map); + +#endif /* COGL_ENABLE_DEBUG && HAVE_CAIRO */ + +typedef struct _CoglRectangleMapNode CoglRectangleMapNode; +typedef struct _CoglRectangleMapStackEntry CoglRectangleMapStackEntry; + +typedef void (* CoglRectangleMapInternalForeachCb) (CoglRectangleMapNode *node, + void *data); + +typedef enum +{ + COGL_RECTANGLE_MAP_BRANCH, + COGL_RECTANGLE_MAP_FILLED_LEAF, + COGL_RECTANGLE_MAP_EMPTY_LEAF +} CoglRectangleMapNodeType; + +struct _CoglRectangleMap +{ + CoglRectangleMapNode *root; + + unsigned int n_rectangles; + + unsigned int space_remaining; + + GDestroyNotify value_destroy_func; + + /* Stack used for walking the structure. This is only used during + the lifetime of a single function call but it is kept here as an + optimisation to avoid reallocating it every time it is needed */ + GArray *stack; +}; + +struct _CoglRectangleMapNode +{ + CoglRectangleMapNodeType type; + + CoglRectangleMapEntry rectangle; + + unsigned int largest_gap; + + CoglRectangleMapNode *parent; + + union + { + /* Fields used when this is a branch */ + struct + { + CoglRectangleMapNode *left; + CoglRectangleMapNode *right; + } branch; + + /* Field used when this is a filled leaf */ + void *data; + } d; +}; + +struct _CoglRectangleMapStackEntry +{ + /* The node to search */ + CoglRectangleMapNode *node; + /* Index of next branch of this node to explore. Basically either 0 + to go left or 1 to go right */ + CoglBool next_index; +}; + +static CoglRectangleMapNode * +_cogl_rectangle_map_node_new (void) +{ + return g_slice_new (CoglRectangleMapNode); +} + +static void +_cogl_rectangle_map_node_free (CoglRectangleMapNode *node) +{ + g_slice_free (CoglRectangleMapNode, node); +} + +CoglRectangleMap * +_cogl_rectangle_map_new (unsigned int width, + unsigned int height, + GDestroyNotify value_destroy_func) +{ + CoglRectangleMap *map = g_new (CoglRectangleMap, 1); + CoglRectangleMapNode *root = _cogl_rectangle_map_node_new (); + + root->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + root->parent = NULL; + root->rectangle.x = 0; + root->rectangle.y = 0; + root->rectangle.width = width; + root->rectangle.height = height; + root->largest_gap = width * height; + + map->root = root; + map->n_rectangles = 0; + map->value_destroy_func = value_destroy_func; + map->space_remaining = width * height; + + map->stack = g_array_new (FALSE, FALSE, sizeof (CoglRectangleMapStackEntry)); + + return map; +} + +static void +_cogl_rectangle_map_stack_push (GArray *stack, + CoglRectangleMapNode *node, + CoglBool next_index) +{ + CoglRectangleMapStackEntry *new_entry; + + g_array_set_size (stack, stack->len + 1); + + new_entry = &g_array_index (stack, CoglRectangleMapStackEntry, + stack->len - 1); + + new_entry->node = node; + new_entry->next_index = next_index; +} + +static void +_cogl_rectangle_map_stack_pop (GArray *stack) +{ + g_array_set_size (stack, stack->len - 1); +} + +static CoglRectangleMapStackEntry * +_cogl_rectangle_map_stack_get_top (GArray *stack) +{ + return &g_array_index (stack, CoglRectangleMapStackEntry, + stack->len - 1); +} + +static CoglRectangleMapNode * +_cogl_rectangle_map_node_split_horizontally (CoglRectangleMapNode *node, + unsigned int left_width) +{ + /* Splits the node horizontally (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The leftmost branch will have the width left_width and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglRectangleMapNode *left_node, *right_node; + + if (node->rectangle.width == left_width) + return node; + + left_node = _cogl_rectangle_map_node_new (); + left_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + left_node->parent = node; + left_node->rectangle.x = node->rectangle.x; + left_node->rectangle.y = node->rectangle.y; + left_node->rectangle.width = left_width; + left_node->rectangle.height = node->rectangle.height; + left_node->largest_gap = (left_node->rectangle.width * + left_node->rectangle.height); + node->d.branch.left = left_node; + + right_node = _cogl_rectangle_map_node_new (); + right_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + right_node->parent = node; + right_node->rectangle.x = node->rectangle.x + left_width; + right_node->rectangle.y = node->rectangle.y; + right_node->rectangle.width = node->rectangle.width - left_width; + right_node->rectangle.height = node->rectangle.height; + right_node->largest_gap = (right_node->rectangle.width * + right_node->rectangle.height); + node->d.branch.right = right_node; + + node->type = COGL_RECTANGLE_MAP_BRANCH; + + return left_node; +} + +static CoglRectangleMapNode * +_cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node, + unsigned int top_height) +{ + /* Splits the node vertically (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The topmost branch will have the height top_height and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglRectangleMapNode *top_node, *bottom_node; + + if (node->rectangle.height == top_height) + return node; + + top_node = _cogl_rectangle_map_node_new (); + top_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + top_node->parent = node; + top_node->rectangle.x = node->rectangle.x; + top_node->rectangle.y = node->rectangle.y; + top_node->rectangle.width = node->rectangle.width; + top_node->rectangle.height = top_height; + top_node->largest_gap = (top_node->rectangle.width * + top_node->rectangle.height); + node->d.branch.left = top_node; + + bottom_node = _cogl_rectangle_map_node_new (); + bottom_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + bottom_node->parent = node; + bottom_node->rectangle.x = node->rectangle.x; + bottom_node->rectangle.y = node->rectangle.y + top_height; + bottom_node->rectangle.width = node->rectangle.width; + bottom_node->rectangle.height = node->rectangle.height - top_height; + bottom_node->largest_gap = (bottom_node->rectangle.width * + bottom_node->rectangle.height); + node->d.branch.right = bottom_node; + + node->type = COGL_RECTANGLE_MAP_BRANCH; + + return top_node; +} + +#ifdef COGL_ENABLE_DEBUG + +static unsigned int +_cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node) +{ + /* This is just used for debugging the data structure. It + recursively walks the tree to verify that the largest gap values + all add up */ + + switch (node->type) + { + case COGL_RECTANGLE_MAP_BRANCH: + { + int sum = + _cogl_rectangle_map_verify_recursive (node->d.branch.left) + + _cogl_rectangle_map_verify_recursive (node->d.branch.right); + g_assert (node->largest_gap == + MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap)); + return sum; + } + + case COGL_RECTANGLE_MAP_EMPTY_LEAF: + g_assert (node->largest_gap == + node->rectangle.width * node->rectangle.height); + return 0; + + case COGL_RECTANGLE_MAP_FILLED_LEAF: + g_assert (node->largest_gap == 0); + return 1; + } + + return 0; +} + +static unsigned int +_cogl_rectangle_map_get_space_remaining_recursive (CoglRectangleMapNode *node) +{ + /* This is just used for debugging the data structure. It + recursively walks the tree to verify that the remaining space + value adds up */ + + switch (node->type) + { + case COGL_RECTANGLE_MAP_BRANCH: + { + CoglRectangleMapNode *l = node->d.branch.left; + CoglRectangleMapNode *r = node->d.branch.right; + + return (_cogl_rectangle_map_get_space_remaining_recursive (l) + + _cogl_rectangle_map_get_space_remaining_recursive (r)); + } + + case COGL_RECTANGLE_MAP_EMPTY_LEAF: + return node->rectangle.width * node->rectangle.height; + + case COGL_RECTANGLE_MAP_FILLED_LEAF: + return 0; + } + + return 0; +} + +static void +_cogl_rectangle_map_verify (CoglRectangleMap *map) +{ + unsigned int actual_n_rectangles = + _cogl_rectangle_map_verify_recursive (map->root); + unsigned int actual_space_remaining = + _cogl_rectangle_map_get_space_remaining_recursive (map->root); + + g_assert_cmpuint (actual_n_rectangles, ==, map->n_rectangles); + g_assert_cmpuint (actual_space_remaining, ==, map->space_remaining); +} + +#endif /* COGL_ENABLE_DEBUG */ + +CoglBool +_cogl_rectangle_map_add (CoglRectangleMap *map, + unsigned int width, + unsigned int height, + void *data, + CoglRectangleMapEntry *rectangle) +{ + unsigned int rectangle_size = width * height; + /* Stack of nodes to search in */ + GArray *stack = map->stack; + CoglRectangleMapNode *found_node = NULL; + + /* Zero-sized rectangles break the algorithm for removing rectangles + so we'll disallow them */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, FALSE); + + /* Start with the root node */ + g_array_set_size (stack, 0); + _cogl_rectangle_map_stack_push (stack, map->root, FALSE); + + /* Depth-first search for an empty node that is big enough */ + while (stack->len > 0) + { + CoglRectangleMapStackEntry *stack_top; + CoglRectangleMapNode *node; + int next_index; + + /* Pop an entry off the stack */ + stack_top = _cogl_rectangle_map_stack_get_top (stack); + node = stack_top->node; + next_index = stack_top->next_index; + _cogl_rectangle_map_stack_pop (stack); + + /* Regardless of the type of the node, there's no point + descending any further if the new rectangle won't fit within + it */ + if (node->rectangle.width >= width && + node->rectangle.height >= height && + node->largest_gap >= rectangle_size) + { + if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) + { + /* We've found a node we can use */ + found_node = node; + break; + } + else if (node->type == COGL_RECTANGLE_MAP_BRANCH) + { + if (next_index) + /* Try the right branch */ + _cogl_rectangle_map_stack_push (stack, + node->d.branch.right, + 0); + else + { + /* Make sure we remember to try the right branch once + we've finished descending the left branch */ + _cogl_rectangle_map_stack_push (stack, + node, + 1); + /* Try the left branch */ + _cogl_rectangle_map_stack_push (stack, + node->d.branch.left, + 0); + } + } + } + } + + if (found_node) + { + CoglRectangleMapNode *node; + + /* Split according to whichever axis will leave us with the + largest space */ + if (found_node->rectangle.width - width > + found_node->rectangle.height - height) + { + found_node = + _cogl_rectangle_map_node_split_horizontally (found_node, width); + found_node = + _cogl_rectangle_map_node_split_vertically (found_node, height); + } + else + { + found_node = + _cogl_rectangle_map_node_split_vertically (found_node, height); + found_node = + _cogl_rectangle_map_node_split_horizontally (found_node, width); + } + + found_node->type = COGL_RECTANGLE_MAP_FILLED_LEAF; + found_node->d.data = data; + found_node->largest_gap = 0; + if (rectangle) + *rectangle = found_node->rectangle; + + /* Walk back up the tree and update the stored largest gap for + the node's sub tree */ + for (node = found_node->parent; node; node = node->parent) + { + /* This node is a parent so it should always be a branch */ + g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH); + + node->largest_gap = MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap); + } + + /* There is now an extra rectangle in the map */ + map->n_rectangles++; + /* and less space */ + map->space_remaining -= rectangle_size; + +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DUMP_ATLAS_IMAGE))) + { +#ifdef HAVE_CAIRO + _cogl_rectangle_map_dump_image (map); +#endif + /* Dumping the rectangle map is really slow so we might as well + verify the space remaining here as it is also quite slow */ + _cogl_rectangle_map_verify (map); + } +#endif + + return TRUE; + } + else + return FALSE; +} + +void +_cogl_rectangle_map_remove (CoglRectangleMap *map, + const CoglRectangleMapEntry *rectangle) +{ + CoglRectangleMapNode *node = map->root; + unsigned int rectangle_size = rectangle->width * rectangle->height; + + /* We can do a binary-chop down the search tree to find the rectangle */ + while (node->type == COGL_RECTANGLE_MAP_BRANCH) + { + CoglRectangleMapNode *left_node = node->d.branch.left; + + /* If and only if the rectangle is in the left node then the x,y + position of the rectangle will be within the node's + rectangle */ + if (rectangle->x < left_node->rectangle.x + left_node->rectangle.width && + rectangle->y < left_node->rectangle.y + left_node->rectangle.height) + /* Go left */ + node = left_node; + else + /* Go right */ + node = node->d.branch.right; + } + + /* Make sure we found the right node */ + if (node->type != COGL_RECTANGLE_MAP_FILLED_LEAF || + node->rectangle.x != rectangle->x || + node->rectangle.y != rectangle->y || + node->rectangle.width != rectangle->width || + node->rectangle.height != rectangle->height) + /* This should only happen if someone tried to remove a rectangle + that was not in the map so something has gone wrong */ + g_return_if_reached (); + else + { + /* Convert the node back to an empty node */ + if (map->value_destroy_func) + map->value_destroy_func (node->d.data); + node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + node->largest_gap = rectangle_size; + + /* Walk back up the tree combining branch nodes that have two + empty leaves back into a single empty leaf */ + for (node = node->parent; node; node = node->parent) + { + /* This node is a parent so it should always be a branch */ + g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH); + + if (node->d.branch.left->type == COGL_RECTANGLE_MAP_EMPTY_LEAF && + node->d.branch.right->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) + { + _cogl_rectangle_map_node_free (node->d.branch.left); + _cogl_rectangle_map_node_free (node->d.branch.right); + node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + + node->largest_gap = (node->rectangle.width * + node->rectangle.height); + } + else + break; + } + + /* Reduce the amount of space remaining in all of the parents + further up the chain */ + for (; node; node = node->parent) + node->largest_gap = MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap); + + /* There is now one less rectangle */ + g_assert (map->n_rectangles > 0); + map->n_rectangles--; + /* and more space */ + map->space_remaining += rectangle_size; + } + +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DUMP_ATLAS_IMAGE))) + { +#ifdef HAVE_CAIRO + _cogl_rectangle_map_dump_image (map); +#endif + /* Dumping the rectangle map is really slow so we might as well + verify the space remaining here as it is also quite slow */ + _cogl_rectangle_map_verify (map); + } +#endif +} + +unsigned int +_cogl_rectangle_map_get_width (CoglRectangleMap *map) +{ + return map->root->rectangle.width; +} + +unsigned int +_cogl_rectangle_map_get_height (CoglRectangleMap *map) +{ + return map->root->rectangle.height; +} + +unsigned int +_cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map) +{ + return map->space_remaining; +} + +unsigned int +_cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map) +{ + return map->n_rectangles; +} + +static void +_cogl_rectangle_map_internal_foreach (CoglRectangleMap *map, + CoglRectangleMapInternalForeachCb func, + void *data) +{ + /* Stack of nodes to search in */ + GArray *stack = map->stack; + + /* Start with the root node */ + g_array_set_size (stack, 0); + _cogl_rectangle_map_stack_push (stack, map->root, 0); + + /* Iterate all nodes depth-first */ + while (stack->len > 0) + { + CoglRectangleMapStackEntry *stack_top = + _cogl_rectangle_map_stack_get_top (stack); + CoglRectangleMapNode *node = stack_top->node; + + switch (node->type) + { + case COGL_RECTANGLE_MAP_BRANCH: + if (stack_top->next_index == 0) + { + /* Next time we come back to this node, go to the right */ + stack_top->next_index = 1; + + /* Explore the left branch next */ + _cogl_rectangle_map_stack_push (stack, + node->d.branch.left, + 0); + } + else if (stack_top->next_index == 1) + { + /* Next time we come back to this node, stop processing it */ + stack_top->next_index = 2; + + /* Explore the right branch next */ + _cogl_rectangle_map_stack_push (stack, + node->d.branch.right, + 0); + } + else + { + /* We're finished with this node so we can call the callback */ + func (node, data); + _cogl_rectangle_map_stack_pop (stack); + } + break; + + default: + /* Some sort of leaf node, just call the callback */ + func (node, data); + _cogl_rectangle_map_stack_pop (stack); + break; + } + } + + /* The stack should now be empty */ + g_assert (stack->len == 0); +} + +typedef struct _CoglRectangleMapForeachClosure +{ + CoglRectangleMapCallback callback; + void *data; +} CoglRectangleMapForeachClosure; + +static void +_cogl_rectangle_map_foreach_cb (CoglRectangleMapNode *node, void *data) +{ + CoglRectangleMapForeachClosure *closure = data; + + if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF) + closure->callback (&node->rectangle, node->d.data, closure->data); +} + +void +_cogl_rectangle_map_foreach (CoglRectangleMap *map, + CoglRectangleMapCallback callback, + void *data) +{ + CoglRectangleMapForeachClosure closure; + + closure.callback = callback; + closure.data = data; + + _cogl_rectangle_map_internal_foreach (map, + _cogl_rectangle_map_foreach_cb, + &closure); +} + +static void +_cogl_rectangle_map_free_cb (CoglRectangleMapNode *node, void *data) +{ + CoglRectangleMap *map = data; + + if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF && map->value_destroy_func) + map->value_destroy_func (node->d.data); + + _cogl_rectangle_map_node_free (node); +} + +void +_cogl_rectangle_map_free (CoglRectangleMap *map) +{ + _cogl_rectangle_map_internal_foreach (map, + _cogl_rectangle_map_free_cb, + map); + + g_array_free (map->stack, TRUE); + + g_free (map); +} + +#if defined (COGL_ENABLE_DEBUG) && defined (HAVE_CAIRO) + +static void +_cogl_rectangle_map_dump_image_cb (CoglRectangleMapNode *node, void *data) +{ + cairo_t *cr = data; + + if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF || + node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) + { + /* Fill the rectangle using a different colour depending on + whether the rectangle is used */ + if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF) + cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); + else + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + + cairo_rectangle (cr, + node->rectangle.x, + node->rectangle.y, + node->rectangle.width, + node->rectangle.height); + + cairo_fill_preserve (cr); + + /* Draw a white outline around the rectangle */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); + } +} + +static void +_cogl_rectangle_map_dump_image (CoglRectangleMap *map) +{ + /* This dumps a png to help visualize the map. Each leaf rectangle + is drawn with a white outline. Unused leaves are filled in black + and used leaves are blue */ + + cairo_surface_t *surface = + cairo_image_surface_create (CAIRO_FORMAT_RGB24, + _cogl_rectangle_map_get_width (map), + _cogl_rectangle_map_get_height (map)); + cairo_t *cr = cairo_create (surface); + + _cogl_rectangle_map_internal_foreach (map, + _cogl_rectangle_map_dump_image_cb, + cr); + + cairo_destroy (cr); + + cairo_surface_write_to_png (surface, "cogl-rectangle-map-dump.png"); + + cairo_surface_destroy (surface); +} + +#endif /* COGL_ENABLE_DEBUG && HAVE_CAIRO */ diff --git a/cogl/cogl/cogl-rectangle-map.h b/cogl/cogl/cogl-rectangle-map.h new file mode 100644 index 000000000..1dd50fdbe --- /dev/null +++ b/cogl/cogl/cogl-rectangle-map.h @@ -0,0 +1,84 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_RECTANGLE_MAP_H +#define __COGL_RECTANGLE_MAP_H + +#include +#include "cogl-types.h" + +typedef struct _CoglRectangleMap CoglRectangleMap; +typedef struct _CoglRectangleMapEntry CoglRectangleMapEntry; + +typedef void (* CoglRectangleMapCallback) (const CoglRectangleMapEntry *entry, + void *rectangle_data, + void *user_data); + +struct _CoglRectangleMapEntry +{ + unsigned int x, y; + unsigned int width, height; +}; + +CoglRectangleMap * +_cogl_rectangle_map_new (unsigned int width, + unsigned int height, + GDestroyNotify value_destroy_func); + +CoglBool +_cogl_rectangle_map_add (CoglRectangleMap *map, + unsigned int width, + unsigned int height, + void *data, + CoglRectangleMapEntry *rectangle); + +void +_cogl_rectangle_map_remove (CoglRectangleMap *map, + const CoglRectangleMapEntry *rectangle); + +unsigned int +_cogl_rectangle_map_get_width (CoglRectangleMap *map); + +unsigned int +_cogl_rectangle_map_get_height (CoglRectangleMap *map); + +unsigned int +_cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map); + +unsigned int +_cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map); + +void +_cogl_rectangle_map_foreach (CoglRectangleMap *map, + CoglRectangleMapCallback callback, + void *data); + +void +_cogl_rectangle_map_free (CoglRectangleMap *map); + +#endif /* __COGL_RECTANGLE_MAP_H */ diff --git a/cogl/cogl/cogl-renderer-private.h b/cogl/cogl/cogl-renderer-private.h new file mode 100644 index 000000000..080bb325d --- /dev/null +++ b/cogl/cogl/cogl-renderer-private.h @@ -0,0 +1,112 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_RENDERER_PRIVATE_H +#define __COGL_RENDERER_PRIVATE_H + +#include + +#include "cogl-object-private.h" +#include "cogl-winsys-private.h" +#include "cogl-driver.h" +#include "cogl-texture-driver.h" +#include "cogl-context.h" +#include "cogl-closure-list-private.h" + +#ifdef COGL_HAS_XLIB_SUPPORT +#include +#endif + +struct _CoglRenderer +{ + CoglObject _parent; + CoglBool connected; + CoglDriver driver_override; + const CoglDriverVtable *driver_vtable; + const CoglTextureDriver *texture_driver; + const CoglWinsysVtable *winsys_vtable; + CoglWinsysID winsys_id_override; + GList *constraints; + + GArray *poll_fds; + int poll_fds_age; + GList *poll_sources; + + CoglList idle_closures; + + GList *outputs; + +#ifdef COGL_HAS_XLIB_SUPPORT + Display *foreign_xdpy; + CoglBool xlib_enable_event_retrieval; +#endif + + CoglDriver driver; + unsigned long private_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)]; + GModule *libgl_module; + +#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT) + int kms_fd; +#endif + + /* List of callback functions that will be given every native event */ + GSList *event_filters; + void *winsys; +}; + +/* Mask of constraints that effect driver selection. All of the other + * constraints effect only the winsys selection */ +#define COGL_RENDERER_DRIVER_CONSTRAINTS \ + COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2 + +typedef CoglFilterReturn (* CoglNativeFilterFunc) (void *native_event, + void *data); + +CoglFilterReturn +_cogl_renderer_handle_native_event (CoglRenderer *renderer, + void *event); + +void +_cogl_renderer_add_native_filter (CoglRenderer *renderer, + CoglNativeFilterFunc func, + void *data); + +void +_cogl_renderer_remove_native_filter (CoglRenderer *renderer, + CoglNativeFilterFunc func, + void *data); + +void * +_cogl_renderer_get_proc_address (CoglRenderer *renderer, + const char *name, + CoglBool in_core); + +#endif /* __COGL_RENDERER_PRIVATE_H */ diff --git a/cogl/cogl/cogl-renderer.c b/cogl/cogl/cogl-renderer.c new file mode 100644 index 000000000..fabaf81f2 --- /dev/null +++ b/cogl/cogl/cogl-renderer.c @@ -0,0 +1,804 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cogl-util.h" +#include "cogl-private.h" +#include "cogl-object.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" + +#include "cogl-renderer.h" +#include "cogl-renderer-private.h" +#include "cogl-display-private.h" +#include "cogl-winsys-private.h" +#include "cogl-winsys-stub-private.h" +#include "cogl-config-private.h" +#include "cogl-error-private.h" +#include "cogl-gtype-private.h" + +#ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT +#include "cogl-winsys-egl-x11-private.h" +#endif +#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT +#include "cogl-winsys-egl-kms-private.h" +#endif +#ifdef COGL_HAS_GLX_SUPPORT +#include "cogl-winsys-glx-private.h" +#endif + +#ifdef COGL_HAS_XLIB_SUPPORT +#include "cogl-xlib-renderer.h" +#endif + +typedef const CoglWinsysVtable *(*CoglWinsysVtableGetter) (void); + +#ifdef HAVE_COGL_GL +extern const CoglTextureDriver _cogl_texture_driver_gl; +extern const CoglDriverVtable _cogl_driver_gl; +#endif +#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) +extern const CoglTextureDriver _cogl_texture_driver_gles; +extern const CoglDriverVtable _cogl_driver_gles; +#endif + +extern const CoglDriverVtable _cogl_driver_nop; + +typedef struct _CoglDriverDescription +{ + CoglDriver id; + const char *name; + CoglRendererConstraint constraints; + /* It would be nice to make this a pointer and then use a compound + * literal from C99 to initialise it but we probably can't get away + * with using C99 here. Instead we'll just use a fixed-size array. + * GCC should complain if someone adds an 8th feature to a + * driver. */ + const CoglPrivateFeature private_features[8]; + const CoglDriverVtable *vtable; + const CoglTextureDriver *texture_driver; + const char *libgl_name; +} CoglDriverDescription; + +static CoglDriverDescription _cogl_drivers[] = +{ +#ifdef HAVE_COGL_GL + { + COGL_DRIVER_GL, + "gl", + 0, + { COGL_PRIVATE_FEATURE_ANY_GL, + COGL_PRIVATE_FEATURE_GL_FIXED, + COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, + -1 }, + &_cogl_driver_gl, + &_cogl_texture_driver_gl, + COGL_GL_LIBNAME, + }, + { + COGL_DRIVER_GL3, + "gl3", + 0, + { COGL_PRIVATE_FEATURE_ANY_GL, + COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, + -1 }, + &_cogl_driver_gl, + &_cogl_texture_driver_gl, + COGL_GL_LIBNAME, + }, +#endif +#ifdef HAVE_COGL_GLES2 + { + COGL_DRIVER_GLES2, + "gles2", + COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2, + { COGL_PRIVATE_FEATURE_ANY_GL, + COGL_PRIVATE_FEATURE_GL_EMBEDDED, + COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, + -1 }, + &_cogl_driver_gles, + &_cogl_texture_driver_gles, + COGL_GLES2_LIBNAME, + }, +#endif +#ifdef HAVE_COGL_GLES + { + COGL_DRIVER_GLES1, + "gles1", + 0, + { COGL_PRIVATE_FEATURE_ANY_GL, + COGL_PRIVATE_FEATURE_GL_EMBEDDED, + COGL_PRIVATE_FEATURE_GL_FIXED, + -1 }, + &_cogl_driver_gles, + &_cogl_texture_driver_gles, + COGL_GLES1_LIBNAME, + }, +#endif + { + COGL_DRIVER_NOP, + "nop", + 0, /* constraints satisfied */ + { -1 }, + &_cogl_driver_nop, + NULL, /* texture driver */ + NULL /* libgl_name */ + } +}; + +static CoglWinsysVtableGetter _cogl_winsys_vtable_getters[] = +{ +#ifdef COGL_HAS_GLX_SUPPORT + _cogl_winsys_glx_get_vtable, +#endif +#ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT + _cogl_winsys_egl_xlib_get_vtable, +#endif +#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT + _cogl_winsys_egl_kms_get_vtable, +#endif + _cogl_winsys_stub_get_vtable, +}; + +static void _cogl_renderer_free (CoglRenderer *renderer); + +COGL_OBJECT_DEFINE (Renderer, renderer); +COGL_GTYPE_DEFINE_CLASS (Renderer, renderer); + +typedef struct _CoglNativeFilterClosure +{ + CoglNativeFilterFunc func; + void *data; +} CoglNativeFilterClosure; + +uint32_t +cogl_renderer_error_quark (void) +{ + return g_quark_from_static_string ("cogl-renderer-error-quark"); +} + +static const CoglWinsysVtable * +_cogl_renderer_get_winsys (CoglRenderer *renderer) +{ + return renderer->winsys_vtable; +} + +static void +native_filter_closure_free (CoglNativeFilterClosure *closure) +{ + g_slice_free (CoglNativeFilterClosure, closure); +} + +static void +_cogl_renderer_free (CoglRenderer *renderer) +{ + const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); + + _cogl_closure_list_disconnect_all (&renderer->idle_closures); + + if (winsys) + winsys->renderer_disconnect (renderer); + + if (renderer->libgl_module) + g_module_close (renderer->libgl_module); + + g_slist_foreach (renderer->event_filters, + (GFunc) native_filter_closure_free, + NULL); + g_slist_free (renderer->event_filters); + + g_array_free (renderer->poll_fds, TRUE); + + g_free (renderer); +} + +CoglRenderer * +cogl_renderer_new (void) +{ + CoglRenderer *renderer = g_new0 (CoglRenderer, 1); + + _cogl_init (); + + renderer->connected = FALSE; + renderer->event_filters = NULL; + + renderer->poll_fds = g_array_new (FALSE, TRUE, sizeof (CoglPollFD)); + + _cogl_list_init (&renderer->idle_closures); + +#ifdef COGL_HAS_XLIB_SUPPORT + renderer->xlib_enable_event_retrieval = TRUE; +#endif + +#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT + renderer->kms_fd = -1; +#endif + + return _cogl_renderer_object_new (renderer); +} + +#ifdef COGL_HAS_XLIB_SUPPORT +void +cogl_xlib_renderer_set_foreign_display (CoglRenderer *renderer, + Display *xdisplay) +{ + _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer)); + + /* NB: Renderers are considered immutable once connected */ + _COGL_RETURN_IF_FAIL (!renderer->connected); + + renderer->foreign_xdpy = xdisplay; + + /* If the application is using a foreign display then we can assume + it will also do its own event retrieval */ + cogl_xlib_renderer_set_event_retrieval_enabled (renderer, FALSE); +} + +Display * +cogl_xlib_renderer_get_foreign_display (CoglRenderer *renderer) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL); + + return renderer->foreign_xdpy; +} + +void +cogl_xlib_renderer_set_event_retrieval_enabled (CoglRenderer *renderer, + CoglBool enable) +{ + _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer)); + /* NB: Renderers are considered immutable once connected */ + _COGL_RETURN_IF_FAIL (!renderer->connected); + + renderer->xlib_enable_event_retrieval = enable; +} +#endif /* COGL_HAS_XLIB_SUPPORT */ + +CoglBool +cogl_renderer_check_onscreen_template (CoglRenderer *renderer, + CoglOnscreenTemplate *onscreen_template, + CoglError **error) +{ + CoglDisplay *display; + + if (!cogl_renderer_connect (renderer, error)) + return FALSE; + + display = cogl_display_new (renderer, onscreen_template); + if (!cogl_display_setup (display, error)) + { + cogl_object_unref (display); + return FALSE; + } + + cogl_object_unref (display); + + return TRUE; +} + +typedef CoglBool (*CoglDriverCallback) (CoglDriverDescription *description, + void *user_data); + +static void +foreach_driver_description (CoglDriver driver_override, + CoglDriverCallback callback, + void *user_data) +{ +#ifdef COGL_DEFAULT_DRIVER + const CoglDriverDescription *default_driver = NULL; +#endif + int i; + + if (driver_override != COGL_DRIVER_ANY) + { + for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) + { + if (_cogl_drivers[i].id == driver_override) + { + callback (&_cogl_drivers[i], user_data); + return; + } + } + + g_warn_if_reached (); + return; + } + +#ifdef COGL_DEFAULT_DRIVER + for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) + { + const CoglDriverDescription *desc = &_cogl_drivers[i]; + if (g_ascii_strcasecmp (desc->name, COGL_DEFAULT_DRIVER) == 0) + { + default_driver = desc; + break; + } + } + + if (default_driver) + { + if (!callback (default_driver, user_data)) + return; + } +#endif + + for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) + { +#ifdef COGL_DEFAULT_DRIVER + if (&_cogl_drivers[i] == default_driver) + continue; +#endif + + if (!callback (&_cogl_drivers[i], user_data)) + return; + } +} + +static CoglDriver +driver_name_to_id (const char *name) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) + { + if (g_ascii_strcasecmp (_cogl_drivers[i].name, name) == 0) + return _cogl_drivers[i].id; + } + + return COGL_DRIVER_ANY; +} + +static const char * +driver_id_to_name (CoglDriver id) +{ + switch (id) + { + case COGL_DRIVER_GL: + return "gl"; + case COGL_DRIVER_GL3: + return "gl3"; + case COGL_DRIVER_GLES1: + return "gles1"; + case COGL_DRIVER_GLES2: + return "gles2"; + case COGL_DRIVER_WEBGL: + return "webgl"; + case COGL_DRIVER_NOP: + return "nop"; + case COGL_DRIVER_ANY: + g_warn_if_reached (); + return "any"; + } + + g_warn_if_reached (); + return "unknown"; +} + +typedef struct _SatisfyConstraintsState +{ + GList *constraints; + const CoglDriverDescription *driver_description; +} SatisfyConstraintsState; + +static CoglBool +satisfy_constraints (CoglDriverDescription *description, + void *user_data) +{ + SatisfyConstraintsState *state = user_data; + GList *l; + + for (l = state->constraints; l; l = l->next) + { + CoglRendererConstraint constraint = GPOINTER_TO_UINT (l->data); + + /* Most of the constraints only affect the winsys selection so + * we'll filter them out */ + if (!(constraint & COGL_RENDERER_DRIVER_CONSTRAINTS)) + continue; + + /* If the driver doesn't satisfy any constraint then continue + * to the next driver description */ + if (!(constraint & description->constraints)) + return TRUE; + } + + state->driver_description = description; + + return FALSE; +} + +static CoglBool +_cogl_renderer_choose_driver (CoglRenderer *renderer, + CoglError **error) +{ + const char *driver_name = g_getenv ("COGL_DRIVER"); + CoglDriver driver_override = COGL_DRIVER_ANY; + const char *invalid_override = NULL; + const char *libgl_name; + SatisfyConstraintsState state; + const CoglDriverDescription *desc; + int i; + + if (!driver_name) + driver_name = _cogl_config_driver; + + if (driver_name) + { + driver_override = driver_name_to_id (driver_name); + if (driver_override == COGL_DRIVER_ANY) + invalid_override = driver_name; + } + + if (renderer->driver_override != COGL_DRIVER_ANY) + { + if (driver_override != COGL_DRIVER_ANY && + renderer->driver_override != driver_override) + { + _cogl_set_error (error, + COGL_RENDERER_ERROR, + COGL_RENDERER_ERROR_BAD_CONSTRAINT, + "Application driver selection conflicts with driver " + "specified in configuration"); + return FALSE; + } + + driver_override = renderer->driver_override; + } + + if (driver_override != COGL_DRIVER_ANY) + { + CoglBool found = FALSE; + int i; + + for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) + { + if (_cogl_drivers[i].id == driver_override) + { + found = TRUE; + break; + } + } + if (!found) + invalid_override = driver_id_to_name (driver_override); + } + + if (invalid_override) + { + _cogl_set_error (error, + COGL_RENDERER_ERROR, + COGL_RENDERER_ERROR_BAD_CONSTRAINT, + "Driver \"%s\" is not available", + invalid_override); + return FALSE; + } + + state.driver_description = NULL; + state.constraints = renderer->constraints; + + foreach_driver_description (driver_override, + satisfy_constraints, + &state); + + if (!state.driver_description) + { + _cogl_set_error (error, + COGL_RENDERER_ERROR, + COGL_RENDERER_ERROR_BAD_CONSTRAINT, + "No suitable driver found"); + return FALSE; + } + + desc = state.driver_description; + renderer->driver = desc->id; + renderer->driver_vtable = desc->vtable; + renderer->texture_driver = desc->texture_driver; + libgl_name = desc->libgl_name; + + memset(renderer->private_features, 0, sizeof (renderer->private_features)); + for (i = 0; desc->private_features[i] != -1; i++) + COGL_FLAGS_SET (renderer->private_features, + desc->private_features[i], TRUE); + + if (COGL_FLAGS_GET (renderer->private_features, + COGL_PRIVATE_FEATURE_ANY_GL)) + { + renderer->libgl_module = g_module_open (libgl_name, + G_MODULE_BIND_LAZY); + + if (renderer->libgl_module == NULL) + { + _cogl_set_error (error, COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_FAILED_TO_LOAD_LIBRARY, + "Failed to dynamically open the GL library \"%s\"", + libgl_name); + return FALSE; + } + } + + return TRUE; +} + +/* Final connection API */ + +CoglBool +cogl_renderer_connect (CoglRenderer *renderer, CoglError **error) +{ + int i; + GString *error_message; + CoglBool constraints_failed = FALSE; + + if (renderer->connected) + return TRUE; + + /* The driver needs to be chosen before connecting the renderer + because eglInitialize requires the library containing the GL API + to be loaded before its called */ + if (!_cogl_renderer_choose_driver (renderer, error)) + return FALSE; + + error_message = g_string_new (""); + for (i = 0; i < G_N_ELEMENTS (_cogl_winsys_vtable_getters); i++) + { + const CoglWinsysVtable *winsys = _cogl_winsys_vtable_getters[i](); + CoglError *tmp_error = NULL; + GList *l; + CoglBool skip_due_to_constraints = FALSE; + + if (renderer->winsys_id_override != COGL_WINSYS_ID_ANY) + { + if (renderer->winsys_id_override != winsys->id) + continue; + } + else + { + char *user_choice = getenv ("COGL_RENDERER"); + if (!user_choice) + user_choice = _cogl_config_renderer; + if (user_choice && + g_ascii_strcasecmp (winsys->name, user_choice) != 0) + continue; + } + + for (l = renderer->constraints; l; l = l->next) + { + CoglRendererConstraint constraint = GPOINTER_TO_UINT (l->data); + if (!(winsys->constraints & constraint)) + { + skip_due_to_constraints = TRUE; + break; + } + } + if (skip_due_to_constraints) + { + constraints_failed |= TRUE; + continue; + } + + /* At least temporarily we will associate this winsys with + * the renderer in-case ->renderer_connect calls API that + * wants to query the current winsys... */ + renderer->winsys_vtable = winsys; + + if (!winsys->renderer_connect (renderer, &tmp_error)) + { + g_string_append_c (error_message, '\n'); + g_string_append (error_message, tmp_error->message); + cogl_error_free (tmp_error); + } + else + { + renderer->connected = TRUE; + g_string_free (error_message, TRUE); + return TRUE; + } + } + + if (!renderer->connected) + { + if (constraints_failed) + { + _cogl_set_error (error, COGL_RENDERER_ERROR, + COGL_RENDERER_ERROR_BAD_CONSTRAINT, + "Failed to connected to any renderer due to constraints"); + return FALSE; + } + + renderer->winsys_vtable = NULL; + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Failed to connected to any renderer: %s", + error_message->str); + g_string_free (error_message, TRUE); + return FALSE; + } + + return TRUE; +} + +CoglFilterReturn +_cogl_renderer_handle_native_event (CoglRenderer *renderer, + void *event) +{ + GSList *l, *next; + + /* Pass the event on to all of the registered filters in turn */ + for (l = renderer->event_filters; l; l = next) + { + CoglNativeFilterClosure *closure = l->data; + + /* The next pointer is taken now so that we can handle the + closure being removed during emission */ + next = l->next; + + if (closure->func (event, closure->data) == COGL_FILTER_REMOVE) + return COGL_FILTER_REMOVE; + } + + /* If the backend for the renderer also wants to see the events, it + should just register its own filter */ + + return COGL_FILTER_CONTINUE; +} + +void +_cogl_renderer_add_native_filter (CoglRenderer *renderer, + CoglNativeFilterFunc func, + void *data) +{ + CoglNativeFilterClosure *closure; + + closure = g_slice_new (CoglNativeFilterClosure); + closure->func = func; + closure->data = data; + + renderer->event_filters = g_slist_prepend (renderer->event_filters, closure); +} + +void +_cogl_renderer_remove_native_filter (CoglRenderer *renderer, + CoglNativeFilterFunc func, + void *data) +{ + GSList *l, *prev = NULL; + + for (l = renderer->event_filters; l; prev = l, l = l->next) + { + CoglNativeFilterClosure *closure = l->data; + + if (closure->func == func && closure->data == data) + { + native_filter_closure_free (closure); + if (prev) + prev->next = g_slist_delete_link (prev->next, l); + else + renderer->event_filters = + g_slist_delete_link (renderer->event_filters, l); + break; + } + } +} + +void +cogl_renderer_set_winsys_id (CoglRenderer *renderer, + CoglWinsysID winsys_id) +{ + _COGL_RETURN_IF_FAIL (!renderer->connected); + + renderer->winsys_id_override = winsys_id; +} + +CoglWinsysID +cogl_renderer_get_winsys_id (CoglRenderer *renderer) +{ + _COGL_RETURN_VAL_IF_FAIL (renderer->connected, 0); + + return renderer->winsys_vtable->id; +} + +void * +_cogl_renderer_get_proc_address (CoglRenderer *renderer, + const char *name, + CoglBool in_core) +{ + const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); + + return winsys->renderer_get_proc_address (renderer, name, in_core); +} + +int +cogl_renderer_get_n_fragment_texture_units (CoglRenderer *renderer) +{ + int n = 0; + + _COGL_GET_CONTEXT (ctx, 0); + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES2) + if (cogl_has_feature (ctx, COGL_FEATURE_ID_GLSL) || + cogl_has_feature (ctx, COGL_FEATURE_ID_ARBFP)) + GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n)); +#endif + + return n; +} + +void +cogl_renderer_add_constraint (CoglRenderer *renderer, + CoglRendererConstraint constraint) +{ + g_return_if_fail (!renderer->connected); + renderer->constraints = g_list_prepend (renderer->constraints, + GUINT_TO_POINTER (constraint)); +} + +void +cogl_renderer_remove_constraint (CoglRenderer *renderer, + CoglRendererConstraint constraint) +{ + g_return_if_fail (!renderer->connected); + renderer->constraints = g_list_remove (renderer->constraints, + GUINT_TO_POINTER (constraint)); +} + +void +cogl_renderer_set_driver (CoglRenderer *renderer, + CoglDriver driver) +{ + _COGL_RETURN_IF_FAIL (!renderer->connected); + renderer->driver_override = driver; +} + +CoglDriver +cogl_renderer_get_driver (CoglRenderer *renderer) +{ + _COGL_RETURN_VAL_IF_FAIL (renderer->connected, 0); + + return renderer->driver; +} + +void +cogl_renderer_foreach_output (CoglRenderer *renderer, + CoglOutputCallback callback, + void *user_data) +{ + GList *l; + + _COGL_RETURN_IF_FAIL (renderer->connected); + _COGL_RETURN_IF_FAIL (callback != NULL); + + for (l = renderer->outputs; l; l = l->next) + callback (l->data, user_data); +} diff --git a/cogl/cogl/cogl-renderer.h b/cogl/cogl/cogl-renderer.h new file mode 100644 index 000000000..20c79068c --- /dev/null +++ b/cogl/cogl/cogl-renderer.h @@ -0,0 +1,432 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_RENDERER_H__ +#define __COGL_RENDERER_H__ + +#include +#include +#include +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-renderer + * @short_description: Choosing a means to render + * + * A #CoglRenderer represents a means to render. It encapsulates the + * selection of an underlying driver, such as OpenGL or OpenGL-ES and + * a selection of a window system binding API such as GLX or EGL. + * + * A #CoglRenderer has two states, "unconnected" and "connected". When + * a renderer is first instantiated using cogl_renderer_new() it is + * unconnected so that it can be configured and constraints can be + * specified for how the backend driver and window system should be + * chosen. + * + * After configuration a #CoglRenderer can (optionally) be explicitly + * connected using cogl_renderer_connect() which allows for the + * handling of connection errors so that fallback configurations can + * be tried if necessary. Applications that don't support any + * fallbacks though can skip using cogl_renderer_connect() and leave + * Cogl to automatically connect the renderer. + * + * Once you have a configured #CoglRenderer it can be used to create a + * #CoglDisplay object using cogl_display_new(). + * + * Many applications don't need to explicitly use + * cogl_renderer_new() or cogl_display_new() and can just jump + * straight to cogl_context_new() and pass a %NULL display argument so + * Cogl will automatically connect and setup a renderer and + * display. + */ + + +/** + * COGL_RENDERER_ERROR: + * + * An error domain for exceptions reported by Cogl + */ +#define COGL_RENDERER_ERROR cogl_renderer_error_quark () + +uint32_t +cogl_renderer_error_quark (void); + +typedef struct _CoglRenderer CoglRenderer; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_renderer_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_renderer_get_gtype (void); +#endif + +/** + * cogl_is_renderer: + * @object: A #CoglObject pointer + * + * Determines if the given @object is a #CoglRenderer + * + * Return value: %TRUE if @object is a #CoglRenderer, else %FALSE. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_renderer (void *object); + +/** + * cogl_renderer_new: + * + * Instantiates a new (unconnected) #CoglRenderer object. A + * #CoglRenderer represents a means to render. It encapsulates the + * selection of an underlying driver, such as OpenGL or OpenGL-ES and + * a selection of a window system binding API such as GLX or EGL. + * + * While the renderer is unconnected it can be configured so that + * applications may specify backend constraints, such as "must use + * x11" for example via cogl_renderer_add_constraint(). + * + * There are also some platform specific configuration apis such + * as cogl_xlib_renderer_set_foreign_display() that may also be + * used while the renderer is unconnected. + * + * Once the renderer has been configured, then it may (optionally) be + * explicitly connected using cogl_renderer_connect() which allows + * errors to be handled gracefully and potentially fallback + * configurations can be tried out if there are initial failures. + * + * If a renderer is not explicitly connected then cogl_display_new() + * will automatically connect the renderer for you. If you don't + * have any code to deal with error/fallback situations then its fine + * to just let Cogl do the connection for you. + * + * Once you have setup your renderer then the next step is to create a + * #CoglDisplay using cogl_display_new(). + * + * Many applications don't need to explicitly use + * cogl_renderer_new() or cogl_display_new() and can just jump + * straight to cogl_context_new() and pass a %NULL display argument + * so Cogl will automatically connect and setup a renderer and + * display. + * + * Return value: (transfer full): A newly created #CoglRenderer. + * + * Since: 1.10 + * Stability: unstable + */ +CoglRenderer * +cogl_renderer_new (void); + +/* optional configuration APIs */ + +/** + * CoglWinsysID: + * @COGL_WINSYS_ID_ANY: Implies no preference for which backend is used + * @COGL_WINSYS_ID_STUB: Use the no-op stub backend + * @COGL_WINSYS_ID_GLX: Use the GLX window system binding API + * @COGL_WINSYS_ID_EGL_XLIB: Use EGL with the X window system via XLib + * @COGL_WINSYS_ID_EGL_KMS: Use EGL with the KMS platform + * + * Identifies specific window system backends that Cogl supports. + * + * These can be used to query what backend Cogl is using or to try and + * explicitly select a backend to use. + */ +typedef enum +{ + COGL_WINSYS_ID_ANY, + COGL_WINSYS_ID_STUB, + COGL_WINSYS_ID_GLX, + COGL_WINSYS_ID_EGL_XLIB, + COGL_WINSYS_ID_EGL_KMS, +} CoglWinsysID; + +/** + * cogl_renderer_set_winsys_id: + * @renderer: A #CoglRenderer + * @winsys_id: An ID of the winsys you explicitly want to use. + * + * This allows you to explicitly select a winsys backend to use instead + * of letting Cogl automatically select a backend. + * + * if you select an unsupported backend then cogl_renderer_connect() + * will fail and report an error. + * + * This may only be called on an un-connected #CoglRenderer. + */ +void +cogl_renderer_set_winsys_id (CoglRenderer *renderer, + CoglWinsysID winsys_id); + +/** + * cogl_renderer_get_winsys_id: + * @renderer: A #CoglRenderer + * + * Queries which window system backend Cogl has chosen to use. + * + * This may only be called on a connected #CoglRenderer. + * + * Returns: The #CoglWinsysID corresponding to the chosen window + * system backend. + */ +CoglWinsysID +cogl_renderer_get_winsys_id (CoglRenderer *renderer); + +/** + * cogl_renderer_get_n_fragment_texture_units: + * @renderer: A #CoglRenderer + * + * Queries how many texture units can be used from fragment programs + * + * Returns: the number of texture image units. + * + * Since: 1.8 + * Stability: Unstable + */ +int +cogl_renderer_get_n_fragment_texture_units (CoglRenderer *renderer); + +/** + * cogl_renderer_check_onscreen_template: + * @renderer: A #CoglRenderer + * @onscreen_template: A #CoglOnscreenTemplate + * @error: A pointer to a #CoglError for reporting exceptions + * + * Tests if a given @onscreen_template can be supported with the given + * @renderer. + * + * Return value: %TRUE if the @onscreen_template can be supported, + * else %FALSE. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_renderer_check_onscreen_template (CoglRenderer *renderer, + CoglOnscreenTemplate *onscreen_template, + CoglError **error); + +/* Final connection API */ + +/** + * cogl_renderer_connect: + * @renderer: An unconnected #CoglRenderer + * @error: a pointer to a #CoglError for reporting exceptions + * + * Connects the configured @renderer. Renderer connection isn't a + * very active process, it basically just means validating that + * any given constraint criteria can be satisfied and that a + * usable driver and window system backend can be found. + * + * Return value: %TRUE if there was no error while connecting the + * given @renderer. %FALSE if there was an error. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_renderer_connect (CoglRenderer *renderer, CoglError **error); + +/** + * CoglRendererConstraint: + * @COGL_RENDERER_CONSTRAINT_USES_X11: Require the renderer to be X11 based + * @COGL_RENDERER_CONSTRAINT_USES_XLIB: Require the renderer to be X11 + * based and use Xlib + * @COGL_RENDERER_CONSTRAINT_USES_EGL: Require the renderer to be EGL based + * @COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2: Require that the + * renderer supports creating a #CoglGLES2Context via + * cogl_gles2_context_new(). This can be used to integrate GLES 2.0 + * code into Cogl based applications. + * + * These constraint flags are hard-coded features of the different renderer + * backends. Sometimes a platform may support multiple rendering options which + * Cogl will usually choose from automatically. Some of these features are + * important to higher level applications and frameworks though, such as + * whether a renderer is X11 based because an application might only support + * X11 based input handling. An application might also need to ensure EGL is + * used internally too if they depend on access to an EGLDisplay for some + * purpose. + * + * Applications should ideally minimize how many of these constraints + * they depend on to ensure maximum portability. + * + * Since: 1.10 + * Stability: unstable + */ +typedef enum +{ + COGL_RENDERER_CONSTRAINT_USES_X11 = (1 << 0), + COGL_RENDERER_CONSTRAINT_USES_XLIB = (1 << 1), + COGL_RENDERER_CONSTRAINT_USES_EGL = (1 << 2), + COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2 = (1 << 3) +} CoglRendererConstraint; + + +/** + * cogl_renderer_add_constraint: + * @renderer: An unconnected #CoglRenderer + * @constraint: A #CoglRendererConstraint to add + * + * This adds a renderer selection @constraint. + * + * Applications should ideally minimize how many of these constraints they + * depend on to ensure maximum portability. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_renderer_add_constraint (CoglRenderer *renderer, + CoglRendererConstraint constraint); + +/** + * cogl_renderer_remove_constraint: + * @renderer: An unconnected #CoglRenderer + * @constraint: A #CoglRendererConstraint to remove + * + * This removes a renderer selection @constraint. + * + * Applications should ideally minimize how many of these constraints they + * depend on to ensure maximum portability. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_renderer_remove_constraint (CoglRenderer *renderer, + CoglRendererConstraint constraint); + +/** + * CoglDriver: + * @COGL_DRIVER_ANY: Implies no preference for which driver is used + * @COGL_DRIVER_NOP: A No-Op driver. + * @COGL_DRIVER_GL: An OpenGL driver. + * @COGL_DRIVER_GL3: An OpenGL driver using the core GL 3.1 profile + * @COGL_DRIVER_GLES1: An OpenGL ES 1.1 driver. + * @COGL_DRIVER_GLES2: An OpenGL ES 2.0 driver. + * @COGL_DRIVER_WEBGL: A WebGL driver. + * + * Identifiers for underlying hardware drivers that may be used by + * Cogl for rendering. + * + * Since: 1.10 + * Stability: unstable + */ +typedef enum +{ + COGL_DRIVER_ANY, + COGL_DRIVER_NOP, + COGL_DRIVER_GL, + COGL_DRIVER_GL3, + COGL_DRIVER_GLES1, + COGL_DRIVER_GLES2, + COGL_DRIVER_WEBGL +} CoglDriver; + +/** + * cogl_renderer_set_driver: + * @renderer: An unconnected #CoglRenderer + * + * Requests that Cogl should try to use a specific underlying driver + * for rendering. + * + * If you select an unsupported driver then cogl_renderer_connect() + * will fail and report an error. Most applications should not + * explicitly select a driver and should rely on Cogl automatically + * choosing the driver. + * + * This may only be called on an un-connected #CoglRenderer. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_renderer_set_driver (CoglRenderer *renderer, + CoglDriver driver); + +/** + * cogl_renderer_get_driver: + * @renderer: A connected #CoglRenderer + * + * Queries what underlying driver is being used by Cogl. + * + * This may only be called on a connected #CoglRenderer. + * + * Since: 1.10 + * Stability: unstable + */ +CoglDriver +cogl_renderer_get_driver (CoglRenderer *renderer); + +/** + * CoglOutputCallback: + * @output: The current display output being iterated + * @user_data: The user pointer passed to + * cogl_renderer_foreach_output() + * + * A callback type that can be passed to + * cogl_renderer_foreach_output() for iterating display outputs for a + * given renderer. + * + * Since: 1.14 + * Stability: Unstable + */ +typedef void (*CoglOutputCallback) (CoglOutput *output, void *user_data); + +/** + * cogl_renderer_foreach_output: + * @renderer: A connected #CoglRenderer + * @callback: (scope call): A #CoglOutputCallback to be called for + * each display output + * @user_data: A user pointer to be passed to @callback + * + * Iterates all known display outputs for the given @renderer and + * passes a corresponding #CoglOutput pointer to the given @callback + * for each one, along with the given @user_data. + * + * Since: 1.14 + * Stability: Unstable + */ +void +cogl_renderer_foreach_output (CoglRenderer *renderer, + CoglOutputCallback callback, + void *user_data); + +COGL_END_DECLS + +#endif /* __COGL_RENDERER_H__ */ + diff --git a/cogl/cogl/cogl-sampler-cache-private.h b/cogl/cogl/cogl-sampler-cache-private.h new file mode 100644 index 000000000..5688effb9 --- /dev/null +++ b/cogl/cogl/cogl-sampler-cache-private.h @@ -0,0 +1,96 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_SAMPLER_CACHE_PRIVATE_H +#define __COGL_SAMPLER_CACHE_PRIVATE_H + +#include "cogl-context.h" +#include "cogl-gl-header.h" + +/* These aren't defined in the GLES headers */ +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812d +#endif +#ifndef GL_MIRRORED_REPEAT +#define GL_MIRRORED_REPEAT 0x8370 +#endif + +/* GL_ALWAYS is just used here as a value that is known not to clash + * with any valid GL wrap modes. + * + * XXX: keep the values in sync with the CoglPipelineWrapMode enum + * so no conversion is actually needed. + */ +typedef enum _CoglSamplerCacheWrapMode +{ + COGL_SAMPLER_CACHE_WRAP_MODE_REPEAT = GL_REPEAT, + COGL_SAMPLER_CACHE_WRAP_MODE_MIRRORED_REPEAT = GL_MIRRORED_REPEAT, + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, + COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC = GL_ALWAYS +} CoglSamplerCacheWrapMode; + +typedef struct _CoglSamplerCache CoglSamplerCache; + +typedef struct _CoglSamplerCacheEntry +{ + GLuint sampler_object; + + GLenum min_filter; + GLenum mag_filter; + + CoglSamplerCacheWrapMode wrap_mode_s; + CoglSamplerCacheWrapMode wrap_mode_t; + CoglSamplerCacheWrapMode wrap_mode_p; +} CoglSamplerCacheEntry; + +CoglSamplerCache * +_cogl_sampler_cache_new (CoglContext *context); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + CoglSamplerCacheWrapMode wrap_mode_s, + CoglSamplerCacheWrapMode wrap_mode_t, + CoglSamplerCacheWrapMode wrap_mode_p); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_filters (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + GLenum min_filter, + GLenum mag_filter); + +void +_cogl_sampler_cache_free (CoglSamplerCache *cache); + +#endif /* __COGL_SAMPLER_CACHE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-sampler-cache.c b/cogl/cogl/cogl-sampler-cache.c new file mode 100644 index 000000000..e21c64c6a --- /dev/null +++ b/cogl/cogl/cogl-sampler-cache.c @@ -0,0 +1,371 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-sampler-cache-private.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" + +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif + +struct _CoglSamplerCache +{ + CoglContext *context; + + /* The samplers are hashed in two tables. One is using the enum + values that Cogl exposes (so it can include the 'automatic' wrap + mode) and the other is using the converted values that will be + given to GL. The first is used to get a unique pointer for the + sampler state so that pipelines only need to store a single + pointer instead of the whole state and the second is used so that + only a single GL sampler object will be created for each unique + GL state. */ + GHashTable *hash_table_cogl; + GHashTable *hash_table_gl; + + /* This is used for generated fake unique sampler object numbers + when the sampler object extension is not supported */ + GLuint next_fake_sampler_object_number; +}; + +static CoglSamplerCacheWrapMode +get_real_wrap_mode (CoglSamplerCacheWrapMode wrap_mode) +{ + if (wrap_mode == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) + return COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE; + + return wrap_mode; +} + +static void +canonicalize_key (CoglSamplerCacheEntry *key) +{ + /* This converts the wrap modes to the enums that will actually be + given to GL so that it can be used as a key to get a unique GL + sampler object for the state */ + key->wrap_mode_s = get_real_wrap_mode (key->wrap_mode_s); + key->wrap_mode_t = get_real_wrap_mode (key->wrap_mode_t); + key->wrap_mode_p = get_real_wrap_mode (key->wrap_mode_p); +} + +static CoglBool +wrap_mode_equal_gl (CoglSamplerCacheWrapMode wrap_mode0, + CoglSamplerCacheWrapMode wrap_mode1) +{ + /* We want to compare the actual GLenum that will be used so that if + two different wrap_modes actually use the same GL state we'll + still use the same sampler object */ + return get_real_wrap_mode (wrap_mode0) == get_real_wrap_mode (wrap_mode1); +} + +static CoglBool +sampler_state_equal_gl (const void *value0, + const void *value1) +{ + const CoglSamplerCacheEntry *state0 = value0; + const CoglSamplerCacheEntry *state1 = value1; + + return (state0->mag_filter == state1->mag_filter && + state0->min_filter == state1->min_filter && + wrap_mode_equal_gl (state0->wrap_mode_s, state1->wrap_mode_s) && + wrap_mode_equal_gl (state0->wrap_mode_t, state1->wrap_mode_t) && + wrap_mode_equal_gl (state0->wrap_mode_p, state1->wrap_mode_p)); +} + +static unsigned int +hash_wrap_mode_gl (unsigned int hash, + CoglSamplerCacheWrapMode wrap_mode) +{ + /* We want to hash the actual GLenum that will be used so that if + two different wrap_modes actually use the same GL state we'll + still use the same sampler object */ + GLenum real_wrap_mode = get_real_wrap_mode (wrap_mode); + + return _cogl_util_one_at_a_time_hash (hash, + &real_wrap_mode, + sizeof (real_wrap_mode)); +} + +static unsigned int +hash_sampler_state_gl (const void *key) +{ + const CoglSamplerCacheEntry *entry = key; + unsigned int hash = 0; + + hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, + sizeof (entry->mag_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, + sizeof (entry->min_filter)); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_s); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_t); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_p); + + return _cogl_util_one_at_a_time_mix (hash); +} + +static CoglBool +sampler_state_equal_cogl (const void *value0, + const void *value1) +{ + const CoglSamplerCacheEntry *state0 = value0; + const CoglSamplerCacheEntry *state1 = value1; + + return (state0->mag_filter == state1->mag_filter && + state0->min_filter == state1->min_filter && + state0->wrap_mode_s == state1->wrap_mode_s && + state0->wrap_mode_t == state1->wrap_mode_t && + state0->wrap_mode_p == state1->wrap_mode_p); +} + +static unsigned int +hash_sampler_state_cogl (const void *key) +{ + const CoglSamplerCacheEntry *entry = key; + unsigned int hash = 0; + + hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, + sizeof (entry->mag_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, + sizeof (entry->min_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_s, + sizeof (entry->wrap_mode_s)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_t, + sizeof (entry->wrap_mode_t)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_p, + sizeof (entry->wrap_mode_p)); + + return _cogl_util_one_at_a_time_mix (hash); +} + +CoglSamplerCache * +_cogl_sampler_cache_new (CoglContext *context) +{ + CoglSamplerCache *cache = g_new (CoglSamplerCache, 1); + + /* No reference is taken on the context because it would create a + circular reference */ + cache->context = context; + + cache->hash_table_gl = g_hash_table_new (hash_sampler_state_gl, + sampler_state_equal_gl); + cache->hash_table_cogl = g_hash_table_new (hash_sampler_state_cogl, + sampler_state_equal_cogl); + cache->next_fake_sampler_object_number = 1; + + return cache; +} + +static void +set_wrap_mode (CoglContext *context, + GLuint sampler_object, + GLenum param, + CoglSamplerCacheWrapMode wrap_mode) +{ + GE( context, glSamplerParameteri (sampler_object, + param, + wrap_mode) ); +} + +static CoglSamplerCacheEntry * +_cogl_sampler_cache_get_entry_gl (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *key) +{ + CoglSamplerCacheEntry *entry; + + entry = g_hash_table_lookup (cache->hash_table_gl, key); + + if (entry == NULL) + { + CoglContext *context = cache->context; + + entry = g_slice_dup (CoglSamplerCacheEntry, key); + + if (_cogl_has_private_feature (context, + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + { + GE( context, glGenSamplers (1, &entry->sampler_object) ); + + GE( context, glSamplerParameteri (entry->sampler_object, + GL_TEXTURE_MIN_FILTER, + entry->min_filter) ); + GE( context, glSamplerParameteri (entry->sampler_object, + GL_TEXTURE_MAG_FILTER, + entry->mag_filter) ); + + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_S, + entry->wrap_mode_s); + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_T, + entry->wrap_mode_t); + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_R, + entry->wrap_mode_p); + } + else + { + /* If sampler objects aren't supported then we'll invent a + unique number so that pipelines can still compare the + unique state just by comparing the sampler object + numbers */ + entry->sampler_object = cache->next_fake_sampler_object_number++; + } + + g_hash_table_insert (cache->hash_table_gl, entry, entry); + } + + return entry; +} + +static CoglSamplerCacheEntry * +_cogl_sampler_cache_get_entry_cogl (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *key) +{ + CoglSamplerCacheEntry *entry; + + entry = g_hash_table_lookup (cache->hash_table_cogl, key); + + if (entry == NULL) + { + CoglSamplerCacheEntry canonical_key; + CoglSamplerCacheEntry *gl_entry; + + entry = g_slice_dup (CoglSamplerCacheEntry, key); + + /* Get the sampler object number from the canonical GL version + of the sampler state cache */ + canonical_key = *key; + canonicalize_key (&canonical_key); + gl_entry = _cogl_sampler_cache_get_entry_gl (cache, &canonical_key); + entry->sampler_object = gl_entry->sampler_object; + + g_hash_table_insert (cache->hash_table_cogl, entry, entry); + } + + return entry; +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache) +{ + CoglSamplerCacheEntry key; + + key.wrap_mode_s = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + key.wrap_mode_t = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + key.wrap_mode_p = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + + key.min_filter = GL_LINEAR; + key.mag_filter = GL_LINEAR; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + CoglSamplerCacheWrapMode wrap_mode_s, + CoglSamplerCacheWrapMode wrap_mode_t, + CoglSamplerCacheWrapMode wrap_mode_p) +{ + CoglSamplerCacheEntry key = *old_entry; + + key.wrap_mode_s = wrap_mode_s; + key.wrap_mode_t = wrap_mode_t; + key.wrap_mode_p = wrap_mode_p; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_filters (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + GLenum min_filter, + GLenum mag_filter) +{ + CoglSamplerCacheEntry key = *old_entry; + + key.min_filter = min_filter; + key.mag_filter = mag_filter; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +static void +hash_table_free_gl_cb (void *key, + void *value, + void *user_data) +{ + CoglContext *context = user_data; + CoglSamplerCacheEntry *entry = value; + + if (_cogl_has_private_feature (context, + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + GE( context, glDeleteSamplers (1, &entry->sampler_object) ); + + g_slice_free (CoglSamplerCacheEntry, entry); +} + +static void +hash_table_free_cogl_cb (void *key, + void *value, + void *user_data) +{ + CoglSamplerCacheEntry *entry = value; + + g_slice_free (CoglSamplerCacheEntry, entry); +} + +void +_cogl_sampler_cache_free (CoglSamplerCache *cache) +{ + g_hash_table_foreach (cache->hash_table_gl, + hash_table_free_gl_cb, + cache->context); + + g_hash_table_destroy (cache->hash_table_gl); + + g_hash_table_foreach (cache->hash_table_cogl, + hash_table_free_cogl_cb, + cache->context); + + g_hash_table_destroy (cache->hash_table_cogl); + + g_free (cache); +} diff --git a/cogl/cogl/cogl-snippet-private.h b/cogl/cogl/cogl-snippet-private.h new file mode 100644 index 000000000..e3269f20d --- /dev/null +++ b/cogl/cogl/cogl-snippet-private.h @@ -0,0 +1,77 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_SNIPPET_PRIVATE_H +#define __COGL_SNIPPET_PRIVATE_H + +#include + +#include "cogl-snippet.h" +#include "cogl-object-private.h" + +/* These values are also used in the enum for CoglSnippetHook. They + are copied here because we don't really want these names to be part + of the public API */ +#define COGL_SNIPPET_HOOK_BAND_SIZE 2048 +#define COGL_SNIPPET_FIRST_PIPELINE_HOOK 0 +#define COGL_SNIPPET_FIRST_PIPELINE_VERTEX_HOOK \ + COGL_SNIPPET_FIRST_PIPELINE_HOOK +#define COGL_SNIPPET_FIRST_PIPELINE_FRAGMENT_HOOK \ + (COGL_SNIPPET_FIRST_PIPELINE_VERTEX_HOOK + COGL_SNIPPET_HOOK_BAND_SIZE) +#define COGL_SNIPPET_FIRST_LAYER_HOOK (COGL_SNIPPET_HOOK_BAND_SIZE * 2) +#define COGL_SNIPPET_FIRST_LAYER_VERTEX_HOOK COGL_SNIPPET_FIRST_LAYER_HOOK +#define COGL_SNIPPET_FIRST_LAYER_FRAGMENT_HOOK \ + (COGL_SNIPPET_FIRST_LAYER_VERTEX_HOOK + COGL_SNIPPET_HOOK_BAND_SIZE) + +struct _CoglSnippet +{ + CoglObject _parent; + + CoglSnippetHook hook; + + /* This is set to TRUE the first time the snippet is attached to the + pipeline. After that any attempts to modify the snippet will be + ignored. */ + CoglBool immutable; + + char *declarations; + char *pre; + char *replace; + char *post; +}; + +void +_cogl_snippet_make_immutable (CoglSnippet *snippet); + +#endif /* __COGL_SNIPPET_PRIVATE_H */ + diff --git a/cogl/cogl/cogl-snippet.c b/cogl/cogl/cogl-snippet.c new file mode 100644 index 000000000..a3f5d6c31 --- /dev/null +++ b/cogl/cogl/cogl-snippet.c @@ -0,0 +1,187 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-types.h" +#include "cogl-snippet-private.h" +#include "cogl-util.h" +#include "cogl-gtype-private.h" + +static void +_cogl_snippet_free (CoglSnippet *snippet); + +COGL_OBJECT_DEFINE (Snippet, snippet); +COGL_GTYPE_DEFINE_CLASS (Snippet, snippet); + +CoglSnippet * +cogl_snippet_new (CoglSnippetHook hook, + const char *declarations, + const char *post) +{ + CoglSnippet *snippet = g_slice_new0 (CoglSnippet); + + _cogl_snippet_object_new (snippet); + + snippet->hook = hook; + + cogl_snippet_set_declarations (snippet, declarations); + cogl_snippet_set_post (snippet, post); + + return snippet; +} + +CoglSnippetHook +cogl_snippet_get_hook (CoglSnippet *snippet) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_snippet (snippet), 0); + + return snippet->hook; +} + +static CoglBool +_cogl_snippet_modify (CoglSnippet *snippet) +{ + if (snippet->immutable) + { + g_warning ("A CoglSnippet should not be modified once it has been " + "attached to a pipeline. Any modifications after that point " + "will be ignored."); + + return FALSE; + } + + return TRUE; +} + +void +cogl_snippet_set_declarations (CoglSnippet *snippet, + const char *declarations) +{ + _COGL_RETURN_IF_FAIL (cogl_is_snippet (snippet)); + + if (!_cogl_snippet_modify (snippet)) + return; + + g_free (snippet->declarations); + snippet->declarations = declarations ? g_strdup (declarations) : NULL; +} + +const char * +cogl_snippet_get_declarations (CoglSnippet *snippet) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_snippet (snippet), NULL); + + return snippet->declarations; +} + +void +cogl_snippet_set_pre (CoglSnippet *snippet, + const char *pre) +{ + _COGL_RETURN_IF_FAIL (cogl_is_snippet (snippet)); + + if (!_cogl_snippet_modify (snippet)) + return; + + g_free (snippet->pre); + snippet->pre = pre ? g_strdup (pre) : NULL; +} + +const char * +cogl_snippet_get_pre (CoglSnippet *snippet) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_snippet (snippet), NULL); + + return snippet->pre; +} + +void +cogl_snippet_set_replace (CoglSnippet *snippet, + const char *replace) +{ + _COGL_RETURN_IF_FAIL (cogl_is_snippet (snippet)); + + if (!_cogl_snippet_modify (snippet)) + return; + + g_free (snippet->replace); + snippet->replace = replace ? g_strdup (replace) : NULL; +} + +const char * +cogl_snippet_get_replace (CoglSnippet *snippet) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_snippet (snippet), NULL); + + return snippet->replace; +} + +void +cogl_snippet_set_post (CoglSnippet *snippet, + const char *post) +{ + _COGL_RETURN_IF_FAIL (cogl_is_snippet (snippet)); + + if (!_cogl_snippet_modify (snippet)) + return; + + g_free (snippet->post); + snippet->post = post ? g_strdup (post) : NULL; +} + +const char * +cogl_snippet_get_post (CoglSnippet *snippet) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_snippet (snippet), NULL); + + return snippet->post; +} + +void +_cogl_snippet_make_immutable (CoglSnippet *snippet) +{ + snippet->immutable = TRUE; +} + +static void +_cogl_snippet_free (CoglSnippet *snippet) +{ + g_free (snippet->declarations); + g_free (snippet->pre); + g_free (snippet->replace); + g_free (snippet->post); + g_slice_free (CoglSnippet, snippet); +} diff --git a/cogl/cogl/cogl-snippet.h b/cogl/cogl/cogl-snippet.h new file mode 100644 index 000000000..b8d9efdec --- /dev/null +++ b/cogl/cogl/cogl-snippet.h @@ -0,0 +1,866 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_SNIPPET_H__ +#define __COGL_SNIPPET_H__ + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-snippet + * @short_description: Functions for creating and manipulating shader snippets + * + * #CoglSnippets are used to modify or replace parts of a + * #CoglPipeline using GLSL. GLSL is a programming language supported + * by OpenGL on programmable hardware to provide a more flexible + * description of what should be rendered. A description of GLSL + * itself is outside the scope of this documentation but any good + * OpenGL book should help to describe it. + * + * Unlike in OpenGL, when using GLSL with Cogl it is possible to write + * short snippets to replace small sections of the pipeline instead of + * having to replace the whole of either the vertex or fragment + * pipelines. Of course it is also possible to replace the whole of + * the pipeline if needed. + * + * Each snippet is a standalone chunk of code which would attach to + * the pipeline at a particular point. The code is split into four + * separate strings (all of which are optional): + * + * + * + * declarations + * + * The code in this string will be inserted outside of any function in + * the global scope of the shader. This can be used to declare + * uniforms, attributes, varyings and functions to be used by the + * snippet. + * + * + * + * pre + * + * The code in this string will be inserted before the hook point. + * + * + * + * post + * + * The code in this string will be inserted after the hook point. This + * can be used to modify the results of the builtin generated code for + * that hook point. + * + * + * + * replace + * + * If present the code in this string will replace the generated code + * for the hook point. + * + * + * + * + * All of the strings apart from the declarations string of a pipeline + * are generated in a single function so they can share variables + * declared from one string in another. The scope of the code is + * limited to each snippet so local variables declared in the snippet + * will not collide with variables declared in another + * snippet. However, code in the 'declarations' string is global to + * the shader so it is the application's responsibility to ensure that + * variables declared here will not collide with those from other + * snippets. + * + * The snippets can be added to a pipeline with + * cogl_pipeline_add_snippet() or + * cogl_pipeline_add_layer_snippet(). Which function to use depends on + * which hook the snippet is targetting. The snippets are all + * generated in the order they are added to the pipeline. That is, the + * post strings are executed in the order they are added to the + * pipeline and the pre strings are executed in reverse order. If any + * replace strings are given for a snippet then any other snippets + * with the same hook added before that snippet will be ignored. The + * different hooks are documented under #CoglSnippetHook. + * + * For portability with GLES2, it is recommended not to use the GLSL + * builtin names such as gl_FragColor. Instead there are replacement + * names under the cogl_* namespace which can be used instead. These + * are: + * + * + * + * uniform mat4 + * cogl_modelview_matrix + * + * The current modelview matrix. This is equivalent to + * #gl_ModelViewMatrix. + * + * + * + * uniform mat4 + * cogl_projection_matrix + * + * The current projection matrix. This is equivalent to + * #gl_ProjectionMatrix. + * + * + * + * uniform mat4 + * cogl_modelview_projection_matrix + * + * The combined modelview and projection matrix. A vertex shader + * would typically use this to transform the incoming vertex + * position. The separate modelview and projection matrices are + * usually only needed for lighting calculations. This is + * equivalent to #gl_ModelViewProjectionMatrix. + * + * + * + * uniform mat4 + * cogl_texture_matrix[] + * + * An array of matrices for transforming the texture + * coordinates. This is equivalent to #gl_TextureMatrix. + * + * + * + * + * In a vertex shader, the following are also available: + * + * + * + * attribute vec4 + * cogl_position_in + * + * The incoming vertex position. This is equivalent to #gl_Vertex. + * + * + * + * attribute vec4 + * cogl_color_in + * + * The incoming vertex color. This is equivalent to #gl_Color. + * + * + * + * attribute vec4 + * cogl_tex_coord_in + * + * The texture coordinate for layer 0. This is an alternative name + * for #cogl_tex_coord0_in. + * + * + * + * attribute vec4 + * cogl_tex_coord0_in + * + * The texture coordinate for the layer 0. This is equivalent to + * #gl_MultiTexCoord0. There will also be #cogl_tex_coord1_in and + * so on if more layers are added to the pipeline. + * + * + * + * attribute vec3 + * cogl_normal_in + * + * The normal of the vertex. This is equivalent to #gl_Normal. + * + * + * + * vec4 + * cogl_position_out + * + * The calculated position of the vertex. This must be written to + * in all vertex shaders. This is equivalent to #gl_Position. + * + * + * + * float + * cogl_point_size_in + * + * The incoming point size from the cogl_point_size_in attribute. + * This is only available if + * cogl_pipeline_set_per_vertex_point_size() is set on the + * pipeline. + * + * + * + * float + * cogl_point_size_out + * + * The calculated size of a point. This is equivalent to #gl_PointSize. + * + * + * + * varying vec4 + * cogl_color_out + * + * The calculated color of a vertex. This is equivalent to #gl_FrontColor. + * + * + * + * varying vec4 + * cogl_tex_coord0_out + * + * The calculated texture coordinate for layer 0 of the pipeline. + * This is equivalent to #gl_TexCoord[0]. There will also be + * #cogl_tex_coord1_out and so on if more layers are added to the + * pipeline. In the fragment shader, this varying is called + * #cogl_tex_coord0_in. + * + * + * + * + * In a fragment shader, the following are also available: + * + * + * + * varying vec4 cogl_color_in + * + * The calculated color of a vertex. This is equivalent to #gl_FrontColor. + * + * + * + * varying vec4 + * cogl_tex_coord0_in + * + * The texture coordinate for layer 0. This is equivalent to + * #gl_TexCoord[0]. There will also be #cogl_tex_coord1_in and so + * on if more layers are added to the pipeline. + * + * + * + * vec4 cogl_color_out + * + * The final calculated color of the fragment. All fragment shaders + * must write to this variable. This is equivalent to + * #gl_FrontColor. + * + * + * + * float cogl_depth_out + * + * An optional output variable specifying the depth value to use + * for this fragment. This is equivalent to #gl_FragDepth. + * + * + * + * bool cogl_front_facing + * + * A readonly variable that will be true if the current primitive + * is front facing. This can be used to implement two-sided + * coloring algorithms. This is equivalent to #gl_FrontFacing. + * + * + * + * vec2 cogl_point_coord + * + * When rendering points, this will contain a vec2 which represents + * the position within the point of the current fragment. + * vec2(0.0,0.0) will be the topleft of the point and vec2(1.0,1.0) + * will be the bottom right. Note that there is currently a bug in + * Cogl where when rendering to an offscreen buffer these + * coordinates will be upside-down. The value is undefined when not + * rendering points. This builtin can only be used if the + * %COGL_FEATURE_ID_POINT_SPRITE feature is available. + * + * + * + * + * Here is an example of using a snippet to add a desaturate effect to the + * generated color on a pipeline. + * + * + * CoglPipeline *pipeline = cogl_pipeline_new (); + * + * /* Set up the pipeline here, ie by adding a texture or other + * layers */ + * + * /* Create the snippet. The first string is the declarations which + * we will use to add a uniform. The second is the 'post' string which + * will contain the code to perform the desaturation. */ + * CoglSnippet *snippet = + * cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + * "uniform float factor;", + * "float gray = dot (vec3 (0.299, 0.587, 0.114), " + * " cogl_color_out.rgb);" + * "cogl_color_out.rgb = mix (vec3 (gray)," + * " cogl_color_out.rgb," + * " factor);"); + * + * /* Add it to the pipeline */ + * cogl_pipeline_add_snippet (pipeline, snippet); + * /* The pipeline keeps a reference to the snippet + * so we don't need to */ + * cogl_object_unref (snippet); + * + * /* Update the custom uniform on the pipeline */ + * int location = cogl_pipeline_get_uniform_location (pipeline, "factor"); + * cogl_pipeline_set_uniform_1f (pipeline, location, 0.5f); + * + * /* Now we can render with the snippet as usual */ + * cogl_push_source (pipeline); + * cogl_rectangle (0, 0, 10, 10); + * cogl_pop_source (); + * + */ +typedef struct _CoglSnippet CoglSnippet; + +#define COGL_SNIPPET(OBJECT) ((CoglSnippet *)OBJECT) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_snippet_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_snippet_get_gtype (void); +#endif + +/* Enumeration of all the hook points that a snippet can be attached + to within a pipeline. */ +/** + * CoglSnippetHook: + * @COGL_SNIPPET_HOOK_VERTEX_GLOBALS: A hook for declaring global data + * that can be shared with all other snippets that are on a vertex + * hook. + * @COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS: A hook for declaring global + * data wthat can be shared with all other snippets that are on a + * fragment hook. + * @COGL_SNIPPET_HOOK_VERTEX: A hook for the entire vertex processing + * stage of the pipeline. + * @COGL_SNIPPET_HOOK_VERTEX_TRANSFORM: A hook for the vertex transformation. + * @COGL_SNIPPET_HOOK_POINT_SIZE: A hook for manipulating the point + * size of a vertex. This is only used if + * cogl_pipeline_set_per_vertex_point_size() is enabled on the + * pipeline. + * @COGL_SNIPPET_HOOK_FRAGMENT: A hook for the entire fragment + * processing stage of the pipeline. + * @COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM: A hook for applying the + * layer matrix to a texture coordinate for a layer. + * @COGL_SNIPPET_HOOK_LAYER_FRAGMENT: A hook for the fragment + * processing of a particular layer. + * @COGL_SNIPPET_HOOK_TEXTURE_LOOKUP: A hook for the texture lookup + * stage of a given layer in a pipeline. + * + * #CoglSnippetHook is used to specify a location within a + * #CoglPipeline where the code of the snippet should be used when it + * is attached to a pipeline. + * + * + * + * %COGL_SNIPPET_HOOK_VERTEX_GLOBALS + * + * + * Adds a shader snippet at the beginning of the global section of the + * shader for the vertex processing. Any declarations here can be + * shared with all other snippets that are attached to a vertex hook. + * Only the ‘declarations’ string is used and the other strings are + * ignored. + * + * + * + * + * %COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS + * + * + * Adds a shader snippet at the beginning of the global section of the + * shader for the fragment processing. Any declarations here can be + * shared with all other snippets that are attached to a fragment + * hook. Only the ‘declarations’ string is used and the other strings + * are ignored. + * + * + * + * + * %COGL_SNIPPET_HOOK_VERTEX + * + * + * Adds a shader snippet that will hook on to the vertex processing + * stage of the pipeline. This gives a chance for the application to + * modify the vertex attributes generated by the shader. Typically the + * snippet will modify cogl_color_out or cogl_position_out builtins. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted at the top of the + * main() function before any vertex processing is done. + * + * + * The ‘replace’ string in @snippet will be used instead of the + * generated vertex processing if it is present. This can be used if + * the application wants to provide a complete vertex shader and + * doesn't need the generated output from Cogl. + * + * + * The ‘post’ string in @snippet will be inserted after all of the + * standard vertex processing is done. This can be used to modify the + * outputs. + * + * + * + * + * %COGL_SNIPPET_HOOK_VERTEX_TRANSFORM + * + * + * Adds a shader snippet that will hook on to the vertex transform stage. + * Typically the snippet will use the cogl_modelview_matrix, + * cogl_projection_matrix and cogl_modelview_projection_matrix matrices and the + * cogl_position_in attribute. The hook must write to cogl_position_out. + * The default processing for this hook will multiply cogl_position_in by + * the combined modelview-projection matrix and store it on cogl_position_out. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted at the top of the + * main() function before the vertex transform is done. + * + * + * The ‘replace’ string in @snippet will be used instead of the + * generated vertex transform if it is present. + * + * + * The ‘post’ string in @snippet will be inserted after all of the + * standard vertex transformation is done. This can be used to modify the + * cogl_position_out in addition to the default processing. + * + * + * + * + * %COGL_SNIPPET_HOOK_POINT_SIZE + * + * + * Adds a shader snippet that will hook on to the point size + * calculation step within the vertex shader stage. The snippet should + * write to the builtin cogl_point_size_out with the new point size. + * The snippet can either read cogl_point_size_in directly and write a + * new value or first read an existing value in cogl_point_size_out + * that would be set by a previous snippet. Note that this hook is + * only used if cogl_pipeline_set_per_vertex_point_size() is enabled + * on the pipeline. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted just before + * calculating the point size. + * + * + * The ‘replace’ string in @snippet will be used instead of the + * generated point size calculation if it is present. + * + * + * The ‘post’ string in @snippet will be inserted after the + * standard point size calculation is done. This can be used to modify + * cogl_point_size_out in addition to the default processing. + * + * + * + * + * %COGL_SNIPPET_HOOK_FRAGMENT + * + * + * Adds a shader snippet that will hook on to the fragment processing + * stage of the pipeline. This gives a chance for the application to + * modify the fragment color generated by the shader. Typically the + * snippet will modify cogl_color_out. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted at the top of the + * main() function before any fragment processing is done. + * + * + * The ‘replace’ string in @snippet will be used instead of the + * generated fragment processing if it is present. This can be used if + * the application wants to provide a complete fragment shader and + * doesn't need the generated output from Cogl. + * + * + * The ‘post’ string in @snippet will be inserted after all of the + * standard fragment processing is done. At this point the generated + * value for the rest of the pipeline state will already be in + * cogl_color_out so the application can modify the result by altering + * this variable. + * + * + * + * + * %COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM + * + * + * Adds a shader snippet that will hook on to the texture coordinate + * transformation of a particular layer. This can be used to replace + * the processing for a layer or to modify the results. + * + * + * Within the snippet code for this hook there are two extra + * variables. The first is a mat4 called cogl_matrix which represents + * the user matrix for this layer. The second is called cogl_tex_coord + * and represents the incoming and outgoing texture coordinate. On + * entry to the hook, cogl_tex_coord contains the value of the + * corresponding texture coordinate attribute for this layer. The hook + * is expected to modify this variable. The output will be passed as a + * varying to the fragment processing stage. The default code will + * just multiply cogl_matrix by cogl_tex_coord and store the result in + * cogl_tex_coord. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted just before the + * fragment processing for this layer. At this point cogl_tex_coord + * still contains the value of the texture coordinate attribute. + * + * + * If a ‘replace’ string is given then this will be used instead of + * the default fragment processing for this layer. The snippet can + * modify cogl_tex_coord or leave it as is to apply no transformation. + * + * + * The ‘post’ string in @snippet will be inserted just after the + * transformation. At this point cogl_tex_coord will contain the + * results of the transformation but it can be further modified by the + * snippet. + * + * + * + * + * %COGL_SNIPPET_HOOK_LAYER_FRAGMENT + * + * + * Adds a shader snippet that will hook on to the fragment processing + * of a particular layer. This can be used to replace the processing + * for a layer or to modify the results. + * + * + * Within the snippet code for this hook there is an extra vec4 + * variable called ‘cogl_layer’. This contains the resulting color + * that will be used for the layer. This can be modified in the ‘post’ + * section or it the default processing can be replaced entirely using + * the ‘replace’ section. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted just before the + * fragment processing for this layer. + * + * + * If a ‘replace’ string is given then this will be used instead of + * the default fragment processing for this layer. The snippet must write to + * the ‘cogl_layer’ variable in that case. + * + * + * The ‘post’ string in @snippet will be inserted just after the + * fragment processing for the layer. The results can be modified by changing + * the value of the ‘cogl_layer’ variable. + * + * + * + * + * %COGL_SNIPPET_HOOK_TEXTURE_LOOKUP + * + * + * Adds a shader snippet that will hook on to the texture lookup part + * of a given layer. This gives a chance for the application to modify + * the coordinates that will be used for the texture lookup or to + * alter the returned texel. + * + * + * Within the snippet code for this hook there are three extra + * variables available. ‘cogl_sampler’ is a sampler object + * representing the sampler for the layer where the snippet is + * attached. ‘cogl_tex_coord’ is a vec4 which contains the texture + * coordinates that will be used for the texture lookup. This can be + * modified. ‘cogl_texel’ will contain the result of the texture + * lookup. This can also be modified. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted at the top of the + * main() function before any fragment processing is done. This is a + * good place to modify the cogl_tex_coord variable. + * + * + * If a ‘replace’ string is given then this will be used instead of a + * the default texture lookup. The snippet would typically use its own + * sampler in this case. + * + * + * The ‘post’ string in @snippet will be inserted after texture lookup + * has been preformed. Here the snippet can modify the cogl_texel + * variable to alter the returned texel. + * + * + * + * + * + * Since: 1.10 + * Stability: Unstable + */ +typedef enum { + /* Per pipeline vertex hooks */ + COGL_SNIPPET_HOOK_VERTEX = 0, + COGL_SNIPPET_HOOK_VERTEX_TRANSFORM, + COGL_SNIPPET_HOOK_VERTEX_GLOBALS, + COGL_SNIPPET_HOOK_POINT_SIZE, + + /* Per pipeline fragment hooks */ + COGL_SNIPPET_HOOK_FRAGMENT = 2048, + COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS, + + /* Per layer vertex hooks */ + COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096, + + /* Per layer fragment hooks */ + COGL_SNIPPET_HOOK_LAYER_FRAGMENT = 6144, + COGL_SNIPPET_HOOK_TEXTURE_LOOKUP +} CoglSnippetHook; + +/** + * cogl_snippet_new: + * @hook: The point in the pipeline that this snippet will wrap around + * or replace. + * @declarations: The source code for the declarations for this + * snippet or %NULL. See cogl_snippet_set_declarations(). + * @post: The source code to run after the hook point where this + * shader snippet is attached or %NULL. See cogl_snippet_set_post(). + * + * Allocates and initializes a new snippet with the given source strings. + * + * Return value: a pointer to a new #CoglSnippet + * + * Since: 1.10 + * Stability: Unstable + */ +CoglSnippet * +cogl_snippet_new (CoglSnippetHook hook, + const char *declarations, + const char *post); + +/** + * cogl_snippet_get_hook: + * @snippet: A #CoglSnippet + * + * Return value: the hook that was set when cogl_snippet_new() was + * called. + * Since: 1.10 + * Stability: Unstable + */ +CoglSnippetHook +cogl_snippet_get_hook (CoglSnippet *snippet); + +/** + * cogl_is_snippet: + * @object: A #CoglObject pointer + * + * Gets whether the given @object references an existing snippet object. + * + * Return value: %TRUE if the @object references a #CoglSnippet, + * %FALSE otherwise + * + * Since: 1.10 + * Stability: Unstable + */ +CoglBool +cogl_is_snippet (void *object); + +/** + * cogl_snippet_set_declarations: + * @snippet: A #CoglSnippet + * @declarations: The new source string for the declarations section + * of this snippet. + * + * Sets a source string that will be inserted in the global scope of + * the generated shader when this snippet is used on a pipeline. This + * string is typically used to declare uniforms, attributes or + * functions that will be used by the other parts of the snippets. + * + * This function should only be called before the snippet is attached + * to its first pipeline. After that the snippet should be considered + * immutable. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_snippet_set_declarations (CoglSnippet *snippet, + const char *declarations); + +/** + * cogl_snippet_get_declarations: + * @snippet: A #CoglSnippet + * + * Return value: the source string that was set with + * cogl_snippet_set_declarations() or %NULL if none was set. + * + * Since: 1.10 + * Stability: Unstable + */ +const char * +cogl_snippet_get_declarations (CoglSnippet *snippet); + +/** + * cogl_snippet_set_pre: + * @snippet: A #CoglSnippet + * @pre: The new source string for the pre section of this snippet. + * + * Sets a source string that will be inserted before the hook point in + * the generated shader for the pipeline that this snippet is attached + * to. Please see the documentation of each hook point in + * #CoglPipeline for a description of how this string should be used. + * + * This function should only be called before the snippet is attached + * to its first pipeline. After that the snippet should be considered + * immutable. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_snippet_set_pre (CoglSnippet *snippet, + const char *pre); + +/** + * cogl_snippet_get_pre: + * @snippet: A #CoglSnippet + * + * Return value: the source string that was set with + * cogl_snippet_set_pre() or %NULL if none was set. + * + * Since: 1.10 + * Stability: Unstable + */ +const char * +cogl_snippet_get_pre (CoglSnippet *snippet); + +/** + * cogl_snippet_set_replace: + * @snippet: A #CoglSnippet + * @replace: The new source string for the replace section of this snippet. + * + * Sets a source string that will be used instead of any generated + * source code or any previous snippets for this hook point. Please + * see the documentation of each hook point in #CoglPipeline for a + * description of how this string should be used. + * + * This function should only be called before the snippet is attached + * to its first pipeline. After that the snippet should be considered + * immutable. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_snippet_set_replace (CoglSnippet *snippet, + const char *replace); + +/** + * cogl_snippet_get_replace: + * @snippet: A #CoglSnippet + * + * Return value: the source string that was set with + * cogl_snippet_set_replace() or %NULL if none was set. + * + * Since: 1.10 + * Stability: Unstable + */ +const char * +cogl_snippet_get_replace (CoglSnippet *snippet); + +/** + * cogl_snippet_set_post: + * @snippet: A #CoglSnippet + * @post: The new source string for the post section of this snippet. + * + * Sets a source string that will be inserted after the hook point in + * the generated shader for the pipeline that this snippet is attached + * to. Please see the documentation of each hook point in + * #CoglPipeline for a description of how this string should be used. + * + * This function should only be called before the snippet is attached + * to its first pipeline. After that the snippet should be considered + * immutable. + * + * Since: 1.10 + * Stability: Unstable + */ +void +cogl_snippet_set_post (CoglSnippet *snippet, + const char *post); + +/** + * cogl_snippet_get_post: + * @snippet: A #CoglSnippet + * + * Return value: the source string that was set with + * cogl_snippet_set_post() or %NULL if none was set. + * + * Since: 1.10 + * Stability: Unstable + */ +const char * +cogl_snippet_get_post (CoglSnippet *snippet); + +COGL_END_DECLS + +#endif /* __COGL_SNIPPET_H__ */ diff --git a/cogl/cogl/cogl-spans.c b/cogl/cogl/cogl-spans.c new file mode 100644 index 000000000..e7ca67830 --- /dev/null +++ b/cogl/cogl/cogl-spans.c @@ -0,0 +1,183 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "math.h" + +#include "cogl-util.h" +#include "cogl-spans.h" + +void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &iter->spans[iter->index]; + + /* Offset next position by span size */ + iter->next_pos = iter->pos + iter->span->size - iter->span->waste; + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +void +_cogl_span_iter_begin (CoglSpanIter *iter, + const CoglSpan *spans, + int n_spans, + float normalize_factor, + float cover_start, + float cover_end, + CoglPipelineWrapMode wrap_mode) +{ + /* XXX: If CLAMP_TO_EDGE needs to be emulated then it needs to be + * done at a higher level than here... */ + _COGL_RETURN_IF_FAIL (wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT || + wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT); + + iter->span = NULL; + + iter->spans = spans; + iter->n_spans = n_spans; + + /* We always iterate in a positive direction from the origin. If + * iter->flipped == TRUE that means whoever is using this API should + * interpreted the current span as extending in the opposite direction. I.e. + * it extends to the left if iterating the X axis, or up if the Y axis. */ + if (cover_start > cover_end) + { + float tmp = cover_start; + cover_start = cover_end; + cover_end = tmp; + iter->flipped = TRUE; + } + else + iter->flipped = FALSE; + + /* The texture spans cover the normalized texture coordinate space ranging + * from [0,1] but to help support repeating of sliced textures we allow + * iteration of any range so we need to relate the start of the range to the + * nearest point equivalent to 0. + */ + if (normalize_factor != 1.0) + { + float cover_start_normalized = cover_start / normalize_factor; + iter->origin = floorf (cover_start_normalized) * normalize_factor; + } + else + iter->origin = floorf (cover_start); + + iter->wrap_mode = wrap_mode; + + if (wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT) + iter->index = 0; + else if (wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT) + { + if ((int)iter->origin % 2) + { + iter->index = iter->n_spans - 1; + iter->mirror_direction = -1; + iter->flipped = !iter->flipped; + } + else + { + iter->index = 0; + iter->mirror_direction = 1; + } + } + else + g_warn_if_reached (); + + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); + + while (iter->next_pos <= iter->cover_start) + _cogl_span_iter_next (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + if (iter->wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT) + iter->index = (iter->index + 1) % iter->n_spans; + else if (iter->wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT) + { + iter->index += iter->mirror_direction; + if (iter->index == iter->n_spans || iter->index == -1) + { + iter->mirror_direction = -iter->mirror_direction; + iter->index += iter->mirror_direction; + iter->flipped = !iter->flipped; + } + } + else + g_warn_if_reached (); + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +CoglBool +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + + diff --git a/cogl/cogl/cogl-spans.h b/cogl/cogl/cogl-spans.h new file mode 100644 index 000000000..a236784c4 --- /dev/null +++ b/cogl/cogl/cogl-spans.h @@ -0,0 +1,81 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_SPANS_PRIVATE_H +#define __COGL_SPANS_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-pipeline-layer-state.h" + +typedef struct _CoglSpan +{ + float start; + float size; + float waste; +} CoglSpan; + +typedef struct _CoglSpanIter +{ + int index; + const CoglSpan *spans; + int n_spans; + const CoglSpan *span; + float pos; + float next_pos; + float origin; + float cover_start; + float cover_end; + float intersect_start; + float intersect_end; + CoglBool intersects; + CoglBool flipped; + CoglPipelineWrapMode wrap_mode; + int mirror_direction; +} CoglSpanIter; + +void +_cogl_span_iter_update (CoglSpanIter *iter); + +void +_cogl_span_iter_begin (CoglSpanIter *iter, + const CoglSpan *spans, + int n_spans, + float normalize_factor, + float cover_start, + float cover_end, + CoglPipelineWrapMode wrap_mode); + +void +_cogl_span_iter_next (CoglSpanIter *iter); + +CoglBool +_cogl_span_iter_end (CoglSpanIter *iter); + +#endif /* __COGL_SPANS_PRIVATE_H */ diff --git a/cogl/cogl/cogl-sub-texture-private.h b/cogl/cogl/cogl-sub-texture-private.h new file mode 100644 index 000000000..75c476d6e --- /dev/null +++ b/cogl/cogl/cogl-sub-texture-private.h @@ -0,0 +1,62 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_SUB_TEXTURE_PRIVATE_H +#define __COGL_SUB_TEXTURE_PRIVATE_H + +#include "cogl-texture-private.h" + +#include + +struct _CoglSubTexture +{ + CoglTexture _parent; + + /* This is the texture that was passed in to + _cogl_sub_texture_new. If this is also a sub texture then we will + use the full texture from that to render instead of making a + chain. However we want to preserve the next texture in case the + user is expecting us to keep a reference and also so that we can + later add a cogl_sub_texture_get_parent_texture() function. */ + CoglTexture *next_texture; + /* This is the texture that will actually be used to draw. It will + point to the end of the chain if a sub texture of a sub texture + is created */ + CoglTexture *full_texture; + + /* The offset of the region represented by this sub-texture. This is + * the offset in full_texture which won't necessarily be the same as + * the offset passed to _cogl_sub_texture_new if next_texture is + * actually already a sub texture */ + int sub_x; + int sub_y; +}; + +#endif /* __COGL_SUB_TEXTURE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-sub-texture.c b/cogl/cogl/cogl-sub-texture.c new file mode 100644 index 000000000..7baf95eb6 --- /dev/null +++ b/cogl/cogl/cogl-sub-texture.c @@ -0,0 +1,480 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-sub-texture.h" +#include "cogl-context-private.h" +#include "cogl-object.h" +#include "cogl-texture-driver.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-texture-2d.h" +#include "cogl-texture-gl-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +static void _cogl_sub_texture_free (CoglSubTexture *sub_tex); + +COGL_TEXTURE_DEFINE (SubTexture, sub_texture); +COGL_GTYPE_DEFINE_CLASS (SubTexture, sub_texture); + +static const CoglTextureVtable cogl_sub_texture_vtable; + +static void +_cogl_sub_texture_unmap_quad (CoglSubTexture *sub_tex, + float *coords) +{ + CoglTexture *tex = COGL_TEXTURE (sub_tex); + + /* NB: coords[] come in as non-normalized if sub_tex->full_texture + * is a CoglTextureRectangle otherwhise they are normalized. The + * coordinates we write out though must always be normalized. + * + * NB: sub_tex->sub_x/y/width/height are in non-normalized + * coordinates. + */ + if (cogl_is_texture_rectangle (sub_tex->full_texture)) + { + coords[0] = (coords[0] - sub_tex->sub_x) / tex->width; + coords[1] = (coords[1] - sub_tex->sub_y) / tex->height; + coords[2] = (coords[2] - sub_tex->sub_x) / tex->width; + coords[3] = (coords[3] - sub_tex->sub_y) / tex->height; + } + else + { + float width = cogl_texture_get_width (sub_tex->full_texture); + float height = cogl_texture_get_height (sub_tex->full_texture); + coords[0] = (coords[0] * width - sub_tex->sub_x) / tex->width; + coords[1] = (coords[1] * height - sub_tex->sub_y) / tex->height; + coords[2] = (coords[2] * width - sub_tex->sub_x) / tex->width; + coords[3] = (coords[3] * height - sub_tex->sub_y) / tex->height; + } +} + +static void +_cogl_sub_texture_map_quad (CoglSubTexture *sub_tex, + float *coords) +{ + CoglTexture *tex = COGL_TEXTURE (sub_tex); + + /* NB: coords[] always come in as normalized coordinates but may go + * out as non-normalized if sub_tex->full_texture is a + * CoglTextureRectangle. + * + * NB: sub_tex->sub_x/y/width/height are in non-normalized + * coordinates. + */ + + if (cogl_is_texture_rectangle (sub_tex->full_texture)) + { + coords[0] = coords[0] * tex->width + sub_tex->sub_x; + coords[1] = coords[1] * tex->height + sub_tex->sub_y; + coords[2] = coords[2] * tex->width + sub_tex->sub_x; + coords[3] = coords[3] * tex->height + sub_tex->sub_y; + } + else + { + float width = cogl_texture_get_width (sub_tex->full_texture); + float height = cogl_texture_get_height (sub_tex->full_texture); + coords[0] = (coords[0] * tex->width + sub_tex->sub_x) / width; + coords[1] = (coords[1] * tex->height + sub_tex->sub_y) / height; + coords[2] = (coords[2] * tex->width + sub_tex->sub_x) / width; + coords[3] = (coords[3] * tex->height + sub_tex->sub_y) / height; + } +} + +typedef struct _CoglSubTextureForeachData +{ + CoglSubTexture *sub_tex; + CoglMetaTextureCallback callback; + void *user_data; +} CoglSubTextureForeachData; + +static void +unmap_coords_cb (CoglTexture *slice_texture, + const float *slice_texture_coords, + const float *meta_coords, + void *user_data) +{ + CoglSubTextureForeachData *data = user_data; + float unmapped_coords[4]; + + memcpy (unmapped_coords, meta_coords, sizeof (unmapped_coords)); + + _cogl_sub_texture_unmap_quad (data->sub_tex, unmapped_coords); + + data->callback (slice_texture, + slice_texture_coords, + unmapped_coords, + data->user_data); +} + +static void +_cogl_sub_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + CoglTexture *full_texture = sub_tex->full_texture; + float mapped_coords[4] = + { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; + float virtual_coords[4] = + { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; + + /* map the virtual coordinates to ->full_texture coordinates */ + _cogl_sub_texture_map_quad (sub_tex, mapped_coords); + + /* TODO: Add something like cogl_is_low_level_texture() */ + if (cogl_is_texture_2d (full_texture) || + cogl_is_texture_rectangle (full_texture)) + { + callback (sub_tex->full_texture, + mapped_coords, + virtual_coords, + user_data); + } + else + { + CoglSubTextureForeachData data; + + data.sub_tex = sub_tex; + data.callback = callback; + data.user_data = user_data; + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (full_texture), + mapped_coords[0], + mapped_coords[1], + mapped_coords[2], + mapped_coords[3], + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + unmap_coords_cb, + &data); + } +} + +static void +_cogl_sub_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_gl_flush_legacy_texobj_wrap_modes (sub_tex->full_texture, + wrap_mode_s, + wrap_mode_t, + wrap_mode_p); +} + +static void +_cogl_sub_texture_free (CoglSubTexture *sub_tex) +{ + cogl_object_unref (sub_tex->next_texture); + cogl_object_unref (sub_tex->full_texture); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (sub_tex)); +} + +CoglSubTexture * +cogl_sub_texture_new (CoglContext *ctx, + CoglTexture *next_texture, + int sub_x, int sub_y, + int sub_width, int sub_height) +{ + CoglTexture *full_texture; + CoglSubTexture *sub_tex; + CoglTexture *tex; + unsigned int next_width, next_height; + + next_width = cogl_texture_get_width (next_texture); + next_height = cogl_texture_get_height (next_texture); + + /* The region must specify a non-zero subset of the full texture */ + _COGL_RETURN_VAL_IF_FAIL (sub_x >= 0 && sub_y >= 0, NULL); + _COGL_RETURN_VAL_IF_FAIL (sub_width > 0 && sub_height > 0, NULL); + _COGL_RETURN_VAL_IF_FAIL (sub_x + sub_width <= next_width, NULL); + _COGL_RETURN_VAL_IF_FAIL (sub_y + sub_height <= next_height, NULL); + + sub_tex = g_new (CoglSubTexture, 1); + + tex = COGL_TEXTURE (sub_tex); + + _cogl_texture_init (tex, ctx, sub_width, sub_height, + _cogl_texture_get_format (next_texture), + NULL, /* no loader */ + &cogl_sub_texture_vtable); + + /* If the next texture is also a sub texture we can avoid one level + of indirection by referencing the full texture of that texture + instead. */ + if (cogl_is_sub_texture (next_texture)) + { + CoglSubTexture *other_sub_tex = COGL_SUB_TEXTURE (next_texture); + full_texture = other_sub_tex->full_texture; + sub_x += other_sub_tex->sub_x; + sub_y += other_sub_tex->sub_y; + } + else + full_texture = next_texture; + + sub_tex->next_texture = cogl_object_ref (next_texture); + sub_tex->full_texture = cogl_object_ref (full_texture); + + sub_tex->sub_x = sub_x; + sub_tex->sub_y = sub_y; + + return _cogl_sub_texture_object_new (sub_tex); +} + +static CoglBool +_cogl_sub_texture_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + CoglBool status = cogl_texture_allocate (sub_tex->full_texture, error); + + _cogl_texture_set_allocated (tex, + _cogl_texture_get_format (sub_tex->full_texture), + tex->width, tex->height); + + return status; +} + +CoglTexture * +cogl_sub_texture_get_parent (CoglSubTexture *sub_texture) +{ + return sub_texture->next_texture; +} + +static int +_cogl_sub_texture_get_max_waste (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_max_waste (sub_tex->full_texture); +} + +static CoglBool +_cogl_sub_texture_is_sliced (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_is_sliced (sub_tex->full_texture); +} + +static CoglBool +_cogl_sub_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + /* We can hardware repeat if the subtexture actually represents all of the + of the full texture */ + return (tex->width == + cogl_texture_get_width (sub_tex->full_texture) && + tex->height == + cogl_texture_get_height (sub_tex->full_texture) && + _cogl_texture_can_hardware_repeat (sub_tex->full_texture)); +} + +static void +_cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + /* This won't work if the sub texture is not the size of the full + texture and the coordinates are outside the range [0,1] */ + *s = ((*s * tex->width + sub_tex->sub_x) / + cogl_texture_get_width (sub_tex->full_texture)); + *t = ((*t * tex->height + sub_tex->sub_y) / + cogl_texture_get_height (sub_tex->full_texture)); + + _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); +} + +static CoglTransformResult +_cogl_sub_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + int i; + + /* We can't support repeating with this method. In this case + cogl-primitives will resort to manual repeating */ + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + return COGL_TRANSFORM_SOFTWARE_REPEAT; + + _cogl_sub_texture_map_quad (sub_tex, coords); + + return _cogl_texture_transform_quad_coords_to_gl (sub_tex->full_texture, + coords); +} + +static CoglBool +_cogl_sub_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_gl_texture (sub_tex->full_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_sub_texture_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_gl_flush_legacy_texobj_filters (sub_tex->full_texture, + min_filter, mag_filter); +} + +static void +_cogl_sub_texture_pre_paint (CoglTexture *tex, + CoglTexturePrePaintFlags flags) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_pre_paint (sub_tex->full_texture, flags); +} + +static void +_cogl_sub_texture_ensure_non_quad_rendering (CoglTexture *tex) +{ +} + +static CoglBool +_cogl_sub_texture_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + if (level != 0) + { + int full_width = cogl_texture_get_width (sub_tex->full_texture); + int full_height = cogl_texture_get_width (sub_tex->full_texture); + + _COGL_RETURN_VAL_IF_FAIL (sub_tex->sub_x == 0 && + cogl_texture_get_width (tex) == full_width, + FALSE); + _COGL_RETURN_VAL_IF_FAIL (sub_tex->sub_y == 0 && + cogl_texture_get_height (tex) == full_height, + FALSE); + } + + return _cogl_texture_set_region_from_bitmap (sub_tex->full_texture, + src_x, src_y, + dst_width, dst_height, + bmp, + dst_x + sub_tex->sub_x, + dst_y + sub_tex->sub_y, + level, + error); +} + +static CoglPixelFormat +_cogl_sub_texture_get_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return _cogl_texture_get_format (sub_tex->full_texture); +} + +static GLenum +_cogl_sub_texture_get_gl_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return _cogl_texture_gl_get_format (sub_tex->full_texture); +} + +static CoglTextureType +_cogl_sub_texture_get_type (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return _cogl_texture_get_type (sub_tex->full_texture); +} + +static const CoglTextureVtable +cogl_sub_texture_vtable = + { + FALSE, /* not primitive */ + _cogl_sub_texture_allocate, + _cogl_sub_texture_set_region, + NULL, /* get_data */ + _cogl_sub_texture_foreach_sub_texture_in_region, + _cogl_sub_texture_get_max_waste, + _cogl_sub_texture_is_sliced, + _cogl_sub_texture_can_hardware_repeat, + _cogl_sub_texture_transform_coords_to_gl, + _cogl_sub_texture_transform_quad_coords_to_gl, + _cogl_sub_texture_get_gl_texture, + _cogl_sub_texture_gl_flush_legacy_texobj_filters, + _cogl_sub_texture_pre_paint, + _cogl_sub_texture_ensure_non_quad_rendering, + _cogl_sub_texture_gl_flush_legacy_texobj_wrap_modes, + _cogl_sub_texture_get_format, + _cogl_sub_texture_get_gl_format, + _cogl_sub_texture_get_type, + NULL, /* is_foreign */ + NULL /* set_auto_mipmap */ + }; diff --git a/cogl/cogl/cogl-sub-texture.h b/cogl/cogl/cogl-sub-texture.h new file mode 100644 index 000000000..ced267787 --- /dev/null +++ b/cogl/cogl/cogl-sub-texture.h @@ -0,0 +1,136 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_SUB_TEXTURE_H +#define __COGL_SUB_TEXTURE_H + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-sub-texture + * @short_description: Functions for creating and manipulating + * sub-textures. + * + * These functions allow high-level textures to be created that + * represent a sub-region of another texture. For example these + * can be used to implement custom texture atlasing schemes. + */ + + +#define COGL_SUB_TEXTURE(tex) ((CoglSubTexture *) tex) +typedef struct _CoglSubTexture CoglSubTexture; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_sub_texture_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_sub_texture_get_gtype (void); +#endif + +/** + * cogl_sub_texture_new: + * @ctx: A #CoglContext pointer + * @parent_texture: The full texture containing a sub-region you want + * to make a #CoglSubTexture from. + * @sub_x: The top-left x coordinate of the parent region to make + * a texture from. + * @sub_y: The top-left y coordinate of the parent region to make + * a texture from. + * @sub_width: The width of the parent region to make a texture from. + * @sub_height: The height of the parent region to make a texture + * from. + * + * Creates a high-level #CoglSubTexture representing a sub-region of + * any other #CoglTexture. The sub-region must strictly lye within the + * bounds of the @parent_texture. The returned texture implements the + * #CoglMetaTexture interface because it's not a low level texture + * that hardware can understand natively. + * + * Remember: Unless you are using high level drawing APIs such + * as cogl_rectangle() or other APIs documented to understand the + * #CoglMetaTexture interface then you need to use the + * #CoglMetaTexture interface to resolve a #CoglSubTexture into a + * low-level texture before drawing. + * + * Return value: (transfer full): A newly allocated #CoglSubTexture + * representing a sub-region of @parent_texture. + * + * Since: 1.10 + * Stability: unstable + */ +CoglSubTexture * +cogl_sub_texture_new (CoglContext *ctx, + CoglTexture *parent_texture, + int sub_x, + int sub_y, + int sub_width, + int sub_height); + +/** + * cogl_sub_texture_get_parent: + * @sub_texture: A pointer to a #CoglSubTexture + * + * Retrieves the parent texture that @sub_texture derives its content + * from. This is the texture that was passed to + * cogl_sub_texture_new() as the parent_texture argument. + * + * Return value: (transfer none): The parent texture that @sub_texture + * derives its content from. + * Since: 1.10 + * Stability: unstable + */ +CoglTexture * +cogl_sub_texture_get_parent (CoglSubTexture *sub_texture); + +/** + * cogl_is_sub_texture: + * @object: a #CoglObject + * + * Checks whether @object is a #CoglSubTexture. + * + * Return value: %TRUE if the passed @object represents a + * #CoglSubTexture and %FALSE otherwise. + * + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_sub_texture (void *object); + +COGL_END_DECLS + +#endif /* __COGL_SUB_TEXTURE_H */ diff --git a/cogl/cogl/cogl-swap-chain-private.h b/cogl/cogl/cogl-swap-chain-private.h new file mode 100644 index 000000000..c67e6f08b --- /dev/null +++ b/cogl/cogl/cogl-swap-chain-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_SWAP_CHAIN_PRIVATE_H +#define __COGL_SWAP_CHAIN_PRIVATE_H + +#include "cogl-object-private.h" + +struct _CoglSwapChain +{ + CoglObject _parent; + + CoglBool has_alpha; + + int length; +}; + +#endif /* __COGL_SWAP_CHAIN_PRIVATE_H */ diff --git a/cogl/cogl/cogl-swap-chain.c b/cogl/cogl/cogl-swap-chain.c new file mode 100644 index 000000000..e5dd2f449 --- /dev/null +++ b/cogl/cogl/cogl-swap-chain.c @@ -0,0 +1,76 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-object.h" + +#include "cogl-swap-chain-private.h" +#include "cogl-swap-chain.h" +#include "cogl-gtype-private.h" + +static void _cogl_swap_chain_free (CoglSwapChain *swap_chain); + +COGL_OBJECT_DEFINE (SwapChain, swap_chain); +COGL_GTYPE_DEFINE_CLASS (SwapChain, swap_chain); + + +static void +_cogl_swap_chain_free (CoglSwapChain *swap_chain) +{ + g_slice_free (CoglSwapChain, swap_chain); +} + +CoglSwapChain * +cogl_swap_chain_new (void) +{ + CoglSwapChain *swap_chain = g_slice_new0 (CoglSwapChain); + + swap_chain->length = -1; /* no preference */ + + return _cogl_swap_chain_object_new (swap_chain); +} + +void +cogl_swap_chain_set_has_alpha (CoglSwapChain *swap_chain, + CoglBool has_alpha) +{ + swap_chain->has_alpha = has_alpha; +} + +void +cogl_swap_chain_set_length (CoglSwapChain *swap_chain, + int length) +{ + swap_chain->length = length; +} diff --git a/cogl/cogl/cogl-swap-chain.h b/cogl/cogl/cogl-swap-chain.h new file mode 100644 index 000000000..d0488674b --- /dev/null +++ b/cogl/cogl/cogl-swap-chain.h @@ -0,0 +1,71 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_SWAP_CHAIN_H__ +#define __COGL_SWAP_CHAIN_H__ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +typedef struct _CoglSwapChain CoglSwapChain; + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_swap_chain_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_swap_chain_get_gtype (void); +#endif + +CoglSwapChain * +cogl_swap_chain_new (void); + +void +cogl_swap_chain_set_has_alpha (CoglSwapChain *swap_chain, + CoglBool has_alpha); + +void +cogl_swap_chain_set_length (CoglSwapChain *swap_chain, + int length); + +CoglBool +cogl_is_swap_chain (void *object); + +COGL_END_DECLS + +#endif /* __COGL_SWAP_CHAIN_H__ */ diff --git a/cogl/cogl/cogl-texture-2d-gl.h b/cogl/cogl/cogl-texture-2d-gl.h new file mode 100644 index 000000000..e7b22df91 --- /dev/null +++ b/cogl/cogl/cogl-texture-2d-gl.h @@ -0,0 +1,78 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef _COGL_TEXTURE_2D_GL_H_ +#define _COGL_TEXTURE_2D_GL_H_ + +#include "cogl-context.h" +#include "cogl-texture-2d.h" + +COGL_BEGIN_DECLS + +/** + * cogl_texture_2d_gl_new_from_foreign: + * @ctx: A #CoglContext + * @gl_handle: A GL handle for a GL_TEXTURE_2D texture object + * @width: Width of the foreign GL texture + * @height: Height of the foreign GL texture + * @format: The format of the texture + * + * Wraps an existing GL_TEXTURE_2D texture object as a #CoglTexture2D. + * This can be used for integrating Cogl with software using OpenGL + * directly. + * + * The texture is still configurable until it has been allocated so + * for example you can declare whether the texture is premultiplied + * with cogl_texture_set_premultiplied(). + * + * The results are undefined for passing an invalid @gl_handle + * or if @width or @height don't have the correct texture + * geometry. + * + * Returns: (transfer full): A newly allocated #CoglTexture2D + * + * Since: 2.0 + */ +CoglTexture2D * +cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + int width, + int height, + CoglPixelFormat format); + +COGL_END_DECLS + +#endif /* _COGL_TEXTURE_2D_GL_H_ */ diff --git a/cogl/cogl/cogl-texture-2d-private.h b/cogl/cogl/cogl-texture-2d-private.h new file mode 100644 index 000000000..27847af64 --- /dev/null +++ b/cogl/cogl/cogl-texture-2d-private.h @@ -0,0 +1,134 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_2D_PRIVATE_H +#define __COGL_TEXTURE_2D_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d.h" + +#ifdef COGL_HAS_EGL_SUPPORT +#include "cogl-egl-defines.h" +#endif + +struct _CoglTexture2D +{ + CoglTexture _parent; + + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat internal_format; + + CoglBool auto_mipmap; + CoglBool mipmaps_dirty; + CoglBool is_foreign; + + /* TODO: factor out these OpenGL specific members into some form + * of driver private state. */ + + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_internal_format; + /* The texture object number */ + GLuint gl_texture; + GLenum gl_legacy_texobj_min_filter; + GLenum gl_legacy_texobj_mag_filter; + GLint gl_legacy_texobj_wrap_mode_s; + GLint gl_legacy_texobj_wrap_mode_t; + CoglTexturePixel first_pixel; +}; + +CoglTexture2D * +_cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, + CoglBool can_convert_in_place); + +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) +/* NB: The reason we require the width, height and format to be passed + * even though they may seem redundant is because GLES 1/2 don't + * provide a way to query these properties. */ +CoglTexture2D * +_cogl_egl_texture_2d_new_from_image (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + EGLImageKHR image, + CoglError **error); +#endif + +CoglTexture2D * +_cogl_texture_2d_create_base (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader); + +void +_cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, + CoglBool value); + +/* + * _cogl_texture_2d_externally_modified: + * @texture: A #CoglTexture2D object + * + * This should be called whenever the texture is modified other than + * by using cogl_texture_set_region. It will cause the mipmaps to be + * invalidated + */ +void +_cogl_texture_2d_externally_modified (CoglTexture *texture); + +/* + * _cogl_texture_2d_copy_from_framebuffer: + * @texture: A #CoglTexture2D pointer + * @src_x: X-position to within the framebuffer to read from + * @src_y: Y-position to within the framebuffer to read from + * @width: width of the rectangle to copy + * @height: height of the rectangle to copy + * @src_fb: A source #CoglFramebuffer to copy from + * @dst_x: X-position to store the image within the texture + * @dst_y: Y-position to store the image within the texture + * @level: The mipmap level of @texture to copy too + * + * This copies a portion of the given @src_fb into the + * texture. + */ +void +_cogl_texture_2d_copy_from_framebuffer (CoglTexture2D *texture, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level); + +#endif /* __COGL_TEXTURE_2D_PRIVATE_H */ diff --git a/cogl/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl/cogl-texture-2d-sliced-private.h new file mode 100644 index 000000000..e827923c0 --- /dev/null +++ b/cogl/cogl/cogl-texture-2d-sliced-private.h @@ -0,0 +1,67 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_2D_SLICED_PRIVATE_H +#define __COGL_TEXTURE_2D_SLICED_PRIVATE_H + +#include "cogl-bitmap-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-sliced.h" + +#include + +struct _CoglTexture2DSliced +{ + CoglTexture _parent; + + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_textures; + int max_waste; + CoglPixelFormat internal_format; +}; + +CoglTexture2DSliced * +_cogl_texture_2d_sliced_new_from_foreign (CoglContext *context, + unsigned int gl_handle, + unsigned int gl_target, + int width, + int height, + int x_pot_waste, + int y_pot_waste, + CoglPixelFormat format); + +CoglTexture2DSliced * +_cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, + int max_waste, + CoglBool can_convert_in_place); + +#endif /* __COGL_TEXTURE_2D_SLICED_PRIVATE_H */ diff --git a/cogl/cogl/cogl-texture-2d-sliced.c b/cogl/cogl/cogl-texture-2d-sliced.c new file mode 100644 index 000000000..e76bef697 --- /dev/null +++ b/cogl/cogl/cogl-texture-2d-sliced.c @@ -0,0 +1,1546 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-gl.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-2d-sliced-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-spans.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-primitive-texture.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-gtype-private.h" + +#include +#include +#include + +static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds); + +COGL_TEXTURE_DEFINE (Texture2DSliced, texture_2d_sliced); +COGL_GTYPE_DEFINE_CLASS (Texture2DSliced, texture_2d_sliced, + COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); + +static const CoglTextureVtable cogl_texture_2d_sliced_vtable; + +typedef struct _ForeachData +{ + CoglMetaTextureCallback callback; + void *user_data; + float x_normalize_factor; + float y_normalize_factor; +} ForeachData; + +static void +re_normalize_sub_texture_coords_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + ForeachData *data = user_data; + /* The coordinates passed to the span iterating code were + * un-normalized so we need to renormalize them before passing them + * on */ + float re_normalized_coords[4] = + { + meta_coords[0] * data->x_normalize_factor, + meta_coords[1] * data->y_normalize_factor, + meta_coords[2] * data->x_normalize_factor, + meta_coords[3] * data->y_normalize_factor + }; + + data->callback (sub_texture, sub_texture_coords, re_normalized_coords, + data->user_data); +} + +static void +_cogl_texture_2d_sliced_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglSpan *x_spans = (CoglSpan *)tex_2ds->slice_x_spans->data; + CoglSpan *y_spans = (CoglSpan *)tex_2ds->slice_y_spans->data; + CoglTexture **textures = (CoglTexture **)tex_2ds->slice_textures->data; + float un_normalized_coords[4]; + ForeachData data; + + /* NB: its convenient for us to store non-normalized coordinates in + * our CoglSpans but that means we need to un-normalize the incoming + * virtual coordinates and make sure we re-normalize the coordinates + * before calling the given callback. + */ + + data.callback = callback; + data.user_data = user_data; + data.x_normalize_factor = 1.0f / tex->width; + data.y_normalize_factor = 1.0f / tex->height; + + un_normalized_coords[0] = virtual_tx_1 * tex->width; + un_normalized_coords[1] = virtual_ty_1 * tex->height; + un_normalized_coords[2] = virtual_tx_2 * tex->width; + un_normalized_coords[3] = virtual_ty_2 * tex->height; + + /* Note that the normalize factors passed here are the reciprocal of + * the factors calculated above because the span iterating code + * normalizes by dividing by the factor instead of multiplying */ + _cogl_texture_spans_foreach_in_region (x_spans, + tex_2ds->slice_x_spans->len, + y_spans, + tex_2ds->slice_y_spans->len, + textures, + un_normalized_coords, + tex->width, + tex->height, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + re_normalize_sub_texture_coords_cb, + &data); +} + +static uint8_t * +_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds, + CoglPixelFormat format) +{ + CoglSpan *last_x_span; + CoglSpan *last_y_span; + uint8_t *waste_buf = NULL; + + /* If the texture has any waste then allocate a buffer big enough to + fill the gaps */ + last_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, + tex_2ds->slice_x_spans->len - 1); + last_y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, + tex_2ds->slice_y_spans->len - 1); + if (last_x_span->waste > 0 || last_y_span->waste > 0) + { + int bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + CoglSpan *first_x_span + = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); + CoglSpan *first_y_span + = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); + unsigned int right_size = first_y_span->size * last_x_span->waste; + unsigned int bottom_size = first_x_span->size * last_y_span->waste; + + waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); + } + + return waste_buf; +} + +static CoglBool +_cogl_texture_2d_sliced_set_waste (CoglTexture2DSliced *tex_2ds, + CoglBitmap *source_bmp, + CoglTexture2D *slice_tex, + uint8_t *waste_buf, + CoglSpan *x_span, + CoglSpan *y_span, + CoglSpanIter *x_iter, + CoglSpanIter *y_iter, + int src_x, + int src_y, + int dst_x, + int dst_y, + CoglError **error) +{ + CoglBool need_x, need_y; + CoglContext *ctx = COGL_TEXTURE (tex_2ds)->context; + + /* If the x_span is sliced and the upload touches the + rightmost pixels then fill the waste with copies of the + pixels */ + need_x = x_span->waste > 0 && + x_iter->intersect_end - x_iter->pos >= x_span->size - x_span->waste; + + /* same for the bottom-most pixels */ + need_y = y_span->waste > 0 && + y_iter->intersect_end - y_iter->pos >= y_span->size - y_span->waste; + + if (need_x || need_y) + { + int bmp_rowstride = cogl_bitmap_get_rowstride (source_bmp); + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + uint8_t *bmp_data; + const uint8_t *src; + uint8_t *dst; + unsigned int wy, wx; + CoglBitmap *waste_bmp; + + bmp_data = _cogl_bitmap_map (source_bmp, COGL_BUFFER_ACCESS_READ, 0, error); + if (bmp_data == NULL) + return FALSE; + + if (need_x) + { + src = (bmp_data + ((src_y + (int) y_iter->intersect_start - dst_y) * + bmp_rowstride) + + (src_x + (int)x_span->start + (int)x_span->size - + (int)x_span->waste - dst_x - 1) * bpp); + + dst = waste_buf; + + for (wy = 0; + wy < y_iter->intersect_end - y_iter->intersect_start; + wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += bmp_rowstride; + } + + waste_bmp = cogl_bitmap_new_for_data (ctx, + x_span->waste, + y_iter->intersect_end - + y_iter->intersect_start, + source_format, + x_span->waste * bpp, + waste_buf); + + if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), + 0, /* src_x */ + 0, /* src_y */ + x_span->waste, /* width */ + /* height */ + y_iter->intersect_end - + y_iter->intersect_start, + waste_bmp, + /* dst_x */ + x_span->size - x_span->waste, + y_iter->intersect_start - + y_span->start, /* dst_y */ + 0, /* level */ + error)) + { + cogl_object_unref (waste_bmp); + _cogl_bitmap_unmap (source_bmp); + return FALSE; + } + + cogl_object_unref (waste_bmp); + } + + if (need_y) + { + unsigned int copy_width, intersect_width; + + src = (bmp_data + ((src_x + (int) x_iter->intersect_start - dst_x) * + bpp) + + (src_y + (int)y_span->start + (int)y_span->size - + (int)y_span->waste - dst_y - 1) * bmp_rowstride); + + dst = waste_buf; + + if (x_iter->intersect_end - x_iter->pos + >= x_span->size - x_span->waste) + copy_width = x_span->size + x_iter->pos - x_iter->intersect_start; + else + copy_width = x_iter->intersect_end - x_iter->intersect_start; + + intersect_width = x_iter->intersect_end - x_iter->intersect_start; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, intersect_width * bpp); + dst += intersect_width * bpp; + + for (wx = intersect_width; wx < copy_width; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + waste_bmp = cogl_bitmap_new_for_data (ctx, + copy_width, + y_span->waste, + source_format, + copy_width * bpp, + waste_buf); + + if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), + 0, /* src_x */ + 0, /* src_y */ + copy_width, /* width */ + y_span->waste, /* height */ + waste_bmp, + /* dst_x */ + x_iter->intersect_start - + x_iter->pos, + /* dst_y */ + y_span->size - y_span->waste, + 0, /* level */ + error)) + { + cogl_object_unref (waste_bmp); + _cogl_bitmap_unmap (source_bmp); + return FALSE; + } + + cogl_object_unref (waste_bmp); + } + + _cogl_bitmap_unmap (source_bmp); + } + + return TRUE; +} + +static CoglBool +_cogl_texture_2d_sliced_upload_bitmap (CoglTexture2DSliced *tex_2ds, + CoglBitmap *bmp, + CoglError **error) +{ + CoglSpan *x_span; + CoglSpan *y_span; + CoglTexture2D *slice_tex; + int x, y; + uint8_t *waste_buf; + CoglPixelFormat bmp_format; + + bmp_format = cogl_bitmap_get_format (bmp); + + waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, + bmp_format); + + /* Iterate vertical slices */ + for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex_2ds->slice_x_spans->len; ++x) + { + int slice_num = y * tex_2ds->slice_x_spans->len + x; + CoglSpanIter x_iter, y_iter; + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x); + + /* Pick the gl texture object handle */ + slice_tex = g_array_index (tex_2ds->slice_textures, + CoglTexture2D *, slice_num); + + if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), + x_span->start, /* src x */ + y_span->start, /* src y */ + x_span->size - + x_span->waste, /* width */ + y_span->size - + y_span->waste, /* height */ + bmp, + 0, /* dst x */ + 0, /* dst y */ + 0, /* level */ + error)) + { + if (waste_buf) + g_free (waste_buf); + return FALSE; + } + + /* Set up a fake iterator that covers the whole slice */ + x_iter.intersect_start = x_span->start; + x_iter.intersect_end = (x_span->start + + x_span->size - + x_span->waste); + x_iter.pos = x_span->start; + + y_iter.intersect_start = y_span->start; + y_iter.intersect_end = (y_span->start + + y_span->size - + y_span->waste); + y_iter.pos = y_span->start; + + if (!_cogl_texture_2d_sliced_set_waste (tex_2ds, + bmp, + slice_tex, + waste_buf, + x_span, y_span, + &x_iter, &y_iter, + 0, /* src_x */ + 0, /* src_y */ + 0, /* dst_x */ + 0, + error)) /* dst_y */ + { + if (waste_buf) + g_free (waste_buf); + return FALSE; + } + } + } + + if (waste_buf) + g_free (waste_buf); + + return TRUE; +} + +static CoglBool +_cogl_texture_2d_sliced_upload_subregion (CoglTexture2DSliced *tex_2ds, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + CoglBitmap *source_bmp, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglSpan *x_span; + CoglSpan *y_span; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + CoglTexture2D *slice_tex; + int source_x = 0, source_y = 0; + int inter_w = 0, inter_h = 0; + int local_x = 0, local_y = 0; + uint8_t *waste_buf; + CoglPixelFormat source_format; + + source_format = cogl_bitmap_get_format (source_bmp); + + waste_buf = + _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, source_format); + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, + (CoglSpan *)tex_2ds->slice_y_spans->data, + tex_2ds->slice_y_spans->len, + tex->height, + dst_y, + dst_y + height, + COGL_PIPELINE_WRAP_MODE_REPEAT); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, + y_iter.index); + + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, + (CoglSpan *)tex_2ds->slice_x_spans->data, + tex_2ds->slice_x_spans->len, + tex->width, + dst_x, + dst_x + width, + COGL_PIPELINE_WRAP_MODE_REPEAT); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + int slice_num; + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, + x_iter.index); + + /* Pick intersection width and height */ + inter_w = (x_iter.intersect_end - x_iter.intersect_start); + inter_h = (y_iter.intersect_end - y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = (x_iter.intersect_start - x_iter.pos); + local_y = (y_iter.intersect_start - y_iter.pos); + + slice_num = y_iter.index * tex_2ds->slice_x_spans->len + x_iter.index; + + /* Pick slice texture */ + slice_tex = g_array_index (tex_2ds->slice_textures, + CoglTexture2D *, slice_num); + + if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), + source_x, + source_y, + inter_w, /* width */ + inter_h, /* height */ + source_bmp, + local_x, /* dst x */ + local_y, /* dst y */ + 0, /* level */ + error)) + { + if (waste_buf) + g_free (waste_buf); + return FALSE; + } + + if (!_cogl_texture_2d_sliced_set_waste (tex_2ds, + source_bmp, + slice_tex, + waste_buf, + x_span, y_span, + &x_iter, &y_iter, + src_x, src_y, + dst_x, dst_y, + error)) + { + if (waste_buf) + g_free (waste_buf); + return FALSE; + } + } + } + + if (waste_buf) + g_free (waste_buf); + + return TRUE; +} + +static int +_cogl_rect_slices_for_size (int size_to_fill, + int max_span_size, + int max_waste, + GArray *out_spans) +{ + int n_spans = 0; + CoglSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Repeat until whole area covered */ + while (size_to_fill >= span.size) + { + /* Add another slice span of same size */ + if (out_spans) + g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + + /* Add one last smaller slice span */ + if (size_to_fill > 0) + { + span.size = size_to_fill; + if (out_spans) + g_array_append_val (out_spans, span); + n_spans++; + } + + return n_spans; +} + +static int +_cogl_pot_slices_for_size (int size_to_fill, + int max_span_size, + int max_waste, + GArray *out_spans) +{ + int n_spans = 0; + CoglSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) + max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) + g_array_append_val (out_spans, span); + + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + /* Pick the next power of two up from size_to_fill. This can + sometimes be less than the span.size that would be chosen + otherwise */ + span.size = _cogl_util_next_p2 (size_to_fill); + span.waste = span.size - size_to_fill; + if (out_spans) + g_array_append_val (out_spans, span); + + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +static void +_cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + int i; + + /* Pass the set wrap mode on to all of the child textures */ + for (i = 0; i < tex_2ds->slice_textures->len; i++) + { + CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, + CoglTexture2D *, + i); + + _cogl_texture_gl_flush_legacy_texobj_wrap_modes (COGL_TEXTURE (slice_tex), + wrap_mode_s, + wrap_mode_t, + wrap_mode_p); + } +} + +static void +free_spans (CoglTexture2DSliced *tex_2ds) +{ + if (tex_2ds->slice_x_spans != NULL) + { + g_array_free (tex_2ds->slice_x_spans, TRUE); + tex_2ds->slice_x_spans = NULL; + } + + if (tex_2ds->slice_y_spans != NULL) + { + g_array_free (tex_2ds->slice_y_spans, TRUE); + tex_2ds->slice_y_spans = NULL; + } +} + +static CoglBool +setup_spans (CoglContext *ctx, + CoglTexture2DSliced *tex_2ds, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglError **error) +{ + int max_width; + int max_height; + int n_x_slices; + int n_y_slices; + + int (*slices_for_size) (int, int, int, GArray*); + + /* Initialize size of largest slice according to supported features */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT)) + { + max_width = width; + max_height = height; + slices_for_size = _cogl_rect_slices_for_size; + } + else + { + max_width = _cogl_util_next_p2 (width); + max_height = _cogl_util_next_p2 (height); + slices_for_size = _cogl_pot_slices_for_size; + } + + /* Negative number means no slicing forced by the user */ + if (max_waste <= -1) + { + CoglSpan span; + + /* Check if size supported else bail out */ + if (!ctx->driver_vtable->texture_2d_can_create (ctx, + max_width, + max_height, + internal_format)) + { + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "Sliced texture size of %d x %d not possible " + "with max waste set to -1", + width, + height); + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglSpan), + 1); + + tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - width; + g_array_append_val (tex_2ds->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - height; + g_array_append_val (tex_2ds->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!ctx->driver_vtable->texture_2d_can_create (ctx, + max_width, + max_height, + internal_format)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + { + /* Maybe it would be ok to just g_warn_if_reached() for this + * codepath */ + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "No suitable slice geometry found"); + free_spans (tex_2ds); + return FALSE; + } + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (width, + max_width, max_waste, + NULL); + + n_y_slices = slices_for_size (height, + max_height, max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglSpan), + n_x_slices); + + tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (width, + max_width, max_waste, + tex_2ds->slice_x_spans); + + slices_for_size (height, + max_height, max_waste, + tex_2ds->slice_y_spans); + } + + return TRUE; +} + +static void +free_slices (CoglTexture2DSliced *tex_2ds) +{ + if (tex_2ds->slice_textures != NULL) + { + int i; + + for (i = 0; i < tex_2ds->slice_textures->len; i++) + { + CoglTexture2D *slice_tex = + g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); + cogl_object_unref (slice_tex); + } + + g_array_free (tex_2ds->slice_textures, TRUE); + } + + free_spans (tex_2ds); +} + +static CoglBool +allocate_slices (CoglTexture2DSliced *tex_2ds, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglContext *ctx = tex->context; + int n_x_slices; + int n_y_slices; + int n_slices; + int x, y; + CoglSpan *x_span; + CoglSpan *y_span; + + tex_2ds->internal_format = internal_format; + + if (!setup_spans (ctx, tex_2ds, + width, + height, + max_waste, + internal_format, + error)) + { + return FALSE; + } + + n_x_slices = tex_2ds->slice_x_spans->len; + n_y_slices = tex_2ds->slice_y_spans->len; + n_slices = n_x_slices * n_y_slices; + + tex_2ds->slice_textures = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexture2D *), + n_slices); + + /* Allocate each slice */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + CoglTexture *slice; + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x); + + COGL_NOTE (SLICING, "CREATE SLICE (%d,%d)\tsize (%d,%d)", + x, y, + (int)(x_span->size - x_span->waste), + (int)(y_span->size - y_span->waste)); + + slice = COGL_TEXTURE ( + cogl_texture_2d_new_with_size (ctx, + x_span->size, y_span->size)); + + _cogl_texture_copy_internal_format (tex, slice); + + g_array_append_val (tex_2ds->slice_textures, slice); + if (!cogl_texture_allocate (slice, error)) + { + free_slices (tex_2ds); + return FALSE; + } + } + } + + return TRUE; +} + +static void +_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) +{ + free_slices (tex_2ds); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_2ds)); +} + +static CoglTexture2DSliced * +_cogl_texture_2d_sliced_create_base (CoglContext *ctx, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglTexture2DSliced *tex_2ds = g_new0 (CoglTexture2DSliced, 1); + + _cogl_texture_init (COGL_TEXTURE (tex_2ds), ctx, width, height, + internal_format, loader, + &cogl_texture_2d_sliced_vtable); + + tex_2ds->max_waste = max_waste; + + return _cogl_texture_2d_sliced_object_new (tex_2ds); +} + +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, + int width, + int height, + int max_waste) +{ + CoglTextureLoader *loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_texture_2d_sliced_create_base (ctx, + width, + height, + max_waste, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + loader); +} + +CoglTexture2DSliced * +_cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, + int max_waste, + CoglBool can_convert_in_place) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; + + return _cogl_texture_2d_sliced_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + max_waste, + cogl_bitmap_get_format (bmp), + loader); +} + +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, + int max_waste) +{ + return _cogl_texture_2d_sliced_new_from_bitmap (bmp, + max_waste, + FALSE); +} + +CoglTexture2DSliced * +_cogl_texture_2d_sliced_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + unsigned int gl_target, + int width, + int height, + int x_pot_waste, + int y_pot_waste, + CoglPixelFormat format) +{ + CoglTextureLoader *loader; + + /* NOTE: width, height and internal format are not queriable + * in GLES, hence such a function prototype. + */ + + /* This should only be called when the texture target is 2D. If a + rectangle texture is used then _cogl_texture_new_from_foreign + will create a cogl_texture_rectangle instead */ + _COGL_RETURN_VAL_IF_FAIL (gl_target == GL_TEXTURE_2D, NULL); + + /* Assert it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), FALSE); + + /* Validate width and height */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + /* Validate pot waste */ + _COGL_RETURN_VAL_IF_FAIL (x_pot_waste >= 0 && x_pot_waste < width && + y_pot_waste >= 0 && y_pot_waste < height, + NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width + x_pot_waste; + loader->src.gl_foreign.height = height + y_pot_waste; + loader->src.gl_foreign.format = format; + + return _cogl_texture_2d_sliced_create_base (ctx, + width, + height, + 0, /* max waste */ + format, loader); +} + +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, + int width, + int height, + int max_waste, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture2DSliced *tex_2ds; + + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + tex_2ds = cogl_texture_2d_sliced_new_from_bitmap (bmp, max_waste); + + cogl_object_unref (bmp); + + if (tex_2ds && + !cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error)) + { + cogl_object_unref (tex_2ds); + return NULL; + } + + return tex_2ds; +} + +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, + const char *filename, + int max_waste, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture2DSliced *tex_2ds = NULL; + + _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL); + + bmp = _cogl_bitmap_from_file (ctx, filename, error); + if (bmp == NULL) + return NULL; + + tex_2ds = _cogl_texture_2d_sliced_new_from_bitmap (bmp, + max_waste, + TRUE); /* can convert in-place */ + + cogl_object_unref (bmp); + + return tex_2ds; +} + +static CoglBool +allocate_with_size (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglPixelFormat internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (allocate_slices (tex_2ds, + loader->src.sized.width, + loader->src.sized.height, + tex_2ds->max_waste, + internal_format, + error)) + { + _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds), + internal_format, + loader->src.sized.width, + loader->src.sized.height); + return TRUE; + } + else + return FALSE; +} + +static CoglBool +allocate_from_bitmap (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglPixelFormat internal_format; + CoglBitmap *upload_bmp; + + _COGL_RETURN_VAL_IF_FAIL (tex_2ds->slice_textures == NULL, FALSE); + + internal_format = + _cogl_texture_determine_internal_format (tex, + cogl_bitmap_get_format (bmp)); + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + if (!allocate_slices (tex_2ds, + width, + height, + tex_2ds->max_waste, + internal_format, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds, + upload_bmp, + error)) + { + free_slices (tex_2ds); + cogl_object_unref (upload_bmp); + return FALSE; + } + + cogl_object_unref (upload_bmp); + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_gl_foreign (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; + int gl_width = loader->src.gl_foreign.width; + int gl_height = loader->src.gl_foreign.height; + int x_pot_waste = gl_width - tex->width; + int y_pot_waste = gl_height - tex->height; + CoglSpan x_span; + CoglSpan y_span; + CoglTexture2D *tex_2d = + cogl_texture_2d_gl_new_from_foreign (ctx, + loader->src.gl_foreign.gl_handle, + gl_width, + gl_height, + format); + + if (!cogl_texture_allocate (COGL_TEXTURE (tex_2d), error)) + { + cogl_object_unref (tex_2d); + return FALSE; + } + + /* The texture 2d backend may use a different pixel format if it + queries the actual texture so we'll refetch the format it + actually used */ + format = _cogl_texture_get_format (tex); + + tex_2ds->internal_format = format; + + /* Create slice arrays */ + tex_2ds->slice_x_spans = + g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); + + tex_2ds->slice_y_spans = + g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); + + tex_2ds->slice_textures = + g_array_sized_new (FALSE, FALSE, sizeof (CoglTexture2D *), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = gl_width; + x_span.waste = x_pot_waste; + g_array_append_val (tex_2ds->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = gl_height; + y_span.waste = y_pot_waste; + g_array_append_val (tex_2ds->slice_y_spans, y_span); + + g_array_append_val (tex_2ds->slice_textures, tex_2d); + + _cogl_texture_set_allocated (tex, + format, + tex->width, + tex->height); + + return TRUE; +} + +static CoglBool +_cogl_texture_2d_sliced_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_2ds, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_2ds, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_2ds, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +static CoglBool +_cogl_texture_2d_sliced_is_foreign (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTexture2D *slice_tex; + + /* Make sure slices were created */ + if (tex_2ds->slice_textures == NULL) + return FALSE; + + /* Pass the call on to the first slice */ + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); + return _cogl_texture_is_foreign (COGL_TEXTURE (slice_tex)); +} + +static int +_cogl_texture_2d_sliced_get_max_waste (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + + return tex_2ds->max_waste; +} + +static CoglBool +_cogl_texture_2d_sliced_is_sliced (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + + /* It's only after allocating a sliced texture that we will know + * whether it really needed to be sliced... */ + if (!tex->allocated) + cogl_texture_allocate (tex, NULL); + + if (tex_2ds->slice_x_spans->len != 1 || + tex_2ds->slice_y_spans->len != 1) + return TRUE; + else + return FALSE; +} + +static CoglBool +_cogl_texture_2d_sliced_can_hardware_repeat (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTexture2D *slice_tex; + CoglSpan *x_span; + CoglSpan *y_span; + + /* If there's more than one texture then we can't hardware repeat */ + if (tex_2ds->slice_textures->len != 1) + return FALSE; + + /* If there's any waste then we can't hardware repeat */ + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); + if (x_span->waste > 0 || y_span->waste > 0) + return FALSE; + + /* Otherwise pass the query on to the single slice texture */ + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); + return _cogl_texture_can_hardware_repeat (COGL_TEXTURE (slice_tex)); +} + +static void +_cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglSpan *x_span; + CoglSpan *y_span; + CoglTexture2D *slice_tex; + + g_assert (!_cogl_texture_2d_sliced_is_sliced (tex)); + + /* Don't include the waste in the texture coordinates */ + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); + + *s *= tex->width / (float)x_span->size; + *t *= tex->height / (float)y_span->size; + + /* Let the child texture further transform the coords */ + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); + _cogl_texture_transform_coords_to_gl (COGL_TEXTURE (slice_tex), s, t); +} + +static CoglTransformResult +_cogl_texture_2d_sliced_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglBool need_repeat = FALSE; + int i; + + /* This is a bit lazy - in the case where the quad lies entirely + * within a single slice we could avoid the fallback. But that + * could likely lead to visual inconsistency if the fallback involves + * dropping layers, so this might be the right thing to do anyways. + */ + if (_cogl_texture_2d_sliced_is_sliced (tex)) + return COGL_TRANSFORM_SOFTWARE_REPEAT; + + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + need_repeat = TRUE; + + if (need_repeat && !_cogl_texture_2d_sliced_can_hardware_repeat (tex)) + return COGL_TRANSFORM_SOFTWARE_REPEAT; + + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 0, coords + 1); + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 2, coords + 3); + + return (need_repeat + ? COGL_TRANSFORM_HARDWARE_REPEAT : COGL_TRANSFORM_NO_REPEAT); +} + +static CoglBool +_cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTexture2D *slice_tex; + + if (tex_2ds->slice_textures == NULL) + return FALSE; + + if (tex_2ds->slice_textures->len < 1) + return FALSE; + + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); + + return cogl_texture_get_gl_texture (COGL_TEXTURE (slice_tex), + out_gl_handle, out_gl_target); +} + +static void +_cogl_texture_2d_sliced_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTexture2D *slice_tex; + int i; + + _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL); + + /* Apply new filters to every slice. The slice texture itself should + cache the value and avoid resubmitting the same filter value to + GL */ + for (i = 0; i < tex_2ds->slice_textures->len; i++) + { + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); + _cogl_texture_gl_flush_legacy_texobj_filters (COGL_TEXTURE (slice_tex), + min_filter, mag_filter); + } +} + +static void +_cogl_texture_2d_sliced_pre_paint (CoglTexture *tex, + CoglTexturePrePaintFlags flags) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + int i; + + _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL); + + /* Pass the pre-paint on to every slice */ + for (i = 0; i < tex_2ds->slice_textures->len; i++) + { + CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, + CoglTexture2D *, i); + _cogl_texture_pre_paint (COGL_TEXTURE (slice_tex), flags); + } +} + +static void +_cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + int i; + + _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL); + + /* Pass the call on to every slice */ + for (i = 0; i < tex_2ds->slice_textures->len; i++) + { + CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, + CoglTexture2D *, i); + _cogl_texture_ensure_non_quad_rendering (COGL_TEXTURE (slice_tex)); + } +} + +static CoglBool +_cogl_texture_2d_sliced_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglBitmap *upload_bmp; + CoglBool status; + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + _cogl_texture_get_format (tex), + FALSE, /* can't convert in + place */ + error); + if (!upload_bmp) + return FALSE; + + status = _cogl_texture_2d_sliced_upload_subregion (tex_2ds, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + upload_bmp, + error); + cogl_object_unref (upload_bmp); + + return status; +} + +static CoglPixelFormat +_cogl_texture_2d_sliced_get_format (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + + return tex_2ds->internal_format; +} + +static GLenum +_cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTexture2D *slice_tex; + + /* Assert that we've allocated our slices at this point */ + cogl_texture_allocate (tex, NULL); /* (abort on error) */ + + /* Pass the call on to the first slice */ + slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); + return _cogl_texture_gl_get_format (COGL_TEXTURE (slice_tex)); +} + +static CoglTextureType +_cogl_texture_2d_sliced_get_type (CoglTexture *tex) +{ + return COGL_TEXTURE_TYPE_2D; +} + +static const CoglTextureVtable +cogl_texture_2d_sliced_vtable = + { + FALSE, /* not primitive */ + _cogl_texture_2d_sliced_allocate, + _cogl_texture_2d_sliced_set_region, + NULL, /* get_data */ + _cogl_texture_2d_sliced_foreach_sub_texture_in_region, + _cogl_texture_2d_sliced_get_max_waste, + _cogl_texture_2d_sliced_is_sliced, + _cogl_texture_2d_sliced_can_hardware_repeat, + _cogl_texture_2d_sliced_transform_coords_to_gl, + _cogl_texture_2d_sliced_transform_quad_coords_to_gl, + _cogl_texture_2d_sliced_get_gl_texture, + _cogl_texture_2d_sliced_gl_flush_legacy_texobj_filters, + _cogl_texture_2d_sliced_pre_paint, + _cogl_texture_2d_sliced_ensure_non_quad_rendering, + _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes, + _cogl_texture_2d_sliced_get_format, + _cogl_texture_2d_sliced_get_gl_format, + _cogl_texture_2d_sliced_get_type, + _cogl_texture_2d_sliced_is_foreign, + NULL /* set_auto_mipmap */ + }; diff --git a/cogl/cogl/cogl-texture-2d-sliced.h b/cogl/cogl/cogl-texture-2d-sliced.h new file mode 100644 index 000000000..ec959a91b --- /dev/null +++ b/cogl/cogl/cogl-texture-2d-sliced.h @@ -0,0 +1,301 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_TEXURE_2D_SLICED_H +#define __COGL_TEXURE_2D_SLICED_H + +#include "cogl-context.h" +#include "cogl-types.h" + +/** + * SECTION:cogl-texture-2d-sliced + * @short_description: Functions for creating and manipulating 2D meta + * textures that may internally be comprised of + * multiple 2D textures with power-of-two sizes. + * + * These functions allow high-level meta textures (See the + * #CoglMetaTexture interface) to be allocated that may internally be + * comprised of multiple 2D texture "slices" with power-of-two sizes. + * + * This API can be useful when working with GPUs that don't have + * native support for non-power-of-two textures or if you want to load + * a texture that is larger than the GPUs maximum texture size limits. + * + * The algorithm for slicing works by first trying to map a virtual + * size to the next larger power-of-two size and then seeing how many + * wasted pixels that would result in. For example if you have a + * virtual texture that's 259 texels wide, the next pot size = 512 and + * the amount of waste would be 253 texels. If the amount of waste is + * above a max-waste threshold then we would next slice that texture + * into one that's 256 texels and then looking at how many more texels + * remain unallocated after that we choose the next power-of-two size. + * For the example of a 259 texel image that would mean having a 256 + * texel wide texture, leaving 3 texels unallocated so we'd then + * create a 4 texel wide texture - now there is only one texel of + * waste. The algorithm continues to slice the right most textures + * until the amount of waste is less than or equal to a specfied + * max-waste threshold. The same logic for slicing from left to right + * is also applied from top to bottom. + */ + +typedef struct _CoglTexture2DSliced CoglTexture2DSliced; +#define COGL_TEXTURE_2D_SLICED(X) ((CoglTexture2DSliced *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_2d_sliced_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_2d_sliced_get_gtype (void); +#endif + +/** + * cogl_texture_2d_sliced_new_with_size: + * @ctx: A #CoglContext + * @width: The virtual width of your sliced texture. + * @height: The virtual height of your sliced texture. + * @max_waste: The threshold of how wide a strip of wasted texels + * are allowed along the right and bottom textures before + * they must be sliced to reduce the amount of waste. A + * negative can be passed to disable slicing. + * + * Creates a #CoglTexture2DSliced that may internally be comprised of + * 1 or more #CoglTexture2D textures depending on GPU limitations. + * For example if the GPU only supports power-of-two sized textures + * then a sliced texture will turn a non-power-of-two size into a + * combination of smaller power-of-two sized textures. If the + * requested texture size is larger than is supported by the hardware + * then the texture will be sliced into smaller textures that can be + * accessed by the hardware. + * + * @max_waste is used as a threshold for recursively slicing the + * right-most or bottom-most slices into smaller sizes until the + * wasted padding at the bottom and right of the textures is less than + * specified. A negative @max_waste will disable slicing. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * It's possible for the allocation of a sliced texture to fail + * later due to impossible slicing constraints if a negative + * @max_waste value is given. If the given virtual texture size size + * is larger than is supported by the hardware but slicing is disabled + * the texture size would be too large to handle. + * + * Returns: (transfer full): A new #CoglTexture2DSliced object with no storage + * allocated yet. + * + * Since: 1.10 + * Stability: unstable + */ +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, + int width, + int height, + int max_waste); + +/** + * cogl_texture_2d_sliced_new_from_file: + * @ctx: A #CoglContext + * @filename: the file to load + * @max_waste: The threshold of how wide a strip of wasted texels + * are allowed along the right and bottom textures before + * they must be sliced to reduce the amount of waste. A + * negative can be passed to disable slicing. + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a #CoglTexture2DSliced from an image file. + * + * A #CoglTexture2DSliced may internally be comprised of 1 or more + * #CoglTexture2D textures depending on GPU limitations. For example + * if the GPU only supports power-of-two sized textures then a sliced + * texture will turn a non-power-of-two size into a combination of + * smaller power-of-two sized textures. If the requested texture size + * is larger than is supported by the hardware then the texture will + * be sliced into smaller textures that can be accessed by the + * hardware. + * + * @max_waste is used as a threshold for recursively slicing the + * right-most or bottom-most slices into smaller sizes until the + * wasted padding at the bottom and right of the textures is less than + * specified. A negative @max_waste will disable slicing. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * It's possible for the allocation of a sliced texture to fail + * later due to impossible slicing constraints if a negative + * @max_waste value is given. If the given virtual texture size is + * larger than is supported by the hardware but slicing is disabled + * the texture size would be too large to handle. + * + * Return value: (transfer full): A newly created #CoglTexture2DSliced + * or %NULL on failure and @error will be updated. + * + * Since: 1.16 + */ +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, + const char *filename, + int max_waste, + CoglError **error); + +/** + * cogl_texture_2d_sliced_new_from_data: + * @ctx: A #CoglContext + * @width: width of texture in pixels + * @height: height of texture in pixels + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @max_waste: The threshold of how wide a strip of wasted texels + * are allowed along the right and bottom textures before + * they must be sliced to reduce the amount of waste. A + * negative can be passed to disable slicing. + * @rowstride: the memory offset in bytes between the start of each + * row in @data. A value of 0 will make Cogl automatically + * calculate @rowstride from @width and @format. + * @data: pointer the memory region where the source buffer resides + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a new #CoglTexture2DSliced texture based on data residing + * in memory. + * + * A #CoglTexture2DSliced may internally be comprised of 1 or more + * #CoglTexture2D textures depending on GPU limitations. For example + * if the GPU only supports power-of-two sized textures then a sliced + * texture will turn a non-power-of-two size into a combination of + * smaller power-of-two sized textures. If the requested texture size + * is larger than is supported by the hardware then the texture will + * be sliced into smaller textures that can be accessed by the + * hardware. + * + * @max_waste is used as a threshold for recursively slicing the + * right-most or bottom-most slices into smaller sizes until the + * wasted padding at the bottom and right of the textures is less than + * specified. A negative @max_waste will disable slicing. + * + * This api will always immediately allocate GPU memory for all + * the required texture slices and upload the given data so that the + * @data pointer does not need to remain valid once this function + * returns. This means it is not possible to configure the texture + * before it is allocated. If you do need to configure the texture + * before allocation (to specify constraints on the internal format + * for example) then you can instead create a #CoglBitmap for your + * data and use cogl_texture_2d_sliced_new_from_bitmap() or use + * cogl_texture_2d_sliced_new_with_size() and then upload data using + * cogl_texture_set_data() + * + * It's possible for the allocation of a sliced texture to fail + * due to impossible slicing constraints if a negative @max_waste + * value is given. If the given virtual texture size is larger than is + * supported by the hardware but slicing is disabled the texture size + * would be too large to handle. + * + * Return value: (transfer full): A newly created #CoglTexture2DSliced + * or %NULL on failure and @error will be updated. + * + * Since: 1.16 + */ +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, + int width, + int height, + int max_waste, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error); + +/** + * cogl_texture_2d_sliced_new_from_bitmap: + * @bmp: A #CoglBitmap + * @max_waste: The threshold of how wide a strip of wasted texels + * are allowed along the right and bottom textures before + * they must be sliced to reduce the amount of waste. A + * negative can be passed to disable slicing. + * + * Creates a new #CoglTexture2DSliced texture based on data residing + * in a bitmap. + * + * A #CoglTexture2DSliced may internally be comprised of 1 or more + * #CoglTexture2D textures depending on GPU limitations. For example + * if the GPU only supports power-of-two sized textures then a sliced + * texture will turn a non-power-of-two size into a combination of + * smaller power-of-two sized textures. If the requested texture size + * is larger than is supported by the hardware then the texture will + * be sliced into smaller textures that can be accessed by the + * hardware. + * + * @max_waste is used as a threshold for recursively slicing the + * right-most or bottom-most slices into smaller sizes until the + * wasted padding at the bottom and right of the textures is less than + * specified. A negative @max_waste will disable slicing. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * It's possible for the allocation of a sliced texture to fail + * later due to impossible slicing constraints if a negative + * @max_waste value is given. If the given virtual texture size is + * larger than is supported by the hardware but slicing is disabled + * the texture size would be too large to handle. + * + * Return value: (transfer full): A newly created #CoglTexture2DSliced + * or %NULL on failure and @error will be updated. + * + * Since: 1.16 + */ +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, + int max_waste); + +/** + * cogl_is_texture_2d_sliced: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglTexture2DSliced. + * + * Return value: %TRUE if the object references a #CoglTexture2DSliced + * and %FALSE otherwise. + * Since: 1.10 + * Stability: unstable + */ +CoglBool +cogl_is_texture_2d_sliced (void *object); + +#endif /* __COGL_TEXURE_2D_SLICED_H */ diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c new file mode 100644 index 000000000..cc28cd951 --- /dev/null +++ b/cogl/cogl/cogl-texture-2d.c @@ -0,0 +1,695 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-2d-gl-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-error-private.h" +#ifdef COGL_HAS_EGL_SUPPORT +#include "cogl-winsys-egl-private.h" +#endif +#include "cogl-gtype-private.h" + +#include +#include + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT +#include "cogl-wayland-server.h" +#endif + +static void _cogl_texture_2d_free (CoglTexture2D *tex_2d); + +COGL_TEXTURE_DEFINE (Texture2D, texture_2d); +COGL_GTYPE_DEFINE_CLASS (Texture2D, texture_2d, + COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); + +static const CoglTextureVtable cogl_texture_2d_vtable; + +typedef struct _CoglTexture2DManualRepeatData +{ + CoglTexture2D *tex_2d; + CoglMetaTextureCallback callback; + void *user_data; +} CoglTexture2DManualRepeatData; + +static void +_cogl_texture_2d_free (CoglTexture2D *tex_2d) +{ + CoglContext *ctx = COGL_TEXTURE (tex_2d)->context; + + ctx->driver_vtable->texture_2d_free (tex_2d); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_2d)); +} + +void +_cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, + CoglBool value) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + tex_2d->auto_mipmap = value; +} + +CoglTexture2D * +_cogl_texture_2d_create_base (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); + CoglTexture *tex = COGL_TEXTURE (tex_2d); + + _cogl_texture_init (tex, ctx, width, height, internal_format, loader, + &cogl_texture_2d_vtable); + + tex_2d->mipmaps_dirty = TRUE; + tex_2d->auto_mipmap = TRUE; + + tex_2d->is_foreign = FALSE; + + ctx->driver_vtable->texture_2d_init (tex_2d); + + return _cogl_texture_2d_object_new (tex_2d); +} + +CoglTexture2D * +cogl_texture_2d_new_with_size (CoglContext *ctx, + int width, + int height) +{ + CoglTextureLoader *loader; + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_texture_2d_create_base (ctx, width, height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, loader); +} + +static CoglBool +_cogl_texture_2d_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglContext *ctx = tex->context; + + return ctx->driver_vtable->texture_2d_allocate (tex, error); +} + +CoglTexture2D * +_cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, + CoglBool can_convert_in_place) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (bmp != NULL, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; + + return _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + cogl_bitmap_get_format (bmp), + loader); +} + +CoglTexture2D * +cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp) +{ + return _cogl_texture_2d_new_from_bitmap (bmp, + FALSE); /* can't convert in place */ +} + +CoglTexture2D * +cogl_texture_2d_new_from_file (CoglContext *ctx, + const char *filename, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture2D *tex_2d = NULL; + + _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL); + + bmp = _cogl_bitmap_from_file (ctx, filename, error); + if (bmp == NULL) + return NULL; + + tex_2d = _cogl_texture_2d_new_from_bitmap (bmp, + TRUE); /* can convert in-place */ + + cogl_object_unref (bmp); + + return tex_2d; +} + +CoglTexture2D * +cogl_texture_2d_new_from_data (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture2D *tex_2d; + + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + tex_2d = cogl_texture_2d_new_from_bitmap (bmp); + + cogl_object_unref (bmp); + + if (tex_2d && + !cogl_texture_allocate (COGL_TEXTURE (tex_2d), error)) + { + cogl_object_unref (tex_2d); + return NULL; + } + + return tex_2d; +} + +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) +/* NB: The reason we require the width, height and format to be passed + * even though they may seem redundant is because GLES 1/2 don't + * provide a way to query these properties. */ +CoglTexture2D * +_cogl_egl_texture_2d_new_from_image (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + EGLImageKHR image, + CoglError **error) +{ + CoglTextureLoader *loader; + CoglTexture2D *tex; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_context_get_winsys (ctx)->constraints & + COGL_RENDERER_CONSTRAINT_USES_EGL, + NULL); + + _COGL_RETURN_VAL_IF_FAIL (_cogl_has_private_feature + (ctx, + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE), + NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE; + loader->src.egl_image.image = image; + loader->src.egl_image.width = width; + loader->src.egl_image.height = height; + loader->src.egl_image.format = format; + + tex = _cogl_texture_2d_create_base (ctx, width, height, format, loader); + + if (!cogl_texture_allocate (COGL_TEXTURE (tex), error)) + { + cogl_object_unref (tex); + return NULL; + } + + return tex; +} +#endif /* defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) */ + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT +static void +shm_buffer_get_cogl_pixel_format (struct wl_shm_buffer *shm_buffer, + CoglPixelFormat *format_out, + CoglTextureComponents *components_out) +{ + CoglPixelFormat format; + CoglTextureComponents components = COGL_TEXTURE_COMPONENTS_RGBA; + + switch (wl_shm_buffer_get_format (shm_buffer)) + { +#if G_BYTE_ORDER == G_BIG_ENDIAN + case WL_SHM_FORMAT_ARGB8888: + format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; + break; + case WL_SHM_FORMAT_XRGB8888: + format = COGL_PIXEL_FORMAT_ARGB_8888; + components = COGL_TEXTURE_COMPONENTS_RGB; + break; +#elif G_BYTE_ORDER == G_LITTLE_ENDIAN + case WL_SHM_FORMAT_ARGB8888: + format = COGL_PIXEL_FORMAT_BGRA_8888_PRE; + break; + case WL_SHM_FORMAT_XRGB8888: + format = COGL_PIXEL_FORMAT_BGRA_8888; + components = COGL_TEXTURE_COMPONENTS_RGB; + break; +#endif + default: + g_warn_if_reached (); + format = COGL_PIXEL_FORMAT_ARGB_8888; + } + + if (format_out) + *format_out = format; + if (components_out) + *components_out = components; +} + +CoglBool +cogl_wayland_texture_set_region_from_shm_buffer (CoglTexture *texture, + int src_x, + int src_y, + int width, + int height, + struct wl_shm_buffer * + shm_buffer, + int dst_x, + int dst_y, + int level, + CoglError **error) +{ + const uint8_t *data = wl_shm_buffer_get_data (shm_buffer); + int32_t stride = wl_shm_buffer_get_stride (shm_buffer); + CoglPixelFormat format; + int bpp; + + shm_buffer_get_cogl_pixel_format (shm_buffer, &format, NULL); + bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + + return _cogl_texture_set_region (COGL_TEXTURE (texture), + width, height, + format, + stride, + data + src_x * bpp + src_y * stride, + dst_x, dst_y, + level, + error); +} + +CoglTexture2D * +cogl_wayland_texture_2d_new_from_buffer (CoglContext *ctx, + struct wl_resource *buffer, + CoglError **error) +{ + struct wl_shm_buffer *shm_buffer; + CoglTexture2D *tex = NULL; + + shm_buffer = wl_shm_buffer_get (buffer); + + if (shm_buffer) + { + int stride = wl_shm_buffer_get_stride (shm_buffer); + int width = wl_shm_buffer_get_width (shm_buffer); + int height = wl_shm_buffer_get_height (shm_buffer); + CoglPixelFormat format; + CoglTextureComponents components; + CoglBitmap *bmp; + + shm_buffer_get_cogl_pixel_format (shm_buffer, &format, &components); + + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + stride, + wl_shm_buffer_get_data (shm_buffer)); + + tex = cogl_texture_2d_new_from_bitmap (bmp); + + cogl_texture_set_components (COGL_TEXTURE (tex), components); + + cogl_object_unref (bmp); + + if (!cogl_texture_allocate (COGL_TEXTURE (tex), error)) + { + cogl_object_unref (tex); + return NULL; + } + else + return tex; + } + else + { + int format, width, height; + + if (_cogl_egl_query_wayland_buffer (ctx, + buffer, + EGL_TEXTURE_FORMAT, + &format) && + _cogl_egl_query_wayland_buffer (ctx, + buffer, + EGL_WIDTH, + &width) && + _cogl_egl_query_wayland_buffer (ctx, + buffer, + EGL_HEIGHT, + &height)) + { + EGLImageKHR image; + CoglPixelFormat internal_format; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_context_get_winsys (ctx)->constraints & + COGL_RENDERER_CONSTRAINT_USES_EGL, + NULL); + + switch (format) + { + case EGL_TEXTURE_RGB: + internal_format = COGL_PIXEL_FORMAT_RGB_888; + break; + case EGL_TEXTURE_RGBA: + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + break; + default: + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Can't create texture from unknown " + "wayland buffer format %d\n", format); + return NULL; + } + + image = _cogl_egl_create_image (ctx, + EGL_WAYLAND_BUFFER_WL, + buffer, + NULL); + tex = _cogl_egl_texture_2d_new_from_image (ctx, + width, height, + internal_format, + image, + error); + _cogl_egl_destroy_image (ctx, image); + return tex; + } + } + + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Can't create texture from unknown " + "wayland buffer type\n"); + return NULL; +} +#endif /* COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT */ + +void +_cogl_texture_2d_externally_modified (CoglTexture *texture) +{ + if (!cogl_is_texture_2d (texture)) + return; + + COGL_TEXTURE_2D (texture)->mipmaps_dirty = TRUE; +} + +void +_cogl_texture_2d_copy_from_framebuffer (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + + /* Assert that the storage for this texture has been allocated */ + cogl_texture_allocate (tex, NULL); /* (abort on error) */ + + ctx->driver_vtable->texture_2d_copy_from_framebuffer (tex_2d, + src_x, + src_y, + width, + height, + src_fb, + dst_x, + dst_y, + level); + + tex_2d->mipmaps_dirty = TRUE; +} + +static int +_cogl_texture_2d_get_max_waste (CoglTexture *tex) +{ + return -1; +} + +static CoglBool +_cogl_texture_2d_is_sliced (CoglTexture *tex) +{ + return FALSE; +} + +static CoglBool +_cogl_texture_2d_can_hardware_repeat (CoglTexture *tex) +{ + CoglContext *ctx = tex->context; + + if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT) || + (_cogl_util_is_pot (tex->width) && + _cogl_util_is_pot (tex->height))) + return TRUE; + else + return FALSE; +} + +static void +_cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + /* The texture coordinates map directly so we don't need to do + anything */ +} + +static CoglTransformResult +_cogl_texture_2d_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + /* The texture coordinates map directly so we don't need to do + anything other than check for repeats */ + + int i; + + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + { + /* Repeat is needed */ + return (_cogl_texture_2d_can_hardware_repeat (tex) ? + COGL_TRANSFORM_HARDWARE_REPEAT : + COGL_TRANSFORM_SOFTWARE_REPEAT); + } + + /* No repeat is needed */ + return COGL_TRANSFORM_NO_REPEAT; +} + +static CoglBool +_cogl_texture_2d_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglContext *ctx = tex->context; + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (ctx->driver_vtable->texture_2d_get_gl_handle) + { + GLuint handle; + + if (out_gl_target) + *out_gl_target = GL_TEXTURE_2D; + + handle = ctx->driver_vtable->texture_2d_get_gl_handle (tex_2d); + + if (out_gl_handle) + *out_gl_handle = handle; + + return handle ? TRUE : FALSE; + } + else + return FALSE; +} + +static void +_cogl_texture_2d_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + /* Only update if the mipmaps are dirty */ + if ((flags & COGL_TEXTURE_NEEDS_MIPMAP) && + tex_2d->auto_mipmap && tex_2d->mipmaps_dirty) + { + CoglContext *ctx = tex->context; + + ctx->driver_vtable->texture_2d_generate_mipmap (tex_2d); + + tex_2d->mipmaps_dirty = FALSE; + } +} + +static void +_cogl_texture_2d_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + +static CoglBool +_cogl_texture_2d_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + CoglContext *ctx = tex->context; + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (!ctx->driver_vtable->texture_2d_copy_from_bitmap (tex_2d, + src_x, + src_y, + width, + height, + bmp, + dst_x, + dst_y, + level, + error)) + { + return FALSE; + } + + tex_2d->mipmaps_dirty = TRUE; + + return TRUE; +} + +static CoglBool +_cogl_texture_2d_get_data (CoglTexture *tex, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + CoglContext *ctx = tex->context; + + if (ctx->driver_vtable->texture_2d_get_data) + { + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + ctx->driver_vtable->texture_2d_get_data (tex_2d, format, rowstride, data); + return TRUE; + } + else + return FALSE; +} + +static CoglPixelFormat +_cogl_texture_2d_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->internal_format; +} + +static GLenum +_cogl_texture_2d_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->gl_internal_format; +} + +static CoglBool +_cogl_texture_2d_is_foreign (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->is_foreign; +} + +static CoglTextureType +_cogl_texture_2d_get_type (CoglTexture *tex) +{ + return COGL_TEXTURE_TYPE_2D; +} + +static const CoglTextureVtable +cogl_texture_2d_vtable = + { + TRUE, /* primitive */ + _cogl_texture_2d_allocate, + _cogl_texture_2d_set_region, + _cogl_texture_2d_get_data, + NULL, /* foreach_sub_texture_in_region */ + _cogl_texture_2d_get_max_waste, + _cogl_texture_2d_is_sliced, + _cogl_texture_2d_can_hardware_repeat, + _cogl_texture_2d_transform_coords_to_gl, + _cogl_texture_2d_transform_quad_coords_to_gl, + _cogl_texture_2d_get_gl_texture, + _cogl_texture_2d_gl_flush_legacy_texobj_filters, + _cogl_texture_2d_pre_paint, + _cogl_texture_2d_ensure_non_quad_rendering, + _cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes, + _cogl_texture_2d_get_format, + _cogl_texture_2d_get_gl_format, + _cogl_texture_2d_get_type, + _cogl_texture_2d_is_foreign, + _cogl_texture_2d_set_auto_mipmap + }; diff --git a/cogl/cogl/cogl-texture-2d.h b/cogl/cogl/cogl-texture-2d.h new file mode 100644 index 000000000..c806ced5a --- /dev/null +++ b/cogl/cogl/cogl-texture-2d.h @@ -0,0 +1,234 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_TEXTURE_2D_H +#define __COGL_TEXTURE_2D_H + +#include "cogl-context.h" +#include "cogl-bitmap.h" + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-texture-2d + * @short_description: Functions for creating and manipulating 2D textures + * + * These functions allow low-level 2D textures to be allocated. These + * differ from sliced textures for example which may internally be + * made up of multiple 2D textures, or atlas textures where Cogl must + * internally modify user texture coordinates before they can be used + * by the GPU. + * + * You should be aware that many GPUs only support power of two sizes + * for #CoglTexture2D textures. You can check support for non power of + * two textures by checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature + * via cogl_has_feature(). + */ + +typedef struct _CoglTexture2D CoglTexture2D; +#define COGL_TEXTURE_2D(X) ((CoglTexture2D *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_2d_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_2d_get_gtype (void); +#endif + +/** + * cogl_is_texture_2d: + * @object: A #CoglObject + * + * Gets whether the given object references an existing #CoglTexture2D + * object. + * + * Return value: %TRUE if the object references a #CoglTexture2D, + * %FALSE otherwise + */ +CoglBool +cogl_is_texture_2d (void *object); + +/** + * cogl_texture_2d_new_with_size: + * @ctx: A #CoglContext + * @width: Width of the texture to allocate + * @height: Height of the texture to allocate + * + * Creates a low-level #CoglTexture2D texture with a given @width and + * @height that your GPU can texture from directly. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Many GPUs only support power of two sizes for #CoglTexture2D + * textures. You can check support for non power of two textures by + * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via + * cogl_has_feature(). + * + * Returns: (transfer full): A new #CoglTexture2D object with no storage yet allocated. + * + * Since: 2.0 + */ +CoglTexture2D * +cogl_texture_2d_new_with_size (CoglContext *ctx, + int width, + int height); + +/** + * cogl_texture_2d_new_from_file: + * @ctx: A #CoglContext + * @filename: the file to load + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a low-level #CoglTexture2D texture from an image file. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Many GPUs only support power of two sizes for #CoglTexture2D + * textures. You can check support for non power of two textures by + * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via + * cogl_has_feature(). + * + * Return value: (transfer full): A newly created #CoglTexture2D or %NULL on failure + * and @error will be updated. + * + * Since: 1.16 + */ +CoglTexture2D * +cogl_texture_2d_new_from_file (CoglContext *ctx, + const char *filename, + CoglError **error); + +/** + * cogl_texture_2d_new_from_data: + * @ctx: A #CoglContext + * @width: width of texture in pixels + * @height: height of texture in pixels + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @rowstride: the memory offset in bytes between the starts of + * scanlines in @data. A value of 0 will make Cogl automatically + * calculate @rowstride from @width and @format. + * @data: pointer the memory region where the source buffer resides + * @error: A #CoglError for exceptions + * + * Creates a low-level #CoglTexture2D texture based on data residing + * in memory. + * + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_texture_2d_new_from_bitmap() or use + * cogl_texture_2d_new_with_size() and then upload data using + * cogl_texture_set_data() + * + * Many GPUs only support power of two sizes for #CoglTexture2D + * textures. You can check support for non power of two textures by + * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via + * cogl_has_feature(). + * + * Returns: (transfer full): A newly allocated #CoglTexture2D, or if + * the size is not supported (because it is too large or a + * non-power-of-two size that the hardware doesn't support) + * it will return %NULL and set @error. + * + * Since: 2.0 + */ +CoglTexture2D * +cogl_texture_2d_new_from_data (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + CoglError **error); + +/** + * cogl_texture_2d_new_from_bitmap: + * @bitmap: A #CoglBitmap + * + * Creates a low-level #CoglTexture2D texture based on data residing + * in a #CoglBitmap. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Many GPUs only support power of two sizes for #CoglTexture2D + * textures. You can check support for non power of two textures by + * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via + * cogl_has_feature(). + * + * Returns: (transfer full): A newly allocated #CoglTexture2D + * + * Since: 2.0 + * Stability: unstable + */ +CoglTexture2D * +cogl_texture_2d_new_from_bitmap (CoglBitmap *bitmap); + +COGL_END_DECLS + +#endif /* __COGL_TEXTURE_2D_H */ diff --git a/cogl/cogl/cogl-texture-3d-private.h b/cogl/cogl/cogl-texture-3d-private.h new file mode 100644 index 000000000..b6e0066d3 --- /dev/null +++ b/cogl/cogl/cogl-texture-3d-private.h @@ -0,0 +1,66 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_TEXTURE_3D_PRIVATE_H +#define __COGL_TEXTURE_3D_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-3d.h" + +struct _CoglTexture3D +{ + CoglTexture _parent; + + /* The internal format of the texture represented as a + CoglPixelFormat */ + CoglPixelFormat internal_format; + int depth; + CoglBool auto_mipmap; + CoglBool mipmaps_dirty; + + /* TODO: factor out these OpenGL specific members into some form + * of driver private state. */ + + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + /* The texture object number */ + GLuint gl_texture; + GLenum gl_legacy_texobj_min_filter; + GLenum gl_legacy_texobj_mag_filter; + GLint gl_legacy_texobj_wrap_mode_s; + GLint gl_legacy_texobj_wrap_mode_t; + GLint gl_legacy_texobj_wrap_mode_p; + CoglTexturePixel first_pixel; +}; + +#endif /* __COGL_TEXTURE_3D_PRIVATE_H */ diff --git a/cogl/cogl/cogl-texture-3d.c b/cogl/cogl/cogl-texture-3d.c new file mode 100644 index 000000000..8e2ff0821 --- /dev/null +++ b/cogl/cogl/cogl-texture-3d.c @@ -0,0 +1,761 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-texture-3d-private.h" +#include "cogl-texture-3d.h" +#include "cogl-texture-gl-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-error-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +/* These might not be defined on GLES */ +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif + +static void _cogl_texture_3d_free (CoglTexture3D *tex_3d); + +COGL_TEXTURE_DEFINE (Texture3D, texture_3d); +COGL_GTYPE_DEFINE_CLASS (Texture3D, texture_3d, + COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); + +static const CoglTextureVtable cogl_texture_3d_vtable; + +static void +_cogl_texture_3d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + CoglContext *ctx = tex->context; + + /* Only set the wrap mode if it's different from the current value + to avoid too many GL calls. */ + if (tex_3d->gl_legacy_texobj_wrap_mode_s != wrap_mode_s || + tex_3d->gl_legacy_texobj_wrap_mode_t != wrap_mode_t || + tex_3d->gl_legacy_texobj_wrap_mode_p != wrap_mode_p) + { + _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, + tex_3d->gl_texture, + FALSE); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, + GL_TEXTURE_WRAP_S, + wrap_mode_s) ); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, + GL_TEXTURE_WRAP_T, + wrap_mode_t) ); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, + GL_TEXTURE_WRAP_R, + wrap_mode_p) ); + + tex_3d->gl_legacy_texobj_wrap_mode_s = wrap_mode_s; + tex_3d->gl_legacy_texobj_wrap_mode_t = wrap_mode_t; + tex_3d->gl_legacy_texobj_wrap_mode_p = wrap_mode_p; + } +} + +static void +_cogl_texture_3d_free (CoglTexture3D *tex_3d) +{ + if (tex_3d->gl_texture) + _cogl_delete_gl_texture (tex_3d->gl_texture); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_3d)); +} + +static void +_cogl_texture_3d_set_auto_mipmap (CoglTexture *tex, + CoglBool value) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + + tex_3d->auto_mipmap = value; +} + +static CoglTexture3D * +_cogl_texture_3d_create_base (CoglContext *ctx, + int width, + int height, + int depth, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1); + CoglTexture *tex = COGL_TEXTURE (tex_3d); + + _cogl_texture_init (tex, ctx, width, height, + internal_format, loader, &cogl_texture_3d_vtable); + + tex_3d->gl_texture = 0; + + tex_3d->depth = depth; + tex_3d->mipmaps_dirty = TRUE; + tex_3d->auto_mipmap = TRUE; + + /* We default to GL_LINEAR for both filters */ + tex_3d->gl_legacy_texobj_min_filter = GL_LINEAR; + tex_3d->gl_legacy_texobj_mag_filter = GL_LINEAR; + + /* Wrap mode not yet set */ + tex_3d->gl_legacy_texobj_wrap_mode_s = GL_FALSE; + tex_3d->gl_legacy_texobj_wrap_mode_t = GL_FALSE; + tex_3d->gl_legacy_texobj_wrap_mode_p = GL_FALSE; + + return _cogl_texture_3d_object_new (tex_3d); +} + +CoglTexture3D * +cogl_texture_3d_new_with_size (CoglContext *ctx, + int width, + int height, + int depth) +{ + CoglTextureLoader *loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + loader->src.sized.depth = depth; + + return _cogl_texture_3d_create_base (ctx, width, height, depth, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + loader); +} + +CoglTexture3D * +cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp, + int height, + int depth) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (bmp, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.height = height; + loader->src.bitmap.depth = depth; + loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */ + + return _cogl_texture_3d_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + height, + depth, + cogl_bitmap_get_format (bmp), + loader); +} + +CoglTexture3D * +cogl_texture_3d_new_from_data (CoglContext *context, + int width, + int height, + int depth, + CoglPixelFormat format, + int rowstride, + int image_stride, + const uint8_t *data, + CoglError **error) +{ + CoglBitmap *bitmap; + CoglTexture3D *ret; + + _COGL_RETURN_VAL_IF_FAIL (data, NULL); + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + /* Image stride from height and rowstride if not given */ + if (image_stride == 0) + image_stride = height * rowstride; + + if (image_stride < rowstride * height) + return NULL; + + /* GL doesn't support uploading when the image_stride isn't a + multiple of the rowstride. If this happens we'll just pack the + image into a new bitmap. The documentation for this function + recommends avoiding this situation. */ + if (image_stride % rowstride != 0) + { + uint8_t *bmp_data; + int bmp_rowstride; + int z, y; + + bitmap = _cogl_bitmap_new_with_malloc_buffer (context, + width, + depth * height, + format, + error); + if (!bitmap) + return NULL; + + bmp_data = _cogl_bitmap_map (bitmap, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + error); + + if (bmp_data == NULL) + { + cogl_object_unref (bitmap); + return NULL; + } + + bmp_rowstride = cogl_bitmap_get_rowstride (bitmap); + + /* Copy all of the images in */ + for (z = 0; z < depth; z++) + for (y = 0; y < height; y++) + memcpy (bmp_data + (z * bmp_rowstride * height + + bmp_rowstride * y), + data + z * image_stride + rowstride * y, + bmp_rowstride); + + _cogl_bitmap_unmap (bitmap); + } + else + bitmap = cogl_bitmap_new_for_data (context, + width, + image_stride / rowstride * depth, + format, + rowstride, + (uint8_t *) data); + + ret = cogl_texture_3d_new_from_bitmap (bitmap, + height, + depth); + + cogl_object_unref (bitmap); + + if (ret && + !cogl_texture_allocate (COGL_TEXTURE (ret), error)) + { + cogl_object_unref (ret); + return NULL; + } + + return ret; +} + +static CoglBool +_cogl_texture_3d_can_create (CoglContext *ctx, + int width, + int height, + int depth, + CoglPixelFormat internal_format, + CoglError **error) +{ + GLenum gl_intformat; + GLenum gl_type; + + /* This should only happen on GLES */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "3D textures are not supported by the GPU"); + return FALSE; + } + + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT) && + (!_cogl_util_is_pot (width) || + !_cogl_util_is_pot (height) || + !_cogl_util_is_pot (depth))) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "A non-power-of-two size was requested but this is not " + "supported by the GPU"); + return FALSE; + } + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!ctx->texture_driver->size_supported_3d (ctx, + GL_TEXTURE_3D, + gl_intformat, + gl_type, + width, + height, + depth)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "The requested dimensions are not supported by the GPU"); + return FALSE; + } + + return TRUE; +} + +static CoglBool +allocate_with_size (CoglTexture3D *tex_3d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_3d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; + int depth = loader->src.sized.depth; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GLenum gl_error; + GLenum gl_texture; + + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (!_cogl_texture_3d_can_create (ctx, + width, + height, + depth, + internal_format, + error)) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); + _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, + gl_texture, + FALSE); + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage3D (GL_TEXTURE_3D, 0, gl_intformat, + width, height, depth, + 0, gl_format, gl_type, NULL); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + GE( ctx, glDeleteTextures (1, &gl_texture) ); + return FALSE; + } + + tex_3d->gl_texture = gl_texture; + tex_3d->gl_format = gl_intformat; + + tex_3d->depth = depth; + + tex_3d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_bitmap (CoglTexture3D *tex_3d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_3d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int bmp_width = cogl_bitmap_get_width (bmp); + int height = loader->src.bitmap.height; + int depth = loader->src.bitmap.depth; + CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglBitmap *upload_bmp; + CoglPixelFormat upload_format; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); + + if (!_cogl_texture_3d_can_create (ctx, + bmp_width, height, depth, + internal_format, + error)) + return FALSE; + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + upload_format = cogl_bitmap_get_format (upload_bmp); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + upload_format, + NULL, /* internal format */ + &gl_format, + &gl_type); + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + NULL); + + /* Keep a copy of the first pixel so that if glGenerateMipmap isn't + supported we can fallback to using GL_GENERATE_MIPMAP */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + CoglError *ignore = NULL; + uint8_t *data = _cogl_bitmap_map (upload_bmp, + COGL_BUFFER_ACCESS_READ, 0, + &ignore); + + tex_3d->first_pixel.gl_format = gl_format; + tex_3d->first_pixel.gl_type = gl_type; + + if (data) + { + memcpy (tex_3d->first_pixel.data, data, + _cogl_pixel_format_get_bytes_per_pixel (upload_format)); + _cogl_bitmap_unmap (upload_bmp); + } + else + { + g_warning ("Failed to read first pixel of bitmap for " + "glGenerateMipmap fallback"); + cogl_error_free (ignore); + memset (tex_3d->first_pixel.data, 0, + _cogl_pixel_format_get_bytes_per_pixel (upload_format)); + } + } + + tex_3d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); + + if (!ctx->texture_driver->upload_to_gl_3d (ctx, + GL_TEXTURE_3D, + tex_3d->gl_texture, + FALSE, /* is_foreign */ + height, + depth, + upload_bmp, + gl_intformat, + gl_format, + gl_type, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + tex_3d->gl_format = gl_intformat; + + cogl_object_unref (upload_bmp); + + tex_3d->depth = loader->src.bitmap.depth; + + tex_3d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, + bmp_width, loader->src.bitmap.height); + + return TRUE; +} + +static CoglBool +_cogl_texture_3d_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_3d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_3d, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +static int +_cogl_texture_3d_get_max_waste (CoglTexture *tex) +{ + return -1; +} + +static CoglBool +_cogl_texture_3d_is_sliced (CoglTexture *tex) +{ + return FALSE; +} + +static CoglBool +_cogl_texture_3d_can_hardware_repeat (CoglTexture *tex) +{ + return TRUE; +} + +static void +_cogl_texture_3d_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + /* The texture coordinates map directly so we don't need to do + anything */ +} + +static CoglTransformResult +_cogl_texture_3d_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + /* The texture coordinates map directly so we don't need to do + anything other than check for repeats */ + + CoglBool need_repeat = FALSE; + int i; + + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + need_repeat = TRUE; + + return (need_repeat ? COGL_TRANSFORM_HARDWARE_REPEAT + : COGL_TRANSFORM_NO_REPEAT); +} + +static CoglBool +_cogl_texture_3d_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + + if (out_gl_handle) + *out_gl_handle = tex_3d->gl_texture; + + if (out_gl_target) + *out_gl_target = GL_TEXTURE_3D; + + return TRUE; +} + +static void +_cogl_texture_3d_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + CoglContext *ctx = tex->context; + + if (min_filter == tex_3d->gl_legacy_texobj_min_filter + && mag_filter == tex_3d->gl_legacy_texobj_mag_filter) + return; + + /* Store new values */ + tex_3d->gl_legacy_texobj_min_filter = min_filter; + tex_3d->gl_legacy_texobj_mag_filter = mag_filter; + + /* Apply new filters to the texture */ + _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, + tex_3d->gl_texture, + FALSE); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, mag_filter) ); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, min_filter) ); +} + +static void +_cogl_texture_3d_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + CoglContext *ctx = tex->context; + + /* Only update if the mipmaps are dirty */ + if ((flags & COGL_TEXTURE_NEEDS_MIPMAP) && + tex_3d->auto_mipmap && tex_3d->mipmaps_dirty) + { + /* glGenerateMipmap is defined in the FBO extension. If it's not + available we'll fallback to temporarily enabling + GL_GENERATE_MIPMAP and reuploading the first pixel */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + _cogl_texture_gl_generate_mipmaps (tex); +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + else if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) + { + _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, + tex_3d->gl_texture, + FALSE); + + GE( ctx, glTexParameteri (GL_TEXTURE_3D, + GL_GENERATE_MIPMAP, + GL_TRUE) ); + GE( ctx, glTexSubImage3D (GL_TEXTURE_3D, + 0, /* level */ + 0, /* xoffset */ + 0, /* yoffset */ + 0, /* zoffset */ + 1, /* width */ + 1, /* height */ + 1, /* depth */ + tex_3d->first_pixel.gl_format, + tex_3d->first_pixel.gl_type, + tex_3d->first_pixel.data) ); + GE( ctx, glTexParameteri (GL_TEXTURE_3D, + GL_GENERATE_MIPMAP, + GL_FALSE) ); + } +#endif + + tex_3d->mipmaps_dirty = FALSE; + } +} + +static void +_cogl_texture_3d_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + +static CoglBool +_cogl_texture_3d_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + /* This function doesn't really make sense for 3D textures because + it can't specify which image to upload to */ + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Setting a 2D region on a 3D texture isn't " + "currently supported"); + + return FALSE; +} + +static int +_cogl_texture_3d_get_data (CoglTexture *tex, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + /* FIXME: we could probably implement this by assuming the data is + big enough to hold all of the images and that there is no stride + between the images. However it would be better to have an API + that can provide an image stride and this function probably isn't + particularly useful anyway so for now it just reports failure */ + return 0; +} + +static CoglPixelFormat +_cogl_texture_3d_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_3D (tex)->internal_format; +} + +static GLenum +_cogl_texture_3d_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_3D (tex)->gl_format; +} + +static CoglTextureType +_cogl_texture_3d_get_type (CoglTexture *tex) +{ + return COGL_TEXTURE_TYPE_3D; +} + +static const CoglTextureVtable +cogl_texture_3d_vtable = + { + TRUE, /* primitive */ + _cogl_texture_3d_allocate, + _cogl_texture_3d_set_region, + _cogl_texture_3d_get_data, + NULL, /* foreach_sub_texture_in_region */ + _cogl_texture_3d_get_max_waste, + _cogl_texture_3d_is_sliced, + _cogl_texture_3d_can_hardware_repeat, + _cogl_texture_3d_transform_coords_to_gl, + _cogl_texture_3d_transform_quad_coords_to_gl, + _cogl_texture_3d_get_gl_texture, + _cogl_texture_3d_gl_flush_legacy_texobj_filters, + _cogl_texture_3d_pre_paint, + _cogl_texture_3d_ensure_non_quad_rendering, + _cogl_texture_3d_gl_flush_legacy_texobj_wrap_modes, + _cogl_texture_3d_get_format, + _cogl_texture_3d_get_gl_format, + _cogl_texture_3d_get_type, + NULL, /* is_foreign */ + _cogl_texture_3d_set_auto_mipmap + }; diff --git a/cogl/cogl/cogl-texture-3d.h b/cogl/cogl/cogl-texture-3d.h new file mode 100644 index 000000000..b3b038ae8 --- /dev/null +++ b/cogl/cogl/cogl-texture-3d.h @@ -0,0 +1,204 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Neil Roberts + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_TEXTURE_3D_H +#define __COGL_TEXTURE_3D_H + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-texture-3d + * @short_description: Functions for creating and manipulating 3D textures + * + * These functions allow 3D textures to be used. 3D textures can be + * thought of as layers of 2D images arranged into a cuboid + * shape. When choosing a texel from the texture, Cogl will take into + * account the 'r' texture coordinate to select one of the images. + */ + +typedef struct _CoglTexture3D CoglTexture3D; + +#define COGL_TEXTURE_3D(X) ((CoglTexture3D *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_3d_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_3d_get_gtype (void); +#endif + +/** + * cogl_texture_3d_new_with_size: + * @context: a #CoglContext + * @width: width of the texture in pixels. + * @height: height of the texture in pixels. + * @depth: depth of the texture in pixels. + * + * Creates a low-level #CoglTexture3D texture with the specified + * dimensions and pixel format. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * This texture will fail to allocate later if + * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also + * fail if the requested dimensions are not supported by the + * GPU. + * + * Returns: (transfer full): A new #CoglTexture3D object with no storage yet allocated. + * Since: 1.10 + * Stability: Unstable + */ +CoglTexture3D * +cogl_texture_3d_new_with_size (CoglContext *context, + int width, + int height, + int depth); + +/** + * cogl_texture_3d_new_from_data: + * @context: a #CoglContext + * @width: width of the texture in pixels. + * @height: height of the texture in pixels. + * @depth: depth of the texture in pixels. + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @rowstride: the memory offset in bytes between the starts of + * scanlines in @data or 0 to infer it from the width and format + * @image_stride: the number of bytes from one image to the next. This + * can be used to add padding between the images in a similar way + * that the rowstride can be used to add padding between + * rows. Alternatively 0 can be passed to infer the @image_stride + * from the @height. + * @data: pointer the memory region where the source buffer resides + * @error: A CoglError return location. + * + * Creates a low-level 3D texture and initializes it with @data. The + * data is assumed to be packed array of @depth images. There can be + * padding between the images using @image_stride. + * + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_texture_3d_new_from_bitmap(). + * + * Return value: (transfer full): the newly created #CoglTexture3D or + * %NULL if there was an error and an exception will be + * returned through @error. + * Since: 1.10 + * Stability: Unstable + */ +CoglTexture3D * +cogl_texture_3d_new_from_data (CoglContext *context, + int width, + int height, + int depth, + CoglPixelFormat format, + int rowstride, + int image_stride, + const uint8_t *data, + CoglError **error); + +/** + * cogl_texture_3d_new_from_bitmap: + * @bitmap: A #CoglBitmap object. + * @height: height of the texture in pixels. + * @depth: depth of the texture in pixels. + * + * Creates a low-level 3D texture and initializes it with the images + * in @bitmap. The images are assumed to be packed together after one + * another in the increasing y axis. The height of individual image is + * given as @height and the number of images is given in @depth. The + * actual height of the bitmap can be larger than @height × @depth. In + * this case it assumes there is padding between the images. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * This texture will fail to allocate later if + * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also + * fail if the requested dimensions are not supported by the + * GPU. + * + * Return value: (transfer full): a newly created #CoglTexture3D + * Since: 2.0 + * Stability: unstable + */ +CoglTexture3D * +cogl_texture_3d_new_from_bitmap (CoglBitmap *bitmap, + int height, + int depth); + +/** + * cogl_is_texture_3d: + * @object: a #CoglObject + * + * Checks whether the given object references a #CoglTexture3D + * + * Return value: %TRUE if the passed object represents a 3D texture + * and %FALSE otherwise + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_is_texture_3d (void *object); + +COGL_END_DECLS + +#endif /* __COGL_TEXTURE_3D_H */ diff --git a/cogl/cogl/cogl-texture-driver.h b/cogl/cogl/cogl-texture-driver.h new file mode 100644 index 000000000..3ab86edb2 --- /dev/null +++ b/cogl/cogl/cogl-texture-driver.h @@ -0,0 +1,206 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_DRIVER_H +#define __COGL_TEXTURE_DRIVER_H + +typedef struct _CoglTextureDriver CoglTextureDriver; + +struct _CoglTextureDriver +{ + /* + * A very small wrapper around glGenTextures() that ensures we default to + * non-mipmap filters when creating textures. This is to save some memory as + * the driver will not allocate room for the mipmap tree. + */ + GLuint + (* gen) (CoglContext *ctx, + GLenum gl_target, + CoglPixelFormat internal_format); + + /* + * This sets up the glPixelStore state for an upload to a destination with + * the same size, and with no offset. + */ + /* NB: GLES can't upload a sub region of pixel data from a larger source + * buffer which is why this interface is limited. The GL driver has a more + * flexible version of this function that is uses internally */ + void + (* prep_gl_for_pixels_upload) (CoglContext *ctx, + int pixels_rowstride, + int pixels_bpp); + + /* + * This uploads a sub-region from source_bmp to a single GL texture + * handle (i.e a single CoglTexture slice) + * + * It also updates the array of tex->first_pixels[slice_index] if + * dst_{x,y} == 0 + * + * The driver abstraction is in place because GLES doesn't support the pixel + * store options required to source from a subregion, so for GLES we have + * to manually create a transient source bitmap. + * + * XXX: sorry for the ridiculous number of arguments :-( + */ + CoglBool + (* upload_subregion_to_gl) (CoglContext *ctx, + CoglTexture *texture, + CoglBool is_foreign, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + int level, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error); + + /* + * Replaces the contents of the GL texture with the entire bitmap. On + * GL this just directly calls glTexImage2D, but under GLES it needs + * to copy the bitmap if the rowstride is not a multiple of a possible + * alignment value because there is no GL_UNPACK_ROW_LENGTH + */ + CoglBool + (* upload_to_gl) (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error); + + /* + * Replaces the contents of the GL texture with the entire bitmap. The + * width of the texture is inferred from the bitmap. The height and + * depth of the texture is given directly. The 'image_height' (which + * is the number of rows between images) is inferred by dividing the + * height of the bitmap by the depth. + */ + CoglBool + (* upload_to_gl_3d) (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + GLint height, + GLint depth, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error); + + /* + * This sets up the glPixelStore state for an download to a destination with + * the same size, and with no offset. + */ + /* NB: GLES can't download pixel data into a sub region of a larger + * destination buffer, the GL driver has a more flexible version of + * this function that it uses internally. */ + void + (* prep_gl_for_pixels_download) (CoglContext *ctx, + int image_width, + int pixels_rowstride, + int pixels_bpp); + + /* + * This driver abstraction is needed because GLES doesn't support + * glGetTexImage (). On GLES this currently just returns FALSE which + * will lead to a generic fallback path being used that simply + * renders the texture and reads it back from the framebuffer. (See + * _cogl_texture_draw_and_read () ) + */ + CoglBool + (* gl_get_tex_image) (CoglContext *ctx, + GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + uint8_t *dest); + + /* + * It may depend on the driver as to what texture sizes are supported... + */ + CoglBool + (* size_supported) (CoglContext *ctx, + GLenum gl_target, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type, + int width, + int height); + + CoglBool + (* size_supported_3d) (CoglContext *ctx, + GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height, + int depth); + + /* + * This driver abstraction is needed because GLES doesn't support setting + * a texture border color. + */ + void + (* try_setting_gl_border_color) (CoglContext *ctx, + GLuint gl_target, + const GLfloat *transparent_color); + + /* + * It may depend on the driver as to what texture targets may be used when + * creating a foreign texture. E.g. OpenGL supports ARB_texture_rectangle + * but GLES doesn't + */ + CoglBool + (* allows_foreign_gl_target) (CoglContext *ctx, + GLenum gl_target); + + /* + * The driver may impose constraints on what formats can be used to store + * texture data read from textures. For example GLES currently only supports + * RGBA_8888, and so we need to manually convert the data if the final + * destination has another format. + */ + CoglPixelFormat + (* find_best_gl_get_data_format) (CoglContext *context, + CoglPixelFormat format, + GLenum *closest_gl_format, + GLenum *closest_gl_type); +}; + +#endif /* __COGL_TEXTURE_DRIVER_H */ + diff --git a/cogl/cogl/cogl-texture-private.h b/cogl/cogl/cogl-texture-private.h new file mode 100644 index 000000000..472c41d9d --- /dev/null +++ b/cogl/cogl/cogl-texture-private.h @@ -0,0 +1,409 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_PRIVATE_H +#define __COGL_TEXTURE_PRIVATE_H + +#include "cogl-bitmap-private.h" +#include "cogl-object-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-spans.h" +#include "cogl-meta-texture.h" +#include "cogl-framebuffer.h" + +#ifdef COGL_HAS_EGL_SUPPORT +#include "cogl-egl-defines.h" +#endif + +typedef struct _CoglTextureVtable CoglTextureVtable; + +/* Encodes three possibiloities result of transforming a quad */ +typedef enum { + /* quad doesn't cross the boundaries of a texture */ + COGL_TRANSFORM_NO_REPEAT, + /* quad crosses boundaries, hardware wrap mode can handle */ + COGL_TRANSFORM_HARDWARE_REPEAT, + /* quad crosses boundaries, needs software fallback; + * for a sliced texture, this might not actually involve + * repeating, just a quad crossing multiple slices */ + COGL_TRANSFORM_SOFTWARE_REPEAT, +} CoglTransformResult; + +/* Flags given to the pre_paint method */ +typedef enum { + /* The texture is going to be used with filters that require + mipmapping. This gives the texture the opportunity to + automatically update the mipmap tree */ + COGL_TEXTURE_NEEDS_MIPMAP = 1 +} CoglTexturePrePaintFlags; + +struct _CoglTextureVtable +{ + /* Virtual functions that must be implemented for a texture + backend */ + + CoglBool is_primitive; + + CoglBool (* allocate) (CoglTexture *tex, + CoglError **error); + + /* This should update the specified sub region of the texture with a + sub region of the given bitmap. The bitmap is not converted + before being set so the caller is expected to have called + _cogl_bitmap_convert_for_upload with a suitable internal_format + before passing here */ + CoglBool (* set_region) (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bitmap, + CoglError **error); + + /* This should copy the image data of the texture into @data. The + requested format will have been first passed through + ctx->texture_driver->find_best_gl_get_data_format so it should + always be a format that is valid for GL (ie, no conversion should + be necessary). */ + CoglBool (* get_data) (CoglTexture *tex, + CoglPixelFormat format, + int rowstride, + uint8_t *data); + + void (* foreach_sub_texture_in_region) (CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglMetaTextureCallback callback, + void *user_data); + + int (* get_max_waste) (CoglTexture *tex); + + CoglBool (* is_sliced) (CoglTexture *tex); + + CoglBool (* can_hardware_repeat) (CoglTexture *tex); + + void (* transform_coords_to_gl) (CoglTexture *tex, + float *s, + float *t); + CoglTransformResult (* transform_quad_coords_to_gl) (CoglTexture *tex, + float *coords); + + CoglBool (* get_gl_texture) (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target); + + /* OpenGL driver specific virtual function */ + void (* gl_flush_legacy_texobj_filters) (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter); + + void (* pre_paint) (CoglTexture *tex, CoglTexturePrePaintFlags flags); + void (* ensure_non_quad_rendering) (CoglTexture *tex); + + /* OpenGL driver specific virtual function */ + void (* gl_flush_legacy_texobj_wrap_modes) (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p); + + CoglPixelFormat (* get_format) (CoglTexture *tex); + GLenum (* get_gl_format) (CoglTexture *tex); + + CoglTextureType (* get_type) (CoglTexture *tex); + + CoglBool (* is_foreign) (CoglTexture *tex); + + /* Only needs to be implemented if is_primitive == TRUE */ + void (* set_auto_mipmap) (CoglTexture *texture, + CoglBool value); +}; + +typedef enum _CoglTextureSoureType { + COGL_TEXTURE_SOURCE_TYPE_SIZED = 1, + COGL_TEXTURE_SOURCE_TYPE_BITMAP, + COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE, + COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN +} CoglTextureSourceType; + +typedef struct _CoglTextureLoader +{ + CoglTextureSourceType src_type; + union { + struct { + int width; + int height; + int depth; /* for 3d textures */ + } sized; + struct { + CoglBitmap *bitmap; + int height; /* for 3d textures */ + int depth; /* for 3d textures */ + CoglBool can_convert_in_place; + } bitmap; +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) + struct { + EGLImageKHR image; + int width; + int height; + CoglPixelFormat format; + } egl_image; +#endif + struct { + int width; + int height; + unsigned int gl_handle; + CoglPixelFormat format; + } gl_foreign; + } src; +} CoglTextureLoader; + +struct _CoglTexture +{ + CoglObject _parent; + CoglContext *context; + CoglTextureLoader *loader; + GList *framebuffers; + int max_level; + int width; + int height; + CoglBool allocated; + + /* + * Internal format + */ + CoglTextureComponents components; + unsigned int premultiplied:1; + + const CoglTextureVtable *vtable; +}; + +typedef enum _CoglTextureChangeFlags +{ + /* Whenever the internals of a texture are changed such that the + * underlying GL textures that represent the CoglTexture change then + * we notify cogl-material.c via + * _cogl_pipeline_texture_pre_change_notify + */ + COGL_TEXTURE_CHANGE_GL_TEXTURES + +} CoglTextureChangeFlags; + +typedef struct _CoglTexturePixel CoglTexturePixel; + +/* This is used by the texture backends to store the first pixel of + each GL texture. This is only used when glGenerateMipmap is not + available so that we can temporarily set GL_GENERATE_MIPMAP and + reupload a pixel */ +struct _CoglTexturePixel +{ + /* We need to store the format of the pixel because we store the + data in the source format which might end up being different for + each slice if a subregion is updated with a different format */ + GLenum gl_format; + GLenum gl_type; + uint8_t data[4]; +}; + +void +_cogl_texture_init (CoglTexture *texture, + CoglContext *ctx, + int width, + int height, + CoglPixelFormat src_format, + CoglTextureLoader *loader, + const CoglTextureVtable *vtable); + +void +_cogl_texture_free (CoglTexture *texture); + +/* This is used to register a type to the list of handle types that + will be considered a texture in cogl_is_texture() */ +void +_cogl_texture_register_texture_type (const CoglObjectClass *klass); + +#define COGL_TEXTURE_DEFINE(TypeName, type_name) \ + COGL_OBJECT_DEFINE_WITH_CODE_GTYPE \ + (TypeName, type_name, \ + _cogl_texture_register_texture_type (&_cogl_##type_name##_class)) + +#define COGL_TEXTURE_INTERNAL_DEFINE(TypeName, type_name) \ + COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE \ + (TypeName, type_name, \ + _cogl_texture_register_texture_type (&_cogl_##type_name##_class)) + +CoglBool +_cogl_texture_can_hardware_repeat (CoglTexture *texture); + +void +_cogl_texture_transform_coords_to_gl (CoglTexture *texture, + float *s, + float *t); +CoglTransformResult +_cogl_texture_transform_quad_coords_to_gl (CoglTexture *texture, + float *coords); + +void +_cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags); + +void +_cogl_texture_ensure_non_quad_rendering (CoglTexture *texture); + +/* + * This determines a CoglPixelFormat according to texture::components + * and texture::premultiplied (i.e. the user required components and + * whether the texture should be considered premultiplied) + * + * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY) + * and wherever possible this function tries to simply return the + * given source format if its compatible with the required components. + * + * Texture backends can call this when allocating a texture to know + * how to convert a source image in preparation for uploading. + */ +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglTexture *texture, + CoglPixelFormat src_format); + +/* This is called by texture backends when they have successfully + * allocated a texture. + * + * Most texture backends currently track the internal layout of + * textures using a CoglPixelFormat which will be finalized when a + * texture is allocated. At this point we need to update + * texture::components and texture::premultiplied according to the + * determined layout. + * + * XXX: Going forward we should probably aim to stop using + * CoglPixelFormat at all for tracking the internal layout of + * textures. + */ +void +_cogl_texture_set_internal_format (CoglTexture *texture, + CoglPixelFormat internal_format); + +CoglBool +_cogl_texture_is_foreign (CoglTexture *texture); + +void +_cogl_texture_associate_framebuffer (CoglTexture *texture, + CoglFramebuffer *framebuffer); + +const GList * +_cogl_texture_get_associated_framebuffers (CoglTexture *texture); + +void +_cogl_texture_flush_journal_rendering (CoglTexture *texture); + +void +_cogl_texture_spans_foreach_in_region (CoglSpan *x_spans, + int n_x_spans, + CoglSpan *y_spans, + int n_y_spans, + CoglTexture **textures, + float *virtual_coords, + float x_normalize_factor, + float y_normalize_factor, + CoglPipelineWrapMode wrap_x, + CoglPipelineWrapMode wrap_y, + CoglMetaTextureCallback callback, + void *user_data); + +/* + * _cogl_texture_get_type: + * @texture: a #CoglTexture pointer + * + * Retrieves the texture type of the underlying hardware texture that + * this #CoglTexture will use. + * + * Return value: The type of the hardware texture. + */ +CoglTextureType +_cogl_texture_get_type (CoglTexture *texture); + +CoglBool +_cogl_texture_set_region (CoglTexture *texture, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + int dst_x, + int dst_y, + int level, + CoglError **error); + +CoglBool +_cogl_texture_set_region_from_bitmap (CoglTexture *texture, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bmp, + int dst_x, + int dst_y, + int level, + CoglError **error); + +CoglBool +_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, + CoglPixelFormat dst_format); + +int +_cogl_texture_get_n_levels (CoglTexture *texture); + +void +_cogl_texture_get_level_size (CoglTexture *texture, + int level, + int *width, + int *height, + int *depth); + +void +_cogl_texture_set_allocated (CoglTexture *texture, + CoglPixelFormat internal_format, + int width, + int height); + +CoglPixelFormat +_cogl_texture_get_format (CoglTexture *texture); + +CoglTextureLoader * +_cogl_texture_create_loader (void); + +void +_cogl_texture_copy_internal_format (CoglTexture *src, + CoglTexture *dest); + +#endif /* __COGL_TEXTURE_PRIVATE_H */ diff --git a/cogl/cogl/cogl-texture-rectangle-private.h b/cogl/cogl/cogl-texture-rectangle-private.h new file mode 100644 index 000000000..75029e76e --- /dev/null +++ b/cogl/cogl/cogl-texture-rectangle-private.h @@ -0,0 +1,66 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_RECTANGLE_H +#define __COGL_TEXTURE_RECTANGLE_H + +#include "cogl-pipeline-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-rectangle.h" + +struct _CoglTextureRectangle +{ + CoglTexture _parent; + + /* The internal format of the texture represented as a + CoglPixelFormat */ + CoglPixelFormat internal_format; + + /* TODO: factor out these OpenGL specific members into some form + * of driver private state. */ + + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + /* The texture object number */ + GLuint gl_texture; + GLenum gl_legacy_texobj_min_filter; + GLenum gl_legacy_texobj_mag_filter; + GLint gl_legacy_texobj_wrap_mode_s; + GLint gl_legacy_texobj_wrap_mode_t; + CoglBool is_foreign; +}; + +CoglTextureRectangle * +_cogl_texture_rectangle_new_from_foreign (GLuint gl_handle, + GLuint width, + GLuint height, + CoglPixelFormat format); + +#endif /* __COGL_TEXTURE_RECTANGLE_H */ diff --git a/cogl/cogl/cogl-texture-rectangle.c b/cogl/cogl/cogl-texture-rectangle.c new file mode 100644 index 000000000..65d2f062b --- /dev/null +++ b/cogl/cogl/cogl-texture-rectangle.c @@ -0,0 +1,781 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-journal-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-error-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +/* These aren't defined under GLES */ +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#endif +#ifndef GL_CLAMP +#define GL_CLAMP 0x2900 +#endif +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +static void _cogl_texture_rectangle_free (CoglTextureRectangle *tex_rect); + +COGL_TEXTURE_DEFINE (TextureRectangle, texture_rectangle); +COGL_GTYPE_DEFINE_CLASS (TextureRectangle, texture_rectangle, + COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); + +static const CoglTextureVtable cogl_texture_rectangle_vtable; + +static CoglBool +can_use_wrap_mode (GLenum wrap_mode) +{ + return (wrap_mode == GL_CLAMP || + wrap_mode == GL_CLAMP_TO_EDGE || + wrap_mode == GL_CLAMP_TO_BORDER); +} + +static void +_cogl_texture_rectangle_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglContext *ctx = tex->context; + + /* Only set the wrap mode if it's different from the current value + to avoid too many GL calls. Texture rectangle doesn't make use of + the r coordinate so we can ignore its wrap mode */ + if (tex_rect->gl_legacy_texobj_wrap_mode_s != wrap_mode_s || + tex_rect->gl_legacy_texobj_wrap_mode_t != wrap_mode_t) + { + g_assert (can_use_wrap_mode (wrap_mode_s)); + g_assert (can_use_wrap_mode (wrap_mode_t)); + + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + tex_rect->gl_texture, + tex_rect->is_foreign); + GE( ctx, glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_S, wrap_mode_s) ); + GE( ctx, glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_T, wrap_mode_t) ); + + tex_rect->gl_legacy_texobj_wrap_mode_s = wrap_mode_s; + tex_rect->gl_legacy_texobj_wrap_mode_t = wrap_mode_t; + } +} + +static void +_cogl_texture_rectangle_free (CoglTextureRectangle *tex_rect) +{ + if (!tex_rect->is_foreign && tex_rect->gl_texture) + _cogl_delete_gl_texture (tex_rect->gl_texture); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_rect)); +} + +static CoglBool +_cogl_texture_rectangle_can_create (CoglContext *ctx, + unsigned int width, + unsigned int height, + CoglPixelFormat internal_format, + CoglError **error) +{ + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE)) + { + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_TYPE, + "The CoglTextureRectangle feature isn't available"); + return FALSE; + } + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!ctx->texture_driver->size_supported (ctx, + GL_TEXTURE_RECTANGLE_ARB, + gl_intformat, + gl_format, + gl_type, + width, + height)) + { + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "The requested texture size + format is unsupported"); + return FALSE; + } + + return TRUE; +} + +static void +_cogl_texture_rectangle_set_auto_mipmap (CoglTexture *tex, + CoglBool value) +{ + /* Rectangle textures currently never support mipmapping so there's + no point in doing anything here */ +} + +static CoglTextureRectangle * +_cogl_texture_rectangle_create_base (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1); + CoglTexture *tex = COGL_TEXTURE (tex_rect); + + _cogl_texture_init (tex, ctx, width, height, + internal_format, loader, + &cogl_texture_rectangle_vtable); + + tex_rect->gl_texture = 0; + tex_rect->is_foreign = FALSE; + + /* We default to GL_LINEAR for both filters */ + tex_rect->gl_legacy_texobj_min_filter = GL_LINEAR; + tex_rect->gl_legacy_texobj_mag_filter = GL_LINEAR; + + /* Wrap mode not yet set */ + tex_rect->gl_legacy_texobj_wrap_mode_s = GL_FALSE; + tex_rect->gl_legacy_texobj_wrap_mode_t = GL_FALSE; + + return _cogl_texture_rectangle_object_new (tex_rect); +} + +CoglTextureRectangle * +cogl_texture_rectangle_new_with_size (CoglContext *ctx, + int width, + int height) +{ + CoglTextureLoader *loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_texture_rectangle_create_base (ctx, width, height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + loader); +} + +static CoglBool +allocate_with_size (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_rect); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GLenum gl_error; + GLenum gl_texture; + + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (!_cogl_texture_rectangle_can_create (ctx, + width, + height, + internal_format, + error)) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + gl_texture = + ctx->texture_driver->gen (ctx, + GL_TEXTURE_RECTANGLE_ARB, + internal_format); + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + gl_texture, + tex_rect->is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_intformat, + width, height, 0, gl_format, gl_type, NULL); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + GE( ctx, glDeleteTextures (1, &gl_texture) ); + return FALSE; + } + + tex_rect->internal_format = internal_format; + + tex_rect->gl_texture = gl_texture; + tex_rect->gl_format = gl_intformat; + + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_bitmap (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_rect); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglBitmap *upload_bmp; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + internal_format = + _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); + + if (!_cogl_texture_rectangle_can_create (ctx, + width, + height, + internal_format, + error)) + return FALSE; + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + cogl_bitmap_get_format (upload_bmp), + NULL, /* internal format */ + &gl_format, + &gl_type); + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + NULL); + + tex_rect->gl_texture = + ctx->texture_driver->gen (ctx, + GL_TEXTURE_RECTANGLE_ARB, + internal_format); + if (!ctx->texture_driver->upload_to_gl (ctx, + GL_TEXTURE_RECTANGLE_ARB, + tex_rect->gl_texture, + FALSE, + upload_bmp, + gl_intformat, + gl_format, + gl_type, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + tex_rect->gl_format = gl_intformat; + tex_rect->internal_format = internal_format; + + cogl_object_unref (upload_bmp); + + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_gl_foreign (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_rect); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; + GLenum gl_error = 0; + GLint gl_compressed = GL_FALSE; + GLenum gl_int_format = 0; + + if (!ctx->texture_driver->allows_foreign_gl_target (ctx, + GL_TEXTURE_RECTANGLE_ARB)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Foreign GL_TEXTURE_RECTANGLE textures are not " + "supported by your system"); + return FALSE; + } + + /* Make sure binding succeeds */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + loader->src.gl_foreign.gl_handle, TRUE); + if (ctx->glGetError () != GL_NO_ERROR) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Failed to bind foreign GL_TEXTURE_RECTANGLE texture"); + return FALSE; + } + + /* Obtain texture parameters */ + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS)) + { + GLint val; + + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_RECTANGLE_ARB, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_RECTANGLE_ARB, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &val) ); + + gl_int_format = val; + + /* If we can query GL for the actual pixel format then we'll ignore + the passed in format and use that. */ + if (!ctx->driver_vtable->pixel_format_from_gl_internal (ctx, + gl_int_format, + &format)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Unsupported internal format for foreign texture"); + return FALSE; + } + } + else +#endif + { + /* Otherwise we'll assume we can derive the GL format from the + passed in format */ + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_int_format, + NULL, + NULL); + } + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Compressed foreign textures aren't currently supported"); + return FALSE; + } + + /* Setup bitmap info */ + tex_rect->is_foreign = TRUE; + + tex_rect->gl_texture = loader->src.gl_foreign.gl_handle; + tex_rect->gl_format = gl_int_format; + + /* Unknown filter */ + tex_rect->gl_legacy_texobj_min_filter = GL_FALSE; + tex_rect->gl_legacy_texobj_mag_filter = GL_FALSE; + + tex_rect->internal_format = format; + + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + format, + loader->src.gl_foreign.width, + loader->src.gl_foreign.height); + + return TRUE; +} + +static CoglBool +_cogl_texture_rectangle_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_rect, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_rect, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_rect, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +CoglTextureRectangle * +cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */ + + return _cogl_texture_rectangle_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + cogl_bitmap_get_format (bmp), + loader); +} + +CoglTextureRectangle * +cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + int width, + int height, + CoglPixelFormat format) +{ + CoglTextureLoader *loader; + + /* NOTE: width, height and internal format are not queriable in + * GLES, hence such a function prototype. Also in the case of full + * opengl the user may be creating a Cogl texture for a + * texture_from_pixmap object where glTexImage2D may not have been + * called and the texture_from_pixmap spec doesn't clarify that it + * is reliable to query back the size from OpenGL. + */ + + /* Assert that it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), NULL); + + /* Validate width and height */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width; + loader->src.gl_foreign.height = height; + loader->src.gl_foreign.format = format; + + return _cogl_texture_rectangle_create_base (ctx, width, height, + format, loader); +} + +static int +_cogl_texture_rectangle_get_max_waste (CoglTexture *tex) +{ + return -1; +} + +static CoglBool +_cogl_texture_rectangle_is_sliced (CoglTexture *tex) +{ + return FALSE; +} + +static CoglBool +_cogl_texture_rectangle_can_hardware_repeat (CoglTexture *tex) +{ + return FALSE; +} + +static void +_cogl_texture_rectangle_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + *s *= tex->width; + *t *= tex->height; +} + +static CoglTransformResult +_cogl_texture_rectangle_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglBool need_repeat = FALSE; + int i; + + for (i = 0; i < 4; i++) + { + if (coords[i] < 0.0f || coords[i] > 1.0f) + need_repeat = TRUE; + coords[i] *= (i & 1) ? tex->height : tex->width; + } + + return (need_repeat ? COGL_TRANSFORM_SOFTWARE_REPEAT + : COGL_TRANSFORM_NO_REPEAT); +} + +static CoglBool +_cogl_texture_rectangle_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + + if (out_gl_handle) + *out_gl_handle = tex_rect->gl_texture; + + if (out_gl_target) + *out_gl_target = GL_TEXTURE_RECTANGLE_ARB; + + return TRUE; +} + +static void +_cogl_texture_rectangle_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglContext *ctx = tex->context; + + if (min_filter == tex_rect->gl_legacy_texobj_min_filter + && mag_filter == tex_rect->gl_legacy_texobj_mag_filter) + return; + + /* Rectangle textures don't support mipmapping */ + g_assert (min_filter == GL_LINEAR || min_filter == GL_NEAREST); + + /* Store new values */ + tex_rect->gl_legacy_texobj_min_filter = min_filter; + tex_rect->gl_legacy_texobj_mag_filter = mag_filter; + + /* Apply new filters to the texture */ + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + tex_rect->gl_texture, + tex_rect->is_foreign); + GE( ctx, glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, + mag_filter) ); + GE( ctx, glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, + min_filter) ); +} + +static void +_cogl_texture_rectangle_pre_paint (CoglTexture *tex, + CoglTexturePrePaintFlags flags) +{ + /* Rectangle textures don't support mipmaps */ + g_assert ((flags & COGL_TEXTURE_NEEDS_MIPMAP) == 0); +} + +static void +_cogl_texture_rectangle_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + +static CoglBool +_cogl_texture_rectangle_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + CoglBitmap *upload_bmp; + GLenum gl_format; + GLenum gl_type; + CoglContext *ctx = tex->context; + CoglBool status; + + upload_bmp = + _cogl_bitmap_convert_for_upload (bmp, + _cogl_texture_get_format (tex), + FALSE, /* can't convert in place */ + error); + if (upload_bmp == NULL) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + cogl_bitmap_get_format (upload_bmp), + NULL, /* internal format */ + &gl_format, + &gl_type); + + /* Send data to GL */ + status = + ctx->texture_driver->upload_subregion_to_gl (ctx, + tex, + FALSE, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + level, + upload_bmp, + gl_format, + gl_type, + error); + + cogl_object_unref (upload_bmp); + + return status; +} + +static CoglBool +_cogl_texture_rectangle_get_data (CoglTexture *tex, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglContext *ctx = tex->context; + int bpp; + GLenum gl_format; + GLenum gl_type; + + bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + NULL, /* internal format */ + &gl_format, + &gl_type); + + ctx->texture_driver->prep_gl_for_pixels_download (ctx, + rowstride, + tex->width, + bpp); + + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + tex_rect->gl_texture, + tex_rect->is_foreign); + return ctx->texture_driver->gl_get_tex_image (ctx, + GL_TEXTURE_RECTANGLE_ARB, + gl_format, + gl_type, + data); +} + +static CoglPixelFormat +_cogl_texture_rectangle_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_RECTANGLE (tex)->internal_format; +} + +static GLenum +_cogl_texture_rectangle_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_RECTANGLE (tex)->gl_format; +} + +static CoglBool +_cogl_texture_rectangle_is_foreign (CoglTexture *tex) +{ + return COGL_TEXTURE_RECTANGLE (tex)->is_foreign; +} + +static CoglTextureType +_cogl_texture_rectangle_get_type (CoglTexture *tex) +{ + return COGL_TEXTURE_TYPE_RECTANGLE; +} + +static const CoglTextureVtable +cogl_texture_rectangle_vtable = + { + TRUE, /* primitive */ + _cogl_texture_rectangle_allocate, + _cogl_texture_rectangle_set_region, + _cogl_texture_rectangle_get_data, + NULL, /* foreach_sub_texture_in_region */ + _cogl_texture_rectangle_get_max_waste, + _cogl_texture_rectangle_is_sliced, + _cogl_texture_rectangle_can_hardware_repeat, + _cogl_texture_rectangle_transform_coords_to_gl, + _cogl_texture_rectangle_transform_quad_coords_to_gl, + _cogl_texture_rectangle_get_gl_texture, + _cogl_texture_rectangle_gl_flush_legacy_texobj_filters, + _cogl_texture_rectangle_pre_paint, + _cogl_texture_rectangle_ensure_non_quad_rendering, + _cogl_texture_rectangle_gl_flush_legacy_texobj_wrap_modes, + _cogl_texture_rectangle_get_format, + _cogl_texture_rectangle_get_gl_format, + _cogl_texture_rectangle_get_type, + _cogl_texture_rectangle_is_foreign, + _cogl_texture_rectangle_set_auto_mipmap + }; diff --git a/cogl/cogl/cogl-texture-rectangle.h b/cogl/cogl/cogl-texture-rectangle.h new file mode 100644 index 000000000..761968608 --- /dev/null +++ b/cogl/cogl/cogl-texture-rectangle.h @@ -0,0 +1,218 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_TEXURE_RECTANGLE_H +#define __COGL_TEXURE_RECTANGLE_H + +#include "cogl-context.h" + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-texture-rectangle + * @short_description: Functions for creating and manipulating rectangle + * textures for use with non-normalized coordinates. + * + * These functions allow low-level "rectangle" textures to be allocated. + * These textures are never constrained to power-of-two sizes but they + * also don't support having a mipmap and can only be wrapped with + * %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE. + * + * The most notable difference between rectangle textures and 2D + * textures is that rectangle textures are sampled using un-normalized + * texture coordinates, so instead of using coordinates (0,0) and + * (1,1) to map to the top-left and bottom right corners of the + * texture you would instead use (0,0) and (width,height). + * + * The use of non-normalized coordinates can be particularly + * convenient when writing glsl shaders that use a texture as a lookup + * table since you don't need to upload separate uniforms to map + * normalized coordinates to texels. + * + * If you want to sample from a rectangle texture from GLSL you should + * use the sampler2DRect sampler type. + * + * Applications wanting to use #CoglTextureRectangle should first check + * for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature using + * cogl_has_feature(). + */ + +typedef struct _CoglTextureRectangle CoglTextureRectangle; +#define COGL_TEXTURE_RECTANGLE(X) ((CoglTextureRectangle *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_rectangle_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_rectangle_get_gtype (void); +#endif + +/** + * cogl_is_texture_rectangle: + * @object: A #CoglObject + * + * Gets whether the given object references an existing + * #CoglTextureRectangle object. + * + * Return value: %TRUE if the object references a + * #CoglTextureRectangle, %FALSE otherwise. + */ +CoglBool +cogl_is_texture_rectangle (void *object); + +/** + * cogl_texture_rectangle_new_with_size: + * @ctx: A #CoglContext pointer + * @width: The texture width to allocate + * @height: The texture height to allocate + * + * Creates a new #CoglTextureRectangle texture with a given @width, + * and @height. This texture is a low-level texture that the GPU can + * sample from directly unlike high-level textures such as + * #CoglTexture2DSliced and #CoglAtlasTexture. + * + * Unlike for #CoglTexture2D textures, coordinates for + * #CoglTextureRectangle textures should not be normalized. So instead + * of using the coordinate (1, 1) to sample the bottom right corner of + * a rectangle texture you would use (@width, @height) where @width + * and @height are the width and height of the texture. + * + * If you want to sample from a rectangle texture from GLSL you + * should use the sampler2DRect sampler type. + * + * Applications wanting to use #CoglTextureRectangle should + * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature + * using cogl_has_feature(). + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * + * Returns value: (transfer full): A pointer to a new #CoglTextureRectangle + * object with no storage allocated yet. + * + * Since: 1.10 + * Stability: unstable + */ +CoglTextureRectangle * +cogl_texture_rectangle_new_with_size (CoglContext *ctx, + int width, + int height); + +/** + * cogl_texture_rectangle_new_from_bitmap: + * @bitmap: A #CoglBitmap + * + * Allocates a new #CoglTextureRectangle texture which will be + * initialized with the pixel data from @bitmap. This texture is a + * low-level texture that the GPU can sample from directly unlike + * high-level textures such as #CoglTexture2DSliced and + * #CoglAtlasTexture. + * + * Unlike for #CoglTexture2D textures, coordinates for + * #CoglTextureRectangle textures should not be normalized. So instead + * of using the coordinate (1, 1) to sample the bottom right corner of + * a rectangle texture you would use (@width, @height) where @width + * and @height are the width and height of the texture. + * + * If you want to sample from a rectangle texture from GLSL you + * should use the sampler2DRect sampler type. + * + * Applications wanting to use #CoglTextureRectangle should + * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature + * using cogl_has_feature(). + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * + * Return value: (transfer full): A pointer to a new + * #CoglTextureRectangle texture. + * Since: 2.0 + * Stability: unstable + */ +CoglTextureRectangle * +cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bitmap); + +/** + * cogl_texture_rectangle_new_from_foreign: + * @ctx: A #CoglContext + * @gl_handle: A GL handle for a GL_TEXTURE_RECTANGLE texture object + * @width: Width of the foreign GL texture + * @height: Height of the foreign GL texture + * @format: The format of the texture + * + * Wraps an existing GL_TEXTURE_RECTANGLE texture object as a + * #CoglTextureRectangle. This can be used for integrating Cogl with + * software using OpenGL directly. + * + * Unlike for #CoglTexture2D textures, coordinates for + * #CoglTextureRectangle textures should not be normalized. So instead + * of using the coordinate (1, 1) to sample the bottom right corner of + * a rectangle texture you would use (@width, @height) where @width + * and @height are the width and height of the texture. + * + * The results are undefined for passing an invalid @gl_handle + * or if @width or @height don't have the correct texture + * geometry. + * + * If you want to sample from a rectangle texture from GLSL you + * should use the sampler2DRect sampler type. + * + * Applications wanting to use #CoglTextureRectangle should + * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature + * using cogl_has_feature(). + * + * The texture is still configurable until it has been allocated so + * for example you can declare whether the texture is premultiplied + * with cogl_texture_set_premultiplied(). + * + * Return value: (transfer full): A new #CoglTextureRectangle texture + */ +CoglTextureRectangle * +cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + int width, + int height, + CoglPixelFormat format); + +COGL_END_DECLS + +#endif /* __COGL_TEXURE_RECTANGLE_H */ diff --git a/cogl/cogl/cogl-texture.c b/cogl/cogl/cogl-texture.c new file mode 100644 index 000000000..d93db2276 --- /dev/null +++ b/cogl/cogl/cogl-texture.c @@ -0,0 +1,1540 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * Copyright (C) 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-bitmap-private.h" +#include "cogl-buffer-private.h" +#include "cogl-pixel-buffer-private.h" +#include "cogl-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-driver.h" +#include "cogl-texture-2d-sliced-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-2d-gl.h" +#include "cogl-texture-3d-private.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-atlas-texture-private.h" +#include "cogl-pipeline.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-object-private.h" +#include "cogl-primitives.h" +#include "cogl-framebuffer-private.h" +#include "cogl1-context.h" +#include "cogl-sub-texture.h" +#include "cogl-primitive-texture.h" +#include "cogl-error-private.h" +#include "cogl-gtype-private.h" + +#include +#include +#include + +/* This isn't defined in the GLES headers */ +#ifndef GL_RED +#define GL_RED 0x1903 +#endif + +COGL_GTYPE_DEFINE_INTERFACE (Texture, texture); + +uint32_t +cogl_texture_error_quark (void) +{ + return g_quark_from_static_string ("cogl-texture-error-quark"); +} + +/* XXX: + * The CoglObject macros don't support any form of inheritance, so for + * now we implement the CoglObject support for the CoglTexture + * abstract class manually. + */ + +static GSList *_cogl_texture_types; + +void +_cogl_texture_register_texture_type (const CoglObjectClass *klass) +{ + _cogl_texture_types = g_slist_prepend (_cogl_texture_types, (void *) klass); +} + +CoglBool +cogl_is_texture (void *object) +{ + CoglObject *obj = (CoglObject *)object; + GSList *l; + + if (object == NULL) + return FALSE; + + for (l = _cogl_texture_types; l; l = l->next) + if (l->data == obj->klass) + return TRUE; + + return FALSE; +} + +void +_cogl_texture_init (CoglTexture *texture, + CoglContext *context, + int width, + int height, + CoglPixelFormat src_format, + CoglTextureLoader *loader, + const CoglTextureVtable *vtable) +{ + texture->context = context; + texture->max_level = 0; + texture->width = width; + texture->height = height; + texture->allocated = FALSE; + texture->vtable = vtable; + texture->framebuffers = NULL; + + texture->loader = loader; + + _cogl_texture_set_internal_format (texture, src_format); + + /* Although we want to initialize texture::components according + * to the source format, we always want the internal layout to + * be considered premultiplied by default. + * + * NB: this ->premultiplied state is user configurable so to avoid + * awkward documentation, setting this to 'true' does not depend on + * ->components having an alpha component (we will simply ignore the + * premultiplied status later if there is no alpha component). + * This way we don't have to worry about updating the + * ->premultiplied state in _set_components(). Similarly we don't + * have to worry about updating the ->components state in + * _set_premultiplied(). + */ + texture->premultiplied = TRUE; +} + +static void +_cogl_texture_free_loader (CoglTexture *texture) +{ + if (texture->loader) + { + CoglTextureLoader *loader = texture->loader; + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + break; + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + cogl_object_unref (loader->src.bitmap.bitmap); + break; + } + g_slice_free (CoglTextureLoader, loader); + texture->loader = NULL; + } +} + +CoglTextureLoader * +_cogl_texture_create_loader (void) +{ + return g_slice_new0 (CoglTextureLoader); +} + +void +_cogl_texture_free (CoglTexture *texture) +{ + _cogl_texture_free_loader (texture); + + g_free (texture); +} + +CoglBool +_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, + CoglPixelFormat dst_format) +{ + return ((src_format & dst_format & COGL_A_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8 && + dst_format != COGL_PIXEL_FORMAT_A_8 && + (src_format & COGL_PREMULT_BIT) != + (dst_format & COGL_PREMULT_BIT)); +} + +CoglBool +_cogl_texture_is_foreign (CoglTexture *texture) +{ + if (texture->vtable->is_foreign) + return texture->vtable->is_foreign (texture); + else + return FALSE; +} + +unsigned int +cogl_texture_get_width (CoglTexture *texture) +{ + return texture->width; +} + +unsigned int +cogl_texture_get_height (CoglTexture *texture) +{ + return texture->height; +} + +CoglPixelFormat +_cogl_texture_get_format (CoglTexture *texture) +{ + if (!texture->allocated) + cogl_texture_allocate (texture, NULL); + return texture->vtable->get_format (texture); +} + +int +cogl_texture_get_max_waste (CoglTexture *texture) +{ + return texture->vtable->get_max_waste (texture); +} + +int +_cogl_texture_get_n_levels (CoglTexture *texture) +{ + int width = cogl_texture_get_width (texture); + int height = cogl_texture_get_height (texture); + int max_dimension = MAX (width, height); + + if (cogl_is_texture_3d (texture)) + { + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (texture); + max_dimension = MAX (max_dimension, tex_3d->depth); + } + + return _cogl_util_fls (max_dimension); +} + +void +_cogl_texture_get_level_size (CoglTexture *texture, + int level, + int *width, + int *height, + int *depth) +{ + int current_width = cogl_texture_get_width (texture); + int current_height = cogl_texture_get_height (texture); + int current_depth; + int i; + + if (cogl_is_texture_3d (texture)) + { + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (texture); + current_depth = tex_3d->depth; + } + else + current_depth = 0; + + /* NB: The OpenGL spec (like D3D) uses a floor() convention to + * round down the size of a mipmap level when dividing the size + * of the previous level results in a fraction... + */ + for (i = 0; i < level; i++) + { + current_width = MAX (1, current_width >> 1); + current_height = MAX (1, current_height >> 1); + current_depth = MAX (1, current_depth >> 1); + } + + if (width) + *width = current_width; + if (height) + *height = current_height; + if (depth) + *depth = current_depth; +} + +CoglBool +cogl_texture_is_sliced (CoglTexture *texture) +{ + if (!texture->allocated) + cogl_texture_allocate (texture, NULL); + return texture->vtable->is_sliced (texture); +} + +/* If this returns FALSE, that implies _foreach_sub_texture_in_region + * will be needed to iterate over multiple sub textures for regions whos + * texture coordinates extend out of the range [0,1] + */ +CoglBool +_cogl_texture_can_hardware_repeat (CoglTexture *texture) +{ + if (!texture->allocated) + cogl_texture_allocate (texture, NULL); + return texture->vtable->can_hardware_repeat (texture); +} + +/* NB: You can't use this with textures comprised of multiple sub textures (use + * cogl_texture_is_sliced() to check) since coordinate transformation for such + * textures will be different for each slice. */ +void +_cogl_texture_transform_coords_to_gl (CoglTexture *texture, + float *s, + float *t) +{ + texture->vtable->transform_coords_to_gl (texture, s, t); +} + +CoglTransformResult +_cogl_texture_transform_quad_coords_to_gl (CoglTexture *texture, + float *coords) +{ + return texture->vtable->transform_quad_coords_to_gl (texture, coords); +} + +CoglBool +cogl_texture_get_gl_texture (CoglTexture *texture, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + if (!texture->allocated) + cogl_texture_allocate (texture, NULL); + + return texture->vtable->get_gl_texture (texture, + out_gl_handle, out_gl_target); +} + +CoglTextureType +_cogl_texture_get_type (CoglTexture *texture) +{ + return texture->vtable->get_type (texture); +} + +void +_cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags) +{ + /* Assert that the storage for the texture exists already if we're + * about to reference it for painting. + * + * Note: we abort on error here since it's a bit late to do anything + * about it if we fail to allocate the texture and the app could + * have explicitly allocated the texture earlier to handle problems + * gracefully. + * + * XXX: Maybe it could even be considered a programmer error if the + * texture hasn't been allocated by this point since it implies we + * are abount to paint with undefined texture contents? + */ + cogl_texture_allocate (texture, NULL); + + texture->vtable->pre_paint (texture, flags); +} + +void +_cogl_texture_ensure_non_quad_rendering (CoglTexture *texture) +{ + texture->vtable->ensure_non_quad_rendering (texture); +} + +CoglBool +_cogl_texture_set_region_from_bitmap (CoglTexture *texture, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bmp, + int dst_x, + int dst_y, + int level, + CoglError **error) +{ + _COGL_RETURN_VAL_IF_FAIL ((cogl_bitmap_get_width (bmp) - src_x) + >= width, FALSE); + _COGL_RETURN_VAL_IF_FAIL ((cogl_bitmap_get_height (bmp) - src_y) + >= height, FALSE); + _COGL_RETURN_VAL_IF_FAIL (width > 0, FALSE); + _COGL_RETURN_VAL_IF_FAIL (height > 0, FALSE); + + /* Assert that the storage for this texture has been allocated */ + if (!cogl_texture_allocate (texture, error)) + return FALSE; + + /* Note that we don't prepare the bitmap for upload here because + some backends may be internally using a different format for the + actual GL texture than that reported by + _cogl_texture_get_format. For example the atlas textures are + always stored in an RGBA texture even if the texture format is + advertised as RGB. */ + + return texture->vtable->set_region (texture, + src_x, src_y, + dst_x, dst_y, + width, height, + level, + bmp, + error); +} + +CoglBool +cogl_texture_set_region_from_bitmap (CoglTexture *texture, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + CoglBitmap *bitmap) +{ + CoglError *ignore_error = NULL; + CoglBool status = + _cogl_texture_set_region_from_bitmap (texture, + src_x, src_y, + dst_width, dst_height, + bitmap, + dst_x, dst_y, + 0, /* level */ + &ignore_error); + + if (!status) + cogl_error_free (ignore_error); + return status; +} + +CoglBool +_cogl_texture_set_region (CoglTexture *texture, + int width, + int height, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + int dst_x, + int dst_y, + int level, + CoglError **error) +{ + CoglContext *ctx = texture->context; + CoglBitmap *source_bmp; + CoglBool ret; + + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, FALSE); + + /* Rowstride from width if none specified */ + if (rowstride == 0) + rowstride = _cogl_pixel_format_get_bytes_per_pixel (format) * width; + + /* Init source bitmap */ + source_bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + ret = _cogl_texture_set_region_from_bitmap (texture, + 0, 0, + width, height, + source_bmp, + dst_x, dst_y, + level, + error); + + cogl_object_unref (source_bmp); + + return ret; +} + +CoglBool +cogl_texture_set_region (CoglTexture *texture, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const uint8_t *data) +{ + CoglError *ignore_error = NULL; + const uint8_t *first_pixel; + int bytes_per_pixel = _cogl_pixel_format_get_bytes_per_pixel (format); + CoglBool status; + + /* Rowstride from width if none specified */ + if (rowstride == 0) + rowstride = bytes_per_pixel * width; + + first_pixel = data + rowstride * src_y + bytes_per_pixel * src_x; + + status = _cogl_texture_set_region (texture, + dst_width, + dst_height, + format, + rowstride, + first_pixel, + dst_x, + dst_y, + 0, + &ignore_error); + if (!status) + cogl_error_free (ignore_error); + return status; +} + +CoglBool +cogl_texture_set_data (CoglTexture *texture, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + int level, + CoglError **error) +{ + int level_width; + int level_height; + + _cogl_texture_get_level_size (texture, + level, + &level_width, + &level_height, + NULL); + + return _cogl_texture_set_region (texture, + level_width, + level_height, + format, + rowstride, + data, + 0, 0, /* dest x, y */ + level, + error); +} + +/* Reads back the contents of a texture by rendering it to the framebuffer + * and reading back the resulting pixels. + * + * It will perform multiple renders if the texture is larger than the + * current glViewport. + * + * It assumes the projection and modelview have already been setup so + * that rendering to 0,0 with the same width and height of the viewport + * will exactly cover the viewport. + * + * NB: Normally this approach isn't normally used since we can just use + * glGetTexImage, but may be used as a fallback in some circumstances. + */ +static CoglBool +do_texture_draw_and_read (CoglFramebuffer *fb, + CoglPipeline *pipeline, + CoglTexture *texture, + CoglBitmap *target_bmp, + float *viewport, + CoglError **error) +{ + float rx1, ry1; + float rx2, ry2; + float tx1, ty1; + float tx2, ty2; + int bw, bh; + CoglBitmap *rect_bmp; + unsigned int tex_width, tex_height; + CoglContext *ctx = fb->context; + + tex_width = cogl_texture_get_width (texture); + tex_height = cogl_texture_get_height (texture); + + ry2 = 0; + ty2 = 0; + + /* Walk Y axis until whole bitmap height consumed */ + for (bh = tex_height; bh > 0; bh -= viewport[3]) + { + /* Rectangle Y coords */ + ry1 = ry2; + ry2 += (bh < viewport[3]) ? bh : viewport[3]; + + /* Normalized texture Y coords */ + ty1 = ty2; + ty2 = (ry2 / (float) tex_height); + + rx2 = 0; + tx2 = 0; + + /* Walk X axis until whole bitmap width consumed */ + for (bw = tex_width; bw > 0; bw-=viewport[2]) + { + int width; + int height; + + /* Rectangle X coords */ + rx1 = rx2; + rx2 += (bw < viewport[2]) ? bw : viewport[2]; + + width = rx2 - rx1; + height = ry2 - ry1; + + /* Normalized texture X coords */ + tx1 = tx2; + tx2 = (rx2 / (float) tex_width); + + /* Draw a portion of texture */ + cogl_framebuffer_draw_textured_rectangle (fb, + pipeline, + 0, 0, + rx2 - rx1, + ry2 - ry1, + tx1, ty1, + tx2, ty2); + + /* Read into a temporary bitmap */ + rect_bmp = _cogl_bitmap_new_with_malloc_buffer + (ctx, + width, height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + error); + if (!rect_bmp) + return FALSE; + + if (!_cogl_framebuffer_read_pixels_into_bitmap + (fb, + viewport[0], viewport[1], + COGL_READ_PIXELS_COLOR_BUFFER, + rect_bmp, + error)) + { + cogl_object_unref (rect_bmp); + return FALSE; + } + + /* Copy to target bitmap */ + if (!_cogl_bitmap_copy_subregion (rect_bmp, + target_bmp, + 0, 0, + rx1, ry1, + width, + height, + error)) + { + cogl_object_unref (rect_bmp); + return FALSE; + } + + /* Free temp bitmap */ + cogl_object_unref (rect_bmp); + } + } + + return TRUE; +} + +/* Reads back the contents of a texture by rendering it to the framebuffer + * and reading back the resulting pixels. + * + * NB: Normally this approach isn't normally used since we can just use + * glGetTexImage, but may be used as a fallback in some circumstances. + */ +static CoglBool +_cogl_texture_draw_and_read (CoglTexture *texture, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type, + CoglError **error) +{ + CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); + CoglContext *ctx = framebuffer->context; + float save_viewport[4]; + float viewport[4]; + CoglBool status = FALSE; + + viewport[0] = 0; + viewport[1] = 0; + viewport[2] = cogl_framebuffer_get_width (framebuffer); + viewport[3] = cogl_framebuffer_get_height (framebuffer); + + cogl_framebuffer_get_viewport4fv (framebuffer, save_viewport); + _cogl_framebuffer_push_projection (framebuffer); + cogl_framebuffer_orthographic (framebuffer, + 0, 0, + viewport[2], + viewport[3], + 0, 100); + + cogl_framebuffer_push_matrix (framebuffer); + cogl_framebuffer_identity_matrix (framebuffer); + + /* Direct copy operation */ + + if (ctx->texture_download_pipeline == NULL) + { + ctx->texture_download_pipeline = cogl_pipeline_new (ctx); + cogl_pipeline_set_blend (ctx->texture_download_pipeline, + "RGBA = ADD (SRC_COLOR, 0)", + NULL); + } + + cogl_pipeline_set_layer_texture (ctx->texture_download_pipeline, 0, texture); + + cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE)", + NULL); + + cogl_pipeline_set_layer_filters (ctx->texture_download_pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + if (!do_texture_draw_and_read (framebuffer, + ctx->texture_download_pipeline, + texture, target_bmp, viewport, + error)) + return FALSE; + + /* XXX: As an alleged PowerVR driver bug workaround where the driver + * is apparently not maintaining the alpha component of some + * framebuffers we render the alpha component of the texture + * separately to be sure we retrieve all components of the texture. + * + * TODO: verify if this is still an issue + */ + if ((_cogl_texture_get_format (texture) & COGL_A_BIT)/* && a_bits == 0*/) + { + uint8_t *srcdata; + uint8_t *dstdata; + uint8_t *srcpixel; + uint8_t *dstpixel; + int target_width = cogl_bitmap_get_width (target_bmp); + int target_height = cogl_bitmap_get_height (target_bmp); + int target_rowstride = cogl_bitmap_get_rowstride (target_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (COGL_PIXEL_FORMAT_RGBA_8888); + int alpha_rowstride = bpp * target_width; + CoglBitmap *alpha_bmp; + int x,y; + + if ((dstdata = _cogl_bitmap_map (target_bmp, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + error)) == NULL) + goto EXIT; + + /* Create temp bitmap for alpha values */ + alpha_bmp = + _cogl_bitmap_new_with_malloc_buffer (ctx, + target_width, + target_height, + COGL_PIXEL_FORMAT_RGBA_8888, + error); + if (!alpha_bmp) + { + _cogl_bitmap_unmap (target_bmp); + goto EXIT; + } + + + /* Draw alpha values into RGB channels */ + cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE[A])", + NULL); + + if (!do_texture_draw_and_read (framebuffer, + ctx->texture_download_pipeline, + texture, alpha_bmp, viewport, + error)) + { + cogl_object_unref (alpha_bmp); + _cogl_bitmap_unmap (target_bmp); + goto EXIT; + } + + /* Copy temp R to target A */ + + /* Note: we don't try to catch errors since "mapping" an + * malloc buffer should never fail */ + srcdata = _cogl_bitmap_map (alpha_bmp, + COGL_BUFFER_ACCESS_READ, + 0 /* hints */, + NULL); + + for (y=0; ycontext; + CoglOffscreen *offscreen; + CoglFramebuffer *framebuffer; + CoglBitmap *bitmap; + CoglBool ret; + CoglError *ignore_error = NULL; + CoglPixelFormat real_format; + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + return FALSE; + + offscreen = _cogl_offscreen_new_with_texture_full + (sub_texture, + COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, + 0); + + framebuffer = COGL_FRAMEBUFFER (offscreen); + if (!cogl_framebuffer_allocate (framebuffer, &ignore_error)) + { + cogl_error_free (ignore_error); + return FALSE; + } + + /* Currently the framebuffer's internal format corresponds to the + * internal format of @sub_texture but in the case of atlas textures + * it's possible that this format doesn't reflect the correct + * premultiplied alpha status or what components are valid since + * atlas textures are always stored in a shared texture with a + * format of _RGBA_8888. + * + * Here we override the internal format to make sure the + * framebuffer's internal format matches the internal format of the + * parent meta_texture instead. + */ + real_format = _cogl_texture_get_format (meta_texture); + _cogl_framebuffer_set_internal_format (framebuffer, real_format); + + bitmap = cogl_bitmap_new_for_data (ctx, + width, height, + closest_format, + dst_rowstride, + dst_bits); + ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, + COGL_READ_PIXELS_COLOR_BUFFER, + bitmap, + &ignore_error); + + if (!ret) + cogl_error_free (ignore_error); + + cogl_object_unref (bitmap); + + cogl_object_unref (framebuffer); + + return ret; +} + +static CoglBool +get_texture_bits_via_copy (CoglTexture *texture, + int x, + int y, + int width, + int height, + uint8_t *dst_bits, + unsigned int dst_rowstride, + CoglPixelFormat dst_format) +{ + unsigned int full_rowstride; + uint8_t *full_bits; + CoglBool ret = TRUE; + int bpp; + int full_tex_width, full_tex_height; + + full_tex_width = cogl_texture_get_width (texture); + full_tex_height = cogl_texture_get_height (texture); + + bpp = _cogl_pixel_format_get_bytes_per_pixel (dst_format); + + full_rowstride = bpp * full_tex_width; + full_bits = g_malloc (full_rowstride * full_tex_height); + + if (texture->vtable->get_data (texture, + dst_format, + full_rowstride, + full_bits)) + { + uint8_t *dst = dst_bits; + uint8_t *src = full_bits + x * bpp + y * full_rowstride; + int i; + + for (i = 0; i < height; i++) + { + memcpy (dst, src, bpp * width); + dst += dst_rowstride; + src += full_rowstride; + } + } + else + ret = FALSE; + + g_free (full_bits); + + return ret; +} + +typedef struct +{ + CoglTexture *meta_texture; + int orig_width; + int orig_height; + CoglBitmap *target_bmp; + uint8_t *target_bits; + CoglBool success; + CoglError *error; +} CoglTextureGetData; + +static void +texture_get_cb (CoglTexture *subtexture, + const float *subtexture_coords, + const float *virtual_coords, + void *user_data) +{ + CoglTextureGetData *tg_data = user_data; + CoglTexture *meta_texture = tg_data->meta_texture; + CoglPixelFormat closest_format = cogl_bitmap_get_format (tg_data->target_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (closest_format); + unsigned int rowstride = cogl_bitmap_get_rowstride (tg_data->target_bmp); + int subtexture_width = cogl_texture_get_width (subtexture); + int subtexture_height = cogl_texture_get_height (subtexture); + + int x_in_subtexture = (int) (0.5 + subtexture_width * subtexture_coords[0]); + int y_in_subtexture = (int) (0.5 + subtexture_height * subtexture_coords[1]); + int width = ((int) (0.5 + subtexture_width * subtexture_coords[2]) + - x_in_subtexture); + int height = ((int) (0.5 + subtexture_height * subtexture_coords[3]) + - y_in_subtexture); + int x_in_bitmap = (int) (0.5 + tg_data->orig_width * virtual_coords[0]); + int y_in_bitmap = (int) (0.5 + tg_data->orig_height * virtual_coords[1]); + + uint8_t *dst_bits; + + if (!tg_data->success) + return; + + dst_bits = tg_data->target_bits + x_in_bitmap * bpp + y_in_bitmap * rowstride; + + /* If we can read everything as a single slice, then go ahead and do that + * to avoid allocating an FBO. We'll leave it up to the GL implementation to + * do glGetTexImage as efficiently as possible. (GLES doesn't have that, + * so we'll fall through) + */ + if (x_in_subtexture == 0 && y_in_subtexture == 0 && + width == subtexture_width && height == subtexture_height) + { + if (subtexture->vtable->get_data (subtexture, + closest_format, + rowstride, + dst_bits)) + return; + } + + /* Next best option is a FBO and glReadPixels */ + if (get_texture_bits_via_offscreen (meta_texture, + subtexture, + x_in_subtexture, y_in_subtexture, + width, height, + dst_bits, + rowstride, + closest_format)) + return; + + /* Getting ugly: read the entire texture, copy out the part we want */ + if (get_texture_bits_via_copy (subtexture, + x_in_subtexture, y_in_subtexture, + width, height, + dst_bits, + rowstride, + closest_format)) + return; + + /* No luck, the caller will fall back to the draw-to-backbuffer and + * read implementation */ + tg_data->success = FALSE; +} + +int +cogl_texture_get_data (CoglTexture *texture, + CoglPixelFormat format, + unsigned int rowstride, + uint8_t *data) +{ + CoglContext *ctx = texture->context; + int bpp; + int byte_size; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap *target_bmp; + int tex_width; + int tex_height; + CoglPixelFormat texture_format; + CoglError *ignore_error = NULL; + + CoglTextureGetData tg_data; + + texture_format = _cogl_texture_get_format (texture); + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = texture_format; + + tex_width = cogl_texture_get_width (texture); + tex_height = cogl_texture_get_height (texture); + + /* Rowstride from texture width if none specified */ + bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + if (rowstride == 0) + rowstride = tex_width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex_height * rowstride; + if (data == NULL) + return byte_size; + + closest_format = + ctx->texture_driver->find_best_gl_get_data_format (ctx, + format, + &closest_gl_format, + &closest_gl_type); + + /* We can assume that whatever data GL gives us will have the + premult status of the original texture */ + if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (closest_format)) + closest_format = ((closest_format & ~COGL_PREMULT_BIT) | + (texture_format & COGL_PREMULT_BIT)); + + /* If the application is requesting a conversion from a + * component-alpha texture and the driver doesn't support them + * natively then we can only read into an alpha-format buffer. In + * this case the driver will be faking the alpha textures with a + * red-component texture and it won't swizzle to the correct format + * while reading */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES)) + { + if (texture_format == COGL_PIXEL_FORMAT_A_8) + { + closest_format = COGL_PIXEL_FORMAT_A_8; + closest_gl_format = GL_RED; + closest_gl_type = GL_UNSIGNED_BYTE; + } + else if (format == COGL_PIXEL_FORMAT_A_8) + { + /* If we are converting to a component-alpha texture then we + * need to read all of the components to a temporary buffer + * because there is no way to get just the 4th component. + * Note: it doesn't matter whether the texture is + * pre-multiplied here because we're only going to look at + * the alpha component */ + closest_format = COGL_PIXEL_FORMAT_RGBA_8888; + closest_gl_format = GL_RGBA; + closest_gl_type = GL_UNSIGNED_BYTE; + } + } + + /* Is the requested format supported? */ + if (closest_format == format) + /* Target user data directly */ + target_bmp = cogl_bitmap_new_for_data (ctx, + tex_width, + tex_height, + format, + rowstride, + data); + else + { + target_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, + tex_width, tex_height, + closest_format, + &ignore_error); + if (!target_bmp) + { + cogl_error_free (ignore_error); + return 0; + } + } + + tg_data.target_bits = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + &ignore_error); + if (tg_data.target_bits) + { + tg_data.meta_texture = texture; + tg_data.orig_width = tex_width; + tg_data.orig_height = tex_height; + tg_data.target_bmp = target_bmp; + tg_data.error = NULL; + tg_data.success = TRUE; + + /* If there are any dependent framebuffers on the texture then we + need to flush their journals so the texture contents will be + up-to-date */ + _cogl_texture_flush_journal_rendering (texture); + + /* Iterating through the subtextures allows piecing together + * the data for a sliced texture, and allows us to do the + * read-from-framebuffer logic here in a simple fashion rather than + * passing offsets down through the code. */ + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + texture_get_cb, + &tg_data); + + _cogl_bitmap_unmap (target_bmp); + } + else + { + cogl_error_free (ignore_error); + tg_data.success = FALSE; + } + + /* XXX: In some cases _cogl_texture_2d_download_from_gl may fail + * to read back the texture data; such as for GLES which doesn't + * support glGetTexImage, so here we fallback to drawing the + * texture and reading the pixels from the framebuffer. */ + if (!tg_data.success) + { + if (!_cogl_texture_draw_and_read (texture, target_bmp, + closest_gl_format, + closest_gl_type, + &ignore_error)) + { + /* We have no more fallbacks so we just give up and + * hope for the best */ + g_warning ("Failed to read texture since draw-and-read " + "fallback failed: %s", ignore_error->message); + cogl_error_free (ignore_error); + cogl_object_unref (target_bmp); + return 0; + } + } + + /* Was intermediate used? */ + if (closest_format != format) + { + CoglBitmap *new_bmp; + CoglBool result; + CoglError *error = NULL; + + /* Convert to requested format directly into the user's buffer */ + new_bmp = cogl_bitmap_new_for_data (ctx, + tex_width, tex_height, + format, + rowstride, + data); + result = _cogl_bitmap_convert_into_bitmap (target_bmp, new_bmp, &error); + + if (!result) + { + cogl_error_free (error); + /* Return failure after cleaning up */ + byte_size = 0; + } + + cogl_object_unref (new_bmp); + } + + cogl_object_unref (target_bmp); + + return byte_size; +} + +static void +_cogl_texture_framebuffer_destroy_cb (void *user_data, + void *instance) +{ + CoglTexture *tex = user_data; + CoglFramebuffer *framebuffer = instance; + + tex->framebuffers = g_list_remove (tex->framebuffers, framebuffer); +} + +void +_cogl_texture_associate_framebuffer (CoglTexture *texture, + CoglFramebuffer *framebuffer) +{ + static CoglUserDataKey framebuffer_destroy_notify_key; + + /* Note: we don't take a reference on the framebuffer here because + * that would introduce a circular reference. */ + texture->framebuffers = g_list_prepend (texture->framebuffers, framebuffer); + + /* Since we haven't taken a reference on the framebuffer we setup + * some private data so we will be notified if it is destroyed... */ + _cogl_object_set_user_data (COGL_OBJECT (framebuffer), + &framebuffer_destroy_notify_key, + texture, + _cogl_texture_framebuffer_destroy_cb); +} + +const GList * +_cogl_texture_get_associated_framebuffers (CoglTexture *texture) +{ + return texture->framebuffers; +} + +void +_cogl_texture_flush_journal_rendering (CoglTexture *texture) +{ + GList *l; + + /* It could be that a referenced texture is part of a framebuffer + * which has an associated journal that must be flushed before it + * can be sampled from by the current primitive... */ + for (l = texture->framebuffers; l; l = l->next) + _cogl_framebuffer_flush_journal (l->data); +} + +/* This function lets you define a meta texture as a grid of textures + * whereby the x and y grid-lines are defined by an array of + * CoglSpans. With that grid based description this function can then + * iterate all the cells of the grid that lye within a region + * specified as virtual, meta-texture, coordinates. This function can + * also cope with regions that extend beyond the original meta-texture + * grid by iterating cells repeatedly according to the wrap_x/y + * arguments. + * + * To differentiate between texture coordinates of a specific, real, + * slice texture and the texture coordinates of a composite, meta + * texture, the coordinates of the meta texture are called "virtual" + * coordinates and the coordinates of spans are called "slice" + * coordinates. + * + * Note: no guarantee is given about the order in which the slices + * will be visited. + * + * Note: The slice coordinates passed to @callback are always + * normalized coordinates even if the span coordinates aren't + * normalized. + */ +void +_cogl_texture_spans_foreach_in_region (CoglSpan *x_spans, + int n_x_spans, + CoglSpan *y_spans, + int n_y_spans, + CoglTexture **textures, + float *virtual_coords, + float x_normalize_factor, + float y_normalize_factor, + CoglPipelineWrapMode wrap_x, + CoglPipelineWrapMode wrap_y, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglSpanIter iter_x; + CoglSpanIter iter_y; + float slice_coords[4]; + float span_virtual_coords[4]; + + /* Iterate the y axis of the virtual rectangle */ + for (_cogl_span_iter_begin (&iter_y, + y_spans, + n_y_spans, + y_normalize_factor, + virtual_coords[1], + virtual_coords[3], + wrap_y); + !_cogl_span_iter_end (&iter_y); + _cogl_span_iter_next (&iter_y)) + { + if (iter_y.flipped) + { + slice_coords[1] = iter_y.intersect_end; + slice_coords[3] = iter_y.intersect_start; + span_virtual_coords[1] = iter_y.intersect_end; + span_virtual_coords[3] = iter_y.intersect_start; + } + else + { + slice_coords[1] = iter_y.intersect_start; + slice_coords[3] = iter_y.intersect_end; + span_virtual_coords[1] = iter_y.intersect_start; + span_virtual_coords[3] = iter_y.intersect_end; + } + + /* Map the current intersection to normalized slice coordinates */ + slice_coords[1] = (slice_coords[1] - iter_y.pos) / iter_y.span->size; + slice_coords[3] = (slice_coords[3] - iter_y.pos) / iter_y.span->size; + + /* Iterate the x axis of the virtual rectangle */ + for (_cogl_span_iter_begin (&iter_x, + x_spans, + n_x_spans, + x_normalize_factor, + virtual_coords[0], + virtual_coords[2], + wrap_x); + !_cogl_span_iter_end (&iter_x); + _cogl_span_iter_next (&iter_x)) + { + CoglTexture *span_tex; + + if (iter_x.flipped) + { + slice_coords[0] = iter_x.intersect_end; + slice_coords[2] = iter_x.intersect_start; + span_virtual_coords[0] = iter_x.intersect_end; + span_virtual_coords[2] = iter_x.intersect_start; + } + else + { + slice_coords[0] = iter_x.intersect_start; + slice_coords[2] = iter_x.intersect_end; + span_virtual_coords[0] = iter_x.intersect_start; + span_virtual_coords[2] = iter_x.intersect_end; + } + + /* Map the current intersection to normalized slice coordinates */ + slice_coords[0] = (slice_coords[0] - iter_x.pos) / iter_x.span->size; + slice_coords[2] = (slice_coords[2] - iter_x.pos) / iter_x.span->size; + + /* Pluck out the cogl texture for this span */ + span_tex = textures[iter_y.index * n_x_spans + iter_x.index]; + + callback (COGL_TEXTURE (span_tex), + slice_coords, + span_virtual_coords, + user_data); + } + } +} + +void +_cogl_texture_set_allocated (CoglTexture *texture, + CoglPixelFormat internal_format, + int width, + int height) +{ + _cogl_texture_set_internal_format (texture, internal_format); + + texture->width = width; + texture->height = height; + texture->allocated = TRUE; + + _cogl_texture_free_loader (texture); +} + +CoglBool +cogl_texture_allocate (CoglTexture *texture, + CoglError **error) +{ + if (texture->allocated) + return TRUE; + + if (texture->components == COGL_TEXTURE_COMPONENTS_RG && + !cogl_has_feature (texture->context, COGL_FEATURE_ID_TEXTURE_RG)) + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_FORMAT, + "A red-green texture was requested but the driver " + "does not support them"); + + texture->allocated = texture->vtable->allocate (texture, error); + + return texture->allocated; +} + +void +_cogl_texture_set_internal_format (CoglTexture *texture, + CoglPixelFormat internal_format) +{ + texture->premultiplied = FALSE; + + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + + if (internal_format == COGL_PIXEL_FORMAT_A_8) + { + texture->components = COGL_TEXTURE_COMPONENTS_A; + return; + } + else if (internal_format == COGL_PIXEL_FORMAT_RG_88) + { + texture->components = COGL_TEXTURE_COMPONENTS_RG; + return; + } + else if (internal_format & COGL_DEPTH_BIT) + { + texture->components = COGL_TEXTURE_COMPONENTS_DEPTH; + return; + } + else if (internal_format & COGL_A_BIT) + { + texture->components = COGL_TEXTURE_COMPONENTS_RGBA; + if (internal_format & COGL_PREMULT_BIT) + texture->premultiplied = TRUE; + return; + } + else + texture->components = COGL_TEXTURE_COMPONENTS_RGB; +} + +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglTexture *texture, + CoglPixelFormat src_format) +{ + switch (texture->components) + { + case COGL_TEXTURE_COMPONENTS_DEPTH: + if (src_format & COGL_DEPTH_BIT) + return src_format; + else + { + CoglContext *ctx = texture->context; + + if (_cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || + _cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) + { + return COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8; + } + else + return COGL_PIXEL_FORMAT_DEPTH_16; + } + case COGL_TEXTURE_COMPONENTS_A: + return COGL_PIXEL_FORMAT_A_8; + case COGL_TEXTURE_COMPONENTS_RG: + return COGL_PIXEL_FORMAT_RG_88; + case COGL_TEXTURE_COMPONENTS_RGB: + if (src_format != COGL_PIXEL_FORMAT_ANY && + !(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT)) + return src_format; + else + return COGL_PIXEL_FORMAT_RGB_888; + case COGL_TEXTURE_COMPONENTS_RGBA: + { + CoglPixelFormat format; + + if (src_format != COGL_PIXEL_FORMAT_ANY && + (src_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8) + format = src_format; + else + format = COGL_PIXEL_FORMAT_RGBA_8888; + + if (texture->premultiplied) + { + if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) + return format |= COGL_PREMULT_BIT; + else + return COGL_PIXEL_FORMAT_RGBA_8888_PRE; + } + else + return format & ~COGL_PREMULT_BIT; + } + } + + g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE); +} + +void +cogl_texture_set_components (CoglTexture *texture, + CoglTextureComponents components) +{ + _COGL_RETURN_IF_FAIL (!texture->allocated); + + if (texture->components == components) + return; + + texture->components = components; +} + +CoglTextureComponents +cogl_texture_get_components (CoglTexture *texture) +{ + return texture->components; +} + +void +cogl_texture_set_premultiplied (CoglTexture *texture, + CoglBool premultiplied) +{ + _COGL_RETURN_IF_FAIL (!texture->allocated); + + premultiplied = !!premultiplied; + + if (texture->premultiplied == premultiplied) + return; + + texture->premultiplied = premultiplied; +} + +CoglBool +cogl_texture_get_premultiplied (CoglTexture *texture) +{ + return texture->premultiplied; +} + +void +_cogl_texture_copy_internal_format (CoglTexture *src, + CoglTexture *dest) +{ + cogl_texture_set_components (dest, src->components); + cogl_texture_set_premultiplied (dest, src->premultiplied); +} diff --git a/cogl/cogl/cogl-texture.h b/cogl/cogl/cogl-texture.h new file mode 100644 index 000000000..27188309b --- /dev/null +++ b/cogl/cogl/cogl-texture.h @@ -0,0 +1,524 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_TEXTURE_H__ +#define __COGL_TEXTURE_H__ + +/* We forward declare the CoglTexture type here to avoid some circular + * dependency issues with the following headers. + */ +#ifdef __COGL_H_INSIDE__ +/* For the public C api we typedef interface types as void to avoid needing + * lots of casting in code and instead we will rely on runtime type checking + * for these objects. */ +typedef void CoglTexture; +#else +typedef struct _CoglTexture CoglTexture; +#define COGL_TEXTURE(X) ((CoglTexture *)X) +#endif + +#include +#include +#include +#if defined (COGL_ENABLE_EXPERIMENTAL_API) +#include +#endif +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-texture + * @short_description: Functions for creating and manipulating textures + * + * Cogl allows creating and manipulating textures using a uniform + * API that tries to hide all the various complexities of creating, + * loading and manipulating textures. + */ + +#define COGL_TEXTURE_MAX_WASTE 127 + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_get_gtype (void); +#endif + +/** + * COGL_TEXTURE_ERROR: + * + * #CoglError domain for texture errors. + * + * Since: 1.8 + * Stability: Unstable + */ +#define COGL_TEXTURE_ERROR (cogl_texture_error_quark ()) + +/** + * CoglTextureError: + * @COGL_TEXTURE_ERROR_SIZE: Unsupported size + * @COGL_TEXTURE_ERROR_FORMAT: Unsupported format + * @COGL_TEXTURE_ERROR_TYPE: A primitive texture type that is + * unsupported by the driver was used + * + * Error codes that can be thrown when allocating textures. + * + * Since: 1.8 + * Stability: Unstable + */ +typedef enum { + COGL_TEXTURE_ERROR_SIZE, + COGL_TEXTURE_ERROR_FORMAT, + COGL_TEXTURE_ERROR_BAD_PARAMETER, + COGL_TEXTURE_ERROR_TYPE +} CoglTextureError; + +/** + * CoglTextureType: + * @COGL_TEXTURE_TYPE_2D: A #CoglTexture2D + * @COGL_TEXTURE_TYPE_3D: A #CoglTexture3D + * @COGL_TEXTURE_TYPE_RECTANGLE: A #CoglTextureRectangle + * + * Constants representing the underlying hardware texture type of a + * #CoglTexture. + * + * Stability: unstable + * Since: 1.10 + */ +typedef enum { + COGL_TEXTURE_TYPE_2D, + COGL_TEXTURE_TYPE_3D, + COGL_TEXTURE_TYPE_RECTANGLE +} CoglTextureType; + +uint32_t cogl_texture_error_quark (void); + +/** + * cogl_is_texture: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a texture object. + * + * Return value: %TRUE if the @object references a texture, and + * %FALSE otherwise + */ +CoglBool +cogl_is_texture (void *object); + +/** + * CoglTextureComponents: + * @COGL_TEXTURE_COMPONENTS_A: Only the alpha component + * @COGL_TEXTURE_COMPONENTS_RG: Red and green components. Note that + * this can only be used if the %COGL_FEATURE_ID_TEXTURE_RG feature + * is advertised. + * @COGL_TEXTURE_COMPONENTS_RGB: Red, green and blue components + * @COGL_TEXTURE_COMPONENTS_RGBA: Red, green, blue and alpha components + * @COGL_TEXTURE_COMPONENTS_DEPTH: Only a depth component + * + * See cogl_texture_set_components(). + * + * Since: 1.18 + */ +typedef enum _CoglTextureComponents +{ + COGL_TEXTURE_COMPONENTS_A = 1, + COGL_TEXTURE_COMPONENTS_RG, + COGL_TEXTURE_COMPONENTS_RGB, + COGL_TEXTURE_COMPONENTS_RGBA, + COGL_TEXTURE_COMPONENTS_DEPTH +} CoglTextureComponents; + +/** + * cogl_texture_set_components: + * @texture: a #CoglTexture pointer. + * + * Affects the internal storage format for this texture by specifying + * what components will be required for sampling later. + * + * This api affects how data is uploaded to the GPU since unused + * components can potentially be discarded from source data. + * + * For textures created by the ‘_with_size’ constructors the default + * is %COGL_TEXTURE_COMPONENTS_RGBA. The other constructors which take + * a %CoglBitmap or a data pointer default to the same components as + * the pixel format of the data. + * + * Note that the %COGL_TEXTURE_COMPONENTS_RG format is not available + * on all drivers. The availability can be determined by checking for + * the %COGL_FEATURE_ID_TEXTURE_RG feature. If this format is used on + * a driver where it is not available then %COGL_TEXTURE_ERROR_FORMAT + * will be raised when the texture is allocated. Even if the feature + * is not available then %COGL_PIXEL_FORMAT_RG_88 can still be used as + * an image format as long as %COGL_TEXTURE_COMPONENTS_RG isn't used + * as the texture's components. + * + * Since: 1.18 + */ +void +cogl_texture_set_components (CoglTexture *texture, + CoglTextureComponents components); + +/** + * cogl_texture_get_components: + * @texture: a #CoglTexture pointer. + * + * Queries what components the given @texture stores internally as set + * via cogl_texture_set_components(). + * + * For textures created by the ‘_with_size’ constructors the default + * is %COGL_TEXTURE_COMPONENTS_RGBA. The other constructors which take + * a %CoglBitmap or a data pointer default to the same components as + * the pixel format of the data. + * + * Since: 1.18 + */ +CoglTextureComponents +cogl_texture_get_components (CoglTexture *texture); + +/** + * cogl_texture_set_premultiplied: + * @texture: a #CoglTexture pointer. + * @premultiplied: Whether any internally stored red, green or blue + * components are pre-multiplied by an alpha + * component. + * + * Affects the internal storage format for this texture by specifying + * whether red, green and blue color components should be stored as + * pre-multiplied alpha values. + * + * This api affects how data is uploaded to the GPU since Cogl will + * convert source data to have premultiplied or unpremultiplied + * components according to this state. + * + * For example if you create a texture via + * cogl_texture_2d_new_with_size() and then upload data via + * cogl_texture_set_data() passing a source format of + * %COGL_PIXEL_FORMAT_RGBA_8888 then Cogl will internally multiply the + * red, green and blue components of the source data by the alpha + * component, for each pixel so that the internally stored data has + * pre-multiplied alpha components. If you instead upload data that + * already has pre-multiplied components by passing + * %COGL_PIXEL_FORMAT_RGBA_8888_PRE as the source format to + * cogl_texture_set_data() then the data can be uploaded without being + * converted. + * + * By default the @premultipled state is @TRUE. + * + * Since: 1.18 + */ +void +cogl_texture_set_premultiplied (CoglTexture *texture, + CoglBool premultiplied); + +/** + * cogl_texture_get_premultiplied: + * @texture: a #CoglTexture pointer. + * + * Queries the pre-multiplied alpha status for internally stored red, + * green and blue components for the given @texture as set by + * cogl_texture_set_premultiplied(). + * + * By default the pre-multipled state is @TRUE. + * + * Return value: %TRUE if red, green and blue components are + * internally stored pre-multiplied by the alpha + * value or %FALSE if not. + * Since: 1.18 + */ +CoglBool +cogl_texture_get_premultiplied (CoglTexture *texture); + +/** + * cogl_texture_get_width: + * @texture: a #CoglTexture pointer. + * + * Queries the width of a cogl texture. + * + * Return value: the width of the GPU side texture in pixels + */ +unsigned int +cogl_texture_get_width (CoglTexture *texture); + +/** + * cogl_texture_get_height: + * @texture: a #CoglTexture pointer. + * + * Queries the height of a cogl texture. + * + * Return value: the height of the GPU side texture in pixels + */ +unsigned int +cogl_texture_get_height (CoglTexture *texture); + +/** + * cogl_texture_get_max_waste: + * @texture: a #CoglTexture pointer. + * + * Queries the maximum wasted (unused) pixels in one dimension of a GPU side + * texture. + * + * Return value: the maximum waste + */ +int +cogl_texture_get_max_waste (CoglTexture *texture); + +/** + * cogl_texture_is_sliced: + * @texture: a #CoglTexture pointer. + * + * Queries if a texture is sliced (stored as multiple GPU side tecture + * objects). + * + * Return value: %TRUE if the texture is sliced, %FALSE if the texture + * is stored as a single GPU texture + */ +CoglBool +cogl_texture_is_sliced (CoglTexture *texture); + +/** + * cogl_texture_get_gl_texture: + * @texture: a #CoglTexture pointer. + * @out_gl_handle: (out) (allow-none): pointer to return location for the + * textures GL handle, or %NULL. + * @out_gl_target: (out) (allow-none): pointer to return location for the + * GL target type, or %NULL. + * + * Queries the GL handles for a GPU side texture through its #CoglTexture. + * + * If the texture is spliced the data for the first sub texture will be + * queried. + * + * Return value: %TRUE if the handle was successfully retrieved, %FALSE + * if the handle was invalid + */ +CoglBool +cogl_texture_get_gl_texture (CoglTexture *texture, + unsigned int *out_gl_handle, + unsigned int *out_gl_target); + +/** + * cogl_texture_get_data: + * @texture: a #CoglTexture pointer. + * @format: the #CoglPixelFormat to store the texture as. + * @rowstride: the rowstride of @data in bytes or pass 0 to calculate + * from the bytes-per-pixel of @format multiplied by the + * @texture width. + * @data: memory location to write the @texture's contents, or %NULL + * to only query the data size through the return value. + * + * Copies the pixel data from a cogl texture to system memory. + * + * Don't pass the value of cogl_texture_get_rowstride() as the + * @rowstride argument, the rowstride should be the rowstride you + * want for the destination @data buffer not the rowstride of the + * source texture + * + * Return value: the size of the texture data in bytes + */ +int +cogl_texture_get_data (CoglTexture *texture, + CoglPixelFormat format, + unsigned int rowstride, + uint8_t *data); + +/** + * cogl_texture_set_region: + * @texture: a #CoglTexture. + * @src_x: upper left coordinate to use from source data. + * @src_y: upper left coordinate to use from source data. + * @dst_x: upper left destination horizontal coordinate. + * @dst_y: upper left destination vertical coordinate. + * @dst_width: width of destination region to write. (Must be less + * than or equal to @width) + * @dst_height: height of destination region to write. (Must be less + * than or equal to @height) + * @width: width of source data buffer. + * @height: height of source data buffer. + * @format: the #CoglPixelFormat used in the source buffer. + * @rowstride: rowstride of source buffer (computed from width if none + * specified) + * @data: the actual pixel data. + * + * Sets the pixels in a rectangular subregion of @texture from an in-memory + * buffer containing pixel data. + * + * The region set can't be larger than the source @data + * + * Return value: %TRUE if the subregion upload was successful, and + * %FALSE otherwise + */ +CoglBool +cogl_texture_set_region (CoglTexture *texture, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const uint8_t *data); + +#if defined (COGL_ENABLE_EXPERIMENTAL_API) + +/** + * cogl_texture_set_data: + * @texture a #CoglTexture. + * @format: the #CoglPixelFormat used in the source @data buffer. + * @rowstride: rowstride of the source @data buffer (computed from + * the texture width and @format if it equals 0) + * @data: the source data, pointing to the first top-left pixel to set + * @level: The mipmap level to update (Normally 0 for the largest, + * base texture) + * @error: A #CoglError to return exceptional errors + * + * Sets all the pixels for a given mipmap @level by copying the pixel + * data pointed to by the @data argument into the given @texture. + * + * @data should point to the first pixel to copy corresponding + * to the top left of the mipmap @level being set. + * + * If @rowstride equals 0 then it will be automatically calculated + * from the width of the mipmap level and the bytes-per-pixel for the + * given @format. + * + * A mipmap @level of 0 corresponds to the largest, base image of a + * texture and @level 1 is half the width and height of level 0. If + * dividing any dimension of the previous level by two results in a + * fraction then round the number down (floor()), but clamp to 1 + * something like this: + * + * |[ + * next_width = MAX (1, floor (prev_width)); + * ]| + * + * You can determine the number of mipmap levels for a given texture + * like this: + * + * |[ + * n_levels = 1 + floor (log2 (max_dimension)); + * ]| + * + * Where %max_dimension is the larger of cogl_texture_get_width() and + * cogl_texture_get_height(). + * + * It is an error to pass a @level number >= the number of levels that + * @texture can have according to the above calculation. + * + * Since the storage for a #CoglTexture is allocated lazily then + * if the given @texture has not previously been allocated then this + * api can return %FALSE and throw an exceptional @error if there is + * not enough memory to allocate storage for @texture. + * + * Return value: %TRUE if the data upload was successful, and + * %FALSE otherwise + */ +CoglBool +cogl_texture_set_data (CoglTexture *texture, + CoglPixelFormat format, + int rowstride, + const uint8_t *data, + int level, + CoglError **error); + +/** + * cogl_texture_set_region_from_bitmap: + * @texture: a #CoglTexture pointer + * @src_x: upper left coordinate to use from the source bitmap. + * @src_y: upper left coordinate to use from the source bitmap + * @dst_x: upper left destination horizontal coordinate. + * @dst_y: upper left destination vertical coordinate. + * @dst_width: width of destination region to write. (Must be less + * than or equal to the bitmap width) + * @dst_height: height of destination region to write. (Must be less + * than or equal to the bitmap height) + * @bitmap: The source bitmap to read from + * + * Copies a specified source region from @bitmap to the position + * (@src_x, @src_y) of the given destination texture @handle. + * + * The region updated can't be larger than the source + * bitmap + * + * Return value: %TRUE if the subregion upload was successful, and + * %FALSE otherwise + * + * Since: 1.8 + * Stability: unstable + */ +CoglBool +cogl_texture_set_region_from_bitmap (CoglTexture *texture, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + CoglBitmap *bitmap); +#endif + +/** + * cogl_texture_allocate: + * @texture: A #CoglTexture + * @error: A #CoglError to return exceptional errors or %NULL + * + * Explicitly allocates the storage for the given @texture which + * allows you to be sure that there is enough memory for the + * texture and if not then the error can be handled gracefully. + * + * Normally applications don't need to use this api directly + * since the texture will be implicitly allocated when data is set on + * the texture, or if the texture is attached to a #CoglOffscreen + * framebuffer and rendered too. + * + * Return value: %TRUE if the texture was successfully allocated, + * otherwise %FALSE and @error will be updated if it + * wasn't %NULL. + */ +CoglBool +cogl_texture_allocate (CoglTexture *texture, + CoglError **error); + +COGL_END_DECLS + +#endif /* __COGL_TEXTURE_H__ */ diff --git a/cogl/cogl/cogl-types.h b/cogl/cogl/cogl-types.h new file mode 100644 index 000000000..6accf8d88 --- /dev/null +++ b/cogl/cogl/cogl-types.h @@ -0,0 +1,940 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_TYPES_H__ +#define __COGL_TYPES_H__ + +#include +#include + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif /* COGL_HAS_GTYPE_SUPPORT */ + +/* Guard C code in headers, while including them from C++ */ +#ifdef __cplusplus +#define COGL_BEGIN_DECLS extern "C" { +#define COGL_END_DECLS } +#else +#define COGL_BEGIN_DECLS +#define COGL_END_DECLS +#endif + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-types + * @short_description: Types used throughout the library + * + * General types used by various Cogl functions. +*/ + +/** + * CoglBool: + * + * A boolean data type used throughout the Cogl C api. This should be + * used in conjunction with the %TRUE and %FALSE macro defines for + * setting and testing boolean values. + * + * Since: 2.0 + * Stability: stable + */ +typedef int CoglBool; + +/** + * TRUE: + * + * A constant to be used with #CoglBool types to indicate a boolean in + * the "true" state. + * + * Since: 2.0 + * Stability: stable + */ +#ifndef TRUE +#define TRUE 1 +#endif + +/** + * FALSE: + * + * A constant to be used with #CoglBool types to indicate a boolean in + * the "false" state. + * + * Since: 2.0 + * Stability: stable + */ +#ifndef FALSE +#define FALSE 0 +#endif + +#if __GNUC__ >= 4 +#define COGL_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +#else +#define COGL_GNUC_NULL_TERMINATED +#endif + +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) && \ + !defined (COGL_COMPILATION) +#define COGL_GNUC_DEPRECATED \ + __attribute__((__deprecated__)) +#else +#define COGL_GNUC_DEPRECATED +#endif /* __GNUC__ */ + +/* Some structures are meant to be opaque but they have public + definitions because we want the size to be public so they can be + allocated on the stack. This macro is used to ensure that users + don't accidentally access private members */ +#ifdef COGL_COMPILATION +#define COGL_PRIVATE(x) x +#else +#define COGL_PRIVATE(x) private_member_ ## x +#endif + +/* To help catch accidental changes to public structs that should + * be stack allocated we use this macro to compile time assert that + * a struct size is as expected. + */ +#define COGL_STRUCT_SIZE_ASSERT(TYPE, SIZE) \ +typedef struct { \ + char compile_time_assert_ ## TYPE ## _size[ \ + (sizeof (TYPE) == (SIZE)) ? 1 : -1]; \ + } _ ## TYPE ## SizeCheck + +/** + * CoglHandle: + * + * Type used for storing references to cogl objects, the CoglHandle is + * a fully opaque type without any public data members. + */ +typedef void * CoglHandle; + +/** + * COGL_INVALID_HANDLE: + * + * A COGL handle that is not valid, used for unitialized handles as well as + * error conditions. + */ +#define COGL_INVALID_HANDLE NULL + +#ifdef COGL_HAS_GTYPE_SUPPORT + +#define COGL_TYPE_HANDLE (cogl_handle_get_type ()) +GType +cogl_handle_get_type (void) G_GNUC_CONST; + +#endif /* COGL_HAS_GTYPE_SUPPORT */ + +/** + * cogl_handle_ref: + * @handle: a #CoglHandle + * + * Increases the reference count of @handle by 1 + * + * Return value: (transfer none): the handle, with its reference count increased + */ +CoglHandle +cogl_handle_ref (CoglHandle handle); + +/** + * cogl_handle_unref: + * @handle: a #CoglHandle + * + * Drecreases the reference count of @handle by 1; if the reference + * count reaches 0, the resources allocated by @handle will be freed + */ +void +cogl_handle_unref (CoglHandle handle); + +/** + * CoglFuncPtr: + * + * The type used by cogl for function pointers, note that this type + * is used as a generic catch-all cast for function pointers and the + * actual arguments and return type may be different. + */ +typedef void (* CoglFuncPtr) (void); + +/* We forward declare this in cogl-types to avoid circular dependencies + * between cogl-matrix.h, cogl-euler.h and cogl-quaterion.h */ +typedef struct _CoglMatrix CoglMatrix; + +/* Same as above we forward declared CoglQuaternion to avoid + * circular dependencies. */ +typedef struct _CoglQuaternion CoglQuaternion; + +/* Same as above we forward declared CoglEuler to avoid + * circular dependencies. */ +typedef struct _CoglEuler CoglEuler; + +/** + * CoglFixed: + * + * Fixed point number using a (16.16) notation. + */ +typedef int32_t CoglFixed; + +#define COGL_TYPE_FIXED (cogl_fixed_get_type ()) +GType +cogl_fixed_get_type (void) G_GNUC_CONST; + +/** + * CoglAngle: + * + * Integer representation of an angle such that 1024 corresponds to + * full circle (i.e., 2 * pi). + * + * Since: 1.0 + */ +typedef int32_t CoglAngle; + +typedef struct _CoglColor CoglColor; +typedef struct _CoglTextureVertex CoglTextureVertex; + +/* Enum declarations */ + +#define COGL_A_BIT (1 << 4) +#define COGL_BGR_BIT (1 << 5) +#define COGL_AFIRST_BIT (1 << 6) +#define COGL_PREMULT_BIT (1 << 7) +#define COGL_DEPTH_BIT (1 << 8) +#define COGL_STENCIL_BIT (1 << 9) + +/* XXX: Notes to those adding new formats here... + * + * First this diagram outlines how we allocate the 32bits of a + * CoglPixelFormat currently... + * + * 6 bits for flags + * |-----| + * enum unused 4 bits for the bytes-per-pixel + * and component alignment info + * |------| |-------------| |--| + * 00000000 xxxxxxxx xxxxxxSD PFBA0000 + * ^ stencil + * ^ depth + * ^ premult + * ^ alpha first + * ^ bgr order + * ^ has alpha + * + * The most awkward part about the formats is how we use the last 4 + * bits to encode the bytes per pixel and component alignment + * information. Ideally we should have had 3 bits for the bpp and a + * flag for alignment but we didn't plan for that in advance so we + * instead use a small lookup table to query the bpp and whether the + * components are byte aligned or not. + * + * The mapping is the following (see discussion on bug #660188): + * + * 0 = undefined + * 1, 8 = 1 bpp (e.g. A_8, G_8) + * 2 = 3 bpp, aligned (e.g. 888) + * 3 = 4 bpp, aligned (e.g. 8888) + * 4-6 = 2 bpp, not aligned (e.g. 565, 4444, 5551) + * 7 = YUV: undefined bpp, undefined alignment + * 9 = 2 bpp, aligned + * 10 = depth, aligned (8, 16, 24, 32, 32f) + * 11 = undefined + * 12 = 3 bpp, not aligned + * 13 = 4 bpp, not aligned (e.g. 2101010) + * 14-15 = undefined + * + * Note: the gap at 10-11 is just because we wanted to maintain that + * all non-aligned formats have the third bit set in case that's + * useful later. + * + * Since we don't want to waste bits adding more and more flags, we'd + * like to see most new pixel formats that can't be represented + * uniquely with the existing flags in the least significant byte + * simply be enumerated with sequential values in the most significant + * enum byte. + * + * Note: Cogl avoids exposing any padded XRGB or RGBX formats and + * instead we leave it up to applications to decided whether they + * consider the A component as padding or valid data. We shouldn't + * change this policy without good reasoning. + * + * So to add a new format: + * 1) Use the mapping table above to figure out what to but in + * the lowest nibble. + * 2) OR in the COGL_PREMULT_BIT, COGL_AFIRST_BIT, COGL_A_BIT and + * COGL_BGR_BIT flags as appropriate. + * 3) If the result is not yet unique then also combine with an + * increment of the last sequence number in the most significant + * byte. + * + * The last sequence number used was 0 (i.e. no formats currently need + * a sequence number) + * Update this note whenever a new sequence number is used. + */ +/** + * CoglPixelFormat: + * @COGL_PIXEL_FORMAT_ANY: Any format + * @COGL_PIXEL_FORMAT_A_8: 8 bits alpha mask + * @COGL_PIXEL_FORMAT_RG_88: RG, 16 bits. Note that red-green textures + * are only available if %COGL_FEATURE_ID_TEXTURE_RG is advertised. + * See cogl_texture_set_components() for details. + * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits + * @COGL_PIXEL_FORMAT_YUV: Not currently supported + * @COGL_PIXEL_FORMAT_G_8: Single luminance component + * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits + * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits + * @COGL_PIXEL_FORMAT_RGBA_8888: RGBA, 32 bits + * @COGL_PIXEL_FORMAT_BGRA_8888: BGRA, 32 bits + * @COGL_PIXEL_FORMAT_ARGB_8888: ARGB, 32 bits + * @COGL_PIXEL_FORMAT_ABGR_8888: ABGR, 32 bits + * @COGL_PIXEL_FORMAT_RGBA_1010102 : RGBA, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_BGRA_1010102 : BGRA, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_ARGB_2101010 : ARGB, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_ABGR_2101010 : ABGR, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: Premultiplied RGBA, 32 bits + * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: Premultiplied BGRA, 32 bits + * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: Premultiplied ARGB, 32 bits + * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: Premultiplied ABGR, 32 bits + * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: Premultiplied RGBA, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: Premultiplied RGBA, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_1010102_PRE: Premultiplied RGBA, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_BGRA_1010102_PRE: Premultiplied BGRA, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_ARGB_2101010_PRE: Premultiplied ARGB, 32 bits, 10 bpc + * @COGL_PIXEL_FORMAT_ABGR_2101010_PRE: Premultiplied ABGR, 32 bits, 10 bpc + * + * Pixel formats used by Cogl. For the formats with a byte per + * component, the order of the components specify the order in + * increasing memory addresses. So for example + * %COGL_PIXEL_FORMAT_RGB_888 would have the red component in the + * lowest address, green in the next address and blue after that + * regardless of the endianness of the system. + * + * For the formats with non byte aligned components the component + * order specifies the order within a 16-bit or 32-bit number from + * most significant bit to least significant. So for + * %COGL_PIXEL_FORMAT_RGB_565, the red component would be in bits + * 11-15, the green component would be in 6-11 and the blue component + * would be in 1-5. Therefore the order in memory depends on the + * endianness of the system. + * + * When uploading a texture %COGL_PIXEL_FORMAT_ANY can be used as the + * internal format. Cogl will try to pick the best format to use + * internally and convert the texture data if necessary. + * + * Since: 0.8 + */ +typedef enum { /*< prefix=COGL_PIXEL_FORMAT >*/ + COGL_PIXEL_FORMAT_ANY = 0, + COGL_PIXEL_FORMAT_A_8 = 1 | COGL_A_BIT, + + COGL_PIXEL_FORMAT_RGB_565 = 4, + COGL_PIXEL_FORMAT_RGBA_4444 = 5 | COGL_A_BIT, + COGL_PIXEL_FORMAT_RGBA_5551 = 6 | COGL_A_BIT, + COGL_PIXEL_FORMAT_YUV = 7, + COGL_PIXEL_FORMAT_G_8 = 8, + + COGL_PIXEL_FORMAT_RG_88 = 9, + + COGL_PIXEL_FORMAT_RGB_888 = 2, + COGL_PIXEL_FORMAT_BGR_888 = (2 | COGL_BGR_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888 = (3 | COGL_A_BIT), + COGL_PIXEL_FORMAT_BGRA_8888 = (3 | COGL_A_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888 = (3 | COGL_A_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888 = (3 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGBA_1010102 = (13 | COGL_A_BIT), + COGL_PIXEL_FORMAT_BGRA_1010102 = (13 | COGL_A_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_2101010 = (13 | COGL_A_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_2101010 = (13 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_BGRA_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_RGBA_4444_PRE = (COGL_PIXEL_FORMAT_RGBA_4444 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_RGBA_5551_PRE = (COGL_PIXEL_FORMAT_RGBA_5551 | COGL_A_BIT | COGL_PREMULT_BIT), + + COGL_PIXEL_FORMAT_RGBA_1010102_PRE = (COGL_PIXEL_FORMAT_RGBA_1010102 | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_BGRA_1010102_PRE = (COGL_PIXEL_FORMAT_BGRA_1010102 | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_ARGB_2101010_PRE = (COGL_PIXEL_FORMAT_ARGB_2101010 | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_ABGR_2101010_PRE = (COGL_PIXEL_FORMAT_ABGR_2101010 | COGL_PREMULT_BIT), + + COGL_PIXEL_FORMAT_DEPTH_16 = (9 | COGL_DEPTH_BIT), + COGL_PIXEL_FORMAT_DEPTH_32 = (3 | COGL_DEPTH_BIT), + + COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = (3 | COGL_DEPTH_BIT | COGL_STENCIL_BIT) +} CoglPixelFormat; + +/** + * CoglFeatureFlags: + * @COGL_FEATURE_TEXTURE_RECTANGLE: ARB_texture_rectangle support + * @COGL_FEATURE_TEXTURE_NPOT: Non power of two textures are supported + * by the hardware. This is a equivalent to the + * %COGL_FEATURE_TEXTURE_NPOT_BASIC, %COGL_FEATURE_TEXTURE_NPOT_MIPMAP + * and %COGL_FEATURE_TEXTURE_NPOT_REPEAT features combined. + * @COGL_FEATURE_TEXTURE_YUV: ycbcr conversion support + * @COGL_FEATURE_TEXTURE_READ_PIXELS: glReadPixels() support + * @COGL_FEATURE_SHADERS_GLSL: GLSL support + * @COGL_FEATURE_SHADERS_ARBFP: ARBFP support + * @COGL_FEATURE_OFFSCREEN: FBO support + * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: Multisample support on FBOs + * @COGL_FEATURE_OFFSCREEN_BLIT: Blit support on FBOs + * @COGL_FEATURE_FOUR_CLIP_PLANES: At least 4 clip planes available + * @COGL_FEATURE_STENCIL_BUFFER: Stencil buffer support + * @COGL_FEATURE_VBOS: VBO support + * @COGL_FEATURE_PBOS: PBO support + * @COGL_FEATURE_UNSIGNED_INT_INDICES: Set if + * %COGL_INDICES_TYPE_UNSIGNED_INT is supported in + * cogl_vertex_buffer_indices_new(). + * @COGL_FEATURE_DEPTH_RANGE: cogl_material_set_depth_range() support + * @COGL_FEATURE_TEXTURE_NPOT_BASIC: The hardware supports non power + * of two textures, but you also need to check the + * %COGL_FEATURE_TEXTURE_NPOT_MIPMAP and %COGL_FEATURE_TEXTURE_NPOT_REPEAT + * features to know if the hardware supports npot texture mipmaps + * or repeat modes other than + * %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE respectively. + * @COGL_FEATURE_TEXTURE_NPOT_MIPMAP: Mipmapping is supported in + * conjuntion with non power of two textures. + * @COGL_FEATURE_TEXTURE_NPOT_REPEAT: Repeat modes other than + * %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE are supported by the + * hardware. + * @COGL_FEATURE_POINT_SPRITE: Whether + * cogl_material_set_layer_point_sprite_coords_enabled() is supported. + * @COGL_FEATURE_TEXTURE_3D: 3D texture support + * @COGL_FEATURE_MAP_BUFFER_FOR_READ: Whether cogl_buffer_map() is + * supported with CoglBufferAccess including read support. + * @COGL_FEATURE_MAP_BUFFER_FOR_WRITE: Whether cogl_buffer_map() is + * supported with CoglBufferAccess including write support. + * @COGL_FEATURE_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering the + * depth buffer to a texture. + * + * Flags for the supported features. + * + * Since: 0.8 + */ +typedef enum +{ + COGL_FEATURE_TEXTURE_RECTANGLE = (1 << 1), + COGL_FEATURE_TEXTURE_NPOT = (1 << 2), + COGL_FEATURE_TEXTURE_YUV = (1 << 3), + COGL_FEATURE_TEXTURE_READ_PIXELS = (1 << 4), + COGL_FEATURE_SHADERS_GLSL = (1 << 5), + COGL_FEATURE_OFFSCREEN = (1 << 6), + COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7), + COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8), + COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9), + COGL_FEATURE_STENCIL_BUFFER = (1 << 10), + COGL_FEATURE_VBOS = (1 << 11), + COGL_FEATURE_PBOS = (1 << 12), + COGL_FEATURE_UNSIGNED_INT_INDICES = (1 << 13), + COGL_FEATURE_DEPTH_RANGE = (1 << 14), + COGL_FEATURE_TEXTURE_NPOT_BASIC = (1 << 15), + COGL_FEATURE_TEXTURE_NPOT_MIPMAP = (1 << 16), + COGL_FEATURE_TEXTURE_NPOT_REPEAT = (1 << 17), + COGL_FEATURE_POINT_SPRITE = (1 << 18), + COGL_FEATURE_TEXTURE_3D = (1 << 19), + COGL_FEATURE_SHADERS_ARBFP = (1 << 20), + COGL_FEATURE_MAP_BUFFER_FOR_READ = (1 << 21), + COGL_FEATURE_MAP_BUFFER_FOR_WRITE = (1 << 22), + COGL_FEATURE_ONSCREEN_MULTIPLE = (1 << 23), + COGL_FEATURE_DEPTH_TEXTURE = (1 << 24) +} CoglFeatureFlags; + +/** + * CoglBufferTarget: + * @COGL_WINDOW_BUFFER: FIXME + * @COGL_OFFSCREEN_BUFFER: FIXME + * + * Target flags for FBOs. + * + * Since: 0.8 + */ +typedef enum +{ + COGL_WINDOW_BUFFER = (1 << 1), + COGL_OFFSCREEN_BUFFER = (1 << 2) +} CoglBufferTarget; + +/** + * CoglColor: + * @red: amount of red + * @green: amount of green + * @blue: amount of green + * @alpha: alpha + * + * A structure for holding a color definition. The contents of + * the CoglColor structure are private and should never by accessed + * directly. + * + * Since: 1.0 + */ +struct _CoglColor +{ + /*< private >*/ + uint8_t COGL_PRIVATE (red); + uint8_t COGL_PRIVATE (green); + uint8_t COGL_PRIVATE (blue); + + uint8_t COGL_PRIVATE (alpha); + + /* padding in case we want to change to floats at + * some point */ + uint32_t COGL_PRIVATE (padding0); + uint32_t COGL_PRIVATE (padding1); + uint32_t COGL_PRIVATE (padding2); +}; +COGL_STRUCT_SIZE_ASSERT (CoglColor, 16); + +/** + * CoglTextureVertex: + * @x: Model x-coordinate + * @y: Model y-coordinate + * @z: Model z-coordinate + * @tx: Texture x-coordinate + * @ty: Texture y-coordinate + * @color: The color to use at this vertex. This is ignored if + * use_color is %FALSE when calling cogl_polygon() + * + * Used to specify vertex information when calling cogl_polygon() + */ +struct _CoglTextureVertex +{ + float x, y, z; + float tx, ty; + + CoglColor color; +}; +COGL_STRUCT_SIZE_ASSERT (CoglTextureVertex, 36); + +/** + * CoglTextureFlags: + * @COGL_TEXTURE_NONE: No flags specified + * @COGL_TEXTURE_NO_AUTO_MIPMAP: Disables the automatic generation of + * the mipmap pyramid from the base level image whenever it is + * updated. The mipmaps are only generated when the texture is + * rendered with a mipmap filter so it should be free to leave out + * this flag when using other filtering modes + * @COGL_TEXTURE_NO_SLICING: Disables the slicing of the texture + * @COGL_TEXTURE_NO_ATLAS: Disables the insertion of the texture inside + * the texture atlas used by Cogl + * + * Flags to pass to the cogl_texture_new_* family of functions. + * + * Since: 1.0 + */ +typedef enum { + COGL_TEXTURE_NONE = 0, + COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, + COGL_TEXTURE_NO_SLICING = 1 << 1, + COGL_TEXTURE_NO_ATLAS = 1 << 2 +} CoglTextureFlags; + +/** + * CoglFogMode: + * @COGL_FOG_MODE_LINEAR: Calculates the fog blend factor as: + * |[ + * f = end - eye_distance / end - start + * ]| + * @COGL_FOG_MODE_EXPONENTIAL: Calculates the fog blend factor as: + * |[ + * f = e ^ -(density * eye_distance) + * ]| + * @COGL_FOG_MODE_EXPONENTIAL_SQUARED: Calculates the fog blend factor as: + * |[ + * f = e ^ -(density * eye_distance)^2 + * ]| + * + * The fog mode determines the equation used to calculate the fogging blend + * factor while fogging is enabled. The simplest %COGL_FOG_MODE_LINEAR mode + * determines f as: + * + * |[ + * f = end - eye_distance / end - start + * ]| + * + * Where eye_distance is the distance of the current fragment in eye + * coordinates from the origin. + * + * Since: 1.0 + */ +typedef enum { + COGL_FOG_MODE_LINEAR, + COGL_FOG_MODE_EXPONENTIAL, + COGL_FOG_MODE_EXPONENTIAL_SQUARED +} CoglFogMode; + +/** + * COGL_BLEND_STRING_ERROR: + * + * #CoglError domain for blend string parser errors + * + * Since: 1.0 + */ +#define COGL_BLEND_STRING_ERROR (cogl_blend_string_error_quark ()) + +/** + * CoglBlendStringError: + * @COGL_BLEND_STRING_ERROR_PARSE_ERROR: Generic parse error + * @COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR: Argument parse error + * @COGL_BLEND_STRING_ERROR_INVALID_ERROR: Internal parser error + * @COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR: Blend string not + * supported by the GPU + * + * Error enumeration for the blend strings parser + * + * Since: 1.0 + */ +typedef enum { /*< prefix=COGL_BLEND_STRING_ERROR >*/ + COGL_BLEND_STRING_ERROR_PARSE_ERROR, + COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, + COGL_BLEND_STRING_ERROR_INVALID_ERROR, + COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR +} CoglBlendStringError; + +uint32_t +cogl_blend_string_error_quark (void); + +#define COGL_SYSTEM_ERROR (_cogl_system_error_quark ()) + +/** + * CoglSystemError: + * @COGL_SYSTEM_ERROR_UNSUPPORTED: You tried to use a feature or + * configuration not currently available. + * @COGL_SYSTEM_ERROR_NO_MEMORY: You tried to allocate a resource + * such as a texture and there wasn't enough memory. + * + * Error enumeration for Cogl + * + * The @COGL_SYSTEM_ERROR_UNSUPPORTED error can be thrown for a + * variety of reasons. For example: + * + * + * You've tried to use a feature that is not + * advertised by cogl_has_feature(). This could happen if you create + * a 2d texture with a non-power-of-two size when + * %COGL_FEATURE_ID_TEXTURE_NPOT is not advertised. + * The GPU can not handle the configuration you have + * requested. An example might be if you try to use too many texture + * layers in a single #CoglPipeline + * The driver does not support some + * configuration. + * + * + * Currently this is only used by Cogl API marked as experimental so + * this enum should also be considered experimental. + * + * Since: 1.4 + * Stability: unstable + */ +typedef enum { /*< prefix=COGL_ERROR >*/ + COGL_SYSTEM_ERROR_UNSUPPORTED, + COGL_SYSTEM_ERROR_NO_MEMORY +} CoglSystemError; + +uint32_t +_cogl_system_error_quark (void); + +/** + * CoglAttributeType: + * @COGL_ATTRIBUTE_TYPE_BYTE: Data is the same size of a byte + * @COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE: Data is the same size of an + * unsigned byte + * @COGL_ATTRIBUTE_TYPE_SHORT: Data is the same size of a short integer + * @COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT: Data is the same size of + * an unsigned short integer + * @COGL_ATTRIBUTE_TYPE_FLOAT: Data is the same size of a float + * + * Data types for the components of a vertex attribute. + * + * Since: 1.0 + */ +typedef enum { + COGL_ATTRIBUTE_TYPE_BYTE = 0x1400, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE = 0x1401, + COGL_ATTRIBUTE_TYPE_SHORT = 0x1402, + COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT = 0x1403, + COGL_ATTRIBUTE_TYPE_FLOAT = 0x1406 +} CoglAttributeType; + +/** + * CoglIndicesType: + * @COGL_INDICES_TYPE_UNSIGNED_BYTE: Your indices are unsigned bytes + * @COGL_INDICES_TYPE_UNSIGNED_SHORT: Your indices are unsigned shorts + * @COGL_INDICES_TYPE_UNSIGNED_INT: Your indices are unsigned ints + * + * You should aim to use the smallest data type that gives you enough + * range, since it reduces the size of your index array and can help + * reduce the demand on memory bandwidth. + * + * Note that %COGL_INDICES_TYPE_UNSIGNED_INT is only supported if the + * %COGL_FEATURE_ID_UNSIGNED_INT_INDICES feature is available. This + * should always be available on OpenGL but on OpenGL ES it will only + * be available if the GL_OES_element_index_uint extension is + * advertized. + */ +typedef enum { + COGL_INDICES_TYPE_UNSIGNED_BYTE, + COGL_INDICES_TYPE_UNSIGNED_SHORT, + COGL_INDICES_TYPE_UNSIGNED_INT +} CoglIndicesType; + +/** + * CoglVerticesMode: + * @COGL_VERTICES_MODE_POINTS: FIXME, equivalent to + * GL_POINTS + * @COGL_VERTICES_MODE_LINES: FIXME, equivalent to GL_LINES + * @COGL_VERTICES_MODE_LINE_LOOP: FIXME, equivalent to + * GL_LINE_LOOP + * @COGL_VERTICES_MODE_LINE_STRIP: FIXME, equivalent to + * GL_LINE_STRIP + * @COGL_VERTICES_MODE_TRIANGLES: FIXME, equivalent to + * GL_TRIANGLES + * @COGL_VERTICES_MODE_TRIANGLE_STRIP: FIXME, equivalent to + * GL_TRIANGLE_STRIP + * @COGL_VERTICES_MODE_TRIANGLE_FAN: FIXME, equivalent to GL_TRIANGLE_FAN + * + * Different ways of interpreting vertices when drawing. + * + * Since: 1.0 + */ +typedef enum { + COGL_VERTICES_MODE_POINTS = 0x0000, + COGL_VERTICES_MODE_LINES = 0x0001, + COGL_VERTICES_MODE_LINE_LOOP = 0x0002, + COGL_VERTICES_MODE_LINE_STRIP = 0x0003, + COGL_VERTICES_MODE_TRIANGLES = 0x0004, + COGL_VERTICES_MODE_TRIANGLE_STRIP = 0x0005, + COGL_VERTICES_MODE_TRIANGLE_FAN = 0x0006 +} CoglVerticesMode; + +/* NB: The above definitions are taken from gl.h equivalents */ + + +/* XXX: should this be CoglMaterialDepthTestFunction? + * It makes it very verbose but would be consistent with + * CoglMaterialWrapMode */ + +/** + * CoglDepthTestFunction: + * @COGL_DEPTH_TEST_FUNCTION_NEVER: Never passes. + * @COGL_DEPTH_TEST_FUNCTION_LESS: Passes if the fragment's depth + * value is less than the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_EQUAL: Passes if the fragment's depth + * value is equal to the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_LEQUAL: Passes if the fragment's depth + * value is less or equal to the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_GREATER: Passes if the fragment's depth + * value is greater than the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_NOTEQUAL: Passes if the fragment's depth + * value is not equal to the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_GEQUAL: Passes if the fragment's depth + * value greater than or equal to the value currently in the depth buffer. + * @COGL_DEPTH_TEST_FUNCTION_ALWAYS: Always passes. + * + * When using depth testing one of these functions is used to compare + * the depth of an incoming fragment against the depth value currently + * stored in the depth buffer. The function is changed using + * cogl_depth_state_set_test_function(). + * + * The test is only done when depth testing is explicitly enabled. (See + * cogl_depth_state_set_test_enabled()) + */ +typedef enum { + COGL_DEPTH_TEST_FUNCTION_NEVER = 0x0200, + COGL_DEPTH_TEST_FUNCTION_LESS = 0x0201, + COGL_DEPTH_TEST_FUNCTION_EQUAL = 0x0202, + COGL_DEPTH_TEST_FUNCTION_LEQUAL = 0x0203, + COGL_DEPTH_TEST_FUNCTION_GREATER = 0x0204, + COGL_DEPTH_TEST_FUNCTION_NOTEQUAL = 0x0205, + COGL_DEPTH_TEST_FUNCTION_GEQUAL = 0x0206, + COGL_DEPTH_TEST_FUNCTION_ALWAYS = 0x0207 +} CoglDepthTestFunction; +/* NB: The above definitions are taken from gl.h equivalents */ + +typedef enum { /*< prefix=COGL_RENDERER_ERROR >*/ + COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN, + COGL_RENDERER_ERROR_BAD_CONSTRAINT +} CoglRendererError; + +/** + * CoglFilterReturn: + * @COGL_FILTER_CONTINUE: The event was not handled, continues the + * processing + * @COGL_FILTER_REMOVE: Remove the event, stops the processing + * + * Return values for the #CoglXlibFilterFunc and #CoglWin32FilterFunc functions. + * + * Stability: Unstable + */ +typedef enum _CoglFilterReturn { /*< prefix=COGL_FILTER >*/ + COGL_FILTER_CONTINUE, + COGL_FILTER_REMOVE +} CoglFilterReturn; + +typedef enum _CoglWinsysFeature +{ + /* Available if the window system can support multiple onscreen + * framebuffers at the same time. */ + COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, + + /* Available if onscreen framebuffer swaps can be automatically + * throttled to the vblank frequency. */ + COGL_WINSYS_FEATURE_SWAP_THROTTLE, + + /* Available if its possible to query a counter that + * increments at each vblank. */ + COGL_WINSYS_FEATURE_VBLANK_COUNTER, + + /* Available if its possible to wait until the next vertical + * blank period */ + COGL_WINSYS_FEATURE_VBLANK_WAIT, + + /* Available if the window system supports mapping native + * pixmaps to textures. */ + COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP, + + /* Available if the window system supports reporting an event + * for swap buffer completions. */ + COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, + + /* Available if it's possible to swap a list of sub rectangles + * from the back buffer to the front buffer */ + COGL_WINSYS_FEATURE_SWAP_REGION, + + /* Available if swap_region requests can be automatically throttled + * to the vblank frequency. */ + COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, + + /* Available if the swap region implementation won't tear and thus + * only needs to be throttled to the framerate */ + COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED, + + /* Avaiable if the age of the back buffer can be queried */ + COGL_WINSYS_FEATURE_BUFFER_AGE, + + /* Avaiable if the winsys directly handles _SYNC and _COMPLETE events */ + COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, + + COGL_WINSYS_FEATURE_N_FEATURES +} CoglWinsysFeature; + +/** + * CoglColorMask: + * @COGL_COLOR_MASK_NONE: None of the color channels are masked + * @COGL_COLOR_MASK_RED: Masks the red color channel + * @COGL_COLOR_MASK_GREEN: Masks the green color channel + * @COGL_COLOR_MASK_BLUE: Masks the blue color channel + * @COGL_COLOR_MASK_ALPHA: Masks the alpha color channel + * @COGL_COLOR_MASK_ALL: All of the color channels are masked + * + * Defines a bit mask of color channels. This can be used with + * cogl_pipeline_set_color_mask() for example to define which color + * channels should be written to the current framebuffer when + * drawing something. + */ +typedef enum +{ + COGL_COLOR_MASK_NONE = 0, + COGL_COLOR_MASK_RED = 1L<<0, + COGL_COLOR_MASK_GREEN = 1L<<1, + COGL_COLOR_MASK_BLUE = 1L<<2, + COGL_COLOR_MASK_ALPHA = 1L<<3, + /* XXX: glib-mkenums is a perl script that can't cope if we split + * this onto multiple lines! *sigh* */ + COGL_COLOR_MASK_ALL = (COGL_COLOR_MASK_RED | COGL_COLOR_MASK_GREEN | COGL_COLOR_MASK_BLUE | COGL_COLOR_MASK_ALPHA) +} CoglColorMask; + +/** + * CoglWinding: + * @COGL_WINDING_CLOCKWISE: Vertices are in a clockwise order + * @COGL_WINDING_COUNTER_CLOCKWISE: Vertices are in a counter-clockwise order + * + * Enum used to represent the two directions of rotation. This can be + * used to set the front face for culling by calling + * cogl_pipeline_set_front_face_winding(). + */ +typedef enum +{ + COGL_WINDING_CLOCKWISE, + COGL_WINDING_COUNTER_CLOCKWISE +} CoglWinding; + +/** + * CoglBufferBit: + * @COGL_BUFFER_BIT_COLOR: Selects the primary color buffer + * @COGL_BUFFER_BIT_DEPTH: Selects the depth buffer + * @COGL_BUFFER_BIT_STENCIL: Selects the stencil buffer + * + * Types of auxiliary buffers + * + * Since: 1.0 + */ +typedef enum { + COGL_BUFFER_BIT_COLOR = 1L<<0, + COGL_BUFFER_BIT_DEPTH = 1L<<1, + COGL_BUFFER_BIT_STENCIL = 1L<<2 +} CoglBufferBit; + +/** + * CoglReadPixelsFlags: + * @COGL_READ_PIXELS_COLOR_BUFFER: Read from the color buffer + * + * Flags for cogl_framebuffer_read_pixels_into_bitmap() + * + * Since: 1.0 + */ +typedef enum { /*< prefix=COGL_READ_PIXELS >*/ + COGL_READ_PIXELS_COLOR_BUFFER = 1L << 0 +} CoglReadPixelsFlags; + +/** + * CoglStereoMode: + * @COGL_STEREO_BOTH: draw to both stereo buffers + * @COGL_STEREO_LEFT: draw only to the left stereo buffer + * @COGL_STEREO_RIGHT: draw only to the left stereo buffer + * + * Represents how draw should affect the two buffers + * of a stereo framebuffer. See cogl_framebuffer_set_stereo_mode(). + */ +typedef enum { + COGL_STEREO_BOTH, + COGL_STEREO_LEFT, + COGL_STEREO_RIGHT +} CoglStereoMode; + +COGL_END_DECLS + +#endif /* __COGL_TYPES_H__ */ diff --git a/cogl/cogl/cogl-util.c b/cogl/cogl/cogl-util.c new file mode 100644 index 000000000..a8a650421 --- /dev/null +++ b/cogl/cogl/cogl-util.c @@ -0,0 +1,286 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-util.h" +#include "cogl-private.h" + +/* + * cogl_util_next_p2: + * @a: Value to get the next power of two + * + * Calculates the next power of two greater than or equal to @a. + * + * Return value: @a if @a is already a power of two, otherwise returns + * the next nearest power of two. + */ +int +_cogl_util_next_p2 (int a) +{ + int rval = 1; + + while (rval < a) + rval <<= 1; + + return rval; +} + +unsigned int +_cogl_util_one_at_a_time_mix (unsigned int hash) +{ + hash += ( hash << 3 ); + hash ^= ( hash >> 11 ); + hash += ( hash << 15 ); + + return hash; +} + +/* The 'ffs' function is part of C99 so it isn't always available */ +#ifndef HAVE_FFS + +int +_cogl_util_ffs (int num) +{ + int i = 1; + + if (num == 0) + return 0; + + while ((num & 1) == 0) + { + num >>= 1; + i++; + } + + return i; +} +#endif /* HAVE_FFS */ + +/* The 'ffsl' is non-standard but when building with GCC we'll use its + builtin instead */ +#ifndef COGL_UTIL_HAVE_BUILTIN_FFSL + +int +_cogl_util_ffsl_wrapper (long int num) +{ + int i = 1; + + if (num == 0) + return 0; + + while ((num & 1) == 0) + { + num >>= 1; + i++; + } + + return i; +} + +#endif /* COGL_UTIL_HAVE_BUILTIN_FFSL */ + +#ifndef COGL_UTIL_HAVE_BUILTIN_POPCOUNTL + +const unsigned char +_cogl_util_popcount_table[256] = + { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 + }; + +#endif /* COGL_UTIL_HAVE_BUILTIN_POPCOUNTL */ + +/* tests/conform/test-bitmask.c tests some cogl internals and includes this + * file directly but since these functions depend on other internal Cogl + * symbols we hide them from test-bitmask.c + * + * XXX: maybe there's a better way for us to handle internal testing + * to avoid needing hacks like this. + */ +#ifndef _COGL_IN_TEST_BITMASK + +/* Given a set of red, green and blue component masks, a depth and + * bits per pixel this function tries to determine a corresponding + * CoglPixelFormat. + * + * The depth is measured in bits not including padding for un-used + * alpha. The bits per pixel (bpp) does include padding for un-used + * alpha. + * + * This function firstly aims to match formats with RGB ordered + * components and only considers alpha coming first, in the most + * significant bits. If the function fails to match then it recurses + * by either switching the r and b masks around to check for BGR + * ordered formats or it recurses with the masks shifted to check for + * formats where the alpha component is the least significant bits. + */ +static CoglPixelFormat +_cogl_util_pixel_format_from_masks_real (unsigned long r_mask, + unsigned long g_mask, + unsigned long b_mask, + int depth, int bpp, + CoglBool check_bgr, + CoglBool check_afirst, + int recursion_depth) +{ + CoglPixelFormat image_format; + + if (depth == 24 && bpp == 24 && + r_mask == 0xff0000 && g_mask == 0xff00 && b_mask == 0xff) + { + return COGL_PIXEL_FORMAT_RGB_888; + } + else if ((depth == 24 || depth == 32) && bpp == 32 && + r_mask == 0xff0000 && g_mask == 0xff00 && b_mask == 0xff) + { + return COGL_PIXEL_FORMAT_ARGB_8888_PRE; + } + else if ((depth == 30 || depth == 32) && + r_mask == 0x3ff00000 && g_mask == 0xffc00 && b_mask == 0x3ff) + { + return COGL_PIXEL_FORMAT_ARGB_2101010_PRE; + } + else if (depth == 16 && bpp == 16 && + r_mask == 0xf800 && g_mask == 0x7e0 && b_mask == 0x1f) + { + return COGL_PIXEL_FORMAT_RGB_565; + } + + if (recursion_depth == 2) + return 0; + + /* Check for BGR ordering if we didn't find a match */ + if (check_bgr) + { + image_format = + _cogl_util_pixel_format_from_masks_real (b_mask, g_mask, r_mask, + depth, bpp, + FALSE, + TRUE, + recursion_depth + 1); + if (image_format) + return image_format ^ COGL_BGR_BIT; + } + + /* Check for alpha in the least significant bits if we still + * haven't found a match... */ + if (check_afirst && depth != bpp) + { + int shift = bpp - depth; + + image_format = + _cogl_util_pixel_format_from_masks_real (r_mask >> shift, + g_mask >> shift, + b_mask >> shift, + depth, bpp, + TRUE, + FALSE, + recursion_depth + 1); + if (image_format) + return image_format ^ COGL_AFIRST_BIT; + } + + return 0; +} + +CoglPixelFormat +_cogl_util_pixel_format_from_masks (unsigned long r_mask, + unsigned long g_mask, + unsigned long b_mask, + int depth, int bpp, + CoglBool byte_order_is_lsb_first) +{ + CoglPixelFormat image_format = + _cogl_util_pixel_format_from_masks_real (r_mask, g_mask, b_mask, + depth, bpp, + TRUE, + TRUE, + 0); + + if (!image_format) + { + const char *byte_order[] = { "MSB first", "LSB first" }; + g_warning ("Could not find a matching pixel format for red mask=0x%lx," + "green mask=0x%lx, blue mask=0x%lx at depth=%d, bpp=%d " + "and byte order=%s\n", r_mask, g_mask, b_mask, depth, bpp, + byte_order[!!byte_order_is_lsb_first]); + return 0; + } + + /* If the image is in little-endian then the order in memory is + reversed */ + if (byte_order_is_lsb_first && + _cogl_pixel_format_is_endian_dependant (image_format)) + { + image_format ^= COGL_BGR_BIT; + if (image_format & COGL_A_BIT) + image_format ^= COGL_AFIRST_BIT; + } + + return image_format; +} + +#ifndef HAVE_MEMMEM + +char * +_cogl_util_memmem (const void *haystack, + size_t haystack_len, + const void *needle, + size_t needle_len) +{ + size_t i; + + if (needle_len > haystack_len) + return NULL; + + for (i = 0; i <= haystack_len - needle_len; i++) + if (!memcmp ((const char *) haystack + i, needle, needle_len)) + return (char *) haystack + i; + + return NULL; +} + +#endif /* HAVE_MEMMEM */ + +#endif /* _COGL_IN_TEST_BITMASK */ diff --git a/cogl/cogl/cogl-util.h b/cogl/cogl/cogl-util.h new file mode 100644 index 000000000..160c97888 --- /dev/null +++ b/cogl/cogl/cogl-util.h @@ -0,0 +1,305 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_UTIL_H +#define __COGL_UTIL_H + +#include +#include + +#include +#include "cogl-types.h" + +#ifndef COGL_HAS_GLIB_SUPPORT +#include +#endif + +/* Double check that config.h has been included */ +#if !defined (PACKAGE_NAME) && !defined (_COGL_IN_TEST_BITMASK) +#error "config.h must be included before including cogl-util.h" +#endif + +/* When compiling with Visual Studio, symbols that represent data that + are exported out of the DLL need to be marked with the dllexport + attribute. */ +#ifdef _MSC_VER +#ifdef COGL_BUILD_EXP +#define COGL_EXPORT __declspec(dllexport) +#else +#define COGL_EXPORT __declspec(dllimport) +#endif +#else +#define COGL_EXPORT +#endif + +int +_cogl_util_next_p2 (int a); + +/* The signbit macro is defined by ISO C99 so it should be available, + however if it's not we can fallback to an evil hack */ +#ifdef signbit +#define cogl_util_float_signbit(x) signbit(x) +#else +/* This trick was stolen from here: + http://lists.boost.org/Archives/boost/2006/08/108731.php + + It xors the integer reinterpretations of -1.0f and 1.0f. In theory + they should only differ by the signbit so that gives a mask for the + sign which we can just test against the value */ +static inline CoglBool +cogl_util_float_signbit (float x) +{ + static const union { float f; uint32_t i; } negative_one = { -1.0f }; + static const union { float f; uint32_t i; } positive_one = { +1.0f }; + union { float f; uint32_t i; } value = { x }; + + return !!((negative_one.i ^ positive_one.i) & value.i); +} +#endif + +/* This is a replacement for the nearbyint function which always + rounds to the nearest integer. nearbyint is apparently a C99 + function so it might not always be available but also it seems in + glibc it is defined as a function call so this macro could end up + faster anyway. We can't just add 0.5f because it will break for + negative numbers. */ +#define COGL_UTIL_NEARBYINT(x) ((int) ((x) < 0.0f ? (x) - 0.5f : (x) + 0.5f)) + +/* Returns whether the given integer is a power of two */ +static inline CoglBool +_cogl_util_is_pot (unsigned int num) +{ + /* Make sure there is only one bit set */ + return (num & (num - 1)) == 0; +} + +/* Split Bob Jenkins' One-at-a-Time hash + * + * This uses the One-at-a-Time hash algorithm designed by Bob Jenkins + * but the mixing step is split out so the function can be used in a + * more incremental fashion. + */ +static inline unsigned int +_cogl_util_one_at_a_time_hash (unsigned int hash, + const void *key, + size_t bytes) +{ + const unsigned char *p = key; + int i; + + for (i = 0; i < bytes; i++) + { + hash += p[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + return hash; +} + +unsigned int +_cogl_util_one_at_a_time_mix (unsigned int hash); + +/* These two builtins are available since GCC 3.4 */ +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define COGL_UTIL_HAVE_BUILTIN_FFSL +#define COGL_UTIL_HAVE_BUILTIN_POPCOUNTL +#define COGL_UTIL_HAVE_BUILTIN_CLZ +#endif + +/* The 'ffs' function is part of C99 so it isn't always available */ +#ifdef HAVE_FFS +#define _cogl_util_ffs ffs +#else +int +_cogl_util_ffs (int num); +#endif + +/* The 'ffsl' function is non-standard but GCC has a builtin for it + since 3.4 which we can use */ +#ifdef COGL_UTIL_HAVE_BUILTIN_FFSL +#define _cogl_util_ffsl __builtin_ffsl +#else +/* If ints and longs are the same size we can just use ffs. Hopefully + the compiler will optimise away this conditional */ +#define _cogl_util_ffsl(x) \ + (sizeof (long int) == sizeof (int) ? _cogl_util_ffs ((int) x) : \ + _cogl_util_ffsl_wrapper (x)) +int +_cogl_util_ffsl_wrapper (long int num); +#endif /* COGL_UTIL_HAVE_BUILTIN_FFSL */ + +static inline unsigned int +_cogl_util_fls (unsigned int n) +{ +#ifdef COGL_UTIL_HAVE_BUILTIN_CLZ + return n == 0 ? 0 : sizeof (unsigned int) * 8 - __builtin_clz (n); +#else + unsigned int v = 1; + + if (n == 0) + return 0; + + while (n >>= 1) + v++; + + return v; +#endif +} + +#ifdef COGL_UTIL_HAVE_BUILTIN_POPCOUNTL +#define _cogl_util_popcountl __builtin_popcountl +#else +extern const unsigned char _cogl_util_popcount_table[256]; + +/* There are many ways of doing popcount but doing a table lookup + seems to be the most robust against different sizes for long. Some + pages seem to claim it's the fastest method anyway. */ +static inline int +_cogl_util_popcountl (unsigned long num) +{ + int i; + int sum = 0; + + /* Let's hope GCC will unroll this loop.. */ + for (i = 0; i < sizeof (num); i++) + sum += _cogl_util_popcount_table[(num >> (i * 8)) & 0xff]; + + return sum; +} + +#endif /* COGL_UTIL_HAVE_BUILTIN_POPCOUNTL */ + +#ifdef COGL_HAS_GLIB_SUPPORT +#define _COGL_RETURN_IF_FAIL(EXPR) g_return_if_fail(EXPR) +#define _COGL_RETURN_VAL_IF_FAIL(EXPR, VAL) g_return_val_if_fail(EXPR, VAL) +#else +#ifdef COGL_ENABLE_DEBUG +#define _COGL_RETURN_START do { +#define _COGL_RETURN_END } while (0) +#else /* COGL_ENABLE_DEBUG */ +/* If debugging is disabled then we don't actually want to do the + * check but we still want the code for the expression to be generated + * so that it won't give loads of warnings about unused variables. + * Therefore we just surround the block with if(0) */ +#define _COGL_RETURN_START do { if (0) { +#define _COGL_RETURN_END } } while (0) +#endif /* COGL_ENABLE_DEBUG */ +#define _COGL_RETURN_IF_FAIL(EXPR) _COGL_RETURN_START { \ + if (!(EXPR)) \ + { \ + fprintf (stderr, "file %s: line %d: assertion `%s' failed", \ + __FILE__, \ + __LINE__, \ + #EXPR); \ + return; \ + }; \ + } _COGL_RETURN_END +#define _COGL_RETURN_VAL_IF_FAIL(EXPR, VAL) _COGL_RETURN_START { \ + if (!(EXPR)) \ + { \ + fprintf (stderr, "file %s: line %d: assertion `%s' failed", \ + __FILE__, \ + __LINE__, \ + #EXPR); \ + return (VAL); \ + }; \ + } _COGL_RETURN_END +#endif /* COGL_HAS_GLIB_SUPPORT */ + +/* Match a CoglPixelFormat according to channel masks, color depth, + * bits per pixel and byte order. These information are provided by + * the Visual and XImage structures. + * + * If no specific pixel format could be found, COGL_PIXEL_FORMAT_ANY + * is returned. + */ +CoglPixelFormat +_cogl_util_pixel_format_from_masks (unsigned long r_mask, + unsigned long g_mask, + unsigned long b_mask, + int depth, int bpp, + int byte_order); + +/* Since we can't rely on _Static_assert always being available for + * all compilers we have limited static assert that can be used in + * C code but not in headers. + */ +#define _COGL_TYPEDEF_ASSERT(EXPRESSION) \ + typedef struct { char Compile_Time_Assertion[(EXPRESSION) ? 1 : -1]; } \ + G_PASTE (_GStaticAssert_, __LINE__) + +/* _COGL_STATIC_ASSERT: + * @expression: An expression to assert evaluates to true at compile + * time. + * @message: A message to print to the console if the assertion fails + * at compile time. + * + * Allows you to assert that an expression evaluates to true at + * compile time and aborts compilation if not. If possible message + * will also be printed if the assertion fails. + * + * Note: Only Gcc >= 4.6 supports the c11 _Static_assert which lets us + * print a nice message if the compile time assertion fails. + */ +#ifdef HAVE_STATIC_ASSERT +#define _COGL_STATIC_ASSERT(EXPRESSION, MESSAGE) \ + _Static_assert (EXPRESSION, MESSAGE); +#else +#define _COGL_STATIC_ASSERT(EXPRESSION, MESSAGE) +#endif + +#ifdef HAVE_MEMMEM +#define _cogl_util_memmem memmem +#else +char * +_cogl_util_memmem (const void *haystack, + size_t haystack_len, + const void *needle, + size_t needle_len); +#endif + +static inline void +_cogl_util_scissor_intersect (int rect_x0, + int rect_y0, + int rect_x1, + int rect_y1, + int *scissor_x0, + int *scissor_y0, + int *scissor_x1, + int *scissor_y1) +{ + *scissor_x0 = MAX (*scissor_x0, rect_x0); + *scissor_y0 = MAX (*scissor_y0, rect_y0); + *scissor_x1 = MIN (*scissor_x1, rect_x1); + *scissor_y1 = MIN (*scissor_y1, rect_y1); +} + +#endif /* __COGL_UTIL_H */ diff --git a/cogl/cogl/cogl-vector.c b/cogl/cogl/cogl-vector.c new file mode 100644 index 000000000..9da94aff4 --- /dev/null +++ b/cogl/cogl/cogl-vector.c @@ -0,0 +1,300 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +#define X 0 +#define Y 1 +#define Z 2 +#define W 3 + +void +cogl_vector3_init (float *vector, float x, float y, float z) +{ + vector[X] = x; + vector[Y] = y; + vector[Z] = z; +} + +void +cogl_vector3_init_zero (float *vector) +{ + memset (vector, 0, sizeof (float) * 3); +} + +CoglBool +cogl_vector3_equal (const void *v1, const void *v2) +{ + float *vector0 = (float *)v1; + float *vector1 = (float *)v2; + + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + /* There's no point picking an arbitrary epsilon that's appropriate + * for comparing the components so we just use == that will at least + * consider -0 and 0 to be equal. */ + return + vector0[X] == vector1[X] && + vector0[Y] == vector1[Y] && + vector0[Z] == vector1[Z]; +} + +CoglBool +cogl_vector3_equal_with_epsilon (const float *vector0, + const float *vector1, + float epsilon) +{ + _COGL_RETURN_VAL_IF_FAIL (vector0 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (vector1 != NULL, FALSE); + + if (fabsf (vector0[X] - vector1[X]) < epsilon && + fabsf (vector0[Y] - vector1[Y]) < epsilon && + fabsf (vector0[Z] - vector1[Z]) < epsilon) + return TRUE; + else + return FALSE; +} + +float * +cogl_vector3_copy (const float *vector) +{ + if (vector) + return g_slice_copy (sizeof (float) * 3, vector); + return NULL; +} + +void +cogl_vector3_free (float *vector) +{ + g_slice_free1 (sizeof (float) * 3, vector); +} + +void +cogl_vector3_invert (float *vector) +{ + vector[X] = -vector[X]; + vector[Y] = -vector[Y]; + vector[Z] = -vector[Z]; +} + +void +cogl_vector3_add (float *result, + const float *a, + const float *b) +{ + result[X] = a[X] + b[X]; + result[Y] = a[Y] + b[Y]; + result[Z] = a[Z] + b[Z]; +} + +void +cogl_vector3_subtract (float *result, + const float *a, + const float *b) +{ + result[X] = a[X] - b[X]; + result[Y] = a[Y] - b[Y]; + result[Z] = a[Z] - b[Z]; +} + +void +cogl_vector3_multiply_scalar (float *vector, + float scalar) +{ + vector[X] *= scalar; + vector[Y] *= scalar; + vector[Z] *= scalar; +} + +void +cogl_vector3_divide_scalar (float *vector, + float scalar) +{ + float one_over_scalar = 1.0f / scalar; + vector[X] *= one_over_scalar; + vector[Y] *= one_over_scalar; + vector[Z] *= one_over_scalar; +} + +void +cogl_vector3_normalize (float *vector) +{ + float mag_squared = + vector[X] * vector[X] + + vector[Y] * vector[Y] + + vector[Z] * vector[Z]; + + if (mag_squared > 0.0f) + { + float one_over_mag = 1.0f / sqrtf (mag_squared); + vector[X] *= one_over_mag; + vector[Y] *= one_over_mag; + vector[Z] *= one_over_mag; + } +} + +float +cogl_vector3_magnitude (const float *vector) +{ + return sqrtf (vector[X] * vector[X] + + vector[Y] * vector[Y] + + vector[Z] * vector[Z]); +} + +void +cogl_vector3_cross_product (float *result, + const float *a, + const float *b) +{ + float tmp[3]; + + tmp[X] = a[Y] * b[Z] - a[Z] * b[Y]; + tmp[Y] = a[Z] * b[X] - a[X] * b[Z]; + tmp[Z] = a[X] * b[Y] - a[Y] * b[X]; + result[X] = tmp[X]; + result[Y] = tmp[Y]; + result[Z] = tmp[Z]; +} + +float +cogl_vector3_dot_product (const float *a, const float *b) +{ + return a[X] * b[X] + a[Y] * b[Y] + a[Z] * b[Z]; +} + +float +cogl_vector3_distance (const float *a, const float *b) +{ + float dx = b[X] - a[X]; + float dy = b[Y] - a[Y]; + float dz = b[Z] - a[Z]; + + return sqrtf (dx * dx + dy * dy + dz * dz); +} + +#if 0 +void +cogl_vector4_init (float *vector, float x, float y, float z) +{ + vector[X] = x; + vector[Y] = y; + vector[Z] = z; + vector[W] = w; +} + +void +cogl_vector4_init_zero (float *vector) +{ + memset (vector, 0, sizeof (CoglVector4)); +} + +void +cogl_vector4_init_from_vector4 (float *vector, float *src) +{ + *vector4 = *src; +} + +CoglBool +cogl_vector4_equal (const void *v0, const void *v1) +{ + _COGL_RETURN_VAL_IF_FAIL (v1 != NULL, FALSE); + _COGL_RETURN_VAL_IF_FAIL (v2 != NULL, FALSE); + + return memcmp (v1, v2, sizeof (float) * 4) == 0 ? TRUE : FALSE; +} + +float * +cogl_vector4_copy (float *vector) +{ + if (vector) + return g_slice_dup (CoglVector4, vector); + return NULL; +} + +void +cogl_vector4_free (float *vector) +{ + g_slice_free (CoglVector4, vector); +} + +void +cogl_vector4_invert (float *vector) +{ + vector.x = -vector.x; + vector.y = -vector.y; + vector.z = -vector.z; + vector.w = -vector.w; +} + +void +cogl_vector4_add (float *result, + float *a, + float *b) +{ + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + result.w = a.w + b.w; +} + +void +cogl_vector4_subtract (float *result, + float *a, + float *b) +{ + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + result.w = a.w - b.w; +} + +void +cogl_vector4_divide (float *vector, + float scalar) +{ + float one_over_scalar = 1.0f / scalar; + result.x *= one_over_scalar; + result.y *= one_over_scalar; + result.z *= one_over_scalar; + result.w *= one_over_scalar; +} + +#endif diff --git a/cogl/cogl/cogl-vector.h b/cogl/cogl/cogl-vector.h new file mode 100644 index 000000000..08cf017f8 --- /dev/null +++ b/cogl/cogl/cogl-vector.h @@ -0,0 +1,356 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_VECTOR_H +#define __COGL_VECTOR_H + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-vector + * @short_description: Functions for handling single precision float + * vectors. + * + * This exposes a utility API that can be used for basic manipulation of 3 + * component float vectors. + */ + +/** + * cogl_vector3_init: + * @vector: The 3 component vector you want to initialize + * @x: The x component + * @y: The y component + * @z: The z component + * + * Initializes a 3 component, single precision float vector which can + * then be manipulated with the cogl_vector convenience APIs. Vectors + * can also be used in places where a "point" is often desired. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_init (float *vector, float x, float y, float z); + +/** + * cogl_vector3_init_zero: + * @vector: The 3 component vector you want to initialize + * + * Initializes a 3 component, single precision float vector with zero + * for each component. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_init_zero (float *vector); + +/** + * cogl_vector3_equal: + * @v1: The first 3 component vector you want to compare + * @v2: The second 3 component vector you want to compare + * + * Compares the components of two vectors and returns TRUE if they are + * the same. + * + * The comparison of the components is done with the '==' operator + * such that -0 is considered equal to 0, but otherwise there is no + * fuzziness such as an epsilon to consider vectors that are + * essentially identical except for some minor precision error + * differences due to the way they have been manipulated. + * + * Returns: TRUE if the vectors are equal else FALSE. + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_vector3_equal (const void *v1, const void *v2); + +/** + * cogl_vector3_equal_with_epsilon: + * @vector0: The first 3 component vector you want to compare + * @vector1: The second 3 component vector you want to compare + * @epsilon: The allowable difference between components to still be + * considered equal + * + * Compares the components of two vectors using the given epsilon and + * returns TRUE if they are the same, using an internal epsilon for + * comparing the floats. + * + * Each component is compared against the epsilon value in this way: + * |[ + * if (fabsf (vector0->x - vector1->x) < epsilon) + * ]| + * + * Returns: TRUE if the vectors are equal else FALSE. + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_vector3_equal_with_epsilon (const float *vector0, + const float *vector1, + float epsilon); + +/** + * cogl_vector3_copy: + * @vector: The 3 component vector you want to copy + * + * Allocates a new 3 component float vector on the heap initializing + * the components from the given @vector and returns a pointer to the + * newly allocated vector. You should free the memory using + * cogl_vector3_free() + * + * Returns: A newly allocated 3 component float vector + * + * Since: 1.4 + * Stability: Unstable + */ +float * +cogl_vector3_copy (const float *vector); + +/** + * cogl_vector3_free: + * @vector: The 3 component you want to free + * + * Frees a 3 component vector that was previously allocated with + * cogl_vector3_copy() + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_free (float *vector); + +/** + * cogl_vector3_invert: + * @vector: The 3 component vector you want to manipulate + * + * Inverts/negates all the components of the given @vector. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_invert (float *vector); + +/** + * cogl_vector3_add: + * @result: Where you want the result written + * @a: The first vector operand + * @b: The second vector operand + * + * Adds each of the corresponding components in vectors @a and @b + * storing the results in @result. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_add (float *result, + const float *a, + const float *b); + +/** + * cogl_vector3_subtract: + * @result: Where you want the result written + * @a: The first vector operand + * @b: The second vector operand + * + * Subtracts each of the corresponding components in vector @b from + * @a storing the results in @result. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_subtract (float *result, + const float *a, + const float *b); + +/** + * cogl_vector3_multiply_scalar: + * @vector: The 3 component vector you want to manipulate + * @scalar: The scalar you want to multiply the vector components by + * + * Multiplies each of the @vector components by the given scalar. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_multiply_scalar (float *vector, + float scalar); + +/** + * cogl_vector3_divide_scalar: + * @vector: The 3 component vector you want to manipulate + * @scalar: The scalar you want to divide the vector components by + * + * Divides each of the @vector components by the given scalar. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_divide_scalar (float *vector, + float scalar); + +/** + * cogl_vector3_normalize: + * @vector: The 3 component vector you want to manipulate + * + * Updates the vector so it is a "unit vector" such that the + * @vectors magnitude or length is equal to 1. + * + * It's safe to use this function with the [0, 0, 0] vector, it will not + * try to divide components by 0 (its norm) and will leave the vector + * untouched. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_normalize (float *vector); + +/** + * cogl_vector3_magnitude: + * @vector: The 3 component vector you want the magnitude for + * + * Calculates the scalar magnitude or length of @vector. + * + * Returns: The magnitude of @vector. + * + * Since: 1.4 + * Stability: Unstable + */ +float +cogl_vector3_magnitude (const float *vector); + +/** + * cogl_vector3_cross_product: + * @result: Where you want the result written + * @u: Your first 3 component vector + * @v: Your second 3 component vector + * + * Calculates the cross product between the two vectors @u and @v. + * + * The cross product is a vector perpendicular to both @u and @v. This + * can be useful for calculating the normal of a polygon by creating + * two vectors in its plane using the polygons vertices and taking + * their cross product. + * + * If the two vectors are parallel then the cross product is 0. + * + * You can use a right hand rule to determine which direction the + * perpendicular vector will point: If you place the two vectors tail, + * to tail and imagine grabbing the perpendicular line that extends + * through the common tail with your right hand such that you fingers + * rotate in the direction from @u to @v then the resulting vector + * points along your extended thumb. + * + * Returns: The cross product between two vectors @u and @v. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_vector3_cross_product (float *result, + const float *u, + const float *v); + +/** + * cogl_vector3_dot_product: + * @a: Your first 3 component vector + * @b: Your second 3 component vector + * + * Calculates the dot product of the two 3 component vectors. This + * can be used to determine the magnitude of one vector projected onto + * another. (for example a surface normal) + * + * For example if you have a polygon with a given normal vector and + * some other point for which you want to calculate its distance from + * the polygon, you can create a vector between one of the polygon + * vertices and that point and use the dot product to calculate the + * magnitude for that vector but projected onto the normal of the + * polygon. This way you don't just get the distance from the point to + * the edge of the polygon you get the distance from the point to the + * nearest part of the polygon. + * + * If you don't use a unit length normal in the above example + * then you would then also have to divide the result by the magnitude + * of the normal + * + * The dot product is calculated as: + * |[ + * (a->x * b->x + a->y * b->y + a->z * b->z) + * ]| + * + * For reference, the dot product can also be calculated from the + * angle between two vectors as: + * |[ + * |a||b|cos𝜃 + * ]| + * + * Returns: The dot product of two vectors. + * + * Since: 1.4 + * Stability: Unstable + */ +float +cogl_vector3_dot_product (const float *a, const float *b); + +/** + * cogl_vector3_distance: + * @a: The first point + * @b: The second point + * + * If you consider the two given vectors as (x,y,z) points instead + * then this will compute the distance between those two points. + * + * Returns: The distance between two points given as 3 component + * vectors. + * + * Since: 1.4 + * Stability: Unstable + */ +float +cogl_vector3_distance (const float *a, const float *b); + +COGL_END_DECLS + +#endif /* __COGL_VECTOR_H */ + diff --git a/cogl/cogl/cogl-version.h b/cogl/cogl/cogl-version.h new file mode 100644 index 000000000..bc82437ba --- /dev/null +++ b/cogl/cogl/cogl-version.h @@ -0,0 +1,358 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_VERSION_H__ +#define __COGL_VERSION_H__ + +#include + +/** + * SECTION:cogl-version + * @short_description: Macros for determining the version of Cogl being used + * + * Cogl offers a set of macros for checking the version of the library + * at compile time. + * + * Cogl adds version information to both API deprecations and additions; + * by definining the macros %COGL_VERSION_MIN_REQUIRED and + * %COGL_VERSION_MAX_ALLOWED, you can specify the range of Cogl versions + * whose API you want to use. Functions that were deprecated before, or + * introduced after, this range will trigger compiler warnings. For instance, + * if we define the following symbols: + * + * |[ + * COGL_VERSION_MIN_REQUIRED = COGL_VERSION_1_6 + * COGL_VERSION_MAX_ALLOWED = COGL_VERSION_1_8 + * ]| + * + * and we have the following functions annotated in the Cogl headers: + * + * |[ + * COGL_DEPRECATED_IN_1_4 void cogl_function_A (void); + * COGL_DEPRECATED_IN_1_6 void cogl_function_B (void); + * COGL_AVAILABLE_IN_1_8 void cogl_function_C (void); + * COGL_AVAILABLE_IN_1_10 void cogl_function_D (void); + * ]| + * + * then any application code using the functions above will get the output: + * + * |[ + * cogl_function_A: deprecation warning + * cogl_function_B: no warning + * cogl_function_C: no warning + * cogl_function_D: symbol not available warning + * ]| + * + * It is possible to disable the compiler warnings by defining the macro + * %COGL_DISABLE_DEPRECATION_WARNINGS before including the cogl.h + * header. + */ + +/** + * COGL_VERSION_MAJOR: + * + * The major version of the Cogl library (1, if %COGL_VERSION is 1.2.3) + * + * Since: 1.12.0 + */ +#define COGL_VERSION_MAJOR COGL_VERSION_MAJOR_INTERNAL + +/** + * COGL_VERSION_MINOR: + * + * The minor version of the Cogl library (2, if %COGL_VERSION is 1.2.3) + * + * Since: 1.12.0 + */ +#define COGL_VERSION_MINOR COGL_VERSION_MINOR_INTERNAL + +/** + * COGL_VERSION_MICRO: + * + * The micro version of the Cogl library (3, if %COGL_VERSION is 1.2.3) + * + * Since: 1.12.0 + */ +#define COGL_VERSION_MICRO COGL_VERSION_MICRO_INTERNAL + +/** + * COGL_VERSION_STRING: + * + * The full version of the Cogl library, in string form (suited for + * string concatenation) + * + * Since: 1.12.0 + */ +#define COGL_VERSION_STRING COGL_VERSION_STRING_INTERNAL + +/* Macros to handle compacting a 3-component version number into an + * int for quick comparison. This assumes all of the components are <= + * 1023 and that an int is >= 31 bits */ +#define COGL_VERSION_COMPONENT_BITS 10 +#define COGL_VERSION_MAX_COMPONENT_VALUE \ + ((1 << COGL_VERSION_COMPONENT_BITS) - 1) + +/** + * COGL_VERSION: + * + * The Cogl version encoded into a single integer using the + * COGL_VERSION_ENCODE() macro. This can be used for quick comparisons + * with particular versions. + * + * Since: 1.12.0 + */ +#define COGL_VERSION \ + COGL_VERSION_ENCODE (COGL_VERSION_MAJOR, \ + COGL_VERSION_MINOR, \ + COGL_VERSION_MICRO) + +/** + * COGL_VERSION_ENCODE: + * @major: The major part of a version number + * @minor: The minor part of a version number + * @micro: The micro part of a version number + * + * Encodes a 3 part version number into a single integer. This can be + * used to compare the Cogl version. For example if there is a known + * bug in Cogl versions between 1.3.2 and 1.3.4 you could use the + * following code to provide a workaround: + * + * |[ + * #if COGL_VERSION >= COGL_VERSION_ENCODE (1, 3, 2) && \ + * COGL_VERSION <= COGL_VERSION_ENCODE (1, 3, 4) + * /* Do the workaround */ + * #endif + * ]| + * + * Since: 1.12.0 + */ +#define COGL_VERSION_ENCODE(major, minor, micro) \ + (((major) << (COGL_VERSION_COMPONENT_BITS * 2)) | \ + ((minor) << COGL_VERSION_COMPONENT_BITS) \ + | (micro)) + +/** + * COGL_VERSION_GET_MAJOR: + * @version: An encoded version number + * + * Extracts the major part of an encoded version number. + * + * Since: 1.12.0 + */ +#define COGL_VERSION_GET_MAJOR(version) \ + (((version) >> (COGL_VERSION_COMPONENT_BITS * 2)) \ + & COGL_VERSION_MAX_COMPONENT_VALUE) + +/** + * COGL_VERSION_GET_MINOR: + * @version: An encoded version number + * + * Extracts the minor part of an encoded version number. + * + * Since: 1.12.0 + */ +#define COGL_VERSION_GET_MINOR(version) \ + (((version) >> COGL_VERSION_COMPONENT_BITS) & \ + COGL_VERSION_MAX_COMPONENT_VALUE) + +/** + * COGL_VERSION_GET_MICRO: + * @version: An encoded version number + * + * Extracts the micro part of an encoded version number. + * + * Since: 1.12.0 + */ +#define COGL_VERSION_GET_MICRO(version) \ + ((version) & COGL_VERSION_MAX_COMPONENT_VALUE) + +/** + * COGL_VERSION_CHECK: + * @major: The major part of a version number + * @minor: The minor part of a version number + * @micro: The micro part of a version number + * + * A convenient macro to check whether the Cogl version being compiled + * against is at least the given version number. For example if the + * function cogl_pipeline_frobnicate was added in version 2.0.1 and + * you want to conditionally use that function when it is available, + * you could write the following: + * + * |[ + * #if COGL_VERSION_CHECK (2, 0, 1) + * cogl_pipeline_frobnicate (pipeline); + * #else + * /* Frobnication is not supported. Use a red color instead */ + * cogl_pipeline_set_color_4f (pipeline, 1.0f, 0.0f, 0.0f, 1.0f); + * #endif + * ]| + * + * Return value: %TRUE if the Cogl version being compiled against is + * greater than or equal to the given three part version number. + * Since: 1.12.0 + */ +#define COGL_VERSION_CHECK(major, minor, micro) \ + (COGL_VERSION >= COGL_VERSION_ENCODE (major, minor, micro)) + +/** + * COGL_VERSION_1_0: + * + * A macro that evaluates to the 1.0 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_0 (COGL_VERSION_ENCODE (1, 0, 0)) + +/** + * COGL_VERSION_1_2: + * + * A macro that evaluates to the 1.2 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_2 (COGL_VERSION_ENCODE (1, 2, 0)) + +/** + * COGL_VERSION_1_4: + * + * A macro that evaluates to the 1.4 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_4 (COGL_VERSION_ENCODE (1, 4, 0)) + +/** + * COGL_VERSION_1_6: + * + * A macro that evaluates to the 1.6 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_6 (COGL_VERSION_ENCODE (1, 6, 0)) + +/** + * COGL_VERSION_1_8: + * + * A macro that evaluates to the 1.8 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_8 (COGL_VERSION_ENCODE (1, 8, 0)) + +/** + * COGL_VERSION_1_10: + * + * A macro that evaluates to the 1.10 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_10 (COGL_VERSION_ENCODE (1, 10, 0)) + +/** + * COGL_VERSION_1_12: + * + * A macro that evaluates to the 1.12 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_12 (COGL_VERSION_ENCODE (1, 12, 0)) + +/** + * COGL_VERSION_1_14: + * + * A macro that evaluates to the 1.14 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_14 (COGL_VERSION_ENCODE (1, 14, 0)) + +/** + * COGL_VERSION_1_16: + * + * A macro that evaluates to the 1.16 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.16 + */ +#define COGL_VERSION_1_16 (COGL_VERSION_ENCODE (1, 16, 0)) + +/** + * COGL_VERSION_1_18: + * + * A macro that evaluates to the 1.18 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.18 + */ +#define COGL_VERSION_1_18 (COGL_VERSION_ENCODE (1, 18, 0)) + +/** + * COGL_VERSION_1_20: + * + * A macro that evaluates to the 1.20 version of Cogl, in a format + * that can be used by the C pre-processor. + * + * Since: 1.20 + */ +#define COGL_VERSION_1_20 (COGL_VERSION_ENCODE (1, 20, 0)) + +/* evaluates to the current stable version; for development cycles, + * this means the next stable target + */ +#if (COGL_VERSION_MINOR_INTERNAL % 2) +#define COGL_VERSION_CURRENT_STABLE \ + (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR_INTERNAL, \ + COGL_VERSION_MINOR_INTERNAL + 1, 0)) +#else +#define COGL_VERSION_CURRENT_STABLE \ + (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR_INTERNAL, \ + COGL_VERSION_MINOR_INTERNAL, 0)) +#endif + +/* evaluates to the previous stable version */ +#if (COGL_VERSION_MINOR_INTERNAL % 2) +#define COGL_VERSION_PREVIOUS_STABLE \ + (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR_INTERNAL, \ + COGL_VERSION_MINOR_INTERNAL - 1, 0)) +#else +#define COGL_VERSION_PREVIOUS_STABLE \ + (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR_INTERNAL, \ + COGL_VERSION_MINOR_INTERNAL - 2, 0)) +#endif + +#endif /* __COGL_VERSION_H__ */ diff --git a/cogl/cogl/cogl-wayland-server.h b/cogl/cogl/cogl-wayland-server.h new file mode 100644 index 000000000..9c1f35fa0 --- /dev/null +++ b/cogl/cogl/cogl-wayland-server.h @@ -0,0 +1,163 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __COGL_WAYLAND_SERVER_H +#define __COGL_WAYLAND_SERVER_H + +#include + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + +#include +#include + +COGL_BEGIN_DECLS + +/** + * cogl_wayland_display_set_compositor_display: + * @display: a #CoglDisplay + * @wayland_display: A compositor's Wayland display pointer + * + * Informs Cogl of a compositor's Wayland display pointer. This + * enables Cogl to register private wayland extensions required to + * pass buffers between the clients and compositor. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_wayland_display_set_compositor_display (CoglDisplay *display, + struct wl_display *wayland_display); + +/** + * cogl_wayland_texture_2d_new_from_buffer: + * @ctx: A #CoglContext + * @buffer: A Wayland resource for a buffer + * @error: A #CoglError for exceptions + * + * Uploads the @buffer referenced by the given Wayland resource to a + * #CoglTexture2D. The buffer resource may refer to a wl_buffer or a + * wl_shm_buffer. + * + * The results are undefined for passing an invalid @buffer + * pointer + * It is undefined if future updates to @buffer outside the + * control of Cogl will affect the allocated #CoglTexture2D. In some + * cases the contents of the buffer are copied (such as shm buffers), + * and in other cases the underlying storage is re-used directly (such + * as drm buffers) + * + * Returns: A newly allocated #CoglTexture2D, or if Cogl could not + * validate the @buffer in some way (perhaps because of + * an unsupported format) it will return %NULL and set + * @error. + * + * Since: 1.10 + * Stability: unstable + */ +CoglTexture2D * +cogl_wayland_texture_2d_new_from_buffer (CoglContext *ctx, + struct wl_resource *buffer, + CoglError **error); + +/** + * cogl_wayland_texture_set_region_from_shm_buffer: + * @texture: a #CoglTexture + * @width: The width of the region to copy + * @height: The height of the region to copy + * @shm_buffer: The source buffer + * @src_x: The X offset within the source bufer to copy from + * @src_y: The Y offset within the source bufer to copy from + * @dst_x: The X offset within the texture to copy to + * @dst_y: The Y offset within the texture to copy to + * @level: The mipmap level of the texture to copy to + * @error: A #CoglError to return exceptional errors + * + * Sets the pixels in a rectangular subregion of @texture from a + * Wayland SHM buffer. Generally this would be used in response to + * wl_surface.damage event in a compositor in order to update the + * texture with the damaged region. This is just a convenience wrapper + * around getting the SHM buffer pointer and calling + * cogl_texture_set_region(). See that function for a description of + * the level parameter. + * + * Since the storage for a #CoglTexture is allocated lazily then + * if the given @texture has not previously been allocated then this + * api can return %FALSE and throw an exceptional @error if there is + * not enough memory to allocate storage for @texture. + * + * Return value: %TRUE if the subregion upload was successful, and + * %FALSE otherwise + * Since: 1.18 + * Stability: unstable + */ +CoglBool +cogl_wayland_texture_set_region_from_shm_buffer (CoglTexture *texture, + int src_x, + int src_y, + int width, + int height, + struct wl_shm_buffer * + shm_buffer, + int dst_x, + int dst_y, + int level, + CoglError **error); + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_WAYLAND_SERVER_H */ diff --git a/cogl/cogl/cogl-x11-renderer-private.h b/cogl/cogl/cogl-x11-renderer-private.h new file mode 100644 index 000000000..17655da2b --- /dev/null +++ b/cogl/cogl/cogl-x11-renderer-private.h @@ -0,0 +1,40 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_RENDERER_X11_PRIVATE_H +#define __COGL_RENDERER_X11_PRIVATE_H + +typedef struct _CoglX11Renderer +{ + int damage_base; + int randr_base; +} CoglX11Renderer; + +#endif /* __COGL_RENDERER_X11_PRIVATE_H */ diff --git a/cogl/cogl/cogl-xlib-private.h b/cogl/cogl/cogl-xlib-private.h new file mode 100644 index 000000000..992a32099 --- /dev/null +++ b/cogl/cogl/cogl-xlib-private.h @@ -0,0 +1,54 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_XLIB_PRIVATE_H +#define __COGL_XLIB_PRIVATE_H + +#include + +typedef struct _CoglXlibTrapState CoglXlibTrapState; + +struct _CoglXlibTrapState +{ + /* These values are intended to be internal to + * _cogl_xlib_{un,}trap_errors but they need to be in the header so + * that the struct can be allocated on the stack */ + int (* old_error_handler) (Display *, XErrorEvent *); + int trapped_error_code; + CoglXlibTrapState *old_state; +}; + +void +_cogl_xlib_query_damage_extension (void); + +int +_cogl_xlib_get_damage_base (void); + +#endif /* __COGL_XLIB_PRIVATE_H */ diff --git a/cogl/cogl/cogl-xlib-renderer-private.h b/cogl/cogl/cogl-xlib-renderer-private.h new file mode 100644 index 000000000..ea0ee906f --- /dev/null +++ b/cogl/cogl/cogl-xlib-renderer-private.h @@ -0,0 +1,103 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_RENDERER_XLIB_PRIVATE_H +#define __COGL_RENDERER_XLIB_PRIVATE_H + +#include "cogl-object-private.h" +#include "cogl-xlib-private.h" +#include "cogl-x11-renderer-private.h" +#include "cogl-context.h" +#include "cogl-output.h" + +typedef struct _CoglXlibRenderer +{ + CoglX11Renderer _parent; + + Display *xdpy; + + /* Current top of the XError trap state stack. The actual memory for + these is expected to be allocated on the stack by the caller */ + CoglXlibTrapState *trap_state; + + unsigned long outputs_update_serial; + + XVisualInfo *xvisinfo; +} CoglXlibRenderer; + +CoglBool +_cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error); + +void +_cogl_xlib_renderer_disconnect (CoglRenderer *renderer); + +/* + * cogl_xlib_renderer_trap_errors: + * @state: A temporary place to store data for the trap. + * + * Traps every X error until _cogl_xlib_renderer_untrap_errors() + * called. You should allocate an uninitialised CoglXlibTrapState + * struct on the stack to pass to this function. The same pointer + * should later be passed to _cogl_xlib_renderer_untrap_errors(). + * + * Calls to _cogl_xlib_renderer_trap_errors() can be nested as long as + * _cogl_xlib_renderer_untrap_errors() is called with the + * corresponding state pointers in reverse order. + */ +void +_cogl_xlib_renderer_trap_errors (CoglRenderer *renderer, + CoglXlibTrapState *state); + +/* + * cogl_xlib_renderer_untrap_errors: + * @state: The state that was passed to _cogl_xlib_renderer_trap_errors(). + * + * Removes the X error trap and returns the current status. + * + * Return value: the trapped error code, or 0 for success + */ +int +_cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer, + CoglXlibTrapState *state); + +CoglXlibRenderer * +_cogl_xlib_renderer_get_data (CoglRenderer *renderer); + +int64_t +_cogl_xlib_renderer_get_dispatch_timeout (CoglRenderer *renderer); + +CoglOutput * +_cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, + int x, + int y, + int width, + int height); + +#endif /* __COGL_RENDERER_XLIB_PRIVATE_H */ diff --git a/cogl/cogl/cogl-xlib-renderer.c b/cogl/cogl/cogl-xlib-renderer.c new file mode 100644 index 000000000..8801c1cbf --- /dev/null +++ b/cogl/cogl/cogl-xlib-renderer.c @@ -0,0 +1,677 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-xlib-renderer.h" +#include "cogl-util.h" +#include "cogl-object.h" + +#include "cogl-output-private.h" +#include "cogl-renderer-private.h" +#include "cogl-xlib-renderer-private.h" +#include "cogl-x11-renderer-private.h" +#include "cogl-winsys-private.h" +#include "cogl-error-private.h" +#include "cogl-poll-private.h" + +#include +#include +#include + +#include +#include + +static char *_cogl_x11_display_name = NULL; +static GList *_cogl_xlib_renderers = NULL; + +static void +destroy_xlib_renderer_data (void *user_data) +{ + CoglXlibRenderer *data = user_data; + + if (data->xvisinfo) + XFree (data->xvisinfo); + + g_slice_free (CoglXlibRenderer, user_data); +} + +CoglXlibRenderer * +_cogl_xlib_renderer_get_data (CoglRenderer *renderer) +{ + static CoglUserDataKey key; + CoglXlibRenderer *data; + + /* Constructs a CoglXlibRenderer struct on demand and attaches it to + the object using user data. It's done this way instead of using a + subclassing hierarchy in the winsys data because all EGL winsys's + need the EGL winsys data but only one of them wants the Xlib + data. */ + + data = cogl_object_get_user_data (COGL_OBJECT (renderer), &key); + + if (data == NULL) + { + data = g_slice_new0 (CoglXlibRenderer); + + cogl_object_set_user_data (COGL_OBJECT (renderer), + &key, + data, + destroy_xlib_renderer_data); + } + + return data; +} + +static void +register_xlib_renderer (CoglRenderer *renderer) +{ + GList *l; + + for (l = _cogl_xlib_renderers; l; l = l->next) + if (l->data == renderer) + return; + + _cogl_xlib_renderers = g_list_prepend (_cogl_xlib_renderers, renderer); +} + +static void +unregister_xlib_renderer (CoglRenderer *renderer) +{ + _cogl_xlib_renderers = g_list_remove (_cogl_xlib_renderers, renderer); +} + +static CoglRenderer * +get_renderer_for_xdisplay (Display *xdpy) +{ + GList *l; + + for (l = _cogl_xlib_renderers; l; l = l->next) + { + CoglRenderer *renderer = l->data; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + + if (xlib_renderer->xdpy == xdpy) + return renderer; + } + + return NULL; +} + +static int +error_handler (Display *xdpy, + XErrorEvent *error) +{ + CoglRenderer *renderer; + CoglXlibRenderer *xlib_renderer; + + renderer = get_renderer_for_xdisplay (xdpy); + + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + g_assert (xlib_renderer->trap_state); + + xlib_renderer->trap_state->trapped_error_code = error->error_code; + + return 0; +} + +void +_cogl_xlib_renderer_trap_errors (CoglRenderer *renderer, + CoglXlibTrapState *state) +{ + CoglXlibRenderer *xlib_renderer; + + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + state->trapped_error_code = 0; + state->old_error_handler = XSetErrorHandler (error_handler); + + state->old_state = xlib_renderer->trap_state; + xlib_renderer->trap_state = state; +} + +int +_cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer, + CoglXlibTrapState *state) +{ + CoglXlibRenderer *xlib_renderer; + + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + g_assert (state == xlib_renderer->trap_state); + + XSetErrorHandler (state->old_error_handler); + + xlib_renderer->trap_state = state->old_state; + + return state->trapped_error_code; +} + +static Display * +assert_xlib_display (CoglRenderer *renderer, CoglError **error) +{ + Display *xdpy = cogl_xlib_renderer_get_foreign_display (renderer); + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + /* A foreign display may have already been set... */ + if (xdpy) + { + xlib_renderer->xdpy = xdpy; + return xdpy; + } + + xdpy = XOpenDisplay (_cogl_x11_display_name); + if (xdpy == NULL) + { + _cogl_set_error (error, + COGL_RENDERER_ERROR, + COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN, + "Failed to open X Display %s", _cogl_x11_display_name); + return NULL; + } + + xlib_renderer->xdpy = xdpy; + return xdpy; +} + +static int +compare_outputs (CoglOutput *a, + CoglOutput *b) +{ + return strcmp (a->name, b->name); +} + +#define CSO(X) COGL_SUBPIXEL_ORDER_ ## X +static CoglSubpixelOrder subpixel_map[6][6] = { + { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), + CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* 0 */ + { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR), + CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB) }, /* 90 */ + { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), + CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* 180 */ + { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB), + CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR) }, /* 270 */ + { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), + CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* Reflect_X */ + { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), + CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* Reflect_Y */ +}; +#undef CSO + +static void +update_outputs (CoglRenderer *renderer, + CoglBool notify) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + XRRScreenResources *resources; + CoglXlibTrapState state; + CoglBool error = FALSE; + GList *new_outputs = NULL; + GList *l, *m; + CoglBool changed = FALSE; + int i; + + xlib_renderer->outputs_update_serial = XNextRequest (xlib_renderer->xdpy); + + resources = XRRGetScreenResources (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy)); + + _cogl_xlib_renderer_trap_errors (renderer, &state); + + for (i = 0; resources && i < resources->ncrtc && !error; i++) + { + XRRCrtcInfo *crtc_info = NULL; + XRROutputInfo *output_info = NULL; + CoglOutput *output; + float refresh_rate = 0; + int j; + + crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy, + resources, resources->crtcs[i]); + if (crtc_info == NULL) + { + error = TRUE; + goto next; + } + + if (crtc_info->mode == None) + goto next; + + for (j = 0; j < resources->nmode; j++) + { + if (resources->modes[j].id == crtc_info->mode) + refresh_rate = (resources->modes[j].dotClock / + ((float)resources->modes[j].hTotal * + resources->modes[j].vTotal)); + } + + output_info = XRRGetOutputInfo (xlib_renderer->xdpy, + resources, + crtc_info->outputs[0]); + if (output_info == NULL) + { + error = TRUE; + goto next; + } + + output = _cogl_output_new (output_info->name); + output->x = crtc_info->x; + output->y = crtc_info->y; + output->width = crtc_info->width; + output->height = crtc_info->height; + if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0) + { + output->mm_width = output_info->mm_height; + output->mm_height = output_info->mm_width; + } + else + { + output->mm_width = output_info->mm_width; + output->mm_height = output_info->mm_height; + } + + output->refresh_rate = refresh_rate; + + switch (output_info->subpixel_order) + { + case SubPixelUnknown: + default: + output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; + break; + case SubPixelNone: + output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; + break; + case SubPixelHorizontalRGB: + output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + break; + case SubPixelHorizontalBGR: + output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; + break; + case SubPixelVerticalRGB: + output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; + break; + case SubPixelVerticalBGR: + output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; + break; + } + + output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + + /* Handle the effect of rotation and reflection on subpixel order (ugh) */ + for (j = 0; j < 6; j++) + { + if ((crtc_info->rotation & (1 << j)) != 0) + output->subpixel_order = subpixel_map[j][output->subpixel_order]; + } + + new_outputs = g_list_prepend (new_outputs, output); + + next: + if (crtc_info != NULL) + XFree (crtc_info); + + if (output_info != NULL) + XFree (output_info); + } + + XFree (resources); + + if (!error) + { + new_outputs = g_list_sort (new_outputs, (GCompareFunc)compare_outputs); + + l = new_outputs; + m = renderer->outputs; + + while (l || m) + { + int cmp; + CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL; + CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL; + + if (l && m) + cmp = compare_outputs (output_l, output_m); + else if (l) + cmp = -1; + else + cmp = 1; + + if (cmp == 0) + { + GList *m_next = m->next; + + if (!_cogl_output_values_equal (output_l, output_m)) + { + renderer->outputs = g_list_remove_link (renderer->outputs, m); + renderer->outputs = g_list_insert_before (renderer->outputs, + m_next, output_l); + cogl_object_ref (output_l); + + changed = TRUE; + } + + l = l->next; + m = m_next; + } + else if (cmp < 0) + { + renderer->outputs = + g_list_insert_before (renderer->outputs, m, output_l); + cogl_object_ref (output_l); + changed = TRUE; + l = l->next; + } + else + { + GList *m_next = m->next; + renderer->outputs = g_list_remove_link (renderer->outputs, m); + changed = TRUE; + m = m_next; + } + } + } + + g_list_free_full (new_outputs, (GDestroyNotify)cogl_object_unref); + _cogl_xlib_renderer_untrap_errors (renderer, &state); + + if (changed) + { + const CoglWinsysVtable *winsys = renderer->winsys_vtable; + + if (notify) + COGL_NOTE (WINSYS, "Outputs changed:"); + else + COGL_NOTE (WINSYS, "Outputs:"); + + for (l = renderer->outputs; l; l = l->next) + { + CoglOutput *output = l->data; + const char *subpixel_string; + + switch (output->subpixel_order) + { + case COGL_SUBPIXEL_ORDER_UNKNOWN: + default: + subpixel_string = "unknown"; + break; + case COGL_SUBPIXEL_ORDER_NONE: + subpixel_string = "none"; + break; + case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: + subpixel_string = "horizontal_rgb"; + break; + case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: + subpixel_string = "horizontal_bgr"; + break; + case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: + subpixel_string = "vertical_rgb"; + break; + case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: + subpixel_string = "vertical_bgr"; + break; + } + + COGL_NOTE (WINSYS, + " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f " + "subpixel_order=%s refresh_rate=%.3f", + output->name, + output->x, output->y, output->width, output->height, + output->mm_width, output->mm_height, + output->width / (output->mm_width / 25.4), + output->height / (output->mm_height / 25.4), + subpixel_string, + output->refresh_rate); + } + + if (notify && winsys->renderer_outputs_changed != NULL) + winsys->renderer_outputs_changed (renderer); + } +} + +static CoglFilterReturn +randr_filter (XEvent *event, + void *data) +{ + CoglRenderer *renderer = data; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglX11Renderer *x11_renderer = + (CoglX11Renderer *) xlib_renderer; + + if (x11_renderer->randr_base != -1 && + (event->xany.type == x11_renderer->randr_base + RRScreenChangeNotify || + event->xany.type == x11_renderer->randr_base + RRNotify) && + event->xany.serial >= xlib_renderer->outputs_update_serial) + update_outputs (renderer, TRUE); + + return COGL_FILTER_CONTINUE; +} + +static int64_t +prepare_xlib_events_timeout (void *user_data) +{ + CoglRenderer *renderer = user_data; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + return XPending (xlib_renderer->xdpy) ? 0 : -1; +} + +static void +dispatch_xlib_events (void *user_data, int revents) +{ + CoglRenderer *renderer = user_data; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + if (renderer->xlib_enable_event_retrieval) + while (XPending (xlib_renderer->xdpy)) + { + XEvent xevent; + + XNextEvent (xlib_renderer->xdpy, &xevent); + + cogl_xlib_renderer_handle_event (renderer, &xevent); + } +} + +CoglBool +_cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglX11Renderer *x11_renderer = + (CoglX11Renderer *) xlib_renderer; + int damage_error; + int randr_error; + + if (!assert_xlib_display (renderer, error)) + return FALSE; + + if (getenv ("COGL_X11_SYNC")) + XSynchronize (xlib_renderer->xdpy, TRUE); + + /* Check whether damage events are supported on this display */ + if (!XDamageQueryExtension (xlib_renderer->xdpy, + &x11_renderer->damage_base, + &damage_error)) + x11_renderer->damage_base = -1; + + /* Check whether randr is supported on this display */ + if (!XRRQueryExtension (xlib_renderer->xdpy, + &x11_renderer->randr_base, + &randr_error)) + x11_renderer->randr_base = -1; + + xlib_renderer->trap_state = NULL; + + if (renderer->xlib_enable_event_retrieval) + { + _cogl_poll_renderer_add_fd (renderer, + ConnectionNumber (xlib_renderer->xdpy), + COGL_POLL_FD_EVENT_IN, + prepare_xlib_events_timeout, + dispatch_xlib_events, + renderer); + } + + XRRSelectInput(xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + RRScreenChangeNotifyMask + | RRCrtcChangeNotifyMask + | RROutputPropertyNotifyMask); + update_outputs (renderer, FALSE); + + register_xlib_renderer (renderer); + + cogl_xlib_renderer_add_filter (renderer, + randr_filter, + renderer); + + return TRUE; +} + +void +_cogl_xlib_renderer_disconnect (CoglRenderer *renderer) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + + g_list_free_full (renderer->outputs, (GDestroyNotify)cogl_object_unref); + renderer->outputs = NULL; + + if (!renderer->foreign_xdpy && xlib_renderer->xdpy) + XCloseDisplay (xlib_renderer->xdpy); + + unregister_xlib_renderer (renderer); +} + +Display * +cogl_xlib_renderer_get_display (CoglRenderer *renderer) +{ + CoglXlibRenderer *xlib_renderer; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL); + + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + return xlib_renderer->xdpy; +} + +CoglFilterReturn +cogl_xlib_renderer_handle_event (CoglRenderer *renderer, + XEvent *event) +{ + return _cogl_renderer_handle_native_event (renderer, event); +} + +void +cogl_xlib_renderer_add_filter (CoglRenderer *renderer, + CoglXlibFilterFunc func, + void *data) +{ + _cogl_renderer_add_native_filter (renderer, + (CoglNativeFilterFunc)func, data); +} + +void +cogl_xlib_renderer_remove_filter (CoglRenderer *renderer, + CoglXlibFilterFunc func, + void *data) +{ + _cogl_renderer_remove_native_filter (renderer, + (CoglNativeFilterFunc)func, data); +} + +int64_t +_cogl_xlib_renderer_get_dispatch_timeout (CoglRenderer *renderer) +{ + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + if (renderer->xlib_enable_event_retrieval) + { + if (XPending (xlib_renderer->xdpy)) + return 0; + else + return -1; + } + else + return -1; +} + +CoglOutput * +_cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, + int x, + int y, + int width, + int height) +{ + int max_overlap = 0; + CoglOutput *max_overlapped = NULL; + GList *l; + int xa1 = x, xa2 = x + width; + int ya1 = y, ya2 = y + height; + + for (l = renderer->outputs; l; l = l->next) + { + CoglOutput *output = l->data; + int xb1 = output->x, xb2 = output->x + output->width; + int yb1 = output->y, yb2 = output->y + output->height; + + int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1); + int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1); + + if (overlap_x > 0 && overlap_y > 0) + { + int overlap = overlap_x * overlap_y; + if (overlap > max_overlap) + { + max_overlap = overlap; + max_overlapped = output; + } + } + } + + return max_overlapped; +} + +XVisualInfo * +cogl_xlib_renderer_get_visual_info (CoglRenderer *renderer) +{ + CoglXlibRenderer *xlib_renderer; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL); + + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + return xlib_renderer->xvisinfo; +} diff --git a/cogl/cogl/cogl-xlib-renderer.h b/cogl/cogl/cogl-xlib-renderer.h new file mode 100644 index 000000000..fdce06b03 --- /dev/null +++ b/cogl/cogl/cogl-xlib-renderer.h @@ -0,0 +1,191 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_XLIB_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_XLIB_RENDERER_H__ +#define __COGL_XLIB_RENDERER_H__ + +#include +#include + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + +#include + +COGL_BEGIN_DECLS + +/* + * cogl_xlib_renderer_handle_event: + * @renderer: a #CoglRenderer + * @event: pointer to an XEvent structure + * + * This function processes a single event; it can be used to hook into + * external event retrieval (for example that done by Clutter or + * GDK). + * + * Return value: #CoglFilterReturn. %COGL_FILTER_REMOVE indicates that + * Cogl has internally handled the event and the caller should do no + * further processing. %COGL_FILTER_CONTINUE indicates that Cogl is + * either not interested in the event, or has used the event to update + * internal state without taking any exclusive action. + */ +CoglFilterReturn +cogl_xlib_renderer_handle_event (CoglRenderer *renderer, + XEvent *event); + +/* + * CoglXlibFilterFunc: + * @event: pointer to an XEvent structure + * @data: the data that was given when the filter was added + * + * A callback function that can be registered with + * cogl_xlib_renderer_add_filter(). The function should return + * %COGL_FILTER_REMOVE if it wants to prevent further processing or + * %COGL_FILTER_CONTINUE otherwise. + */ +typedef CoglFilterReturn (* CoglXlibFilterFunc) (XEvent *event, + void *data); + +/* + * cogl_xlib_renderer_add_filter: + * @renderer: a #CoglRenderer + * @func: the callback function + * @data: user data passed to @func when called + * + * Adds a callback function that will receive all native events. The + * function can stop further processing of the event by return + * %COGL_FILTER_REMOVE. + */ +void +cogl_xlib_renderer_add_filter (CoglRenderer *renderer, + CoglXlibFilterFunc func, + void *data); + +/* + * cogl_xlib_renderer_remove_filter: + * @renderer: a #CoglRenderer + * @func: the callback function + * @data: user data given when the callback was installed + * + * Removes a callback that was previously added with + * cogl_xlib_renderer_add_filter(). + */ +void +cogl_xlib_renderer_remove_filter (CoglRenderer *renderer, + CoglXlibFilterFunc func, + void *data); + +/* + * cogl_xlib_renderer_get_foreign_display: + * @renderer: a #CoglRenderer + * + * Return value: the foreign Xlib display that will be used by any Xlib based + * winsys backend. The display needs to be set with + * cogl_xlib_renderer_set_foreign_display() before this function is called. + */ +Display * +cogl_xlib_renderer_get_foreign_display (CoglRenderer *renderer); + +/* + * cogl_xlib_renderer_set_foreign_display: + * @renderer: a #CoglRenderer + * + * Sets a foreign Xlib display that Cogl will use for and Xlib based winsys + * backend. + * + * Note that calling this function will automatically call + * cogl_xlib_renderer_set_event_retrieval_enabled() to disable Cogl's + * event retrieval. Cogl still needs to see all of the X events so the + * application should also use cogl_xlib_renderer_handle_event() if it + * uses this function. + */ +void +cogl_xlib_renderer_set_foreign_display (CoglRenderer *renderer, + Display *display); + +/** + * cogl_xlib_renderer_set_event_retrieval_enabled: + * @renderer: a #CoglRenderer + * @enable: The new value + * + * Sets whether Cogl should automatically retrieve events from the X + * display. This defaults to %TRUE unless + * cogl_xlib_renderer_set_foreign_display() is called. It can be set + * to %FALSE if the application wants to handle its own event + * retrieval. Note that Cogl still needs to see all of the X events to + * function properly so the application should call + * cogl_xlib_renderer_handle_event() for each event if it disables + * automatic event retrieval. + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_xlib_renderer_set_event_retrieval_enabled (CoglRenderer *renderer, + CoglBool enable); + +Display * +cogl_xlib_renderer_get_display (CoglRenderer *renderer); + +XVisualInfo * +cogl_xlib_renderer_get_visual_info (CoglRenderer *renderer); + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_XLIB_RENDERER_H__ */ diff --git a/cogl/cogl/cogl-xlib.c b/cogl/cogl/cogl-xlib.c new file mode 100644 index 000000000..315a1bfbc --- /dev/null +++ b/cogl/cogl/cogl-xlib.c @@ -0,0 +1,112 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cogl-xlib.h" + +/* FIXME: when we remove the last X11 based Clutter backend then we + * will get rid of these functions and instead rely on the equivalent + * _cogl_xlib_renderer API + */ + +/* This can't be in the Cogl context because it can be set before + context is created */ +static Display *_cogl_xlib_display = NULL; + +Display * +cogl_xlib_get_display (void) +{ + _COGL_GET_CONTEXT (ctx, NULL); + + return cogl_xlib_renderer_get_display (ctx->display->renderer); +} + +void +cogl_xlib_set_display (Display *display) +{ + /* This can only be called once before the Cogl context is created */ + g_assert (_cogl_xlib_display == NULL); + + _cogl_xlib_display = display; +} + +/* These three functions are wrappers around the equivalent renderer + functions. They can be removed once all xlib-based backends in + Clutter know about the renderer */ +CoglFilterReturn +cogl_xlib_handle_event (XEvent *xevent) +{ + _COGL_GET_CONTEXT (ctx, COGL_FILTER_CONTINUE); + + /* Pass the event on to the renderer */ + return cogl_xlib_renderer_handle_event (ctx->display->renderer, xevent); +} + +void +_cogl_xlib_query_damage_extension (void) +{ + int damage_error; + Display *display; + + _COGL_GET_CONTEXT (ctxt, NO_RETVAL); + + /* Check whether damage events are supported on this display */ + display = cogl_xlib_renderer_get_display (ctxt->display->renderer); + if (!XDamageQueryExtension (display, &ctxt->damage_base, &damage_error)) + ctxt->damage_base = -1; +} + +int +_cogl_xlib_get_damage_base (void) +{ + CoglX11Renderer *x11_renderer; + _COGL_GET_CONTEXT (ctxt, -1); + + x11_renderer = + (CoglX11Renderer *) _cogl_xlib_renderer_get_data (ctxt->display->renderer); + return x11_renderer->damage_base; +} diff --git a/cogl/cogl/cogl-xlib.h b/cogl/cogl/cogl-xlib.h new file mode 100644 index 000000000..ac4a14004 --- /dev/null +++ b/cogl/cogl/cogl-xlib.h @@ -0,0 +1,132 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __COGL_XLIB_H__ +#define __COGL_XLIB_H__ + +#include + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_XLIB_H_INSIDE__ */ +#ifndef __COGL_XLIB_H_INSIDE__ +#define __COGL_XLIB_H_INSIDE__ +#endif + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + +#include +#include +#include +#include + +COGL_BEGIN_DECLS + +/* + * cogl_xlib_get_display: + * + * Return value: the Xlib display that will be used by the Xlib winsys + * backend. The display needs to be set with _cogl_xlib_set_display() + * before this function is called. + * + * Stability: Unstable + * Deprecated: 1.16: Use cogl_xlib_renderer_get_display() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_xlib_renderer_get_display) +Display * +cogl_xlib_get_display (void); + +/* + * cogl_xlib_set_display: + * + * Sets the Xlib display that Cogl will use for the Xlib winsys + * backend. This function should eventually go away when Cogl gains a + * more complete winsys abstraction. + * + * Stability: Unstable + * Deprecated: 1.16: Use cogl_xlib_renderer_set_foreign_display() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_xlib_renderer_set_foreign_display) +void +cogl_xlib_set_display (Display *display); + +/* + * cogl_xlib_handle_event: + * @xevent: pointer to XEvent structure + * + * This function processes a single X event; it can be used to hook + * into external X event retrieval (for example that done by Clutter + * or GDK). + * + * Return value: #CoglXlibFilterReturn. %COGL_XLIB_FILTER_REMOVE + * indicates that Cogl has internally handled the event and the + * caller should do no further processing. %COGL_XLIB_FILTER_CONTINUE + * indicates that Cogl is either not interested in the event, + * or has used the event to update internal state without taking + * any exclusive action. + * + * Stability: Unstable + * Deprecated: 1.16: Use cogl_xlib_renderer_handle_event() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_xlib_renderer_handle_event) +CoglFilterReturn +cogl_xlib_handle_event (XEvent *xevent); + +COGL_END_DECLS + + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_XLIB_H_INSIDE__ +#undef __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_XLIB_H__ */ diff --git a/cogl/cogl/cogl.c b/cogl/cogl/cogl.c new file mode 100644 index 000000000..e4e780857 --- /dev/null +++ b/cogl/cogl/cogl.c @@ -0,0 +1,820 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#include + +#include +#include +#include + +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_4 + +#include "cogl-i18n-private.h" +#include "cogl-debug.h" +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-winsys-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-matrix-private.h" +#include "cogl-journal-private.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-driver.h" +#include "cogl-attribute-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-renderer-private.h" +#include "cogl-config-private.h" +#include "cogl-private.h" +#include "cogl1-context.h" +#include "cogl-offscreen.h" +#include "cogl-attribute-gl-private.h" +#include "cogl-clutter.h" + +#include "deprecated/cogl-framebuffer-deprecated.h" + +CoglFuncPtr +cogl_get_proc_address (const char* name) +{ + _COGL_GET_CONTEXT (ctx, NULL); + + return _cogl_renderer_get_proc_address (ctx->display->renderer, name, FALSE); +} + +CoglBool +_cogl_check_extension (const char *name, char * const *ext) +{ + while (*ext) + if (!strcmp (name, *ext)) + return TRUE; + else + ext++; + + return FALSE; +} + +/* XXX: This has been deprecated as public API */ +CoglBool +cogl_check_extension (const char *name, const char *ext) +{ + return cogl_clutter_check_extension (name, ext); +} + +/* XXX: it's expected that we'll deprecated this with + * cogl_framebuffer_clear at some point. */ +void +cogl_clear (const CoglColor *color, unsigned long buffers) +{ + cogl_framebuffer_clear (cogl_get_draw_framebuffer (), buffers, color); +} + +/* XXX: This API has been deprecated */ +void +cogl_set_depth_test_enabled (CoglBool setting) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->legacy_depth_test_enabled == setting) + return; + + ctx->legacy_depth_test_enabled = setting; + if (ctx->legacy_depth_test_enabled) + ctx->legacy_state_set++; + else + ctx->legacy_state_set--; +} + +/* XXX: This API has been deprecated */ +CoglBool +cogl_get_depth_test_enabled (void) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + return ctx->legacy_depth_test_enabled; +} + +void +cogl_set_backface_culling_enabled (CoglBool setting) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->legacy_backface_culling_enabled == setting) + return; + + ctx->legacy_backface_culling_enabled = setting; + + if (ctx->legacy_backface_culling_enabled) + ctx->legacy_state_set++; + else + ctx->legacy_state_set--; +} + +CoglBool +cogl_get_backface_culling_enabled (void) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + return ctx->legacy_backface_culling_enabled; +} + +void +cogl_set_source_color (const CoglColor *color) +{ + CoglPipeline *pipeline; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_color_get_alpha_byte (color) == 0xff) + { + cogl_pipeline_set_color (ctx->opaque_color_pipeline, color); + pipeline = ctx->opaque_color_pipeline; + } + else + { + CoglColor premultiplied = *color; + cogl_color_premultiply (&premultiplied); + cogl_pipeline_set_color (ctx->blended_color_pipeline, &premultiplied); + pipeline = ctx->blended_color_pipeline; + } + + cogl_set_source (pipeline); +} + +void +cogl_set_viewport (int x, + int y, + int width, + int height) +{ + CoglFramebuffer *framebuffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + framebuffer = cogl_get_draw_framebuffer (); + + cogl_framebuffer_set_viewport (framebuffer, + x, + y, + width, + height); +} + +/* XXX: This should be deprecated, and we should expose a way to also + * specify an x and y viewport offset */ +void +cogl_viewport (unsigned int width, + unsigned int height) +{ + cogl_set_viewport (0, 0, width, height); +} + +CoglFeatureFlags +cogl_get_features (void) +{ + _COGL_GET_CONTEXT (ctx, 0); + + return ctx->feature_flags; +} + +CoglBool +cogl_features_available (CoglFeatureFlags features) +{ + _COGL_GET_CONTEXT (ctx, 0); + + return (ctx->feature_flags & features) == features; +} + +CoglBool +cogl_has_feature (CoglContext *ctx, CoglFeatureID feature) +{ + return COGL_FLAGS_GET (ctx->features, feature); +} + +CoglBool +cogl_has_features (CoglContext *ctx, ...) +{ + va_list args; + CoglFeatureID feature; + + va_start (args, ctx); + while ((feature = va_arg (args, CoglFeatureID))) + if (!cogl_has_feature (ctx, feature)) + return FALSE; + va_end (args); + + return TRUE; +} + +void +cogl_foreach_feature (CoglContext *ctx, + CoglFeatureCallback callback, + void *user_data) +{ + int i; + for (i = 0; i < _COGL_N_FEATURE_IDS; i++) + if (COGL_FLAGS_GET (ctx->features, i)) + callback (i, user_data); +} + +/* XXX: This function should either be replaced with one returning + * integers, or removed/deprecated and make the + * _cogl_framebuffer_get_viewport* functions public. + */ +void +cogl_get_viewport (float viewport[4]) +{ + CoglFramebuffer *framebuffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + framebuffer = cogl_get_draw_framebuffer (); + cogl_framebuffer_get_viewport4fv (framebuffer, viewport); +} + +void +cogl_get_bitmasks (int *red, + int *green, + int *blue, + int *alpha) +{ + CoglFramebuffer *framebuffer; + + framebuffer = cogl_get_draw_framebuffer (); + + if (red) + *red = cogl_framebuffer_get_red_bits (framebuffer); + + if (green) + *green = cogl_framebuffer_get_green_bits (framebuffer); + + if (blue) + *blue = cogl_framebuffer_get_blue_bits (framebuffer); + + if (alpha) + *alpha = cogl_framebuffer_get_alpha_bits (framebuffer); +} + +void +cogl_set_fog (const CoglColor *fog_color, + CoglFogMode mode, + float density, + float z_near, + float z_far) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->legacy_fog_state.enabled == FALSE) + ctx->legacy_state_set++; + + ctx->legacy_fog_state.enabled = TRUE; + ctx->legacy_fog_state.color = *fog_color; + ctx->legacy_fog_state.mode = mode; + ctx->legacy_fog_state.density = density; + ctx->legacy_fog_state.z_near = z_near; + ctx->legacy_fog_state.z_far = z_far; +} + +void +cogl_disable_fog (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->legacy_fog_state.enabled == TRUE) + ctx->legacy_state_set--; + + ctx->legacy_fog_state.enabled = FALSE; +} + +void +cogl_flush (void) +{ + GList *l; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (l = ctx->framebuffers; l; l = l->next) + _cogl_framebuffer_flush_journal (l->data); +} + +void +cogl_read_pixels (int x, + int y, + int width, + int height, + CoglReadPixelsFlags source, + CoglPixelFormat format, + uint8_t *pixels) +{ + int bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + CoglBitmap *bitmap; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + bitmap = cogl_bitmap_new_for_data (ctx, + width, height, + format, + bpp * width, /* rowstride */ + pixels); + cogl_framebuffer_read_pixels_into_bitmap (_cogl_get_read_framebuffer (), + x, y, + source, + bitmap); + cogl_object_unref (bitmap); +} + +void +cogl_begin_gl (void) +{ + CoglPipeline *pipeline; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->in_begin_gl_block) + { + static CoglBool shown = FALSE; + if (!shown) + g_warning ("You should not nest cogl_begin_gl/cogl_end_gl blocks"); + shown = TRUE; + return; + } + ctx->in_begin_gl_block = TRUE; + + /* Flush all batched primitives */ + cogl_flush (); + + /* Flush framebuffer state, including clip state, modelview and + * projection matrix state + * + * NB: _cogl_framebuffer_flush_state may disrupt various state (such + * as the pipeline state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (), + _cogl_get_read_framebuffer (), + COGL_FRAMEBUFFER_STATE_ALL); + + /* Setup the state for the current pipeline */ + + /* We considered flushing a specific, minimal pipeline here to try and + * simplify the GL state, but decided to avoid special cases and second + * guessing what would be actually helpful. + * + * A user should instead call cogl_set_source_color4ub() before + * cogl_begin_gl() to simplify the state flushed. + * + * XXX: note defining n_tex_coord_attribs using + * cogl_pipeline_get_n_layers is a hack, but the problem is that + * n_tex_coord_attribs is usually defined when drawing a primitive + * which isn't happening here. + * + * Maybe it would be more useful if this code did flush the + * opaque_color_pipeline and then call into cogl-pipeline-opengl.c to then + * restore all state for the material's backend back to default OpenGL + * values. + */ + pipeline = cogl_get_source (); + _cogl_pipeline_flush_gl_state (ctx, + pipeline, + cogl_get_draw_framebuffer (), + FALSE, + FALSE); + + /* Disable any cached vertex arrays */ + _cogl_gl_disable_all_attributes (ctx); +} + +void +cogl_end_gl (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!ctx->in_begin_gl_block) + { + static CoglBool shown = FALSE; + if (!shown) + g_warning ("cogl_end_gl is being called before cogl_begin_gl"); + shown = TRUE; + return; + } + ctx->in_begin_gl_block = FALSE; +} + +void +cogl_push_matrix (void) +{ + cogl_framebuffer_push_matrix (cogl_get_draw_framebuffer ()); +} + +void +cogl_pop_matrix (void) +{ + cogl_framebuffer_pop_matrix (cogl_get_draw_framebuffer ()); +} + +void +cogl_scale (float x, float y, float z) +{ + cogl_framebuffer_scale (cogl_get_draw_framebuffer (), x, y, z); +} + +void +cogl_translate (float x, float y, float z) +{ + cogl_framebuffer_translate (cogl_get_draw_framebuffer (), x, y, z); +} + +void +cogl_rotate (float angle, float x, float y, float z) +{ + cogl_framebuffer_rotate (cogl_get_draw_framebuffer (), angle, x, y, z); +} + +void +cogl_transform (const CoglMatrix *matrix) +{ + cogl_framebuffer_transform (cogl_get_draw_framebuffer (), matrix); +} + +void +cogl_perspective (float fov_y, + float aspect, + float z_near, + float z_far) +{ + cogl_framebuffer_perspective (cogl_get_draw_framebuffer (), + fov_y, aspect, z_near, z_far); +} + +void +cogl_frustum (float left, + float right, + float bottom, + float top, + float z_near, + float z_far) +{ + cogl_framebuffer_frustum (cogl_get_draw_framebuffer (), + left, right, bottom, top, z_near, z_far); +} + +void +cogl_ortho (float left, + float right, + float bottom, + float top, + float near, + float far) +{ + cogl_framebuffer_orthographic (cogl_get_draw_framebuffer (), + left, top, right, bottom, near, far); +} + +void +cogl_get_modelview_matrix (CoglMatrix *matrix) +{ + cogl_framebuffer_get_modelview_matrix (cogl_get_draw_framebuffer (), matrix); +} + +void +cogl_set_modelview_matrix (CoglMatrix *matrix) +{ + cogl_framebuffer_set_modelview_matrix (cogl_get_draw_framebuffer (), matrix); +} + +void +cogl_get_projection_matrix (CoglMatrix *matrix) +{ + cogl_framebuffer_get_projection_matrix (cogl_get_draw_framebuffer (), matrix); +} + +void +cogl_set_projection_matrix (CoglMatrix *matrix) +{ + cogl_framebuffer_set_projection_matrix (cogl_get_draw_framebuffer (), matrix); +} + +uint32_t +_cogl_driver_error_quark (void) +{ + return g_quark_from_static_string ("cogl-driver-error-quark"); +} + +typedef struct _CoglSourceState +{ + CoglPipeline *pipeline; + int push_count; + /* If this is TRUE then the pipeline will be copied and the legacy + state will be applied whenever the pipeline is used. This is + necessary because some internal Cogl code expects to be able to + push a temporary pipeline to put GL into a known state. For that + to work it also needs to prevent applying the legacy state */ + CoglBool enable_legacy; +} CoglSourceState; + +static void +_push_source_real (CoglPipeline *pipeline, CoglBool enable_legacy) +{ + CoglSourceState *top = g_slice_new (CoglSourceState); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + top->pipeline = cogl_object_ref (pipeline); + top->enable_legacy = enable_legacy; + top->push_count = 1; + + ctx->source_stack = g_list_prepend (ctx->source_stack, top); +} + +/* FIXME: This should take a context pointer for Cogl 2.0 Technically + * we could make it so we can retrieve a context reference from the + * pipeline, but this would not by symmetric with cogl_pop_source. */ +void +cogl_push_source (void *material_or_pipeline) +{ + CoglPipeline *pipeline = COGL_PIPELINE (material_or_pipeline); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + _cogl_push_source (pipeline, TRUE); +} + +/* This internal version of cogl_push_source is the same except it + never applies the legacy state. Some parts of Cogl use this + internally to set a temporary pipeline with a known state */ +void +_cogl_push_source (CoglPipeline *pipeline, CoglBool enable_legacy) +{ + CoglSourceState *top; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + + if (ctx->source_stack) + { + top = ctx->source_stack->data; + if (top->pipeline == pipeline && top->enable_legacy == enable_legacy) + { + top->push_count++; + return; + } + else + _push_source_real (pipeline, enable_legacy); + } + else + _push_source_real (pipeline, enable_legacy); +} + +/* FIXME: This needs to take a context pointer for Cogl 2.0 */ +void +cogl_pop_source (void) +{ + CoglSourceState *top; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (ctx->source_stack); + + top = ctx->source_stack->data; + top->push_count--; + if (top->push_count == 0) + { + cogl_object_unref (top->pipeline); + g_slice_free (CoglSourceState, top); + ctx->source_stack = g_list_delete_link (ctx->source_stack, + ctx->source_stack); + } +} + +/* FIXME: This needs to take a context pointer for Cogl 2.0 */ +void * +cogl_get_source (void) +{ + CoglSourceState *top; + + _COGL_GET_CONTEXT (ctx, NULL); + + _COGL_RETURN_VAL_IF_FAIL (ctx->source_stack, NULL); + + top = ctx->source_stack->data; + return top->pipeline; +} + +CoglBool +_cogl_get_enable_legacy_state (void) +{ + CoglSourceState *top; + + _COGL_GET_CONTEXT (ctx, FALSE); + + _COGL_RETURN_VAL_IF_FAIL (ctx->source_stack, FALSE); + + top = ctx->source_stack->data; + return top->enable_legacy; +} + +void +cogl_set_source (void *material_or_pipeline) +{ + CoglSourceState *top; + CoglPipeline *pipeline = COGL_PIPELINE (material_or_pipeline); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (ctx->source_stack); + + top = ctx->source_stack->data; + if (top->pipeline == pipeline && top->enable_legacy) + return; + + if (top->push_count == 1) + { + /* NB: top->pipeline may be only thing keeping pipeline + * alive currently so ref pipeline first... */ + cogl_object_ref (pipeline); + cogl_object_unref (top->pipeline); + top->pipeline = pipeline; + top->enable_legacy = TRUE; + } + else + { + top->push_count--; + cogl_push_source (pipeline); + } +} + +void +cogl_set_source_texture (CoglTexture *texture) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (texture != NULL); + + cogl_pipeline_set_layer_texture (ctx->texture_pipeline, 0, texture); + cogl_set_source (ctx->texture_pipeline); +} + +void +cogl_set_source_color4ub (uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + CoglColor c = { 0, }; + + cogl_color_init_from_4ub (&c, red, green, blue, alpha); + cogl_set_source_color (&c); +} + +void +cogl_set_source_color4f (float red, + float green, + float blue, + float alpha) +{ + CoglColor c = { 0, }; + + cogl_color_init_from_4f (&c, red, green, blue, alpha); + cogl_set_source_color (&c); +} + +/* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) + * to Cogl window/framebuffer coordinates (ranging from 0 to buffer-size) with + * (0,0) being top left. */ +#define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ + ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) +/* Note: for Y we first flip all coordinates around the X axis while in + * normalized device coodinates */ +#define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ + ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) + +/* Transform a homogeneous vertex position from model space to Cogl + * window coordinates (with 0,0 being top left) */ +void +_cogl_transform_point (const CoglMatrix *matrix_mv, + const CoglMatrix *matrix_p, + const float *viewport, + float *x, + float *y) +{ + float z = 0; + float w = 1; + + /* Apply the modelview matrix transform */ + cogl_matrix_transform_point (matrix_mv, x, y, &z, &w); + + /* Apply the projection matrix transform */ + cogl_matrix_transform_point (matrix_p, x, y, &z, &w); + + /* Perform perspective division */ + *x /= w; + *y /= w; + + /* Apply viewport transform */ + *x = VIEWPORT_TRANSFORM_X (*x, viewport[0], viewport[2]); + *y = VIEWPORT_TRANSFORM_Y (*y, viewport[1], viewport[3]); +} + +#undef VIEWPORT_TRANSFORM_X +#undef VIEWPORT_TRANSFORM_Y + +uint32_t +_cogl_system_error_quark (void) +{ + return g_quark_from_static_string ("cogl-system-error-quark"); +} + +void +_cogl_init (void) +{ + static CoglBool initialized = FALSE; + + if (initialized == FALSE) + { +#if defined(COGL_HAS_GTYPE_SUPPORT) && !GLIB_CHECK_VERSION (2, 36, 0) + g_type_init (); +#endif + + _cogl_config_read (); + _cogl_debug_check_environment (); + initialized = TRUE; + } +} + +/* + * Returns the number of bytes-per-pixel of a given format. The bpp + * can be extracted from the least significant nibble of the pixel + * format (see CoglPixelFormat). + * + * The mapping is the following (see discussion on bug #660188): + * + * 0 = undefined + * 1, 8 = 1 bpp (e.g. A_8, G_8) + * 2 = 3 bpp, aligned (e.g. 888) + * 3 = 4 bpp, aligned (e.g. 8888) + * 4-6 = 2 bpp, not aligned (e.g. 565, 4444, 5551) + * 7 = undefined yuv + * 9 = 2 bpp, aligned + * 10 = undefined + * 11 = undefined + * 12 = 3 bpp, not aligned + * 13 = 4 bpp, not aligned (e.g. 2101010) + * 14-15 = undefined + */ +int +_cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format) +{ + int bpp_lut[] = { 0, 1, 3, 4, + 2, 2, 2, 0, + 1, 2, 0, 0, + 3, 4, 0, 0 }; + + return bpp_lut [format & 0xf]; +} + +/* Note: this also refers to the mapping defined above for + * _cogl_pixel_format_get_bytes_per_pixel() */ +CoglBool +_cogl_pixel_format_is_endian_dependant (CoglPixelFormat format) +{ + int aligned_lut[] = { -1, 1, 1, 1, + 0, 0, 0, -1, + 1, 1, -1, -1, + 0, 0, -1, -1}; + int aligned = aligned_lut[format & 0xf]; + + _COGL_RETURN_VAL_IF_FAIL (aligned != -1, FALSE); + + /* NB: currently checking whether the format components are aligned + * or not determines whether the format is endian dependent or not. + * In the future though we might consider adding formats with + * aligned components that are also endian independant. */ + + return aligned; +} diff --git a/cogl/cogl/cogl.h b/cogl/cogl/cogl.h new file mode 100644 index 000000000..7fa392137 --- /dev/null +++ b/cogl/cogl/cogl.h @@ -0,0 +1,191 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_H__ +#define __COGL_H__ + +#ifdef COGL_COMPILATION +#error " shouldn't be included internally" +#endif + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#ifdef COGL_ENABLE_EXPERIMENTAL_2_0_API +#ifndef COGL_ENABLE_EXPERIMENTAL_API +#define COGL_ENABLE_EXPERIMENTAL_API +#endif +#endif + +/* We currently keep gtype integration delimited in case we eventually + * want to split it out into a separate utility library when Cogl + * becomes a standalone project. (like cairo-gobject.so) + */ +#define _COGL_SUPPORTS_GTYPE_INTEGRATION + +/* + * API common to the 1.x and 2.0 api... + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 1.x only api... + */ +#ifndef COGL_ENABLE_EXPERIMENTAL_2_0_API +#include +#include +#include +#include +#include +#include +#include +#endif + +/* It would be good to move these casts up into 1.x only api if we can + * update Clutter, Mutter and GnomeShell to avoid redundant casts when + * they enable the experimental api... */ +#include + +#include +#include + +/* + * 2.0 api that's compatible with the 1.x api... + */ +#if defined (COGL_ENABLE_EXPERIMENTAL_API) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT) +#include +#include +#endif +#ifdef COGL_HAS_GLIB_SUPPORT +#include +#endif +/* XXX: This will definitly go away once all the Clutter winsys + * code has been migrated down into Cogl! */ +#include +#endif + +/* + * API deprecations + */ +#include + +/* + * Cogl Path api compatability + * + * The cogl_path_ api used to be part of the core Cogl api so for + * compatability we include cogl-path.h via cogl.h + * + * Note: we have to make sure not to include cogl-path.h while + * building core cogl or generating the Cogl .gir data because + * cogl-path now gets built after cogl and some cogl-path headers are + * only generated at build time... + */ +#if defined (COGL_HAS_COGL_PATH_SUPPORT) && \ + !defined (COGL_COMPILATION) && \ + !defined (COGL_GIR_SCANNING) +#include +#endif + +/** + * SECTION:cogl + * @short_description: General purpose API + * + * General utility functions for COGL. + */ + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_H__ */ diff --git a/cogl/cogl/cogl.symbols b/cogl/cogl/cogl.symbols new file mode 100644 index 000000000..ad4240777 --- /dev/null +++ b/cogl/cogl/cogl.symbols @@ -0,0 +1,1085 @@ + + +#ifdef COGL_HAS_EGL_PLATFORM_ANDROID_SUPPORT +cogl_android_set_native_window +#endif + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_atlas_texture_get_gtype +#endif +cogl_atlas_texture_new_with_size +cogl_atlas_texture_new_from_file +cogl_atlas_texture_new_from_data +cogl_atlas_texture_new_from_bitmap + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_attribute_buffer_get_gtype +#endif +cogl_attribute_buffer_new_with_size + +cogl_attribute_buffer_new +cogl_attribute_get_buffer +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_attribute_get_gtype +#endif +cogl_attribute_get_normalized +cogl_attribute_new +cogl_attribute_new_const_1f +cogl_attribute_new_const_2f +cogl_attribute_new_const_2fv +cogl_attribute_new_const_2x2fv +cogl_attribute_new_const_3f +cogl_attribute_new_const_3fv +cogl_attribute_new_const_3x3fv +cogl_attribute_new_const_4f +cogl_attribute_new_const_4fv +cogl_attribute_new_const_4x4fv +cogl_attribute_set_buffer +cogl_attribute_set_normalized +cogl_attribute_type_get_type + +cogl_begin_gl + +cogl_bitmap_error_get_type +cogl_bitmap_get_buffer +cogl_bitmap_get_format +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_bitmap_get_gtype +#endif +cogl_bitmap_get_height +cogl_bitmap_get_rowstride +cogl_bitmap_get_size_from_file +cogl_bitmap_get_width +cogl_bitmap_new_for_data +cogl_bitmap_new_from_file +cogl_bitmap_new_from_buffer +cogl_bitmap_new_with_size +cogl_blend_string_error_get_type + +cogl_buffer_bit_get_type +cogl_buffer_get_size +cogl_buffer_get_update_hint +#if 0 +/* not implemented! */ +cogl_buffer_get_usage_hint +#endif +cogl_buffer_map +cogl_buffer_map_range +cogl_buffer_set_data +cogl_buffer_set_update_hint +#if 0 +/* not implemented! */ +cogl_buffer_set_usage_hint +#endif +cogl_buffer_target_get_type +cogl_buffer_unmap + +#ifndef COGL_DISABLE_DEPRECATED +cogl_check_extension +#endif + +cogl_clear + +#ifndef COGL_DISABLE_DEPRECATED +cogl_clip_ensure +#endif + +cogl_clip_pop + +#ifndef COGL_DISABLE_DEPRECATED +cogl_clip_push +#endif + +cogl_clip_push_rectangle + +cogl_clip_push_window_rect + +cogl_clip_push_primitive + +#ifndef COGL_DISABLE_DEPRECATED +cogl_clip_push_window_rectangle +cogl_clip_stack_restore +cogl_clip_stack_save +#endif + +#ifndef COGL_WINSYS_INTEGRATED +cogl_clutter_check_extension_CLUTTER +cogl_clutter_winsys_has_feature_CLUTTER +#ifdef COGL_HAS_XLIB +cogl_clutter_winsys_xlib_get_visual_info_CLUTTER +#endif +#endif + +cogl_color_copy +cogl_color_equal +cogl_color_free +cogl_color_get_alpha +cogl_color_get_alpha_byte +cogl_color_get_alpha_float +cogl_color_get_blue +cogl_color_get_blue_byte +cogl_color_get_blue_float +cogl_color_get_green +cogl_color_get_green_byte +cogl_color_get_green_float +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_color_get_gtype +#endif +cogl_color_get_red +cogl_color_get_red_byte +cogl_color_get_red_float +cogl_color_init_from_hsl +cogl_color_init_from_4f +cogl_color_init_from_4fv +cogl_color_init_from_4ub +cogl_color_mask_get_type +cogl_color_new +cogl_color_premultiply +cogl_color_set_alpha +cogl_color_set_alpha_byte +cogl_color_set_alpha_float +cogl_color_set_blue +cogl_color_set_blue_byte +cogl_color_set_blue_float +cogl_color_set_from_4f +cogl_color_set_from_4ub +cogl_color_set_green +cogl_color_set_green_byte +cogl_color_set_green_float +cogl_color_set_red +cogl_color_set_red_byte +cogl_color_set_red_float +cogl_color_to_hsl +cogl_color_unpremultiply + + +#ifdef COGL_HAS_EGL_SUPPORT +cogl_egl_context_get_egl_display +cogl_egl_context_get_egl_context +#endif + +#ifdef COGL_HAS_GLX_SUPPORT +cogl_glx_context_get_glx_context +#endif + +cogl_context_get_display +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_context_get_gtype +#endif +cogl_context_get_renderer +cogl_context_new + +cogl_create_program +cogl_create_shader + +cogl_debug_matrix_entry_print +cogl_debug_matrix_print +cogl_debug_object_foreach_type +cogl_debug_object_print_instances +cogl_depth_state_get_range +cogl_depth_state_get_test_enabled +cogl_depth_state_get_test_function +cogl_depth_state_get_write_enabled +cogl_depth_state_init +cogl_depth_state_set_test_enabled +cogl_depth_state_set_test_function +cogl_depth_state_set_range +cogl_depth_state_set_write_enabled +cogl_depth_test_function_get_type + +cogl_disable_fog + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_display_get_gtype +#endif +cogl_display_get_renderer +cogl_display_new +cogl_display_setup +cogl_display_set_onscreen_template + +cogl_double_to_fixed + +cogl_end_gl + +cogl_error_copy +cogl_error_free +cogl_error_matches + +cogl_euler_copy +cogl_euler_equal +cogl_euler_free +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_euler_get_gtype +#endif +cogl_euler_init +cogl_euler_init_from_matrix +#if 0 +/* not yet implemented */ +cogl_euler_init_from_quaternion +#endif + +cogl_features_available +cogl_feature_flags_get_type +cogl_fence_closure_get_user_data +cogl_fixed_atan +cogl_fixed_atan2 +cogl_fixed_cos +cogl_fixed_get_type +cogl_fixed_log2 +cogl_fixed_pow +cogl_fixed_pow2 +cogl_fixed_sin +cogl_fixed_sqrt +cogl_fixed_tan + +cogl_fog_mode_get_type + +cogl_foreach_feature + +cogl_flush + +cogl_framebuffer_add_fence_callback +cogl_framebuffer_allocate +cogl_framebuffer_cancel_fence_callback +cogl_framebuffer_clear4f +cogl_framebuffer_clear +cogl_framebuffer_discard_buffers +cogl_framebuffer_draw_primitive +cogl_framebuffer_draw_rectangle +cogl_framebuffer_draw_rectangles +cogl_framebuffer_draw_textured_rectangle +cogl_framebuffer_draw_textured_rectangles +cogl_framebuffer_finish +cogl_framebuffer_frustum +cogl_framebuffer_get_alpha_bits +cogl_framebuffer_get_blue_bits +cogl_framebuffer_get_color_format +cogl_framebuffer_get_color_mask +cogl_framebuffer_get_context +cogl_framebuffer_get_depth_bits +cogl_framebuffer_get_depth_texture +cogl_framebuffer_get_depth_texture_enabled +cogl_framebuffer_get_depth_write_enabled +cogl_framebuffer_get_dither_enabled +cogl_framebuffer_get_green_bits +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_framebuffer_get_gtype +#endif +cogl_framebuffer_get_height +cogl_framebuffer_get_modelview_matrix +cogl_framebuffer_get_projection_matrix +cogl_framebuffer_get_red_bits +cogl_framebuffer_get_samples_per_pixel +cogl_framebuffer_get_viewport4fv +cogl_framebuffer_get_viewport_height +cogl_framebuffer_get_viewport_width +cogl_framebuffer_get_viewport_x +cogl_framebuffer_get_viewport_y +cogl_framebuffer_get_width +cogl_framebuffer_identity_matrix +cogl_framebuffer_orthographic +cogl_framebuffer_perspective +cogl_framebuffer_pop_clip +cogl_framebuffer_pop_matrix +cogl_framebuffer_push_matrix +cogl_framebuffer_push_primitive_clip +cogl_framebuffer_push_rectangle_clip +cogl_framebuffer_push_scissor_clip +cogl_framebuffer_read_pixels +cogl_framebuffer_read_pixels_into_bitmap +cogl_framebuffer_resolve_samples +cogl_framebuffer_resolve_samples_region +cogl_framebuffer_rotate + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +cogl_framebuffer_rotate_euler +cogl_framebuffer_rotate_quaternion +#endif + +cogl_framebuffer_scale +cogl_framebuffer_set_color_mask +cogl_framebuffer_set_depth_texture_enabled +cogl_framebuffer_set_depth_write_enabled +cogl_framebuffer_set_dither_enabled +cogl_framebuffer_set_modelview_matrix +cogl_framebuffer_set_projection_matrix +cogl_framebuffer_set_samples_per_pixel +cogl_framebuffer_set_viewport +cogl_framebuffer_transform +cogl_framebuffer_translate +cogl_framebuffer_vdraw_attributes +/* cogl_framebuffer_vdraw_indexed_attributes */ /* Not Implemented! */ + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_frame_closure_get_gtype +#endif +cogl_frame_info_get_frame_counter + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_frame_info_get_gtype +#endif +cogl_frame_info_get_output +cogl_frame_info_get_presentation_time +cogl_frame_info_get_refresh_rate + +cogl_frustum + +cogl_get_backface_culling_enabled +cogl_get_bitmasks +cogl_get_clock_time +cogl_get_depth_test_enabled +cogl_get_draw_framebuffer +cogl_get_features +cogl_get_modelview_matrix +cogl_get_option_group +cogl_get_proc_address +cogl_get_projection_matrix +cogl_get_rectangle_indices +cogl_get_source +cogl_get_static_identity_quaternion +cogl_get_static_zero_quaternion +cogl_get_viewport + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_gles2_context_get_gtype +#endif +cogl_gles2_context_get_vtable +cogl_gles2_context_new +cogl_gles2_get_current_vtable +cogl_gles2_texture_get_handle +cogl_gles2_texture_2d_new_from_handle + +#ifdef COGL_HAS_GLIB_SUPPORT +cogl_glib_renderer_source_new +cogl_glib_source_new +#endif + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_gtype_matrix_get_type +#endif + +cogl_handle_get_type +cogl_handle_ref +cogl_handle_unref + +cogl_has_feature +cogl_has_features + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_index_buffer_get_gtype +#endif +cogl_index_buffer_new +cogl_indices_get_buffer +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_indices_get_gtype +#endif +cogl_indices_get_offset +cogl_indices_get_type +cogl_indices_new +cogl_indices_new_for_buffer +cogl_indices_set_offset +cogl_indices_type_get_type + +cogl_is_atlas_texture +cogl_is_attribute +cogl_is_attribute_buffer +cogl_is_bitmap +cogl_is_buffer +cogl_is_context +cogl_is_frame_info +cogl_is_gles2_context +cogl_is_index_buffer +#if 0 +/* not implemented! */ +cogl_is_indices_array +#endif +cogl_is_material +cogl_is_matrix_stack +cogl_is_offscreen +cogl_is_output +cogl_is_pipeline +cogl_is_pixel_buffer +cogl_is_primitive +cogl_is_primitive_texture +cogl_is_program +cogl_is_renderer +cogl_is_shader +cogl_is_snippet +cogl_is_sub_texture +cogl_is_texture +#ifdef COGL_HAS_X11 +cogl_is_texture_pixmap_x11 +#endif +cogl_is_texture_rectangle +cogl_is_texture_2d +cogl_is_texture_3d + +#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT +cogl_kms_display_queue_modes_reset +cogl_kms_display_set_layout +cogl_kms_renderer_get_kms_fd +#endif + +cogl_material_alpha_func_get_type +cogl_material_copy +cogl_material_filter_get_type +cogl_material_foreach_layer +cogl_material_get_ambient +cogl_material_get_color +cogl_material_get_depth_state +cogl_material_get_diffuse +cogl_material_get_emission +cogl_material_get_layers +cogl_material_get_layer_point_sprite_coords_enabled +cogl_material_get_layer_wrap_mode_p +cogl_material_get_layer_wrap_mode_s +cogl_material_get_layer_wrap_mode_t +cogl_material_get_n_layers +cogl_material_get_point_size +cogl_material_get_shininess +cogl_material_get_specular +cogl_material_get_user_program +cogl_material_layer_get_mag_filter +cogl_material_layer_get_min_filter +cogl_material_layer_get_texture +cogl_material_layer_get_type +cogl_material_layer_get_wrap_mode_p +cogl_material_layer_get_wrap_mode_s +cogl_material_layer_get_wrap_mode_t +cogl_material_layer_type_get_type +cogl_material_new +cogl_material_remove_layer +#ifndef COGL_DISABLE_DEPRECATED +cogl_material_ref +#endif +cogl_material_set_alpha_test_function +cogl_material_set_ambient +cogl_material_set_ambient_and_diffuse +cogl_material_set_blend +cogl_material_set_blend_constant +cogl_material_set_color +cogl_material_set_color4f +cogl_material_set_color4ub +cogl_material_set_depth_state +cogl_material_set_diffuse +cogl_material_set_emission +cogl_material_set_layer +cogl_material_set_layer_combine +cogl_material_set_layer_combine_constant +cogl_material_set_layer_filters +cogl_material_set_layer_matrix +cogl_material_set_layer_point_sprite_coords_enabled +cogl_material_set_layer_wrap_mode +cogl_material_set_layer_wrap_mode_p +cogl_material_set_layer_wrap_mode_s +cogl_material_set_layer_wrap_mode_t +cogl_material_set_point_size +cogl_material_set_shininess +cogl_material_set_specular +cogl_material_set_user_program +#ifndef COGL_DISABLE_DEPRECATED +cogl_material_unref +#endif +cogl_material_wrap_mode_get_type + +cogl_matrix_copy +cogl_matrix_entry_calculate_translation +cogl_matrix_entry_equal +cogl_matrix_entry_get +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_matrix_entry_get_gtype +#endif +cogl_matrix_entry_is_identity +cogl_matrix_entry_ref +cogl_matrix_entry_unref +cogl_matrix_equal +cogl_matrix_free +cogl_matrix_frustum +cogl_matrix_get_array +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_matrix_get_gtype +#endif +cogl_matrix_get_inverse +cogl_matrix_init_from_array +cogl_matrix_init_translation +cogl_matrix_is_identity +cogl_matrix_init_from_euler +cogl_matrix_init_from_quaternion +cogl_matrix_init_identity +cogl_matrix_look_at +cogl_matrix_multiply +#ifndef COGL_DISABLE_DEPRECATED +cogl_matrix_ortho +#endif +cogl_matrix_orthographic +cogl_matrix_perspective +cogl_matrix_project_points +cogl_matrix_rotate + +#ifdef COGL_ENABLE_EXPERIMENTAL_API +cogl_matrix_rotate_euler +cogl_matrix_rotate_quaternion +#endif + +cogl_matrix_scale +cogl_matrix_stack_frustum +cogl_matrix_stack_get +cogl_matrix_stack_get_entry +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_matrix_stack_get_gtype +#endif +cogl_matrix_stack_get_inverse +cogl_matrix_stack_load_identity +cogl_matrix_stack_multiply +cogl_matrix_stack_new +cogl_matrix_stack_orthographic +cogl_matrix_stack_perspective +cogl_matrix_stack_pop +cogl_matrix_stack_push +cogl_matrix_stack_rotate +cogl_matrix_stack_rotate_euler +cogl_matrix_stack_rotate_quaternion +cogl_matrix_stack_scale +cogl_matrix_stack_set +cogl_matrix_stack_translate +cogl_matrix_transform_point +cogl_matrix_transform_points +cogl_matrix_translate +cogl_matrix_transpose +cogl_matrix_view_2d_in_frustum +cogl_matrix_view_2d_in_perspective + +cogl_meta_texture_foreach_in_region + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_object_get_gtype +#endif +cogl_object_get_user_data +cogl_object_ref +cogl_object_set_user_data +cogl_object_unref + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_offscreen_get_gtype +#endif +cogl_offscreen_new_to_texture +cogl_offscreen_new_with_texture + +cogl_onscreen_add_dirty_callback +cogl_onscreen_add_frame_callback +cogl_onscreen_add_resize_callback +cogl_onscreen_add_swap_buffers_callback +#ifndef COGL_WINSYS_INTEGRATED +cogl_onscreen_clutter_backend_set_size_CLUTTER +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_onscreen_dirty_closure_get_gtype +#endif +cogl_onscreen_get_buffer_age +cogl_onscreen_get_frame_counter +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_onscreen_get_gtype +#endif +cogl_onscreen_get_resizable +cogl_onscreen_hide +cogl_onscreen_new +cogl_onscreen_set_swap_throttled +cogl_onscreen_remove_dirty_callback +cogl_onscreen_remove_frame_callback +cogl_onscreen_remove_resize_callback +cogl_onscreen_remove_swap_buffers_callback +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_onscreen_resize_closure_get_gtype +#endif +cogl_onscreen_set_resizable +cogl_onscreen_set_swap_throttled +cogl_onscreen_show +cogl_onscreen_swap_buffers +cogl_onscreen_swap_buffers_with_damage +cogl_onscreen_swap_region +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_onscreen_template_get_gtype +#endif +cogl_onscreen_template_new +cogl_onscreen_template_set_samples_per_pixel +cogl_onscreen_template_set_swap_throttled + +cogl_ortho + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_output_get_gtype +#endif +cogl_output_get_height +cogl_output_get_mm_height +cogl_output_get_mm_width +cogl_output_get_refresh_rate +cogl_output_get_subpixel_order +cogl_output_get_width +cogl_output_get_x +cogl_output_get_y + +cogl_perspective + +cogl_pipeline_add_layer_snippet +cogl_pipeline_add_snippet +cogl_pipeline_copy +cogl_pipeline_foreach_layer +cogl_pipeline_get_alpha_test_function +cogl_pipeline_get_alpha_test_reference +cogl_pipeline_get_ambient +cogl_pipeline_get_color +cogl_pipeline_get_color_mask +cogl_pipeline_get_cull_face_mode +cogl_pipeline_get_depth_state +cogl_pipeline_get_diffuse +cogl_pipeline_get_emission +cogl_pipeline_get_front_face_winding +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_pipeline_get_gtype +#endif +cogl_pipeline_get_layer_mag_filter +cogl_pipeline_get_layer_min_filter +cogl_pipeline_get_layer_point_sprite_coords_enabled +cogl_pipeline_get_layer_texture +cogl_pipeline_get_layer_wrap_mode_p +cogl_pipeline_get_layer_wrap_mode_s +cogl_pipeline_get_layer_wrap_mode_t +cogl_pipeline_get_n_layers +cogl_pipeline_get_per_vertex_point_size +cogl_pipeline_get_point_size +cogl_pipeline_get_shininess +cogl_pipeline_get_specular +cogl_pipeline_get_uniform_location +cogl_pipeline_get_user_program +cogl_pipeline_new +cogl_pipeline_set_alpha_test_function +cogl_pipeline_set_ambient +cogl_pipeline_set_ambient_and_diffuse +cogl_pipeline_set_blend +cogl_pipeline_set_blend_constant +cogl_pipeline_set_color +cogl_pipeline_set_color_mask +cogl_pipeline_set_color4f +cogl_pipeline_set_color4ub +cogl_pipeline_set_cull_face_mode +cogl_pipeline_set_depth_state +cogl_pipeline_set_diffuse +cogl_pipeline_set_emission +cogl_pipeline_set_front_face_winding +cogl_pipeline_set_layer_combine +cogl_pipeline_set_layer_combine_constant +cogl_pipeline_set_layer_filters +cogl_pipeline_set_layer_matrix +cogl_pipeline_set_layer_null_texture +cogl_pipeline_set_layer_point_sprite_coords_enabled +cogl_pipeline_set_layer_texture +cogl_pipeline_set_layer_wrap_mode +cogl_pipeline_set_layer_wrap_mode_p +cogl_pipeline_set_layer_wrap_mode_s +cogl_pipeline_set_layer_wrap_mode_t +cogl_pipeline_set_per_vertex_point_size +cogl_pipeline_set_point_size +cogl_pipeline_remove_layer +cogl_pipeline_set_shininess +cogl_pipeline_set_specular +cogl_pipeline_set_uniform_float +cogl_pipeline_set_uniform_int +cogl_pipeline_set_uniform_matrix +cogl_pipeline_set_uniform_1f +cogl_pipeline_set_uniform_1i +cogl_pipeline_set_user_program + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_pixel_buffer_get_gtype +#endif +cogl_pixel_buffer_new +#if 0 +/* not exported in the main APIs for now */ +cogl_pixel_buffer_set_region +#endif +cogl_pixel_format_get_type + +cogl_poll_renderer_dispatch +cogl_poll_renderer_get_info + +cogl_polygon + +#ifndef COGL_DISABLE_DEPRECATED +cogl_pop_draw_buffer +#endif +cogl_pop_framebuffer +cogl_pop_gles2_context +cogl_pop_matrix +cogl_pop_source + +cogl_primitive_copy +cogl_primitive_foreach_attribute +cogl_primitive_get_first_vertex +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_primitive_get_gtype +#endif +cogl_primitive_get_indices +cogl_primitive_get_mode +cogl_primitive_get_n_vertices +cogl_primitive_new +cogl_primitive_new_p2 +cogl_primitive_new_p2c4 +cogl_primitive_new_p2t2 +cogl_primitive_new_p2t2c4 +cogl_primitive_new_p3 +cogl_primitive_new_p3c4 +cogl_primitive_new_p3t2 +cogl_primitive_new_p3t2c4 +cogl_primitive_new_with_attributes +cogl_primitive_set_attributes +cogl_primitive_set_first_vertex +cogl_primitive_set_indices +cogl_primitive_set_mode +cogl_primitive_set_n_vertices +cogl_primitive_draw + +cogl_primitive_texture_set_auto_mipmap + +cogl_program_attach_shader +cogl_program_get_uniform_location +cogl_program_link + +#ifndef COGL_DISABLE_DEPRECATED +cogl_program_ref +#endif + +cogl_program_set_uniform_float +cogl_program_set_uniform_int +cogl_program_set_uniform_matrix +cogl_program_set_uniform_1f +cogl_program_set_uniform_1i + +#ifndef COGL_DISABLE_DEPRECATED +cogl_program_uniform_float +cogl_program_uniform_int +cogl_program_uniform_matrix +cogl_program_uniform_1f +cogl_program_uniform_1i +cogl_program_unref +#endif + +cogl_program_use + +#ifndef COGL_DISABLE_DEPRECATED +cogl_push_draw_buffer +#endif + +cogl_push_framebuffer +cogl_push_gles2_context +cogl_push_matrix +cogl_push_source + +cogl_quaternion_copy +cogl_quaternion_dot_product +cogl_quaternion_equal +cogl_quaternion_free +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_quaternion_get_gtype +#endif +cogl_quaternion_get_rotation_angle +cogl_quaternion_get_rotation_axis +cogl_quaternion_init +cogl_quaternion_init_from_angle_vector +cogl_quaternion_init_from_array +cogl_quaternion_init_from_euler +cogl_quaternion_init_from_x_rotation +cogl_quaternion_init_from_y_rotation +cogl_quaternion_init_from_z_rotation +cogl_quaternion_init_identity +cogl_quaternion_invert +cogl_quaternion_multiply +cogl_quaternion_nlerp +cogl_quaternion_normalize +cogl_quaternion_pow +cogl_quaternion_slerp +cogl_quaternion_squad + +cogl_read_pixels +cogl_read_pixels_flags_get_type + +cogl_rectangle +cogl_rectangles +cogl_rectangles_with_texture_coords +cogl_rectangle_with_multitexture_coords +cogl_rectangle_with_texture_coords + +cogl_renderer_add_constraint +cogl_renderer_check_onscreen_template +cogl_renderer_connect +cogl_renderer_foreach_output +cogl_renderer_get_driver +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_renderer_get_gtype +#endif +cogl_renderer_get_n_fragment_texture_units +cogl_renderer_error_get_type +cogl_renderer_get_winsys_id +cogl_renderer_new +cogl_renderer_remove_constraint +cogl_renderer_set_driver +cogl_renderer_set_winsys_id + +cogl_rotate + +cogl_scale + +cogl_set_backface_culling_enabled +cogl_set_depth_test_enabled +#ifndef COGL_DISABLE_DEPRECATED +cogl_set_draw_buffer +#endif +cogl_set_fog +#ifdef COGL_HAS_SDL_SUPPORT +cogl_sdl_context_new +cogl_sdl_handle_event +cogl_sdl_idle +#if SDL_MAJOR_VERSION >= 2 +cogl_sdl_onscreen_get_window +#endif +cogl_sdl_renderer_get_event_type +cogl_sdl_renderer_set_event_type +#endif + +cogl_set_framebuffer +cogl_set_modelview_matrix +cogl_set_projection_matrix +cogl_set_source +cogl_set_source_color +cogl_set_source_color4f +cogl_set_source_color4ub +cogl_set_source_texture +cogl_set_viewport + +cogl_shader_compile +cogl_shader_get_info_log +cogl_shader_get_type +cogl_shader_is_compiled + +#ifndef COGL_DISABLE_DEPRECATED +cogl_shader_ref +#endif + +cogl_shader_source +cogl_shader_type_get_type + +#ifndef COGL_DISABLE_DEPRECATED +cogl_shader_unref +#endif + +cogl_snippet_get_declarations +cogl_snippet_get_hook +cogl_snippet_get_post +cogl_snippet_get_pre +cogl_snippet_get_replace +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_snippet_get_gtype +#endif +cogl_snippet_new +cogl_snippet_set_declarations +cogl_snippet_set_post +cogl_snippet_set_pre +cogl_snippet_set_replace + +cogl_sqrti + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_sub_texture_get_gtype +#endif +cogl_sub_texture_get_parent +cogl_sub_texture_new + +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_swap_chain_get_gtype +#endif +cogl_swap_chain_new +cogl_swap_chain_set_has_alpha +cogl_swap_chain_set_length + +cogl_system_error_get_type + +cogl_texture_allocate +cogl_texture_components_get_type +cogl_texture_error_get_type +cogl_texture_flags_get_type +cogl_texture_get_components +cogl_texture_get_data +cogl_texture_get_format +cogl_texture_get_gl_texture +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_get_gtype +#endif +cogl_texture_get_height +cogl_texture_get_max_waste +cogl_texture_get_premultiplied +cogl_texture_get_rowstride +cogl_texture_get_width +cogl_texture_is_sliced +cogl_texture_new_from_bitmap +cogl_texture_new_from_data +cogl_texture_new_from_file +cogl_texture_new_from_foreign +cogl_texture_new_from_sub_texture +cogl_texture_new_with_size +#ifdef COGL_HAS_X11 +cogl_texture_pixmap_x11_error_domain +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_pixmap_x11_get_gtype +#endif +cogl_texture_pixmap_x11_is_using_tfp_extension +cogl_texture_pixmap_x11_new +cogl_texture_pixmap_x11_set_damage_object +cogl_texture_pixmap_x11_update_area +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_rectangle_get_gtype +#endif +cogl_texture_rectangle_new_from_bitmap +cogl_texture_rectangle_new_from_foreign +cogl_texture_rectangle_new_with_size +#ifndef COGL_DISABLE_DEPRECATED +cogl_texture_ref +#endif +cogl_texture_set_components +cogl_texture_set_data +cogl_texture_set_premultiplied +cogl_texture_set_region +cogl_texture_set_region_from_bitmap +cogl_texture_type_get_type +#ifndef COGL_DISABLE_DEPRECATED +cogl_texture_unref +#endif +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_2d_get_gtype +#endif +cogl_texture_2d_new_from_bitmap +cogl_texture_2d_new_from_data +cogl_texture_2d_new_from_file +cogl_texture_2d_new_with_size +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_2d_sliced_get_gtype +#endif +cogl_texture_2d_sliced_new_from_bitmap +cogl_texture_2d_sliced_new_from_data +cogl_texture_2d_sliced_new_from_file +cogl_texture_2d_sliced_new_with_size +#ifdef COGL_HAS_GTYPE_SUPPORT +cogl_texture_3d_get_gtype +#endif +cogl_texture_3d_new_from_bitmap +cogl_texture_3d_new_from_data +cogl_texture_3d_new_with_size + +cogl_transform +cogl_translate + +cogl_vector3_add +cogl_vector3_copy +cogl_vector3_cross_product +cogl_vector3_distance +cogl_vector3_divide_scalar +cogl_vector3_dot_product +cogl_vector3_equal +cogl_vector3_equal_with_epsilon +cogl_vector3_free +cogl_vector3_init +cogl_vector3_init_zero +cogl_vector3_invert +cogl_vector3_magnitude +cogl_vector3_multiply_scalar +cogl_vector3_normalize +cogl_vector3_subtract + +cogl_vertex_buffer_add +cogl_vertex_buffer_delete +cogl_vertex_buffer_disable +cogl_vertex_buffer_draw +cogl_vertex_buffer_draw_elements +cogl_vertex_buffer_enable +cogl_vertex_buffer_get_n_vertices +cogl_vertex_buffer_indices_get_for_quads +cogl_vertex_buffer_indices_get_type +cogl_vertex_buffer_indices_new +cogl_vertex_buffer_new +#ifndef COGL_DISABLE_DEPRECATED +cogl_vertex_buffer_ref +#endif +cogl_vertex_buffer_submit +#ifndef COGL_DISABLE_DEPRECATED +cogl_vertex_buffer_unref +#endif + +cogl_vertices_mode_get_type + +#ifdef COGL_DISABLE_DEPRECATED +cogl_viewport +#endif + +cogl_winsys_feature_get_type + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT +cogl_wayland_display_set_compositor_display +cogl_wayland_onscreen_resize +cogl_wayland_renderer_get_display +cogl_wayland_renderer_set_event_dispatch_enabled +cogl_wayland_renderer_set_foreign_display +cogl_wayland_texture_set_region_from_shm_buffer +cogl_wayland_texture_2d_new_from_buffer +#endif + +cogl_winding_get_type + +#ifdef COGL_HAS_XLIB +cogl_xlib_get_display +cogl_xlib_handle_event +cogl_xlib_renderer_add_filter +cogl_xlib_renderer_get_display +cogl_xlib_renderer_get_foreign_display +cogl_xlib_renderer_get_visual_info +cogl_xlib_renderer_handle_event +cogl_xlib_renderer_remove_filter +cogl_xlib_renderer_set_event_retrieval_enabled +cogl_xlib_renderer_set_foreign_display +cogl_xlib_set_display +#endif + +#ifdef COGL_HAS_X11 +cogl_x11_onscreen_get_visual_xid +cogl_x11_onscreen_set_foreign_window_xid +#endif + +#ifndef COGL_NO_EXPORT_UNDERSCORE +/* probably these should not be exported at all, but anyways, for now... */ +/* eventually, this section should disappear (or cogl, cogl-pango, clutter et al */ +/* will link without the following) */ +_cogl_atlas_add_reorganize_callback +_cogl_atlas_new +_cogl_atlas_reserve_space +_cogl_atlas_texture_add_reorganize_callback +_cogl_atlas_texture_remove_reorganize_callback +_cogl_buffer_map_for_fill_or_fallback +_cogl_buffer_unmap_for_fill_or_fallback +_cogl_clip_stack_push_rectangle +_cogl_clip_stack_push_primitive +_cogl_context_get_default +_cogl_debug_instances +_cogl_framebuffer_get_modelview_stack +_cogl_framebuffer_get_projection_stack +_cogl_framebuffer_get_stencil_bits +_cogl_object_default_unref +_cogl_pipeline_foreach_layer_internal +_cogl_pipeline_layer_get_texture +_cogl_pipeline_prune_to_n_layers +_cogl_primitive_draw +_cogl_system_error_quark +_cogl_texture_can_hardware_repeat +_cogl_texture_get_format +#endif + +cogl_fence_closure_get_user_data +cogl_framebuffer_add_fence_callback +cogl_framebuffer_cancel_fence_callback diff --git a/cogl/cogl/cogl1-context.h b/cogl/cogl/cogl1-context.h new file mode 100644 index 000000000..92ac7d08f --- /dev/null +++ b/cogl/cogl/cogl1-context.h @@ -0,0 +1,862 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_1_CONTEXT_H__ +#define __COGL_1_CONTEXT_H__ + +#include +#include +#include +#include + +COGL_BEGIN_DECLS + +/** + * cogl_get_option_group: + * + * Retrieves the #GOptionGroup used by Cogl to parse the command + * line options. Clutter uses this to handle the Cogl command line + * options during its initialization process. + * + * Return value: a #GOptionGroup + * + * Since: 1.0 + * Deprecated: 1.16: Not replaced + */ +COGL_DEPRECATED_IN_1_16 +GOptionGroup * +cogl_get_option_group (void); + +/* Misc */ +/** + * cogl_get_features: + * + * Returns all of the features supported by COGL. + * + * Return value: A logical OR of all the supported COGL features. + * + * Since: 0.8 + * Deprecated: 1.10: Use cogl_foreach_feature() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_foreach_feature) +CoglFeatureFlags +cogl_get_features (void); + +/** + * cogl_features_available: + * @features: A bitmask of features to check for + * + * Checks whether the given COGL features are available. Multiple + * features can be checked for by or-ing them together with the '|' + * operator. %TRUE is only returned if all of the requested features + * are available. + * + * Return value: %TRUE if the features are available, %FALSE otherwise. + * Deprecated: 1.10: Use cogl_has_feature() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_has_feature) +CoglBool +cogl_features_available (CoglFeatureFlags features); + +/** + * cogl_get_proc_address: + * @name: the name of the function. + * + * Gets a pointer to a given GL or GL ES extension function. This acts + * as a wrapper around glXGetProcAddress() or whatever is the + * appropriate function for the current backend. + * + * This function should not be used to query core opengl API + * symbols since eglGetProcAddress for example doesn't allow this and + * and may return a junk pointer if you do. + * + * Return value: a pointer to the requested function or %NULL if the + * function is not available. + */ +CoglFuncPtr +cogl_get_proc_address (const char *name); + +/** + * cogl_check_extension: + * @name: extension to check for + * @ext: list of extensions + * + * Check whether @name occurs in list of extensions in @ext. + * + * Return value: %TRUE if the extension occurs in the list, %FALSE otherwise. + * + * Deprecated: 1.2: OpenGL is an implementation detail for Cogl and so it's + * not appropriate to expose OpenGL extensions through the Cogl API. This + * function can be replaced by the following equivalent code: + * |[ + * CoglBool retval = (strstr (ext, name) != NULL) ? TRUE : FALSE; + * ]| + */ +COGL_DEPRECATED +CoglBool +cogl_check_extension (const char *name, + const char *ext); + +/** + * cogl_get_bitmasks: + * @red: (out): Return location for the number of red bits or %NULL + * @green: (out): Return location for the number of green bits or %NULL + * @blue: (out): Return location for the number of blue bits or %NULL + * @alpha: (out): Return location for the number of alpha bits or %NULL + * + * Gets the number of bitplanes used for each of the color components + * in the color buffer. Pass %NULL for any of the arguments if the + * value is not required. + * + * Deprecated: 1.8: Use cogl_framebuffer_get_red/green/blue/alpha_bits() + * instead + */ +COGL_DEPRECATED_IN_1_8_FOR (cogl_framebuffer_get_red_OR_green_OR_blue_OR_alpha_bits) +void +cogl_get_bitmasks (int *red, + int *green, + int *blue, + int *alpha); + +/** + * cogl_perspective: + * @fovy: Vertical field of view angle in degrees. + * @aspect: The (width over height) aspect ratio for display + * @z_near: The distance to the near clipping plane (Must be positive) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current projection matrix with a perspective matrix + * based on the provided values. + * + * You should be careful not to have to great a @z_far / @z_near + * ratio since that will reduce the effectiveness of depth testing + * since there wont be enough precision to identify the depth of + * objects near to each other. + * + * Deprecated: 1.10: Use cogl_framebuffer_perspective() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_perspective) +void +cogl_perspective (float fovy, + float aspect, + float z_near, + float z_far); + +/** + * cogl_frustum: + * @left: X position of the left clipping plane where it + * intersects the near clipping plane + * @right: X position of the right clipping plane where it + * intersects the near clipping plane + * @bottom: Y position of the bottom clipping plane where it + * intersects the near clipping plane + * @top: Y position of the top clipping plane where it intersects + * the near clipping plane + * @z_near: The distance to the near clipping plane (Must be positive) + * @z_far: The distance to the far clipping plane (Must be positive) + * + * Replaces the current projection matrix with a perspective matrix + * for a given viewing frustum defined by 4 side clip planes that + * all cross through the origin and 2 near and far clip planes. + * + * Since: 0.8.2 + * Deprecated: 1.10: Use cogl_framebuffer_frustum() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_frustum) +void +cogl_frustum (float left, + float right, + float bottom, + float top, + float z_near, + float z_far); + +/** + * cogl_ortho: + * @left: The coordinate for the left clipping plane + * @right: The coordinate for the right clipping plane + * @bottom: The coordinate for the bottom clipping plane + * @top: The coordinate for the top clipping plane + * @near: The distance to the near clipping + * plane (negative if the plane is behind the viewer) + * @far: The distance for the far clipping + * plane (negative if the plane is behind the viewer) + * + * Replaces the current projection matrix with an orthographic projection + * matrix. See to see how the matrix is + * calculated. + * + *
+ * + * + *
+ * + * This function copies the arguments from OpenGL's glOrtho() even + * though they are unnecessarily confusing due to the z near and z far + * arguments actually being a "distance" from the origin, where + * negative values are behind the viewer, instead of coordinates for + * the z clipping planes which would have been consistent with the + * left, right bottom and top arguments. + * + * Since: 1.0 + * Deprecated: 1.10: Use cogl_framebuffer_orthographic() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_orthographic) +void +cogl_ortho (float left, + float right, + float bottom, + float top, + float near, + float far); + +/** + * cogl_viewport: + * @width: Width of the viewport + * @height: Height of the viewport + * + * Replace the current viewport with the given values. + * + * Since: 0.8.2 + * Deprecated: 1.8: Use cogl_framebuffer_set_viewport instead + */ +COGL_DEPRECATED_IN_1_8_FOR (cogl_framebuffer_set_viewport) +void +cogl_viewport (unsigned int width, + unsigned int height); + +/** + * cogl_set_viewport: + * @x: X offset of the viewport + * @y: Y offset of the viewport + * @width: Width of the viewport + * @height: Height of the viewport + * + * Replaces the current viewport with the given values. + * + * Since: 1.2 + * Deprecated: 1.8: Use cogl_framebuffer_set_viewport() instead + */ +COGL_DEPRECATED_IN_1_8_FOR (cogl_framebuffer_set_viewport) +void +cogl_set_viewport (int x, + int y, + int width, + int height); + +/** + * cogl_push_matrix: + * + * Stores the current model-view matrix on the matrix stack. The matrix + * can later be restored with cogl_pop_matrix(). + * + * Deprecated: 1.10: Use cogl_framebuffer_push_matrix() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_push_matrix) +void +cogl_push_matrix (void); + +/** + * cogl_pop_matrix: + * + * Restores the current model-view matrix from the matrix stack. + * + * Deprecated: 1.10: Use cogl_framebuffer_pop_matrix() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_push_matrix) +void +cogl_pop_matrix (void); + +/** + * cogl_scale: + * @x: Amount to scale along the x-axis + * @y: Amount to scale along the y-axis + * @z: Amount to scale along the z-axis + * + * Multiplies the current model-view matrix by one that scales the x, + * y and z axes by the given values. + * + * Deprecated: 1.10: Use cogl_framebuffer_pop_matrix() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_scale) +void +cogl_scale (float x, + float y, + float z); + +/** + * cogl_translate: + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Multiplies the current model-view matrix by one that translates the + * model along all three axes according to the given values. + * + * Deprecated: 1.10: Use cogl_framebuffer_translate() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_translate) +void +cogl_translate (float x, + float y, + float z); + +/** + * cogl_rotate: + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Multiplies the current model-view matrix by one that rotates the + * model around the vertex specified by @x, @y and @z. The rotation + * follows the right-hand thumb rule so for example rotating by 10 + * degrees about the vertex (0, 0, 1) causes a small counter-clockwise + * rotation. + * + * Deprecated: 1.10: Use cogl_framebuffer_rotate() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_rotate) +void +cogl_rotate (float angle, + float x, + float y, + float z); + +/** + * cogl_transform: + * @matrix: the matrix to multiply with the current model-view + * + * Multiplies the current model-view matrix by the given matrix. + * + * Since: 1.4 + * Deprecated: 1.10: Use cogl_framebuffer_transform() instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_transform) +void +cogl_transform (const CoglMatrix *matrix); + +/** + * cogl_get_modelview_matrix: + * @matrix: (out): return location for the model-view matrix + * + * Stores the current model-view matrix in @matrix. + * + * Deprecated: 1.10: Use cogl_framebuffer_get_modelview_matrix() + * instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_get_modelview_matrix) +void +cogl_get_modelview_matrix (CoglMatrix *matrix); + +/** + * cogl_set_modelview_matrix: + * @matrix: the new model-view matrix + * + * Loads @matrix as the new model-view matrix. + * + * Deprecated: 1.10: Use cogl_framebuffer_set_modelview_matrix() + * instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_set_modelview_matrix) +void +cogl_set_modelview_matrix (CoglMatrix *matrix); + +/** + * cogl_get_projection_matrix: + * @matrix: (out): return location for the projection matrix + * + * Stores the current projection matrix in @matrix. + * + * Deprecated: 1.10: Use cogl_framebuffer_get_projection_matrix() + * instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_get_projection_matrix) +void +cogl_get_projection_matrix (CoglMatrix *matrix); + +/** + * cogl_set_projection_matrix: + * @matrix: the new projection matrix + * + * Loads matrix as the new projection matrix. + * + * Deprecated: 1.10: Use cogl_framebuffer_set_projection_matrix() + * instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_set_projection_matrix) +void +cogl_set_projection_matrix (CoglMatrix *matrix); + +/** + * cogl_get_viewport: + * @v: (out) (array fixed-size=4): pointer to a 4 element array + * of #floats to receive the viewport dimensions. + * + * Stores the current viewport in @v. @v[0] and @v[1] get the x and y + * position of the viewport and @v[2] and @v[3] get the width and + * height. + * + * Deprecated: 1.10: Use cogl_framebuffer_get_viewport4fv() + * instead + */ +COGL_DEPRECATED_IN_1_10_FOR (cogl_framebuffer_get_viewport4fv) +void +cogl_get_viewport (float v[4]); + +/** + * cogl_set_depth_test_enabled: + * @setting: %TRUE to enable depth testing or %FALSE to disable. + * + * Sets whether depth testing is enabled. If it is disabled then the + * order that actors are layered on the screen depends solely on the + * order specified using clutter_actor_raise() and + * clutter_actor_lower(), otherwise it will also take into account the + * actor's depth. Depth testing is disabled by default. + * + * Deprecated: 1.16: Use cogl_pipeline_set_depth_state() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_depth_state) +void +cogl_set_depth_test_enabled (CoglBool setting); + +/** + * cogl_get_depth_test_enabled: + * + * Queries if depth testing has been enabled via cogl_set_depth_test_enable() + * + * Return value: %TRUE if depth testing is enabled, and %FALSE otherwise + * + * Deprecated: 1.16: Use cogl_pipeline_set_depth_state() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_depth_state) +CoglBool +cogl_get_depth_test_enabled (void); + +/** + * cogl_set_backface_culling_enabled: + * @setting: %TRUE to enable backface culling or %FALSE to disable. + * + * Sets whether textures positioned so that their backface is showing + * should be hidden. This can be used to efficiently draw two-sided + * textures or fully closed cubes without enabling depth testing. This + * only affects calls to the cogl_rectangle* family of functions and + * cogl_vertex_buffer_draw*. Backface culling is disabled by default. + * + * Deprecated: 1.16: Use cogl_pipeline_set_cull_face_mode() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_cull_face_mode) +void +cogl_set_backface_culling_enabled (CoglBool setting); + +/** + * cogl_get_backface_culling_enabled: + * + * Queries if backface culling has been enabled via + * cogl_set_backface_culling_enabled() + * + * Return value: %TRUE if backface culling is enabled, and %FALSE otherwise + * + * Deprecated: 1.16: Use cogl_pipeline_get_cull_face_mode() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_cull_face_mode) +CoglBool +cogl_get_backface_culling_enabled (void); + +/** + * cogl_set_fog: + * @fog_color: The color of the fog + * @mode: A #CoglFogMode that determines the equation used to calculate the + * fogging blend factor. + * @density: Used by %COGL_FOG_MODE_EXPONENTIAL and by + * %COGL_FOG_MODE_EXPONENTIAL_SQUARED equations. + * @z_near: Position along Z axis where no fogging should be applied + * @z_far: Position along Z axis where full fogging should be applied + * + * Enables fogging. Fogging causes vertices that are further away from the eye + * to be rendered with a different color. The color is determined according to + * the chosen fog mode; at it's simplest the color is linearly interpolated so + * that vertices at @z_near are drawn fully with their original color and + * vertices at @z_far are drawn fully with @fog_color. Fogging will remain + * enabled until you call cogl_disable_fog(). + * + * The fogging functions only work correctly when primitives use + * unmultiplied alpha colors. By default Cogl will premultiply textures + * and cogl_set_source_color() will premultiply colors, so unless you + * explicitly load your textures requesting an unmultiplied internal format + * and use cogl_material_set_color() you can only use fogging with fully + * opaque primitives. This might improve in the future when we can depend + * on fragment shaders. + * + * Deprecated: 1.16: Use #CoglSnippet shader api for fog + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_API) +void +cogl_set_fog (const CoglColor *fog_color, + CoglFogMode mode, + float density, + float z_near, + float z_far); + +/** + * cogl_disable_fog: + * + * This function disables fogging, so primitives drawn afterwards will not be + * blended with any previously set fog color. + * + * Deprecated: 1.16: Use #CoglSnippet shader api for fog + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_API) +void +cogl_disable_fog (void); + +/** + * cogl_clear: + * @color: Background color to clear to + * @buffers: A mask of #CoglBufferBit's identifying which auxiliary + * buffers to clear + * + * Clears all the auxiliary buffers identified in the @buffers mask, and if + * that includes the color buffer then the specified @color is used. + * + * Deprecated: 1.16: Use cogl_framebuffer_clear() api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_clear) +void +cogl_clear (const CoglColor *color, + unsigned long buffers); + +/** + * cogl_set_source: + * @material: A #CoglMaterial + * + * This function changes the material at the top of the source stack. + * The material at the top of this stack defines the GPU state used to + * process subsequent primitives, such as rectangles drawn with + * cogl_rectangle() or vertices drawn using cogl_vertex_buffer_draw(). + * + * Since: 1.0 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_source (void *material); + +/** + * cogl_get_source: + * + * Returns the current source material as previously set using + * cogl_set_source(). + * + * You should typically consider the returned material immutable + * and not try to change any of its properties unless you own a + * reference to that material. At times you may be able to get a + * reference to an internally managed materials and the result of + * modifying such materials is undefined. + * + * Return value: The current source material. + * + * Since: 1.6 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void * +cogl_get_source (void); + +/** + * cogl_push_source: + * @material: A #CoglMaterial + * + * Pushes the given @material to the top of the source stack. The + * material at the top of this stack defines the GPU state used to + * process later primitives as defined by cogl_set_source(). + * + * Since: 1.6 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_push_source (void *material); + +/** + * cogl_pop_source: + * + * Removes the material at the top of the source stack. The material + * at the top of this stack defines the GPU state used to process + * later primitives as defined by cogl_set_source(). + * + * Since: 1.6 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_pop_source (void); + +/** + * cogl_set_source_color: + * @color: a #CoglColor + * + * This is a convenience function for creating a solid fill source material + * from the given color. This color will be used for any subsequent drawing + * operation. + * + * The color will be premultiplied by Cogl, so the color should be + * non-premultiplied. For example: use (1.0, 0.0, 0.0, 0.5) for + * semi-transparent red. + * + * See also cogl_set_source_color4ub() and cogl_set_source_color4f() + * if you already have the color components. + * + * Since: 1.0 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_source_color (const CoglColor *color); + +/** + * cogl_set_source_color4ub: + * @red: value of the red channel, between 0 and 255 + * @green: value of the green channel, between 0 and 255 + * @blue: value of the blue channel, between 0 and 255 + * @alpha: value of the alpha channel, between 0 and 255 + * + * This is a convenience function for creating a solid fill source material + * from the given color using unsigned bytes for each component. This + * color will be used for any subsequent drawing operation. + * + * The value for each component is an unsigned byte in the range + * between 0 and 255. + * + * Since: 1.0 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_source_color4ub (uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); + +/** + * cogl_set_source_color4f: + * @red: value of the red channel, between 0 and %1.0 + * @green: value of the green channel, between 0 and %1.0 + * @blue: value of the blue channel, between 0 and %1.0 + * @alpha: value of the alpha channel, between 0 and %1.0 + * + * This is a convenience function for creating a solid fill source material + * from the given color using normalized values for each component. This color + * will be used for any subsequent drawing operation. + * + * The value for each component is a fixed point number in the range + * between 0 and %1.0. If the values passed in are outside that + * range, they will be clamped. + * + * Since: 1.0 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_source_color4f (float red, + float green, + float blue, + float alpha); + +/** + * cogl_set_source_texture: + * @texture: The #CoglTexture you want as your source + * + * This is a convenience function for creating a material with the first + * layer set to @texture and setting that material as the source with + * cogl_set_source. + * + * Note: There is no interaction between calls to cogl_set_source_color + * and cogl_set_source_texture. If you need to blend a texture with a color then + * you can create a simple material like this: + * + * material = cogl_material_new (); + * cogl_material_set_color4ub (material, 0xff, 0x00, 0x00, 0x80); + * cogl_material_set_layer (material, 0, tex_handle); + * cogl_set_source (material); + * + * + * Since: 1.0 + * Deprecated: 1.16: Latest drawing apis all take an explicit + * #CoglPipeline argument so this stack of + * #CoglMaterials shouldn't be used. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_source_texture (CoglTexture *texture); + +/** + * cogl_flush: + * + * This function should only need to be called in exceptional circumstances. + * + * As an optimization Cogl drawing functions may batch up primitives + * internally, so if you are trying to use raw GL outside of Cogl you stand a + * better chance of being successful if you ask Cogl to flush any batched + * geometry before making your state changes. + * + * It only ensure that the underlying driver is issued all the commands + * necessary to draw the batched primitives. It provides no guarantees about + * when the driver will complete the rendering. + * + * This provides no guarantees about the GL state upon returning and to avoid + * confusing Cogl you should aim to restore any changes you make before + * resuming use of Cogl. + * + * If you are making state changes with the intention of affecting Cogl drawing + * primitives you are 100% on your own since you stand a good chance of + * conflicting with Cogl internals. For example clutter-gst which currently + * uses direct GL calls to bind ARBfp programs will very likely break when Cogl + * starts to use ARBfb programs itself for the material API. + * + * Since: 1.0 + */ +void +cogl_flush (void); + +/** + * cogl_begin_gl: + * + * We do not advise nor reliably support the interleaving of raw GL drawing and + * Cogl drawing functions, but if you insist, cogl_begin_gl() and cogl_end_gl() + * provide a simple mechanism that may at least give you a fighting chance of + * succeeding. + * + * Note: this doesn't help you modify the behaviour of Cogl drawing functions + * through the modification of GL state; that will never be reliably supported, + * but if you are trying to do something like: + * + * |[ + * { + * - setup some OpenGL state. + * - draw using OpenGL (e.g. glDrawArrays() ) + * - reset modified OpenGL state. + * - continue using Cogl to draw + * } + * ]| + * + * You should surround blocks of drawing using raw GL with cogl_begin_gl() + * and cogl_end_gl(): + * + * |[ + * { + * cogl_begin_gl (); + * - setup some OpenGL state. + * - draw using OpenGL (e.g. glDrawArrays() ) + * - reset modified OpenGL state. + * cogl_end_gl (); + * - continue using Cogl to draw + * } + * ]| + * + * Don't ever try and do: + * + * |[ + * { + * - setup some OpenGL state. + * - use Cogl to draw + * - reset modified OpenGL state. + * } + * ]| + * + * When the internals of Cogl evolves, this is very liable to break. + * + * This function will flush all batched primitives, and subsequently flush + * all internal Cogl state to OpenGL as if it were going to draw something + * itself. + * + * The result is that the OpenGL modelview matrix will be setup; the state + * corresponding to the current source material will be set up and other world + * state such as backface culling, depth and fogging enabledness will be sent + * to OpenGL. + * + * No special material state is flushed, so if you want Cogl to setup a + * simplified material state it is your responsibility to set a simple source + * material before calling cogl_begin_gl(). E.g. by calling + * cogl_set_source_color4ub(). + * + * It is your responsibility to restore any OpenGL state that you modify + * to how it was after calling cogl_begin_gl() if you don't do this then the + * result of further Cogl calls is undefined. + * + * You can not nest begin/end blocks. + * + * Again we would like to stress, we do not advise the use of this API and if + * possible we would prefer to improve Cogl than have developers require raw + * OpenGL. + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglGLES2Context api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (CoglGLES2Context_API) +void +cogl_begin_gl (void); + +/** + * cogl_end_gl: + * + * This is the counterpart to cogl_begin_gl() used to delimit blocks of drawing + * code using raw OpenGL. Please refer to cogl_begin_gl() for full details. + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglGLES2Context api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (CoglGLES2Context_API) +void +cogl_end_gl (void); + +COGL_END_DECLS + +#endif /* __COGL_1_CONTEXT_H__ */ diff --git a/cogl/cogl/cogl2-experimental.h b/cogl/cogl/cogl2-experimental.h new file mode 100644 index 000000000..e4623d353 --- /dev/null +++ b/cogl/cogl/cogl2-experimental.h @@ -0,0 +1,37 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL2_EXPERIMENTAL_H__ +#define __COGL2_EXPERIMENTAL_H__ + +#define COGL_ENABLE_EXPERIMENTAL_2_0_API +#include + +#endif /* __COGL2_EXPERIMENTAL_H__ */ diff --git a/cogl/cogl/deprecated/cogl-auto-texture.c b/cogl/cogl/deprecated/cogl-auto-texture.c new file mode 100644 index 000000000..87b19c566 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-auto-texture.c @@ -0,0 +1,419 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2011,2012 Intel Corporation. + * Copyright (C) 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#include + +#include "cogl-context-private.h" +#include "cogl-texture.h" +#include "cogl-util.h" +#include "cogl-texture-2d.h" +#include "cogl-texture-2d-private.h" +#include "cogl-primitive-texture.h" +#include "cogl-texture-2d-sliced-private.h" +#include "cogl-private.h" +#include "cogl-object.h" +#include "cogl-bitmap-private.h" +#include "cogl-atlas-texture-private.h" +#include "cogl-error-private.h" +#include "cogl-texture-rectangle.h" +#include "cogl-sub-texture.h" +#include "cogl-texture-2d-gl.h" + +#include "deprecated/cogl-auto-texture.h" + +static CoglTexture * +_cogl_texture_new_from_bitmap (CoglBitmap *bitmap, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error); + +static void +set_auto_mipmap_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture), + FALSE); +} + +CoglTexture * +cogl_texture_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + CoglError *skip_error = NULL; + + _COGL_GET_CONTEXT (ctx, NULL); + + if ((_cogl_util_is_pot (width) && _cogl_util_is_pot (height)) || + (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + /* First try creating a fast-path non-sliced texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); + + _cogl_texture_set_internal_format (tex, internal_format); + + if (!cogl_texture_allocate (tex, &skip_error)) + { + cogl_error_free (skip_error); + cogl_object_unref (tex); + tex = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + /* If it fails resort to sliced textures */ + int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; + tex = COGL_TEXTURE (cogl_texture_2d_sliced_new_with_size (ctx, + width, + height, + max_waste)); + + _cogl_texture_set_internal_format (tex, internal_format); + } + + /* NB: This api existed before Cogl introduced lazy allocation of + * textures and so we maintain its original synchronous allocation + * semantics and return NULL if allocation fails... */ + if (!cogl_texture_allocate (tex, &skip_error)) + { + cogl_error_free (skip_error); + cogl_object_unref (tex); + return NULL; + } + + if (tex && + flags & COGL_TEXTURE_NO_AUTO_MIPMAP) + { + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); + } + + return tex; +} + +static CoglTexture * +_cogl_texture_new_from_data (CoglContext *ctx, + int width, + int height, + CoglTextureFlags flags, + CoglPixelFormat format, + CoglPixelFormat internal_format, + int rowstride, + const uint8_t *data, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture *tex; + + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + tex = _cogl_texture_new_from_bitmap (bmp, + flags, + internal_format, + FALSE, /* can't convert in place */ + error); + + cogl_object_unref (bmp); + + return tex; +} + +CoglTexture * +cogl_texture_new_from_data (int width, + int height, + CoglTextureFlags flags, + CoglPixelFormat format, + CoglPixelFormat internal_format, + int rowstride, + const uint8_t *data) +{ + CoglError *ignore_error = NULL; + CoglTexture *tex; + + _COGL_GET_CONTEXT (ctx, NULL); + + tex = _cogl_texture_new_from_data (ctx, + width, height, + flags, + format, internal_format, + rowstride, + data, + &ignore_error); + if (!tex) + cogl_error_free (ignore_error); + return tex; +} + +static CoglTexture * +_cogl_texture_new_from_bitmap (CoglBitmap *bitmap, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error) +{ + CoglContext *ctx = _cogl_bitmap_get_context (bitmap); + CoglTexture *tex; + CoglError *internal_error = NULL; + + if (!flags && + !COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ATLAS)) + { + /* First try putting the texture in the atlas */ + CoglAtlasTexture *atlas_tex = + _cogl_atlas_texture_new_from_bitmap (bitmap, + can_convert_in_place); + + _cogl_texture_set_internal_format (COGL_TEXTURE (atlas_tex), + internal_format); + + if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) + return COGL_TEXTURE (atlas_tex); + + cogl_error_free (internal_error); + internal_error = NULL; + cogl_object_unref (atlas_tex); + } + + /* If that doesn't work try a fast path 2D texture */ + if ((_cogl_util_is_pot (bitmap->width) && + _cogl_util_is_pot (bitmap->height)) || + (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + tex = COGL_TEXTURE (_cogl_texture_2d_new_from_bitmap (bitmap, + can_convert_in_place)); + + _cogl_texture_set_internal_format (tex, internal_format); + + if (!cogl_texture_allocate (tex, &internal_error)) + { + cogl_error_free (internal_error); + internal_error = NULL; + cogl_object_unref (tex); + tex = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + /* Otherwise create a sliced texture */ + int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; + tex = COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_bitmap (bitmap, + max_waste, + can_convert_in_place)); + + _cogl_texture_set_internal_format (tex, internal_format); + + if (!cogl_texture_allocate (tex, error)) + { + cogl_object_unref (tex); + tex = NULL; + } + } + + if (tex && + flags & COGL_TEXTURE_NO_AUTO_MIPMAP) + { + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); + } + + return tex; +} + +CoglTexture * +cogl_texture_new_from_bitmap (CoglBitmap *bitmap, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglError *ignore_error = NULL; + CoglTexture *tex = + _cogl_texture_new_from_bitmap (bitmap, + flags, + internal_format, + FALSE, /* can't convert in-place */ + &ignore_error); + if (!tex) + cogl_error_free (ignore_error); + return tex; +} + +CoglTexture * +cogl_texture_new_from_file (const char *filename, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + CoglError **error) +{ + CoglBitmap *bmp; + CoglTexture *texture = NULL; + + _COGL_GET_CONTEXT (ctx, NULL); + + _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL); + + bmp = cogl_bitmap_new_from_file (filename, error); + if (bmp == NULL) + return NULL; + + texture = _cogl_texture_new_from_bitmap (bmp, flags, + internal_format, + TRUE, /* can convert in-place */ + error); + + cogl_object_unref (bmp); + + return texture; +} + +CoglTexture * +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + _COGL_GET_CONTEXT (ctx, NULL); + +#ifdef HAVE_COGL_GL + if (gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + CoglTextureRectangle *texture_rectangle; + CoglSubTexture *sub_texture; + + if (x_pot_waste != 0 || y_pot_waste != 0) + { + /* It shouldn't be necessary to have waste in this case since + * the texture isn't limited to power of two sizes. */ + g_warning ("You can't create a foreign GL_TEXTURE_RECTANGLE cogl " + "texture with waste\n"); + return NULL; + } + + texture_rectangle = cogl_texture_rectangle_new_from_foreign (ctx, + gl_handle, + width, + height, + format); + _cogl_texture_set_internal_format (COGL_TEXTURE (texture_rectangle), + format); + + /* CoglTextureRectangle textures work with non-normalized + * coordinates, but the semantics for this function that people + * depend on are that all returned texture works with normalized + * coordinates so we wrap with a CoglSubTexture... */ + sub_texture = cogl_sub_texture_new (ctx, + COGL_TEXTURE (texture_rectangle), + 0, 0, width, height); + return COGL_TEXTURE (sub_texture); + } +#endif + + if (x_pot_waste != 0 || y_pot_waste != 0) + { + CoglTexture *tex = + COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_foreign (ctx, + gl_handle, + gl_target, + width, + height, + x_pot_waste, + y_pot_waste, + format)); + _cogl_texture_set_internal_format (tex, format); + + cogl_texture_allocate (tex, NULL); + return tex; + } + else + { + CoglTexture *tex = + COGL_TEXTURE (cogl_texture_2d_gl_new_from_foreign (ctx, + gl_handle, + width, + height, + format)); + _cogl_texture_set_internal_format (tex, format); + + cogl_texture_allocate (tex, NULL); + return tex; + } +} + +CoglTexture * +cogl_texture_new_from_sub_texture (CoglTexture *full_texture, + int sub_x, + int sub_y, + int sub_width, + int sub_height) +{ + _COGL_GET_CONTEXT (ctx, NULL); + return COGL_TEXTURE (cogl_sub_texture_new (ctx, + full_texture, sub_x, sub_y, + sub_width, sub_height)); +} diff --git a/cogl/cogl/deprecated/cogl-auto-texture.h b/cogl/cogl/deprecated/cogl-auto-texture.h new file mode 100644 index 000000000..331c5a9e2 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-auto-texture.h @@ -0,0 +1,221 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_AUTO_TEXTURE_H__ +#define __COGL_AUTO_TEXTURE_H__ + +COGL_BEGIN_DECLS + +/** + * cogl_texture_new_with_size: + * @width: width of texture in pixels. + * @height: height of texture in pixels. + * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE + * @internal_format: the #CoglPixelFormat to use for the GPU storage of the + * texture. + * + * Creates a new #CoglTexture with the specified dimensions and pixel format. + * + * Return value: (transfer full): A newly created #CoglTexture or %NULL on failure + * + * Since: 0.8 + * Deprecated: 1.18: Use specific constructors such as + * cogl_texture_2d_new_with_size() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_texture_2d_new_with_size__OR__cogl_texture_2d_sliced_new_with_size) +CoglTexture * +cogl_texture_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +/** + * cogl_texture_new_from_file: + * @filename: the file to load + * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE + * @internal_format: the #CoglPixelFormat to use for the GPU storage of the + * texture. If %COGL_PIXEL_FORMAT_ANY is given then a premultiplied + * format similar to the format of the source data will be used. The + * default blending equations of Cogl expect premultiplied color data; + * the main use of passing a non-premultiplied format here is if you + * have non-premultiplied source data and are going to adjust the blend + * mode (see cogl_material_set_blend()) or use the data for something + * other than straight blending. + * @error: return location for a #CoglError or %NULL + * + * Creates a #CoglTexture from an image file. + * + * Return value: (transfer full): A newly created #CoglTexture or + * %NULL on failure + * + * Since: 0.8 + * Deprecated: 1.18: Use specific constructors such as + * cogl_texture_2d_new_from_file() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_texture_2d_new_from_file__OR__cogl_texture_2d_sliced_new_from_file) +CoglTexture * +cogl_texture_new_from_file (const char *filename, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + CoglError **error); + +/** + * cogl_texture_new_from_data: + * @width: width of texture in pixels + * @height: height of texture in pixels + * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @internal_format: the #CoglPixelFormat that will be used for storing + * the buffer on the GPU. If COGL_PIXEL_FORMAT_ANY is given then a + * premultiplied format similar to the format of the source data will + * be used. The default blending equations of Cogl expect premultiplied + * color data; the main use of passing a non-premultiplied format here + * is if you have non-premultiplied source data and are going to adjust + * the blend mode (see cogl_material_set_blend()) or use the data for + * something other than straight blending. + * @rowstride: the memory offset in bytes between the starts of + * scanlines in @data + * @data: pointer the memory region where the source buffer resides + * + * Creates a new #CoglTexture based on data residing in memory. + * + * Return value: (transfer full): A newly created #CoglTexture or + * %NULL on failure + * + * Since: 0.8 + * Deprecated: 1.18: Use specific constructors such as + * cogl_texture_2d_new_from_data() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_texture_2d_new_from_data__OR__cogl_texture_2d_sliced_new_from_data) +CoglTexture * +cogl_texture_new_from_data (int width, + int height, + CoglTextureFlags flags, + CoglPixelFormat format, + CoglPixelFormat internal_format, + int rowstride, + const uint8_t *data); + +/** + * cogl_texture_new_from_foreign: + * @gl_handle: opengl handle of foreign texture. + * @gl_target: opengl target type of foreign texture + * @width: width of foreign texture + * @height: height of foreign texture. + * @x_pot_waste: horizontal waste on the right hand edge of the texture. + * @y_pot_waste: vertical waste on the bottom edge of the texture. + * @format: format of the foreign texture. + * + * Creates a #CoglTexture based on an existing OpenGL texture; the + * width, height and format are passed along since it is not always + * possible to query these from OpenGL. + * + * The waste arguments allow you to create a Cogl texture that maps to + * a region smaller than the real OpenGL texture. For instance if your + * hardware only supports power-of-two textures you may load a + * non-power-of-two image into a larger power-of-two texture and use + * the waste arguments to tell Cogl which region should be mapped to + * the texture coordinate range [0:1]. + * + * Return value: (transfer full): A newly created #CoglTexture or + * %NULL on failure + * + * Since: 0.8 + * Deprecated: 1.18: Use specific constructors such as + * cogl_texture_2d_new_from_foreign() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_texture_2d_new_from_foreign) +CoglTexture * +cogl_texture_new_from_foreign (unsigned int gl_handle, + unsigned int gl_target, + unsigned int width, + unsigned int height, + unsigned int x_pot_waste, + unsigned int y_pot_waste, + CoglPixelFormat format); + +/** + * cogl_texture_new_from_bitmap: + * @bitmap: A #CoglBitmap pointer + * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE + * @internal_format: the #CoglPixelFormat to use for the GPU storage of the + * texture + * + * Creates a #CoglTexture from a #CoglBitmap. + * + * Return value: (transfer full): A newly created #CoglTexture or + * %NULL on failure + * + * Since: 1.0 + * Deprecated: 1.18: Use specific constructors such as + * cogl_texture_2d_new_from_bitmap() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_texture_2d_new_from_bitmap__OR__cogl_texture_2d_sliced_new_from_bitmap) +CoglTexture * +cogl_texture_new_from_bitmap (CoglBitmap *bitmap, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +/** + * cogl_texture_new_from_sub_texture: + * @full_texture: a #CoglTexture pointer + * @sub_x: X coordinate of the top-left of the subregion + * @sub_y: Y coordinate of the top-left of the subregion + * @sub_width: Width in pixels of the subregion + * @sub_height: Height in pixels of the subregion + * + * Creates a new texture which represents a subregion of another + * texture. The GL resources will be shared so that no new texture + * data is actually allocated. + * + * Sub textures have undefined behaviour texture coordinates outside + * of the range [0,1] are used. They also do not work with + * CoglVertexBuffers. + * + * The sub texture will keep a reference to the full texture so you do + * not need to keep one separately if you only want to use the sub + * texture. + * + * Return value: (transfer full): A newly created #CoglTexture or + * %NULL on failure + * Since: 1.2 + * Deprecated: 1.18: Use cogl_sub_texture_new() + */ +COGL_DEPRECATED_IN_1_18_FOR(cogl_sub_texture_new) +CoglTexture * +cogl_texture_new_from_sub_texture (CoglTexture *full_texture, + int sub_x, + int sub_y, + int sub_width, + int sub_height); + +COGL_END_DECLS + +#endif /* __COGL_AUTO_TEXTURE_H__ */ diff --git a/cogl/cogl/deprecated/cogl-clip-state.c b/cogl/cogl/deprecated/cogl-clip-state.c new file mode 100644 index 000000000..1e0ec9289 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-clip-state.c @@ -0,0 +1,138 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "cogl-clip-state.h" +#include "cogl-clip-stack.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-journal-private.h" +#include "cogl-util.h" +#include "cogl-matrix-private.h" +#include "cogl1-context.h" + +void +cogl_clip_push_window_rectangle (int x_offset, + int y_offset, + int width, + int height) +{ + cogl_framebuffer_push_scissor_clip (cogl_get_draw_framebuffer (), + x_offset, y_offset, width, height); +} + +/* XXX: This is deprecated API */ +void +cogl_clip_push_window_rect (float x_offset, + float y_offset, + float width, + float height) +{ + cogl_clip_push_window_rectangle (x_offset, y_offset, width, height); +} + +void +cogl_clip_push_rectangle (float x_1, + float y_1, + float x_2, + float y_2) +{ + cogl_framebuffer_push_rectangle_clip (cogl_get_draw_framebuffer (), + x_1, y_1, x_2, y_2); +} + +/* XXX: Deprecated API */ +void +cogl_clip_push (float x_offset, + float y_offset, + float width, + float height) +{ + cogl_clip_push_rectangle (x_offset, + y_offset, + x_offset + width, + y_offset + height); +} + +void +cogl_clip_push_primitive (CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2) +{ + cogl_framebuffer_push_primitive_clip (cogl_get_draw_framebuffer (), + primitive, + bounds_x1, + bounds_y1, + bounds_x2, + bounds_y2); +} + +void +cogl_clip_pop (void) +{ + cogl_framebuffer_pop_clip (cogl_get_draw_framebuffer ()); +} + +void +cogl_clip_stack_save (void) +{ + /* This function was just used to temporarily switch the clip stack + * when using an offscreen buffer. This is no longer needed because + * each framebuffer maintains its own clip stack. The function is + * documented to do nothing since version 1.2 */ +} + +void +cogl_clip_stack_restore (void) +{ + /* Do nothing. See cogl_clip_stack_save() */ +} + +/* XXX: This should never have been made public API! */ +void +cogl_clip_ensure (void) +{ + /* Do nothing. + * + * This API shouldn't be used by anyone and the documented semantics + * are basically vague enough that we can get away with doing + * nothing here. + */ +} diff --git a/cogl/cogl/deprecated/cogl-clip-state.h b/cogl/cogl/deprecated/cogl-clip-state.h new file mode 100644 index 000000000..51091abe5 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-clip-state.h @@ -0,0 +1,266 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_CLIP_STATE_H__ +#define __COGL_CLIP_STATE_H__ + +#include +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-clipping + * @short_description: Fuctions for manipulating a stack of clipping regions + * + * To support clipping your geometry to rectangles or paths Cogl exposes a + * stack based API whereby each clip region you push onto the stack is + * intersected with the previous region. + */ + +/** + * cogl_clip_push_window_rectangle: + * @x_offset: left edge of the clip rectangle in window coordinates + * @y_offset: top edge of the clip rectangle in window coordinates + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are not transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_pop(). + * + * Since: 1.2 + * Deprecated: 1.16: Use cogl_framebuffer_push_scissor_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_scissor_clip) +void +cogl_clip_push_window_rectangle (int x_offset, + int y_offset, + int width, + int height); + +/** + * cogl_clip_push_window_rect: + * @x_offset: left edge of the clip rectangle in window coordinates + * @y_offset: top edge of the clip rectangle in window coordinates + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are not transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_pop(). + * + * Deprecated: 1.16: Use cogl_framebuffer_push_scissor_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_scissor_clip) +void +cogl_clip_push_window_rect (float x_offset, + float y_offset, + float width, + float height); + +/** + * cogl_clip_push_rectangle: + * @x0: x coordinate for top left corner of the clip rectangle + * @y0: y coordinate for top left corner of the clip rectangle + * @x1: x coordinate for bottom right corner of the clip rectangle + * @y1: y coordinate for bottom right corner of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_pop(). + * + * Since: 1.2 + * Deprecated: 1.16: Use cogl_framebuffer_push_rectangle_clip() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_rectangle_clip) +void +cogl_clip_push_rectangle (float x0, + float y0, + float x1, + float y1); + +/** + * cogl_clip_push: + * @x_offset: left edge of the clip rectangle + * @y_offset: top edge of the clip rectangle + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_pop(). + * + * Deprecated: 1.16: The x, y, width, height arguments are inconsistent + * with other API that specify rectangles in model space, and when used + * with a coordinate space that puts the origin at the center and y+ + * extending up, it's awkward to use. Please use + * cogl_framebuffer_push_rectangle_clip() + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_rectangle_clip) +void +cogl_clip_push (float x_offset, + float y_offset, + float width, + float height); + +/** + * cogl_clip_push_primitive: + * @primitive: A #CoglPrimitive describing a flat 2D shape + * @bounds_x1: x coordinate for the top-left corner of the primitives + * bounds + * @bounds_y1: y coordinate for the top-left corner of the primitives + * bounds + * @bounds_x2: x coordinate for the bottom-right corner of the primitives + * bounds + * @bounds_y2: y coordinate for the bottom-right corner of the + * primitives bounds. + * + * Sets a new clipping area using a 2D shaped described with a + * #CoglPrimitive. The shape must not contain self overlapping + * geometry and must lie on a single 2D plane. A bounding box of the + * 2D shape in local coordinates (the same coordinates used to + * describe the shape) must be given. It is acceptable for the bounds + * to be larger than the true bounds but behaviour is undefined if the + * bounds are smaller than the true bounds. + * + * The primitive is transformed by the current model-view matrix and + * the silhouette is intersected with the previous clipping area. To + * restore the previous clipping area, call + * cogl_clip_pop(). + * + * Since: 1.10 + * Stability: unstable + * Deprecated: 1.16: Use cogl_framebuffer_push_primitive_clip() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_push_primitive_clip) +void +cogl_clip_push_primitive (CoglPrimitive *primitive, + float bounds_x1, + float bounds_y1, + float bounds_x2, + float bounds_y2); +/** + * cogl_clip_pop: + * + * Reverts the clipping region to the state before the last call to + * cogl_clip_push(). + * + * Deprecated: 1.16: Use cogl_framebuffer_pop_clip() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_pop_clip) +void +cogl_clip_pop (void); + +/** + * cogl_clip_ensure: + * + * Ensures that the current clipping region has been set in GL. This + * will automatically be called before any Cogl primitives but it + * maybe be neccessary to call if you are using raw GL calls with + * clipping. + * + * Deprecated: 1.2: Calling this function has no effect + * + * Since: 1.0 + */ +COGL_DEPRECATED +void +cogl_clip_ensure (void); + +/** + * cogl_clip_stack_save: + * + * Save the entire state of the clipping stack and then clear all + * clipping. The previous state can be returned to with + * cogl_clip_stack_restore(). Each call to cogl_clip_push() after this + * must be matched by a call to cogl_clip_pop() before calling + * cogl_clip_stack_restore(). + * + * Deprecated: 1.2: This was originally added to allow us to save the + * clip stack when switching to an offscreen framebuffer, but it's + * not necessary anymore given that framebuffers now own separate + * clip stacks which will be automatically switched between when a + * new buffer is set. Calling this function has no effect + * + * Since: 0.8.2 + */ +COGL_DEPRECATED +void +cogl_clip_stack_save (void); + +/** + * cogl_clip_stack_restore: + * + * Restore the state of the clipping stack that was previously saved + * by cogl_clip_stack_save(). + * + * Deprecated: 1.2: This was originally added to allow us to restore + * the clip stack when switching back from an offscreen framebuffer, + * but it's not necessary anymore given that framebuffers now own + * separate clip stacks which will be automatically switched between + * when a new buffer is set. Calling this function has no effect + * + * Since: 0.8.2 + */ +COGL_DEPRECATED +void +cogl_clip_stack_restore (void); + +COGL_END_DECLS + +#endif /* __COGL_CLIP_STATE_H__ */ diff --git a/cogl/cogl/deprecated/cogl-clutter-xlib.h b/cogl/cogl/deprecated/cogl-clutter-xlib.h new file mode 100644 index 000000000..424ff49b0 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-clutter-xlib.h @@ -0,0 +1,46 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_XLIB_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_CLUTTER_XLIB_H__ +#define __COGL_CLUTTER_XLIB_H__ + +#include + +COGL_BEGIN_DECLS + +#define cogl_clutter_winsys_xlib_get_visual_info cogl_clutter_winsys_xlib_get_visual_info_CLUTTER +XVisualInfo * +cogl_clutter_winsys_xlib_get_visual_info (void); + +COGL_END_DECLS + +#endif /* __COGL_CLUTTER_XLIB_H__ */ diff --git a/cogl/cogl/deprecated/cogl-clutter.c b/cogl/cogl/deprecated/cogl-clutter.c new file mode 100644 index 000000000..4e53a949d --- /dev/null +++ b/cogl/cogl/deprecated/cogl-clutter.c @@ -0,0 +1,114 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cogl-util.h" +#include "cogl-types.h" +#include "cogl-private.h" +#include "cogl-context-private.h" +#include "cogl-winsys-private.h" +#include "cogl-winsys-stub-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#ifdef COGL_HAS_XLIB_SUPPORT +#include "cogl-clutter-xlib.h" +#include "cogl-xlib-renderer.h" +#endif +#include "cogl-clutter.h" + +CoglBool +cogl_clutter_check_extension (const char *name, const char *ext) +{ + char *end; + int name_len, n; + + if (name == NULL || ext == NULL) + return FALSE; + + end = (char*)(ext + strlen(ext)); + + name_len = strlen(name); + + while (ext < end) + { + n = strcspn(ext, " "); + + if ((name_len == n) && (!strncmp(name, ext, n))) + return TRUE; + ext += (n + 1); + } + + return FALSE; +} + +CoglBool +cogl_clutter_winsys_has_feature (CoglWinsysFeature feature) +{ + return _cogl_winsys_has_feature (feature); +} + +void +cogl_onscreen_clutter_backend_set_size (int width, int height) +{ + CoglFramebuffer *framebuffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (_cogl_context_get_winsys (ctx) != _cogl_winsys_stub_get_vtable ()) + return; + + framebuffer = COGL_FRAMEBUFFER (ctx->window_buffer); + + _cogl_framebuffer_winsys_update_size (framebuffer, width, height); +} + +#ifdef COGL_HAS_XLIB_SUPPORT +XVisualInfo * +cogl_clutter_winsys_xlib_get_visual_info (void) +{ + CoglRenderer *renderer; + + _COGL_GET_CONTEXT (ctx, NULL); + + _COGL_RETURN_VAL_IF_FAIL (ctx->display != NULL, NULL); + + renderer = cogl_display_get_renderer (ctx->display); + _COGL_RETURN_VAL_IF_FAIL (renderer != NULL, NULL); + + return cogl_xlib_renderer_get_visual_info (renderer); +} +#endif diff --git a/cogl/cogl/deprecated/cogl-clutter.h b/cogl/cogl/deprecated/cogl-clutter.h new file mode 100644 index 000000000..cc6c44644 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-clutter.h @@ -0,0 +1,54 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_CLUTTER_H__ +#define __COGL_CLUTTER_H__ + +COGL_BEGIN_DECLS + +#define cogl_clutter_check_extension cogl_clutter_check_extension_CLUTTER +COGL_DEPRECATED_IN_1_16 +CoglBool +cogl_clutter_check_extension (const char *name, const char *ext); + +#define cogl_clutter_winsys_has_feature cogl_clutter_winsys_has_feature_CLUTTER +COGL_DEPRECATED_FOR (cogl_has_feature) +CoglBool +cogl_clutter_winsys_has_feature (CoglWinsysFeature feature); + +#define cogl_onscreen_clutter_backend_set_size cogl_onscreen_clutter_backend_set_size_CLUTTER +void +cogl_onscreen_clutter_backend_set_size (int width, int height); + +COGL_END_DECLS + +#endif /* __COGL_CLUTTER_H__ */ diff --git a/cogl/cogl/deprecated/cogl-fixed.c b/cogl/cogl/deprecated/cogl-fixed.c new file mode 100644 index 000000000..bdae6cdfd --- /dev/null +++ b/cogl/cogl/deprecated/cogl-fixed.c @@ -0,0 +1,1112 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#define G_IMPLEMENT_INLINES + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef HAVE_FLOAT_WORD_ORDER +#include +#endif + +#include "cogl-fixed.h" + +/* pre-computed sin table for 1st quadrant + * + * Currently contains 257 entries. + * + * The current maximum absolute error is about 1.9e-0.5 + * and is greatest around pi/2 where the second derivative + * of sin(x) is greatest. If greater accuracy is needed, + * modestly increasing the table size, or maybe using + * quadratic interpolation would drop the interpolation + * error below the precision limits of CoglFixed. + */ +static const CoglFixed sin_tbl[] = +{ + 0x00000000L, 0x00000192L, 0x00000324L, 0x000004B6L, + 0x00000648L, 0x000007DAL, 0x0000096CL, 0x00000AFEL, + 0x00000C90L, 0x00000E21L, 0x00000FB3L, 0x00001144L, + 0x000012D5L, 0x00001466L, 0x000015F7L, 0x00001787L, + 0x00001918L, 0x00001AA8L, 0x00001C38L, 0x00001DC7L, + 0x00001F56L, 0x000020E5L, 0x00002274L, 0x00002402L, + 0x00002590L, 0x0000271EL, 0x000028ABL, 0x00002A38L, + 0x00002BC4L, 0x00002D50L, 0x00002EDCL, 0x00003067L, + 0x000031F1L, 0x0000337CL, 0x00003505L, 0x0000368EL, + 0x00003817L, 0x0000399FL, 0x00003B27L, 0x00003CAEL, + 0x00003E34L, 0x00003FBAL, 0x0000413FL, 0x000042C3L, + 0x00004447L, 0x000045CBL, 0x0000474DL, 0x000048CFL, + 0x00004A50L, 0x00004BD1L, 0x00004D50L, 0x00004ECFL, + 0x0000504DL, 0x000051CBL, 0x00005348L, 0x000054C3L, + 0x0000563EL, 0x000057B9L, 0x00005932L, 0x00005AAAL, + 0x00005C22L, 0x00005D99L, 0x00005F0FL, 0x00006084L, + 0x000061F8L, 0x0000636BL, 0x000064DDL, 0x0000664EL, + 0x000067BEL, 0x0000692DL, 0x00006A9BL, 0x00006C08L, + 0x00006D74L, 0x00006EDFL, 0x00007049L, 0x000071B2L, + 0x0000731AL, 0x00007480L, 0x000075E6L, 0x0000774AL, + 0x000078ADL, 0x00007A10L, 0x00007B70L, 0x00007CD0L, + 0x00007E2FL, 0x00007F8CL, 0x000080E8L, 0x00008243L, + 0x0000839CL, 0x000084F5L, 0x0000864CL, 0x000087A1L, + 0x000088F6L, 0x00008A49L, 0x00008B9AL, 0x00008CEBL, + 0x00008E3AL, 0x00008F88L, 0x000090D4L, 0x0000921FL, + 0x00009368L, 0x000094B0L, 0x000095F7L, 0x0000973CL, + 0x00009880L, 0x000099C2L, 0x00009B03L, 0x00009C42L, + 0x00009D80L, 0x00009EBCL, 0x00009FF7L, 0x0000A130L, + 0x0000A268L, 0x0000A39EL, 0x0000A4D2L, 0x0000A605L, + 0x0000A736L, 0x0000A866L, 0x0000A994L, 0x0000AAC1L, + 0x0000ABEBL, 0x0000AD14L, 0x0000AE3CL, 0x0000AF62L, + 0x0000B086L, 0x0000B1A8L, 0x0000B2C9L, 0x0000B3E8L, + 0x0000B505L, 0x0000B620L, 0x0000B73AL, 0x0000B852L, + 0x0000B968L, 0x0000BA7DL, 0x0000BB8FL, 0x0000BCA0L, + 0x0000BDAFL, 0x0000BEBCL, 0x0000BFC7L, 0x0000C0D1L, + 0x0000C1D8L, 0x0000C2DEL, 0x0000C3E2L, 0x0000C4E4L, + 0x0000C5E4L, 0x0000C6E2L, 0x0000C7DEL, 0x0000C8D9L, + 0x0000C9D1L, 0x0000CAC7L, 0x0000CBBCL, 0x0000CCAEL, + 0x0000CD9FL, 0x0000CE8EL, 0x0000CF7AL, 0x0000D065L, + 0x0000D14DL, 0x0000D234L, 0x0000D318L, 0x0000D3FBL, + 0x0000D4DBL, 0x0000D5BAL, 0x0000D696L, 0x0000D770L, + 0x0000D848L, 0x0000D91EL, 0x0000D9F2L, 0x0000DAC4L, + 0x0000DB94L, 0x0000DC62L, 0x0000DD2DL, 0x0000DDF7L, + 0x0000DEBEL, 0x0000DF83L, 0x0000E046L, 0x0000E107L, + 0x0000E1C6L, 0x0000E282L, 0x0000E33CL, 0x0000E3F4L, + 0x0000E4AAL, 0x0000E55EL, 0x0000E610L, 0x0000E6BFL, + 0x0000E76CL, 0x0000E817L, 0x0000E8BFL, 0x0000E966L, + 0x0000EA0AL, 0x0000EAABL, 0x0000EB4BL, 0x0000EBE8L, + 0x0000EC83L, 0x0000ED1CL, 0x0000EDB3L, 0x0000EE47L, + 0x0000EED9L, 0x0000EF68L, 0x0000EFF5L, 0x0000F080L, + 0x0000F109L, 0x0000F18FL, 0x0000F213L, 0x0000F295L, + 0x0000F314L, 0x0000F391L, 0x0000F40CL, 0x0000F484L, + 0x0000F4FAL, 0x0000F56EL, 0x0000F5DFL, 0x0000F64EL, + 0x0000F6BAL, 0x0000F724L, 0x0000F78CL, 0x0000F7F1L, + 0x0000F854L, 0x0000F8B4L, 0x0000F913L, 0x0000F96EL, + 0x0000F9C8L, 0x0000FA1FL, 0x0000FA73L, 0x0000FAC5L, + 0x0000FB15L, 0x0000FB62L, 0x0000FBADL, 0x0000FBF5L, + 0x0000FC3BL, 0x0000FC7FL, 0x0000FCC0L, 0x0000FCFEL, + 0x0000FD3BL, 0x0000FD74L, 0x0000FDACL, 0x0000FDE1L, + 0x0000FE13L, 0x0000FE43L, 0x0000FE71L, 0x0000FE9CL, + 0x0000FEC4L, 0x0000FEEBL, 0x0000FF0EL, 0x0000FF30L, + 0x0000FF4EL, 0x0000FF6BL, 0x0000FF85L, 0x0000FF9CL, + 0x0000FFB1L, 0x0000FFC4L, 0x0000FFD4L, 0x0000FFE1L, + 0x0000FFECL, 0x0000FFF5L, 0x0000FFFBL, 0x0000FFFFL, + 0x00010000L, +}; + +/* pre-computed tan table for 1st quadrant */ +static const CoglFixed tan_tbl[] = +{ + 0x00000000L, 0x00000192L, 0x00000324L, 0x000004b7L, + 0x00000649L, 0x000007dbL, 0x0000096eL, 0x00000b01L, + 0x00000c94L, 0x00000e27L, 0x00000fbaL, 0x0000114eL, + 0x000012e2L, 0x00001477L, 0x0000160cL, 0x000017a1L, + 0x00001937L, 0x00001acdL, 0x00001c64L, 0x00001dfbL, + 0x00001f93L, 0x0000212cL, 0x000022c5L, 0x0000245fL, + 0x000025f9L, 0x00002795L, 0x00002931L, 0x00002aceL, + 0x00002c6cL, 0x00002e0aL, 0x00002faaL, 0x0000314aL, + 0x000032ecL, 0x0000348eL, 0x00003632L, 0x000037d7L, + 0x0000397dL, 0x00003b24L, 0x00003cccL, 0x00003e75L, + 0x00004020L, 0x000041ccL, 0x00004379L, 0x00004528L, + 0x000046d8L, 0x0000488aL, 0x00004a3dL, 0x00004bf2L, + 0x00004da8L, 0x00004f60L, 0x0000511aL, 0x000052d5L, + 0x00005492L, 0x00005651L, 0x00005812L, 0x000059d5L, + 0x00005b99L, 0x00005d60L, 0x00005f28L, 0x000060f3L, + 0x000062c0L, 0x0000648fL, 0x00006660L, 0x00006834L, + 0x00006a0aL, 0x00006be2L, 0x00006dbdL, 0x00006f9aL, + 0x0000717aL, 0x0000735dL, 0x00007542L, 0x0000772aL, + 0x00007914L, 0x00007b02L, 0x00007cf2L, 0x00007ee6L, + 0x000080dcL, 0x000082d6L, 0x000084d2L, 0x000086d2L, + 0x000088d6L, 0x00008adcL, 0x00008ce7L, 0x00008ef4L, + 0x00009106L, 0x0000931bL, 0x00009534L, 0x00009750L, + 0x00009971L, 0x00009b95L, 0x00009dbeL, 0x00009febL, + 0x0000a21cL, 0x0000a452L, 0x0000a68cL, 0x0000a8caL, + 0x0000ab0eL, 0x0000ad56L, 0x0000afa3L, 0x0000b1f5L, + 0x0000b44cL, 0x0000b6a8L, 0x0000b909L, 0x0000bb70L, + 0x0000bdddL, 0x0000c04fL, 0x0000c2c7L, 0x0000c545L, + 0x0000c7c9L, 0x0000ca53L, 0x0000cce3L, 0x0000cf7aL, + 0x0000d218L, 0x0000d4bcL, 0x0000d768L, 0x0000da1aL, + 0x0000dcd4L, 0x0000df95L, 0x0000e25eL, 0x0000e52eL, + 0x0000e806L, 0x0000eae7L, 0x0000edd0L, 0x0000f0c1L, + 0x0000f3bbL, 0x0000f6bfL, 0x0000f9cbL, 0x0000fce1L, + 0x00010000L, 0x00010329L, 0x0001065dL, 0x0001099aL, + 0x00010ce3L, 0x00011036L, 0x00011394L, 0x000116feL, + 0x00011a74L, 0x00011df6L, 0x00012184L, 0x0001251fL, + 0x000128c6L, 0x00012c7cL, 0x0001303fL, 0x00013410L, + 0x000137f0L, 0x00013bdfL, 0x00013fddL, 0x000143ebL, + 0x00014809L, 0x00014c37L, 0x00015077L, 0x000154c9L, + 0x0001592dL, 0x00015da4L, 0x0001622eL, 0x000166ccL, + 0x00016b7eL, 0x00017045L, 0x00017523L, 0x00017a17L, + 0x00017f22L, 0x00018444L, 0x00018980L, 0x00018ed5L, + 0x00019445L, 0x000199cfL, 0x00019f76L, 0x0001a53aL, + 0x0001ab1cL, 0x0001b11dL, 0x0001b73fL, 0x0001bd82L, + 0x0001c3e7L, 0x0001ca71L, 0x0001d11fL, 0x0001d7f4L, + 0x0001def1L, 0x0001e618L, 0x0001ed6aL, 0x0001f4e8L, + 0x0001fc96L, 0x00020473L, 0x00020c84L, 0x000214c9L, + 0x00021d44L, 0x000225f9L, 0x00022ee9L, 0x00023818L, + 0x00024187L, 0x00024b3aL, 0x00025534L, 0x00025f78L, + 0x00026a0aL, 0x000274edL, 0x00028026L, 0x00028bb8L, + 0x000297a8L, 0x0002a3fbL, 0x0002b0b5L, 0x0002bdddL, + 0x0002cb79L, 0x0002d98eL, 0x0002e823L, 0x0002f740L, + 0x000306ecL, 0x00031730L, 0x00032816L, 0x000339a6L, + 0x00034bebL, 0x00035ef2L, 0x000372c6L, 0x00038776L, + 0x00039d11L, 0x0003b3a6L, 0x0003cb48L, 0x0003e40aL, + 0x0003fe02L, 0x00041949L, 0x000435f7L, 0x0004542bL, + 0x00047405L, 0x000495a9L, 0x0004b940L, 0x0004def6L, + 0x00050700L, 0x00053196L, 0x00055ef9L, 0x00058f75L, + 0x0005c35dL, 0x0005fb14L, 0x00063709L, 0x000677c0L, + 0x0006bdd0L, 0x000709ecL, 0x00075ce6L, 0x0007b7bbL, + 0x00081b98L, 0x000889e9L, 0x0009046eL, 0x00098d4dL, + 0x000a2736L, 0x000ad593L, 0x000b9cc6L, 0x000c828aL, + 0x000d8e82L, 0x000ecb1bL, 0x001046eaL, 0x00121703L, + 0x00145b00L, 0x0017448dL, 0x001b2672L, 0x002095afL, + 0x0028bc49L, 0x0036519aL, 0x00517bb6L, 0x00a2f8fdL, + 0x46d3eab2L, +}; + +/* 257-value table of atan. + * + * atan_tbl[0] is atan(0.0) and atan_tbl[256] is atan(1). + * The angles are radians in CoglFixed truncated to 16-bit (they're + * all less than one) + */ +static const uint16_t atan_tbl[] = +{ + 0x0000, 0x00FF, 0x01FF, 0x02FF, 0x03FF, 0x04FF, 0x05FF, 0x06FF, + 0x07FF, 0x08FF, 0x09FE, 0x0AFE, 0x0BFD, 0x0CFD, 0x0DFC, 0x0EFB, + 0x0FFA, 0x10F9, 0x11F8, 0x12F7, 0x13F5, 0x14F3, 0x15F2, 0x16F0, + 0x17EE, 0x18EB, 0x19E9, 0x1AE6, 0x1BE3, 0x1CE0, 0x1DDD, 0x1ED9, + 0x1FD5, 0x20D1, 0x21CD, 0x22C8, 0x23C3, 0x24BE, 0x25B9, 0x26B3, + 0x27AD, 0x28A7, 0x29A1, 0x2A9A, 0x2B93, 0x2C8B, 0x2D83, 0x2E7B, + 0x2F72, 0x306A, 0x3160, 0x3257, 0x334D, 0x3442, 0x3538, 0x362D, + 0x3721, 0x3815, 0x3909, 0x39FC, 0x3AEF, 0x3BE2, 0x3CD4, 0x3DC5, + 0x3EB6, 0x3FA7, 0x4097, 0x4187, 0x4277, 0x4365, 0x4454, 0x4542, + 0x462F, 0x471C, 0x4809, 0x48F5, 0x49E0, 0x4ACB, 0x4BB6, 0x4CA0, + 0x4D89, 0x4E72, 0x4F5B, 0x5043, 0x512A, 0x5211, 0x52F7, 0x53DD, + 0x54C2, 0x55A7, 0x568B, 0x576F, 0x5852, 0x5934, 0x5A16, 0x5AF7, + 0x5BD8, 0x5CB8, 0x5D98, 0x5E77, 0x5F55, 0x6033, 0x6110, 0x61ED, + 0x62C9, 0x63A4, 0x647F, 0x6559, 0x6633, 0x670C, 0x67E4, 0x68BC, + 0x6993, 0x6A6A, 0x6B40, 0x6C15, 0x6CEA, 0x6DBE, 0x6E91, 0x6F64, + 0x7036, 0x7108, 0x71D9, 0x72A9, 0x7379, 0x7448, 0x7516, 0x75E4, + 0x76B1, 0x777E, 0x7849, 0x7915, 0x79DF, 0x7AA9, 0x7B72, 0x7C3B, + 0x7D03, 0x7DCA, 0x7E91, 0x7F57, 0x801C, 0x80E1, 0x81A5, 0x8269, + 0x832B, 0x83EE, 0x84AF, 0x8570, 0x8630, 0x86F0, 0x87AF, 0x886D, + 0x892A, 0x89E7, 0x8AA4, 0x8B5F, 0x8C1A, 0x8CD5, 0x8D8E, 0x8E47, + 0x8F00, 0x8FB8, 0x906F, 0x9125, 0x91DB, 0x9290, 0x9345, 0x93F9, + 0x94AC, 0x955F, 0x9611, 0x96C2, 0x9773, 0x9823, 0x98D2, 0x9981, + 0x9A2F, 0x9ADD, 0x9B89, 0x9C36, 0x9CE1, 0x9D8C, 0x9E37, 0x9EE0, + 0x9F89, 0xA032, 0xA0DA, 0xA181, 0xA228, 0xA2CE, 0xA373, 0xA418, + 0xA4BC, 0xA560, 0xA602, 0xA6A5, 0xA746, 0xA7E8, 0xA888, 0xA928, + 0xA9C7, 0xAA66, 0xAB04, 0xABA1, 0xAC3E, 0xACDB, 0xAD76, 0xAE11, + 0xAEAC, 0xAF46, 0xAFDF, 0xB078, 0xB110, 0xB1A7, 0xB23E, 0xB2D5, + 0xB36B, 0xB400, 0xB495, 0xB529, 0xB5BC, 0xB64F, 0xB6E2, 0xB773, + 0xB805, 0xB895, 0xB926, 0xB9B5, 0xBA44, 0xBAD3, 0xBB61, 0xBBEE, + 0xBC7B, 0xBD07, 0xBD93, 0xBE1E, 0xBEA9, 0xBF33, 0xBFBC, 0xC046, + 0xC0CE, 0xC156, 0xC1DD, 0xC264, 0xC2EB, 0xC371, 0xC3F6, 0xC47B, + 0xC4FF, 0xC583, 0xC606, 0xC689, 0xC70B, 0xC78D, 0xC80E, 0xC88F, + 0xC90F +}; + +/* look up table for square root */ +static const CoglFixed sqrt_tbl[] = +{ + 0x00000000L, 0x00010000L, 0x00016A0AL, 0x0001BB68L, + 0x00020000L, 0x00023C6FL, 0x00027312L, 0x0002A550L, + 0x0002D414L, 0x00030000L, 0x0003298BL, 0x0003510EL, + 0x000376CFL, 0x00039B05L, 0x0003BDDDL, 0x0003DF7CL, + 0x00040000L, 0x00041F84L, 0x00043E1EL, 0x00045BE1L, + 0x000478DEL, 0x00049524L, 0x0004B0BFL, 0x0004CBBCL, + 0x0004E624L, 0x00050000L, 0x00051959L, 0x00053237L, + 0x00054AA0L, 0x0005629AL, 0x00057A2BL, 0x00059159L, + 0x0005A828L, 0x0005BE9CL, 0x0005D4B9L, 0x0005EA84L, + 0x00060000L, 0x00061530L, 0x00062A17L, 0x00063EB8L, + 0x00065316L, 0x00066733L, 0x00067B12L, 0x00068EB4L, + 0x0006A21DL, 0x0006B54DL, 0x0006C847L, 0x0006DB0CL, + 0x0006ED9FL, 0x00070000L, 0x00071232L, 0x00072435L, + 0x0007360BL, 0x000747B5L, 0x00075935L, 0x00076A8CL, + 0x00077BBBL, 0x00078CC2L, 0x00079DA3L, 0x0007AE60L, + 0x0007BEF8L, 0x0007CF6DL, 0x0007DFBFL, 0x0007EFF0L, + 0x00080000L, 0x00080FF0L, 0x00081FC1L, 0x00082F73L, + 0x00083F08L, 0x00084E7FL, 0x00085DDAL, 0x00086D18L, + 0x00087C3BL, 0x00088B44L, 0x00089A32L, 0x0008A906L, + 0x0008B7C2L, 0x0008C664L, 0x0008D4EEL, 0x0008E361L, + 0x0008F1BCL, 0x00090000L, 0x00090E2EL, 0x00091C45L, + 0x00092A47L, 0x00093834L, 0x0009460CL, 0x000953CFL, + 0x0009617EL, 0x00096F19L, 0x00097CA1L, 0x00098A16L, + 0x00099777L, 0x0009A4C6L, 0x0009B203L, 0x0009BF2EL, + 0x0009CC47L, 0x0009D94FL, 0x0009E645L, 0x0009F32BL, + 0x000A0000L, 0x000A0CC5L, 0x000A1979L, 0x000A261EL, + 0x000A32B3L, 0x000A3F38L, 0x000A4BAEL, 0x000A5816L, + 0x000A646EL, 0x000A70B8L, 0x000A7CF3L, 0x000A8921L, + 0x000A9540L, 0x000AA151L, 0x000AAD55L, 0x000AB94BL, + 0x000AC534L, 0x000AD110L, 0x000ADCDFL, 0x000AE8A1L, + 0x000AF457L, 0x000B0000L, 0x000B0B9DL, 0x000B172DL, + 0x000B22B2L, 0x000B2E2BL, 0x000B3998L, 0x000B44F9L, + 0x000B504FL, 0x000B5B9AL, 0x000B66D9L, 0x000B720EL, + 0x000B7D37L, 0x000B8856L, 0x000B936AL, 0x000B9E74L, + 0x000BA973L, 0x000BB467L, 0x000BBF52L, 0x000BCA32L, + 0x000BD508L, 0x000BDFD5L, 0x000BEA98L, 0x000BF551L, + 0x000C0000L, 0x000C0AA6L, 0x000C1543L, 0x000C1FD6L, + 0x000C2A60L, 0x000C34E1L, 0x000C3F59L, 0x000C49C8L, + 0x000C542EL, 0x000C5E8CL, 0x000C68E0L, 0x000C732DL, + 0x000C7D70L, 0x000C87ACL, 0x000C91DFL, 0x000C9C0AL, + 0x000CA62CL, 0x000CB047L, 0x000CBA59L, 0x000CC464L, + 0x000CCE66L, 0x000CD861L, 0x000CE254L, 0x000CEC40L, + 0x000CF624L, 0x000D0000L, 0x000D09D5L, 0x000D13A2L, + 0x000D1D69L, 0x000D2727L, 0x000D30DFL, 0x000D3A90L, + 0x000D4439L, 0x000D4DDCL, 0x000D5777L, 0x000D610CL, + 0x000D6A9AL, 0x000D7421L, 0x000D7DA1L, 0x000D871BL, + 0x000D908EL, 0x000D99FAL, 0x000DA360L, 0x000DACBFL, + 0x000DB618L, 0x000DBF6BL, 0x000DC8B7L, 0x000DD1FEL, + 0x000DDB3DL, 0x000DE477L, 0x000DEDABL, 0x000DF6D8L, + 0x000E0000L, 0x000E0922L, 0x000E123DL, 0x000E1B53L, + 0x000E2463L, 0x000E2D6DL, 0x000E3672L, 0x000E3F70L, + 0x000E4869L, 0x000E515DL, 0x000E5A4BL, 0x000E6333L, + 0x000E6C16L, 0x000E74F3L, 0x000E7DCBL, 0x000E869DL, + 0x000E8F6BL, 0x000E9832L, 0x000EA0F5L, 0x000EA9B2L, + 0x000EB26BL, 0x000EBB1EL, 0x000EC3CBL, 0x000ECC74L, + 0x000ED518L, 0x000EDDB7L, 0x000EE650L, 0x000EEEE5L, + 0x000EF775L, 0x000F0000L, 0x000F0886L, 0x000F1107L, + 0x000F1984L, 0x000F21FCL, 0x000F2A6FL, 0x000F32DDL, + 0x000F3B47L, 0x000F43ACL, 0x000F4C0CL, 0x000F5468L, + 0x000F5CBFL, 0x000F6512L, 0x000F6D60L, 0x000F75AAL, + 0x000F7DEFL, 0x000F8630L, 0x000F8E6DL, 0x000F96A5L, + 0x000F9ED9L, 0x000FA709L, 0x000FAF34L, 0x000FB75BL, + 0x000FBF7EL, 0x000FC79DL, 0x000FCFB7L, 0x000FD7CEL, + 0x000FDFE0L, 0x000FE7EEL, 0x000FEFF8L, 0x000FF7FEL, + 0x00100000L, +}; + +/* the difference of the angle for two adjacent values in the + * sin_tbl table, expressed as CoglFixed number + */ +static const int sin_tbl_size = G_N_ELEMENTS (sin_tbl) - 1; + +static const double _magic = 68719476736.0 * 1.5; + +/* Where in the 64 bits of double is the mantissa. + * + * FIXME - this should go inside the configure.ac + */ +#ifdef HAVE_FLOAT_WORD_ORDER +#if (__FLOAT_WORD_ORDER == 1234) +#define _COGL_MAN 0 +#elif (__FLOAT_WORD_ORDER == 4321) +#define _COGL_MAN 1 +#else +#define COGL_NO_FAST_CONVERSIONS +#endif +#else /* HAVE_FLOAT_WORD_ORDER */ +#define COGL_NO_FAST_CONVERSIONS +#endif /* HAVE_FLOAT_WORD_ORDER */ + +/* + * cogl_double_to_fixed : + * @value: value to be converted + * + * A fast conversion from double precision floating to fixed point + * + * Return value: Fixed point representation of the value + */ +CoglFixed +cogl_double_to_fixed (double val) +{ +#ifdef COGL_NO_FAST_CONVERSIONS + return (CoglFixed) (val * (double) COGL_FIXED_1); +#else + union { + double d; + unsigned int i[2]; + } dbl; + + dbl.d = val; + dbl.d = dbl.d + _magic; + + return dbl.i[_COGL_MAN]; +#endif +} + +/* + * cogl_double_to_int : + * @value: value to be converted + * + * A fast conversion from doulbe precision floatint point to int; + * used this instead of casting double/float to int. + * + * Return value: Integer part of the double + */ +int +cogl_double_to_int (double val) +{ +#ifdef COGL_NO_FAST_CONVERSIONS + return (int) (val); +#else + union { + double d; + unsigned int i[2]; + } dbl; + + dbl.d = val; + dbl.d = dbl.d + _magic; + + return ((int) dbl.i[_COGL_MAN]) >> 16; +#endif +} + +unsigned int +cogl_double_to_uint (double val) +{ +#ifdef COGL_NO_FAST_CONVERSIONS + return (unsigned int)(val); +#else + union { + double d; + unsigned int i[2]; + } dbl; + + dbl.d = val; + dbl.d = dbl.d + _magic; + + return (dbl.i[_COGL_MAN]) >> 16; +#endif +} + +#undef _COGL_MAN + +CoglFixed +cogl_fixed_sin (CoglFixed angle) +{ + int sign = 1, indx1, indx2; + CoglFixed low, high; + CoglFixed p1, p2; + CoglFixed d1, d2; + + /* convert negative angle to positive + sign */ + if ((int) angle < 0) + { + sign = -sign; + angle = -angle; + } + + /* reduce to <0, 2*pi) */ + angle = angle % COGL_FIXED_2_PI; + + /* reduce to first quadrant and sign */ + if (angle > COGL_FIXED_PI) + { + sign = -sign; + + if (angle > COGL_FIXED_PI + COGL_FIXED_PI_2) + { + /* fourth qudrant */ + angle = COGL_FIXED_2_PI - angle; + } + else + { + /* third quadrant */ + angle -= COGL_FIXED_PI; + } + } + else + { + if (angle > COGL_FIXED_PI_2) + { + /* second quadrant */ + angle = COGL_FIXED_PI - angle; + } + } + + /* Calculate indices of the two nearest values in our table + * and return weighted average. + * + * We multiple first than divide to preserve precision. Since + * angle is in the first quadrant, angle * SIN_TBL_SIZE (=256) + * can't overflow. + * + * Handle the end of the table gracefully + */ + indx1 = (angle * sin_tbl_size) / COGL_FIXED_PI_2; + + if (indx1 == sin_tbl_size) + { + indx2 = indx1; + indx1 = indx2 - 1; + } + else + { + indx2 = indx1 + 1; + } + + low = sin_tbl[indx1]; + high = sin_tbl[indx2]; + + /* Again multiply the divide; no danger of overflow */ + p1 = (indx1 * COGL_FIXED_PI_2) / sin_tbl_size; + p2 = (indx2 * COGL_FIXED_PI_2) / sin_tbl_size; + d1 = angle - p1; + d2 = p2 - angle; + + angle = ((low * d2 + high * d1) / (p2 - p1)); + + if (sign < 0) + angle = -angle; + + return angle; +} + +CoglFixed +cogl_angle_sin (CoglAngle angle) +{ + int sign = 1; + CoglFixed result; + + /* reduce negative angle to positive + sign */ + if (angle < 0) + { + sign = -sign; + angle = -angle; + } + + /* reduce to <0, 2*pi) */ + angle &= 0x3ff; + + /* reduce to first quadrant and sign */ + if (angle > 512) + { + sign = -sign; + + if (angle > 768) + { + /* fourth qudrant */ + angle = 1024 - angle; + } + else + { + /* third quadrant */ + angle -= 512; + } + } + else + { + if (angle > 256) + { + /* second quadrant */ + angle = 512 - angle; + } + } + + result = sin_tbl[angle]; + + if (sign < 0) + result = -result; + + return result; +} + +CoglFixed +cogl_fixed_tan (CoglFixed angle) +{ + return cogl_angle_tan (COGL_ANGLE_FROM_DEGX (angle)); +} + +CoglFixed +cogl_angle_tan (CoglAngle angle) +{ + int sign = 1; + CoglFixed result; + + /* reduce negative angle to positive + sign */ + if (angle < 0) + { + sign = -sign; + angle = -angle; + } + + /* reduce to <0, pi) */ + angle &= 0x1ff; + + /* reduce to first quadrant and sign */ + if (angle > 256) + { + sign = -sign; + angle = 512 - angle; + } + + result = tan_tbl[angle]; + + if (sign < 0) + result = -result; + + return result; +} + +CoglFixed +cogl_fixed_atan (CoglFixed x) +{ + CoglBool negative = FALSE; + CoglFixed angle; + + if (x < 0) + { + negative = TRUE; + x = -x; + } + + if (x > COGL_FIXED_1) + { + /* if x > 1 then atan(x) = pi/2 - atan(1/x) */ + angle = COGL_FIXED_PI / 2 + - atan_tbl[COGL_FIXED_DIV (COGL_FIXED_1, x) >> 8]; + } + else + angle = atan_tbl[x >> 8]; + + return negative ? -angle : angle; +} + +CoglFixed +cogl_fixed_atan2 (CoglFixed y, CoglFixed x) +{ + CoglFixed angle; + + if (x == 0) + angle = y >= 0 ? COGL_FIXED_PI_2 : -COGL_FIXED_PI_2; + else + { + angle = cogl_fixed_atan (COGL_FIXED_DIV (y, x)); + + if (x < 0) + angle += y >= 0 ? COGL_FIXED_PI : -COGL_FIXED_PI; + } + + return angle; +} + +CoglFixed +cogl_fixed_sqrt (CoglFixed x) +{ + /* The idea for this comes from the Alegro library, exploiting the + * fact that, + * sqrt (x) = sqrt (x/d) * sqrt (d); + * + * For d == 2^(n): + * + * sqrt (x) = sqrt (x/2^(2n)) * 2^n + * + * By locating suitable n for given x such that x >> 2n is in <0,255> + * we can use a LUT of precomputed values. + * + * This algorithm provides both good performance and precision; + * on ARM this function is about 5 times faster than c-lib sqrt, + * whilst producing errors < 1%. + */ + int t = 0; + int sh = 0; + unsigned int mask = 0x40000000; + unsigned fract = x & 0x0000ffff; + unsigned int d1, d2; + CoglFixed v1, v2; + + if (x <= 0) + return 0; + + if (x > COGL_FIXED_255 || x < COGL_FIXED_1) + { + /* + * Find the highest bit set + */ +#if defined (__arm__) && !defined(__ARM_ARCH_4T__) && !defined(__thumb__) + /* This actually requires at least arm v5, but gcc does not seem + * to set the architecture defines correctly, and it is I think + * very unlikely that anyone will want to use clutter on anything + * less than v5. + */ + int bit; + __asm__ ("clz %0, %1\n" + "rsb %0, %0, #31\n" + :"=r"(bit) + :"r" (x)); + + /* make even (2n) */ + bit &= 0xfffffffe; +#else + /* TODO -- add i386 branch using bshr + * + * NB: it's been said that the bshr instruction is poorly implemented + * and that it is possible to write a faster code in C using binary + * search -- at some point we should explore this + */ + int bit = 30; + while (bit >= 0) + { + if (x & mask) + break; + + mask = (mask >> 1 | mask >> 2); + bit -= 2; + } +#endif + + /* now bit indicates the highest bit set; there are two scenarios + * + * 1) bit < 23: Our number is smaller so we shift it left to maximase + * precision (< 16 really, since <16,23> never goes + * through here. + * + * 2) bit > 23: our number is above the table, so we shift right + */ + + sh = ((bit - 22) >> 1); + if (bit >= 8) + t = (x >> (16 - 22 + bit)); + else + t = (x << (22 - 16 - bit)); + } + else + { + t = COGL_FIXED_TO_INT (x); + } + + /* Do a weighted average of the two nearest values */ + v1 = sqrt_tbl[t]; + v2 = sqrt_tbl[t+1]; + + /* + * 12 is fairly arbitrary -- we want integer that is not too big to cost + * us precision + */ + d1 = (unsigned)(fract) >> 12; + d2 = ((unsigned)COGL_FIXED_1 >> 12) - d1; + + x = ((v1*d2) + (v2*d1))/(COGL_FIXED_1 >> 12); + + if (sh > 0) + x = x << sh; + else if (sh < 0) + x = x >> -sh; + + return x; +} + +/** + * cogl_sqrti: + * @x: integer value + * + * Very fast fixed point implementation of square root for integers. + * + * This function is at least 6x faster than clib sqrt() on x86, and (this is + * not a typo!) about 500x faster on ARM without FPU. It's error is < 5% + * for arguments < %COGL_SQRTI_ARG_5_PERCENT and < 10% for arguments < + * %COGL_SQRTI_ARG_10_PERCENT. The maximum argument that can be passed to + * this function is COGL_SQRTI_ARG_MAX. + * + * Return value: integer square root. + * + * + * Since: 0.2 + */ +int +cogl_sqrti (int number) +{ +#if defined __SSE2__ + /* The GCC built-in with SSE2 (sqrtsd) is up to twice as fast as + * the pure integer code below. It is also more accurate. + */ + return __builtin_sqrt (number); +#else + /* This is a fixed point implementation of the Quake III sqrt algorithm, + * described, for example, at + * http://www.codemaestro.com/reviews/review00000105.html + * + * While the original QIII is extremely fast, the use of floating division + * and multiplication makes it perform very on arm processors without FPU. + * + * The key to successfully replacing the floating point operations with + * fixed point is in the choice of the fixed point format. The QIII + * algorithm does not calculate the square root, but its reciprocal ('y' + * below), which is only at the end turned to the inverse value. In order + * for the algorithm to produce satisfactory results, the reciprocal value + * must be represented with sufficient precission; the 16.16 we use + * elsewhere in clutter is not good enough, and 10.22 is used instead. + */ + CoglFixed x; + uint32_t y_1; /* 10.22 fixed point */ + uint32_t f = 0x600000; /* '1.5' as 10.22 fixed */ + + union + { + float f; + uint32_t i; + } flt, flt2; + + flt.f = number; + + x = COGL_FIXED_FROM_INT (number) / 2; + + /* The QIII initial estimate */ + flt.i = 0x5f3759df - ( flt.i >> 1 ); + + /* Now, we convert the float to 10.22 fixed. We exploit the mechanism + * described at http://www.d6.com/users/checker/pdfs/gdmfp.pdf. + * + * We want 22 bit fraction; a single precission float uses 23 bit + * mantisa, so we only need to add 2^(23-22) (no need for the 1.5 + * multiplier as we are only dealing with positive numbers). + * + * Note: we have to use two separate variables here -- for some reason, + * if we try to use just the flt variable, gcc on ARM optimises the whole + * addition out, and it all goes pear shape, since without it, the bits + * in the float will not be correctly aligned. + */ + flt2.f = flt.f + 2.0; + flt2.i &= 0x7FFFFF; + + /* Now we correct the estimate */ + y_1 = (flt2.i >> 11) * (flt2.i >> 11); + y_1 = (y_1 >> 8) * (x >> 8); + + y_1 = f - y_1; + flt2.i = (flt2.i >> 11) * (y_1 >> 11); + + /* If the original argument is less than 342, we do another + * iteration to improve precission (for arguments >= 342, the single + * iteration produces generally better results). + */ + if (x < 171) + { + y_1 = (flt2.i >> 11) * (flt2.i >> 11); + y_1 = (y_1 >> 8) * (x >> 8); + + y_1 = f - y_1; + flt2.i = (flt2.i >> 11) * (y_1 >> 11); + } + + /* Invert, round and convert from 10.22 to an integer + * 0x1e3c68 is a magical rounding constant that produces slightly + * better results than 0x200000. + */ + return (number * flt2.i + 0x1e3c68) >> 22; +#endif +} + +CoglFixed +cogl_fixed_mul (CoglFixed a, + CoglFixed b) +{ +#if defined(__arm__) && !defined(__thumb__) + /* This provides about 12% speedeup on the gcc -O2 optimised + * C version + * + * Based on code found in the following thread: + * http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2006-August/014405.html + */ + int res_low, res_hi; + + __asm__ ("smull %0, %1, %2, %3 \n" + "mov %0, %0, lsr %4 \n" + "add %1, %0, %1, lsl %5 \n" + : "=&r"(res_hi), "=&r"(res_low) \ + : "r"(a), "r"(b), "i"(COGL_FIXED_Q), "i"(32 - COGL_FIXED_Q)); + + return (CoglFixed) res_low; +#else + int64_t r = (int64_t) a * (int64_t) b; + + return (CoglFixed) (r >> COGL_FIXED_Q); +#endif +} + +CoglFixed +cogl_fixed_div (CoglFixed a, + CoglFixed b) +{ + return (CoglFixed) ((((int64_t) a) << COGL_FIXED_Q) / b); +} + +CoglFixed +cogl_fixed_mul_div (CoglFixed a, + CoglFixed b, + CoglFixed c) +{ + CoglFixed ab = cogl_fixed_mul (a, b); + CoglFixed quo = cogl_fixed_div (ab, c); + + return quo; +} + +/* + * The log2x() and pow2x() functions + * + * The implementation of the log2x() and pow2x() exploits the + * well-documented fact that the exponent part of IEEE floating + * number provides a good estimate of log2 of that number, while + * the mantissa serves as a good error-correction. + * + * The implementation here uses a quadratic error correction as + * described by Ian Stephenson at: + * http://www.dctsystems.co.uk/Software/power.html. + */ + +CoglFixed +cogl_fixed_log2 (unsigned int x) +{ + /* Note: we could easily have a version for CoglFixed x, but the int + * precision is enough for the current purposes. + */ + union + { + float f; + CoglFixed i; + } flt; + + CoglFixed magic = 0x58bb; + CoglFixed y; + + /* + * Convert x to float, then extract exponent. + * + * We want the result to be 16.16 fixed, so we shift (23-16) bits only + */ + flt.f = x; + flt.i >>= 7; + flt.i -= COGL_FIXED_FROM_INT (127); + + y = COGL_FIXED_FRACTION (flt.i); + + y = COGL_FIXED_MUL ((y - COGL_FIXED_MUL (y, y)), magic); + + return flt.i + y; +} + +unsigned int +cogl_fixed_pow2 (CoglFixed x) +{ + /* Note: we could easily have a version that produces CoglFixed result, + * but the range would be limited to x < 15, and the int precision + * is enough for the current purposes. + */ + + union + { + float f; + uint32_t i; + } flt; + + CoglFixed magic = 0x56f7; + CoglFixed y; + + flt.i = x; + + /* + * Reverse of the log2x function -- convert the fixed value to a suitable + * floating point exponent, and mantisa adjusted with quadratic error + * correction y. + */ + y = COGL_FIXED_FRACTION (x); + y = COGL_FIXED_MUL ((y - COGL_FIXED_MUL (y, y)), magic); + + /* Shift the exponent into it's position in the floating point + * representation; as our number is not int but 16.16 fixed, shift only + * by (23 - 16) + */ + flt.i += (COGL_FIXED_FROM_INT (127) - y); + flt.i <<= 7; + + return COGL_FLOAT_TO_UINT (flt.f); +} + +unsigned int +cogl_fixed_pow (unsigned int x, + CoglFixed y) +{ + return cogl_fixed_pow2 (COGL_FIXED_MUL (y, cogl_fixed_log2 (x))); +} + +CoglFixed +cogl_angle_cos (CoglAngle angle) +{ + CoglAngle a = angle + 256; + + return cogl_angle_sin (a); +} + +CoglFixed +cogl_fixed_cos (CoglFixed angle) +{ + CoglFixed a = angle + COGL_FIXED_PI_2; + + return cogl_fixed_sin (a); +} + +/* GType */ + +static GTypeInfo _info = { + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + 0, + NULL, + NULL, +}; + +static GTypeFundamentalInfo _finfo = { 0, }; + +static void +cogl_value_init_fixed (GValue *value) +{ + value->data[0].v_int = 0; +} + +static void +cogl_value_copy_fixed (const GValue *src, + GValue *dest) +{ + dest->data[0].v_int = src->data[0].v_int; +} + +static char * +cogl_value_collect_fixed (GValue *value, + unsigned int n_collect_values, + GTypeCValue *collect_values, + unsigned int collect_flags) +{ + value->data[0].v_int = collect_values[0].v_int; + + return NULL; +} + +static char * +cogl_value_lcopy_fixed (const GValue *value, + unsigned int n_collect_values, + GTypeCValue *collect_values, + unsigned int collect_flags) +{ + int32_t *fixed_p = collect_values[0].v_pointer; + + if (!fixed_p) + return g_strdup_printf ("value location for '%s' passed as NULL", + G_VALUE_TYPE_NAME (value)); + + *fixed_p = value->data[0].v_int; + + return NULL; +} + +static void +cogl_value_transform_fixed_int (const GValue *src, + GValue *dest) +{ + dest->data[0].v_int = COGL_FIXED_TO_INT (src->data[0].v_int); +} + +static void +cogl_value_transform_fixed_double (const GValue *src, + GValue *dest) +{ + dest->data[0].v_double = COGL_FIXED_TO_DOUBLE (src->data[0].v_int); +} + +static void +cogl_value_transform_fixed_float (const GValue *src, + GValue *dest) +{ + dest->data[0].v_float = COGL_FIXED_TO_FLOAT (src->data[0].v_int); +} + +static void +cogl_value_transform_int_fixed (const GValue *src, + GValue *dest) +{ + dest->data[0].v_int = COGL_FIXED_FROM_INT (src->data[0].v_int); +} + +static void +cogl_value_transform_double_fixed (const GValue *src, + GValue *dest) +{ + dest->data[0].v_int = COGL_FIXED_FROM_DOUBLE (src->data[0].v_double); +} + +static void +cogl_value_transform_float_fixed (const GValue *src, + GValue *dest) +{ + dest->data[0].v_int = COGL_FIXED_FROM_FLOAT (src->data[0].v_float); +} + + +static const GTypeValueTable _cogl_fixed_value_table = { + cogl_value_init_fixed, + NULL, + cogl_value_copy_fixed, + NULL, + "i", + cogl_value_collect_fixed, + "p", + cogl_value_lcopy_fixed +}; + +GType +cogl_fixed_get_type (void) +{ + static GType _cogl_fixed_type = 0; + + if (G_UNLIKELY (_cogl_fixed_type == 0)) + { + _info.value_table = & _cogl_fixed_value_table; + _cogl_fixed_type = + g_type_register_fundamental (g_type_fundamental_next (), + g_intern_static_string ("CoglFixed"), + &_info, &_finfo, 0); + + g_value_register_transform_func (_cogl_fixed_type, G_TYPE_INT, + cogl_value_transform_fixed_int); + g_value_register_transform_func (G_TYPE_INT, _cogl_fixed_type, + cogl_value_transform_int_fixed); + + g_value_register_transform_func (_cogl_fixed_type, G_TYPE_FLOAT, + cogl_value_transform_fixed_float); + g_value_register_transform_func (G_TYPE_FLOAT, _cogl_fixed_type, + cogl_value_transform_float_fixed); + + g_value_register_transform_func (_cogl_fixed_type, G_TYPE_DOUBLE, + cogl_value_transform_fixed_double); + g_value_register_transform_func (G_TYPE_DOUBLE, _cogl_fixed_type, + cogl_value_transform_double_fixed); + } + + return _cogl_fixed_type; +} diff --git a/cogl/cogl/deprecated/cogl-fixed.h b/cogl/cogl/deprecated/cogl-fixed.h new file mode 100644 index 000000000..73d0ed59b --- /dev/null +++ b/cogl/cogl/deprecated/cogl-fixed.h @@ -0,0 +1,811 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_FIXED_H__ +#define __COGL_FIXED_H__ + +#include + +/** + * SECTION:cogl-fixed + * @short_description: Fixed Point API + * + * COGL has a fixed point API targeted at platforms without a floating + * point unit, such as embedded devices. On such platforms this API should + * be preferred to the floating point one as it does not trigger the slow + * path of software emulation, relying on integer math for fixed-to-floating + * and floating-to-fixed notations conversion. + * + * It is not recommened for use on platforms with a floating point unit + * (e.g. desktop systems), nor for use in language bindings. + * + * Basic rules of Fixed Point arithmethic: + * + * + * Two fixed point numbers can be directly added, subtracted and + * have their modulus taken. + * + * + * To add other numerical type to a fixed point number it has to + * be first converted to fixed point. + * + * + * A fixed point number can be directly multiplied or divided by + * an integer. + * + * + * Two fixed point numbers can only be multiplied and divided by + * the provided %COGL_FIXED_MUL and %COGL_FIXED_DIV macros. + * + * + * + * The fixed point API is available since COGL 1.0. + */ + +COGL_BEGIN_DECLS + +/** + * COGL_FIXED_BITS: + * + * Evaluates to the number of bits used by the #CoglFixed type. + * + * Since: 1.0 + */ +#define COGL_FIXED_BITS (32) + +/** + * COGL_FIXED_Q: + * + * Evaluates to the number of bits used for the non-integer part + * of the #CoglFixed type. + * + * Since: 1.0 + */ +#define COGL_FIXED_Q (COGL_FIXED_BITS - 16) + +/** + * COGL_FIXED_1: + * + * The number 1 expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_1 (1 << COGL_FIXED_Q) + +/** + * COGL_FIXED_0_5: + * + * The number 0.5 expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_0_5 (32768) + +/** + * COGL_FIXED_EPSILON: + * + * A very small number expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_EPSILON (1) + +/** + * COGL_FIXED_MAX: + * + * The biggest number representable using #CoglFixed + * + * Since: 1.0 + */ +#define COGL_FIXED_MAX (0x7fffffff) + +/** + * COGL_FIXED_MIN: + * + * The smallest number representable using #CoglFixed + * + * Since: 1.0 + */ +#define COGL_FIXED_MIN (0x80000000) + +/** + * COGL_FIXED_PI: + * + * The number pi, expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_PI (0x0003243f) + +/** + * COGL_FIXED_2_PI: + * + * Two times pi, expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_2_PI (0x0006487f) + +/** + * COGL_FIXED_PI_2: + * + * Half pi, expressed as a #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_PI_2 (0x00019220) + +/** + * COGL_FIXED_PI_4: + * + * pi / 4, expressed as #CoglFixed number. + * + * Since: 1.0 + */ +#define COGL_FIXED_PI_4 (0x0000c910) + +/** + * COGL_FIXED_360: + * + * Evaluates to the number 360 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_360 (COGL_FIXED_FROM_INT (360)) + +/** + * COGL_FIXED_270: + * + * Evaluates to the number 270 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_270 (COGL_FIXED_FROM_INT (270)) + +/** + * COGL_FIXED_255: + * + * Evaluates to the number 255 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_255 (COGL_FIXED_FROM_INT (255)) + +/** + * COGL_FIXED_240: + * + * Evaluates to the number 240 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_240 (COGL_FIXED_FROM_INT (240)) + +/** + * COGL_FIXED_180: + * + * Evaluates to the number 180 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_180 (COGL_FIXED_FROM_INT (180)) + +/** + * COGL_FIXED_120: + * + * Evaluates to the number 120 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_120 (COGL_FIXED_FROM_INT (120)) + +/** + * COGL_FIXED_90: + * + * Evaluates to the number 90 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_90 (COGL_FIXED_FROM_INT (90)) + +/** + * COGL_FIXED_60: + * + * Evaluates to the number 60 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_60 (COGL_FIXED_FROM_INT (60)) + +/** + * COGL_FIXED_45: + * + * Evaluates to the number 45 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_45 (COGL_FIXED_FROM_INT (45)) + +/** + * COGL_FIXED_30: + * + * Evaluates to the number 30 in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_30 (COGL_FIXED_FROM_INT (30)) + +/** + * COGL_RADIANS_TO_DEGREES: + * + * Evaluates to 180 / pi in fixed point notation. + * + * Since: 1.0 + */ +#define COGL_RADIANS_TO_DEGREES (0x394bb8) + +/* + * conversion macros + */ + +/** + * COGL_FIXED_FROM_FLOAT: + * @x: a floating point number + * + * Converts @x from a floating point to a fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_FROM_FLOAT(x) ((float) cogl_double_to_fixed (x)) + +/** + * COGL_FIXED_TO_FLOAT: + * @x: a #CoglFixed number + * + * Converts @x from a fixed point to a floating point notation, in + * single precision. + * + * Since: 1.0 + */ +#define COGL_FIXED_TO_FLOAT(x) ((float) ((int)(x) / 65536.0)) + +/** + * COGL_FIXED_FROM_DOUBLE: + * @x: a floating point number + * + * Converts @x from a double precision, floating point to a fixed + * point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_FROM_DOUBLE(x) (cogl_double_to_fixed (x)) + +/** + * COGL_FIXED_TO_DOUBLE: + * @x: a #CoglFixed number + * + * Converts @x from a fixed point to a floating point notation, in + * double precision. + * + * Since: 1.0 + */ +#define COGL_FIXED_TO_DOUBLE(x) ((double) ((int)(x) / 65536.0)) + +/** + * COGL_FIXED_FROM_INT: + * @x: an integer number + * + * Converts @x from an integer to a fixed point notation. + * + * Since: 1.0 + */ +#define COGL_FIXED_FROM_INT(x) ((x) << COGL_FIXED_Q) + +/** + * COGL_FIXED_TO_INT: + * @x: a #CoglFixed number + * + * Converts @x from a fixed point notation to an integer, dropping + * the fractional part without rounding. + * + * Since: 1.0 + */ +#define COGL_FIXED_TO_INT(x) ((x) >> COGL_FIXED_Q) + +/** + * COGL_FLOAT_TO_INT: + * @x: a floatint point number + * + * Converts @x from a floating point notation to a signed integer. + * + * Since: 1.0 + */ +#define COGL_FLOAT_TO_INT(x) (cogl_double_to_int ((x))) + +/** + * COGL_FLOAT_TO_UINT: + * @x: a floatint point number + * + * Converts @x from a floating point notation to an unsigned integer. + * + * Since: 1.0 + */ +#define COGL_FLOAT_TO_UINT(x) (cogl_double_to_uint ((x))) + +/* + * fixed point math functions + */ + +/** + * COGL_FIXED_FRACTION: + * @x: a #CoglFixed number + * + * Retrieves the fractionary part of @x. + * + * Since: 1.0 + */ +#define COGL_FIXED_FRACTION(x) ((x) & ((1 << COGL_FIXED_Q) - 1)) + +/** + * COGL_FIXED_FLOOR: + * @x: a #CoglFixed number + * + * Rounds down a fixed point number to the previous integer. + * + * Since: 1.0 + */ +#define COGL_FIXED_FLOOR(x) (((x) >= 0) ? ((x) >> COGL_FIXED_Q) \ + : ~((~(x)) >> COGL_FIXED_Q)) + +/** + * COGL_FIXED_CEIL: + * @x: a #CoglFixed number + * + * Rounds up a fixed point number to the next integer. + * + * Since: 1.0 + */ +#define COGL_FIXED_CEIL(x) (COGL_FIXED_FLOOR ((x) + 0xffff)) + +/** + * COGL_FIXED_MUL: + * @a: a #CoglFixed number + * @b: a #CoglFixed number + * + * Computes (a * b). + * + * Since: 1.0 + */ +#define COGL_FIXED_MUL(a,b) (cogl_fixed_mul ((a), (b))) + +/** + * COGL_FIXED_DIV: + * @a: a #CoglFixed number + * @b: a #CoglFixed number + * + * Computes (a / b). + * + * Since: 1.0 + */ +#define COGL_FIXED_DIV(a,b) (cogl_fixed_div ((a), (b))) + +/** + * COGL_FIXED_MUL_DIV: + * @a: a #CoglFixed number + * @b: a #CoglFixed number + * @c: a #CoglFixed number + * + * Computes ((a * b) / c). It is logically equivalent to: + * + * |[ + * res = COGL_FIXED_DIV (COGL_FIXED_MUL (a, b), c); + * ]| + * + * But it is shorter to type. + * + * Since: 1.0 + */ +#define COGL_FIXED_MUL_DIV(a,b,c) (cogl_fixed_mul_div ((a), (b), (c))) + +/** + * COGL_FIXED_FAST_MUL: + * @a: a #CoglFixed number + * @b: a #CoglFixed number + * + * Fast version of %COGL_FIXED_MUL, implemented as a macro. + * + * This macro might lose precision. If the precision of the result + * is important use %COGL_FIXED_MUL instead. + * + * Since: 1.0 + */ +#define COGL_FIXED_FAST_MUL(a,b) ((a) >> 8) * ((b) >> 8) + +/** + * COGL_FIXED_FAST_DIV: + * @a: a #CoglFixed number + * @b: a #CoglFixed number + * + * Fast version of %COGL_FIXED_DIV, implemented as a macro. + * + * This macro might lose precision. If the precision of the result + * is important use %COGL_FIXED_DIV instead. + * + * Since: 1.0 + */ +#define COGL_FIXED_FAST_DIV(a,b) ((((a) << 8) / (b)) << 8) + +/** + * cogl_fixed_sin: + * @angle: a #CoglFixed number + * + * Computes the sine of @angle. + * + * Return value: the sine of the passed angle, in fixed point notation + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_sin (CoglFixed angle); + +/** + * cogl_fixed_tan: + * @angle: a #CoglFixed number + * + * Computes the tangent of @angle. + * + * Return value: the tangent of the passed angle, in fixed point notation + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_tan (CoglFixed angle); + +/** + * cogl_fixed_cos: + * @angle: a #CoglFixed number + * + * Computes the cosine of @angle. + * + * Return value: the cosine of the passed angle, in fixed point notation + * + * Since: 1.0 + */ +CoglFixed cogl_fixed_cos (CoglFixed angle); + +/** + * cogl_fixed_atan: + * @a: a #CoglFixed number + * + * Computes the arc tangent of @a. + * + * Return value: the arc tangent of the passed value, in fixed point notation + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_atan (CoglFixed a); + +/** + * cogl_fixed_atan2: + * @a: the numerator as a #CoglFixed number + * @b: the denominator as a #CoglFixed number + * + * Computes the arc tangent of @a / @b but uses the sign of both + * arguments to return the angle in right quadrant. + * + * Return value: the arc tangent of the passed fraction, in fixed point + * notation + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_atan2 (CoglFixed a, + CoglFixed b); + +/*< public >*/ + +/* Fixed point math routines */ +G_INLINE_FUNC CoglFixed +cogl_fixed_mul (CoglFixed a, + CoglFixed b); + +G_INLINE_FUNC CoglFixed +cogl_fixed_div (CoglFixed a, + CoglFixed b); + +G_INLINE_FUNC CoglFixed +cogl_fixed_mul_div (CoglFixed a, + CoglFixed b, + CoglFixed c); + +/** + * COGL_SQRTI_ARG_MAX: + * + * Maximum argument that can be passed to cogl_sqrti() function. + * + * Since: 1.0 + */ +#ifndef __SSE2__ +#define COGL_SQRTI_ARG_MAX 0x3fffff +#else +#define COGL_SQRTI_ARG_MAX INT_MAX +#endif + +/** + * COGL_SQRTI_ARG_5_PERCENT: + * + * Maximum argument that can be passed to cogl_sqrti() for which the + * resulting error is < 5% + * + * Since: 1.0 + */ +#ifndef __SSE2__ +#define COGL_SQRTI_ARG_5_PERCENT 210 +#else +#define COGL_SQRTI_ARG_5_PERCENT INT_MAX +#endif + +/** + * COGL_SQRTI_ARG_10_PERCENT: + * + * Maximum argument that can be passed to cogl_sqrti() for which the + * resulting error is < 10% + * + * Since: 1.0 + */ +#ifndef __SSE2__ +#define COGL_SQRTI_ARG_10_PERCENT 5590 +#else +#define COGL_SQRTI_ARG_10_PERCENT INT_MAX +#endif + +/** + * cogl_fixed_sqrt: + * @x: a #CoglFixed number + * + * Computes the square root of @x. + * + * Return value: the square root of the passed value, in floating point + * notation + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_sqrt (CoglFixed x); + +/** + * cogl_fixed_log2: + * @x: value to calculate base 2 logarithm from + * + * Calculates base 2 logarithm. + * + * This function is some 2.5 times faster on x86, and over 12 times faster on + * fpu-less arm, than using libc log(). + * + * Return value: base 2 logarithm. + * + * Since: 1.0 + */ +CoglFixed +cogl_fixed_log2 (unsigned int x); + +/** + * cogl_fixed_pow2: + * @x: a #CoglFixed number + * + * Calculates 2 to the @x power. + * + * This function is around 11 times faster on x86, and around 22 times faster + * on fpu-less arm than libc pow(2, x). + * + * Return value: the power of 2 to the passed value + * + * Since: 1.0 + */ +unsigned int +cogl_fixed_pow2 (CoglFixed x); + +/** + * cogl_fixed_pow: + * @x: base + * @y: #CoglFixed exponent + * + * Calculates @x to the @y power. + * + * Return value: the power of @x to the @y + * + * Since: 1.0 + */ +unsigned int +cogl_fixed_pow (unsigned int x, + CoglFixed y); + +/** + * cogl_sqrti: + * @x: integer value + * + * Very fast fixed point implementation of square root for integers. + * + * This function is at least 6x faster than clib sqrt() on x86, and (this is + * not a typo!) about 500x faster on ARM without FPU. It's error is less than + * 5% for arguments smaller than %COGL_SQRTI_ARG_5_PERCENT and less than 10% + * for narguments smaller than %COGL_SQRTI_ARG_10_PERCENT. The maximum + * argument that can be passed to this function is %COGL_SQRTI_ARG_MAX. + * + * Return value: integer square root. + * + * Since: 1.0 + */ +int +cogl_sqrti (int x); + +/** + * COGL_ANGLE_FROM_DEG: + * @x: an angle in degrees in floating point notation + * + * Converts an angle in degrees into a #CoglAngle. + * + * Since: 1.0 + */ +#define COGL_ANGLE_FROM_DEG(x) (COGL_FLOAT_TO_INT (((float)(x) * 1024.0f) / 360.0f)) + +/** + * COGL_ANGLE_TO_DEG: + * @x: a #CoglAngle + * + * Converts a #CoglAngle into an angle in degrees, using floatint point + * notation. + * + * Since: 1.0 + */ +#define COGL_ANGLE_TO_DEG(x) (((float)(x) * 360.0) / 1024.0) + +/** + * COGL_ANGLE_FROM_DEGX: + * @x: an angle in degrees in fixed point notation + * + * Converts an angle in degrees into a #CoglAngle. + * + * Since: 1.0 + */ +#define COGL_ANGLE_FROM_DEGX(x) (COGL_FIXED_TO_INT ((((x) / 360) * 1024) + COGL_FIXED_0_5)) + +/** + * COGL_ANGLE_TO_DEGX: + * @x: a #CoglAngle + * + * Converts a #CoglAngle into an angle in degrees, using fixed point notation + * + * Since: 1.0 + */ +#define COGL_ANGLE_TO_DEGX(x) (COGL_FIXED_FROM_INT ((x) * 45) / 128) + +/** + * cogl_angle_sin: + * @angle: an angle expressed using #CoglAngle + * + * Computes the sine of @angle + * + * Return value: the sine of the passed angle + * + * Since: 1.0 + */ +CoglFixed +cogl_angle_sin (CoglAngle angle); + +/** + * cogl_angle_tan: + * @angle: an angle expressed using #CoglAngle + * + * Computes the tangent of @angle + * + * Return value: the tangent of the passed angle + * + * Since: 1.0 + */ +CoglFixed +cogl_angle_tan (CoglAngle angle); + +/** + * cogl_angle_cos: + * @angle: an angle expressed using #CoglAngle + * + * Computes the cosine of @angle + * + * Return value: the cosine of the passed angle + * + * Since: 1.0 + */ +CoglFixed +cogl_angle_cos (CoglAngle angle); + +/*< private >*/ + +#if defined (G_CAN_INLINE) +G_INLINE_FUNC CoglFixed +cogl_fixed_mul (CoglFixed a, + CoglFixed b) +{ +# ifdef __arm__ + int res_low, res_hi; + + __asm__ ("smull %0, %1, %2, %3 \n" + "mov %0, %0, lsr %4 \n" + "add %1, %0, %1, lsl %5 \n" + : "=r"(res_hi), "=r"(res_low)\ + : "r"(a), "r"(b), "i"(COGL_FIXED_Q), "i"(32 - COGL_FIXED_Q)); + + return (CoglFixed) res_low; +# else + long long r = (long long) a * (long long) b; + + return (unsigned int)(r >> COGL_FIXED_Q); +# endif +} +#endif + +#if defined (G_CAN_INLINE) +G_INLINE_FUNC CoglFixed +cogl_fixed_div (CoglFixed a, + CoglFixed b) +{ + return (CoglFixed) ((((int64_t) a) << COGL_FIXED_Q) / b); +} +#endif + +#if defined(G_CAN_INLINE) +G_INLINE_FUNC CoglFixed +cogl_fixed_mul_div (CoglFixed a, + CoglFixed b, + CoglFixed c) +{ + CoglFixed ab = cogl_fixed_mul (a, b); + CoglFixed quo = cogl_fixed_div (ab, c); + + return quo; +} +#endif + +CoglFixed +cogl_double_to_fixed (double value); + +int +cogl_double_to_int (double value); + +unsigned int +cogl_double_to_uint (double value); + +COGL_END_DECLS + +#endif /* __COGL_FIXED_H__ */ diff --git a/cogl/cogl/deprecated/cogl-framebuffer-deprecated.c b/cogl/cogl/deprecated/cogl-framebuffer-deprecated.c new file mode 100644 index 000000000..97a2a0aec --- /dev/null +++ b/cogl/cogl/deprecated/cogl-framebuffer-deprecated.c @@ -0,0 +1,295 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#include + +#include "cogl-types.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-framebuffer-deprecated.h" + +typedef struct _CoglFramebufferStackEntry +{ + CoglFramebuffer *draw_buffer; + CoglFramebuffer *read_buffer; +} CoglFramebufferStackEntry; + + +static CoglFramebufferStackEntry * +create_stack_entry (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer) +{ + CoglFramebufferStackEntry *entry = g_slice_new (CoglFramebufferStackEntry); + + entry->draw_buffer = draw_buffer; + entry->read_buffer = read_buffer; + + return entry; +} + +GSList * +_cogl_create_framebuffer_stack (void) +{ + CoglFramebufferStackEntry *entry; + GSList *stack = NULL; + + entry = create_stack_entry (NULL, NULL); + + return g_slist_prepend (stack, entry); +} + +void +_cogl_free_framebuffer_stack (GSList *stack) +{ + GSList *l; + + for (l = stack; l != NULL; l = l->next) + { + CoglFramebufferStackEntry *entry = l->data; + + if (entry->draw_buffer) + cogl_object_unref (entry->draw_buffer); + + if (entry->read_buffer) + cogl_object_unref (entry->read_buffer); + + g_slice_free (CoglFramebufferStackEntry, entry); + } + g_slist_free (stack); +} + +static void +notify_buffers_changed (CoglFramebuffer *old_draw_buffer, + CoglFramebuffer *new_draw_buffer, + CoglFramebuffer *old_read_buffer, + CoglFramebuffer *new_read_buffer) +{ + /* XXX: To support the deprecated cogl_set_draw_buffer API we keep + * track of the last onscreen framebuffer that was set so that it + * can be restored if the COGL_WINDOW_BUFFER enum is used. A + * reference isn't taken to the framebuffer because otherwise we + * would have a circular reference between the context and the + * framebuffer. Instead the pointer is set to NULL in + * _cogl_onscreen_free as a kind of a cheap weak reference */ + if (new_draw_buffer && + new_draw_buffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + new_draw_buffer->context->window_buffer = new_draw_buffer; +} + +/* Set the current framebuffer without checking if it's already the + * current framebuffer. This is used by cogl_pop_framebuffer while + * the top of the stack is currently not up to date. */ +static void +_cogl_set_framebuffers_real (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer) +{ + CoglFramebufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (ctx != NULL); + _COGL_RETURN_IF_FAIL (draw_buffer && read_buffer ? + draw_buffer->context == read_buffer->context : TRUE); + + entry = ctx->framebuffer_stack->data; + + notify_buffers_changed (entry->draw_buffer, + draw_buffer, + entry->read_buffer, + read_buffer); + + if (draw_buffer) + cogl_object_ref (draw_buffer); + if (entry->draw_buffer) + cogl_object_unref (entry->draw_buffer); + + if (read_buffer) + cogl_object_ref (read_buffer); + if (entry->read_buffer) + cogl_object_unref (entry->read_buffer); + + entry->draw_buffer = draw_buffer; + entry->read_buffer = read_buffer; +} + +static void +_cogl_set_framebuffers (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer) +{ + CoglFramebuffer *current_draw_buffer; + CoglFramebuffer *current_read_buffer; + + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (draw_buffer)); + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (read_buffer)); + + current_draw_buffer = cogl_get_draw_framebuffer (); + current_read_buffer = _cogl_get_read_framebuffer (); + + if (current_draw_buffer != draw_buffer || + current_read_buffer != read_buffer) + _cogl_set_framebuffers_real (draw_buffer, read_buffer); +} + +void +cogl_set_framebuffer (CoglFramebuffer *framebuffer) +{ + _cogl_set_framebuffers (framebuffer, framebuffer); +} + +/* XXX: deprecated API */ +void +cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (target == COGL_WINDOW_BUFFER) + handle = ctx->window_buffer; + + /* This is deprecated public API. The public API doesn't currently + really expose the concept of separate draw and read buffers so + for the time being this actually just sets both buffers */ + cogl_set_framebuffer (handle); +} + +CoglFramebuffer * +cogl_get_draw_framebuffer (void) +{ + CoglFramebufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_assert (ctx->framebuffer_stack); + + entry = ctx->framebuffer_stack->data; + + return entry->draw_buffer; +} + +CoglFramebuffer * +_cogl_get_read_framebuffer (void) +{ + CoglFramebufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_assert (ctx->framebuffer_stack); + + entry = ctx->framebuffer_stack->data; + + return entry->read_buffer; +} + +void +_cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer) +{ + CoglContext *ctx; + CoglFramebuffer *old_draw_buffer, *old_read_buffer; + + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (draw_buffer)); + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (read_buffer)); + + ctx = draw_buffer->context; + _COGL_RETURN_IF_FAIL (ctx != NULL); + _COGL_RETURN_IF_FAIL (draw_buffer->context == read_buffer->context); + + _COGL_RETURN_IF_FAIL (ctx->framebuffer_stack != NULL); + + /* Copy the top of the stack so that when we call cogl_set_framebuffer + it will still know what the old framebuffer was */ + old_draw_buffer = cogl_get_draw_framebuffer (); + if (old_draw_buffer) + cogl_object_ref (old_draw_buffer); + old_read_buffer = _cogl_get_read_framebuffer (); + if (old_read_buffer) + cogl_object_ref (old_read_buffer); + ctx->framebuffer_stack = + g_slist_prepend (ctx->framebuffer_stack, + create_stack_entry (old_draw_buffer, + old_read_buffer)); + + _cogl_set_framebuffers (draw_buffer, read_buffer); +} + +void +cogl_push_framebuffer (CoglFramebuffer *buffer) +{ + _cogl_push_framebuffers (buffer, buffer); +} + +/* XXX: deprecated API */ +void +cogl_push_draw_buffer (void) +{ + cogl_push_framebuffer (cogl_get_draw_framebuffer ()); +} + +void +cogl_pop_framebuffer (void) +{ + CoglFramebufferStackEntry *to_pop; + CoglFramebufferStackEntry *to_restore; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_assert (ctx->framebuffer_stack != NULL); + g_assert (ctx->framebuffer_stack->next != NULL); + + to_pop = ctx->framebuffer_stack->data; + to_restore = ctx->framebuffer_stack->next->data; + + if (to_pop->draw_buffer != to_restore->draw_buffer || + to_pop->read_buffer != to_restore->read_buffer) + notify_buffers_changed (to_pop->draw_buffer, + to_restore->draw_buffer, + to_pop->read_buffer, + to_restore->read_buffer); + + cogl_object_unref (to_pop->draw_buffer); + cogl_object_unref (to_pop->read_buffer); + g_slice_free (CoglFramebufferStackEntry, to_pop); + + ctx->framebuffer_stack = + g_slist_delete_link (ctx->framebuffer_stack, + ctx->framebuffer_stack); +} + +/* XXX: deprecated API */ +void +cogl_pop_draw_buffer (void) +{ + cogl_pop_framebuffer (); +} + +CoglPixelFormat +cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer) +{ + return framebuffer->internal_format; +} diff --git a/cogl/cogl/deprecated/cogl-framebuffer-deprecated.h b/cogl/cogl/deprecated/cogl-framebuffer-deprecated.h new file mode 100644 index 000000000..68ed9d336 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-framebuffer-deprecated.h @@ -0,0 +1,264 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_FRAMEBUFFER_DEPRECATED_H__ +#define __COGL_FRAMEBUFFER_DEPRECATED_H__ + +#include + +COGL_BEGIN_DECLS + +/** + * cogl_set_framebuffer: + * @buffer: A #CoglFramebuffer object, either onscreen or offscreen. + * + * This redirects all subsequent drawing to the specified framebuffer. This can + * either be an offscreen buffer created with cogl_offscreen_new_to_texture () + * or in the future it may be an onscreen framebuffers too. + * + * Since: 1.2 + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_framebuffer (CoglFramebuffer *buffer); + +/** + * cogl_push_framebuffer: + * @buffer: A #CoglFramebuffer object, either onscreen or offscreen. + * + * Redirects all subsequent drawing to the specified framebuffer. This can + * either be an offscreen buffer created with cogl_offscreen_new_to_texture () + * or in the future it may be an onscreen framebuffer too. + * + * You should understand that a framebuffer owns the following state: + * + * The projection matrix + * The modelview matrix stack + * The viewport + * The clip stack + * + * So these items will automatically be saved and restored when you + * push and pop between different framebuffers. + * + * Also remember a newly allocated framebuffer will have an identity matrix for + * the projection and modelview matrices which gives you a coordinate space + * like OpenGL with (-1, -1) corresponding to the top left of the viewport, + * (1, 1) corresponding to the bottom right and +z coming out towards the + * viewer. + * + * If you want to set up a coordinate space like Clutter does with (0, 0) + * corresponding to the top left and (framebuffer_width, framebuffer_height) + * corresponding to the bottom right you can do so like this: + * + * |[ + * static void + * setup_viewport (unsigned int width, + * unsigned int height, + * float fovy, + * float aspect, + * float z_near, + * float z_far) + * { + * float z_camera; + * CoglMatrix projection_matrix; + * CoglMatrix mv_matrix; + * + * cogl_set_viewport (0, 0, width, height); + * cogl_perspective (fovy, aspect, z_near, z_far); + * + * cogl_get_projection_matrix (&projection_matrix); + * z_camera = 0.5 * projection_matrix.xx; + * + * cogl_matrix_init_identity (&mv_matrix); + * cogl_matrix_translate (&mv_matrix, -0.5f, -0.5f, -z_camera); + * cogl_matrix_scale (&mv_matrix, 1.0f / width, -1.0f / height, 1.0f / width); + * cogl_matrix_translate (&mv_matrix, 0.0f, -1.0 * height, 0.0f); + * cogl_set_modelview_matrix (&mv_matrix); + * } + * + * static void + * my_init_framebuffer (ClutterStage *stage, + * CoglFramebuffer *framebuffer, + * unsigned int framebuffer_width, + * unsigned int framebuffer_height) + * { + * ClutterPerspective perspective; + * + * clutter_stage_get_perspective (stage, &perspective); + * + * cogl_push_framebuffer (framebuffer); + * setup_viewport (framebuffer_width, + * framebuffer_height, + * perspective.fovy, + * perspective.aspect, + * perspective.z_near, + * perspective.z_far); + * } + * ]| + * + * The previous framebuffer can be restored by calling cogl_pop_framebuffer() + * + * Since: 1.2 + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_push_framebuffer (CoglFramebuffer *buffer); + +/** + * cogl_pop_framebuffer: + * + * Restores the framebuffer that was previously at the top of the stack. + * All subsequent drawing will be redirected to this framebuffer. + * + * Since: 1.2 + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_pop_framebuffer (void); + +/** + * cogl_set_draw_buffer: + * @target: A #CoglBufferTarget that specifies what kind of framebuffer you + * are setting as the render target. + * @offscreen: If you are setting a framebuffer of type COGL_OFFSCREEN_BUFFER + * then this is a CoglHandle for the offscreen buffer. + * + * Redirects all subsequent drawing to the specified framebuffer. This + * can either be an offscreen buffer created with + * cogl_offscreen_new_to_texture () or you can revert to your original + * on screen window buffer. + * + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_set_draw_buffer (CoglBufferTarget target, + CoglHandle offscreen); + +/** + * cogl_push_draw_buffer: + * + * Save cogl_set_draw_buffer() state. + * + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_push_draw_buffer (void); + +/** + * cogl_pop_draw_buffer: + * + * Restore cogl_set_draw_buffer() state. + * + * Deprecated: 1.16: The latest drawing apis take explicit + * #CoglFramebuffer arguments so this stack of + * framebuffers shouldn't be used anymore. + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_pop_draw_buffer (void); + +/** + * cogl_read_pixels: + * @x: The window x position to start reading from + * @y: The window y position to start reading from + * @width: The width of the rectangle you want to read + * @height: The height of the rectangle you want to read + * @source: Identifies which auxillary buffer you want to read + * (only COGL_READ_PIXELS_COLOR_BUFFER supported currently) + * @format: The pixel format you want the result in + * (only COGL_PIXEL_FORMAT_RGBA_8888 supported currently) + * @pixels: The location to write the pixel data. + * + * This reads a rectangle of pixels from the current framebuffer where + * position (0, 0) is the top left. The pixel at (x, y) is the first + * read, and the data is returned with a rowstride of (width * 4). + * + * Currently Cogl assumes that the framebuffer is in a premultiplied + * format so if @format is non-premultiplied it will convert it. To + * read the pixel values without any conversion you should either + * specify a format that doesn't use an alpha channel or use one of + * the formats ending in PRE. + * + * Deprecated: 1.16: Use cogl_framebuffer_read_pixels() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_framebuffer_read_pixels) +void +cogl_read_pixels (int x, + int y, + int width, + int height, + CoglReadPixelsFlags source, + CoglPixelFormat format, + uint8_t *pixels); + + +/* XXX: Since this api was marked unstable, maybe we can just + * remove this api if we can't find anyone is using it. */ +/** + * cogl_framebuffer_get_color_format: + * @framebuffer: A #CoglFramebuffer framebuffer + * + * Queries the common #CoglPixelFormat of all color buffers attached + * to this framebuffer. For an offscreen framebuffer created with + * cogl_offscreen_new_with_texture() this will correspond to the format + * of the texture. + * + * This API is deprecated because it is missleading to report a + * #CoglPixelFormat for the internal format of the @framebuffer since + * #CoglPixelFormat is such a precise format description and it's + * only the set of components and the premultiplied alpha status + * that is really known. + * + * Since: 1.8 + * Stability: unstable + * Deprecated 1.18: Removed since it is misleading + */ +COGL_DEPRECATED_IN_1_18 +CoglPixelFormat +cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer); + +COGL_END_DECLS + +#endif /* __COGL_FRAMEBUFFER_DEPRECATED_H__ */ diff --git a/cogl/cogl/deprecated/cogl-material-compat.c b/cogl/cogl/deprecated/cogl-material-compat.c new file mode 100644 index 000000000..25f97b113 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-material-compat.c @@ -0,0 +1,461 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +CoglMaterial * +cogl_material_new (void) +{ + _COGL_GET_CONTEXT(ctx, NULL); + return COGL_MATERIAL (cogl_pipeline_new (ctx)); +} + +CoglMaterial * +cogl_material_copy (CoglMaterial *source) +{ + return COGL_MATERIAL (cogl_pipeline_copy (COGL_PIPELINE (source))); +} + +CoglHandle +cogl_material_ref (CoglHandle handle) +{ + return cogl_object_ref (handle); +} + +void +cogl_material_unref (CoglHandle handle) +{ + cogl_object_unref (handle); +} + +CoglBool +cogl_is_material (CoglHandle handle) +{ + return cogl_is_pipeline (handle); +} + +void +cogl_material_set_color (CoglMaterial *material, + const CoglColor *color) +{ + cogl_pipeline_set_color (COGL_PIPELINE (material), color); +} + +void +cogl_material_set_color4ub (CoglMaterial *material, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + cogl_pipeline_set_color4ub (COGL_PIPELINE (material), + red, green, blue, alpha); +} + +void +cogl_material_set_color4f (CoglMaterial *material, + float red, + float green, + float blue, + float alpha) +{ + cogl_pipeline_set_color4f (COGL_PIPELINE (material), + red, green, blue, alpha); +} + +void +cogl_material_get_color (CoglMaterial *material, + CoglColor *color) +{ + cogl_pipeline_get_color (COGL_PIPELINE (material), color); +} + +void +cogl_material_set_ambient (CoglMaterial *material, + const CoglColor *ambient) +{ + cogl_pipeline_set_ambient (COGL_PIPELINE (material), ambient); +} + +void +cogl_material_get_ambient (CoglMaterial *material, + CoglColor *ambient) +{ + cogl_pipeline_get_ambient (COGL_PIPELINE (material), ambient); +} + +void +cogl_material_set_diffuse (CoglMaterial *material, + const CoglColor *diffuse) +{ + cogl_pipeline_set_diffuse (COGL_PIPELINE (material), diffuse); +} + +void +cogl_material_get_diffuse (CoglMaterial *material, + CoglColor *diffuse) +{ + cogl_pipeline_get_diffuse (COGL_PIPELINE (material), diffuse); +} + +void +cogl_material_set_ambient_and_diffuse (CoglMaterial *material, + const CoglColor *color) +{ + cogl_pipeline_set_ambient_and_diffuse (COGL_PIPELINE (material), color); + +} + +void +cogl_material_set_specular (CoglMaterial *material, + const CoglColor *specular) +{ + cogl_pipeline_set_specular (COGL_PIPELINE (material), specular); +} + +void +cogl_material_get_specular (CoglMaterial *material, + CoglColor *specular) +{ + cogl_pipeline_get_specular (COGL_PIPELINE (material), specular); +} + +void +cogl_material_set_shininess (CoglMaterial *material, + float shininess) +{ + cogl_pipeline_set_shininess (COGL_PIPELINE (material), shininess); +} + +float +cogl_material_get_shininess (CoglMaterial *material) +{ + return cogl_pipeline_get_shininess (COGL_PIPELINE (material)); +} + +void +cogl_material_set_emission (CoglMaterial *material, + const CoglColor *emission) +{ + cogl_pipeline_set_emission (COGL_PIPELINE (material), emission); + +} + +void +cogl_material_get_emission (CoglMaterial *material, + CoglColor *emission) +{ + cogl_pipeline_get_emission (COGL_PIPELINE (material), emission); + +} + +void +cogl_material_set_alpha_test_function (CoglMaterial *material, + CoglMaterialAlphaFunc alpha_func, + float alpha_reference) +{ + cogl_pipeline_set_alpha_test_function (COGL_PIPELINE (material), + alpha_func, + alpha_reference); +} + +CoglBool +cogl_material_set_blend (CoglMaterial *material, + const char *blend_string, + CoglError **error) +{ + return cogl_pipeline_set_blend (COGL_PIPELINE (material), + blend_string, + error); +} + +void +cogl_material_set_blend_constant (CoglMaterial *material, + const CoglColor *constant_color) +{ + cogl_pipeline_set_blend_constant (COGL_PIPELINE (material), constant_color); +} + +void +cogl_material_set_point_size (CoglMaterial *material, + float point_size) +{ + cogl_pipeline_set_point_size (COGL_PIPELINE (material), point_size); +} + +float +cogl_material_get_point_size (CoglMaterial *material) +{ + return cogl_pipeline_get_point_size (COGL_PIPELINE (material)); +} + +CoglHandle +cogl_material_get_user_program (CoglMaterial *material) +{ + return cogl_pipeline_get_user_program (COGL_PIPELINE (material)); +} + +void +cogl_material_set_user_program (CoglMaterial *material, + CoglHandle program) +{ + cogl_pipeline_set_user_program (COGL_PIPELINE (material), program); +} + +void +cogl_material_set_layer (CoglMaterial *material, + int layer_index, + CoglHandle texture) +{ + cogl_pipeline_set_layer_texture (COGL_PIPELINE (material), + layer_index, texture); +} + +void +cogl_material_remove_layer (CoglMaterial *material, + int layer_index) +{ + cogl_pipeline_remove_layer (COGL_PIPELINE (material), layer_index); +} + +CoglBool +cogl_material_set_layer_combine (CoglMaterial *material, + int layer_index, + const char *blend_string, + CoglError **error) +{ + return cogl_pipeline_set_layer_combine (COGL_PIPELINE (material), + layer_index, + blend_string, + error); +} + +void +cogl_material_set_layer_combine_constant (CoglMaterial *material, + int layer_index, + const CoglColor *constant) +{ + cogl_pipeline_set_layer_combine_constant (COGL_PIPELINE (material), + layer_index, + constant); +} + +void +cogl_material_set_layer_matrix (CoglMaterial *material, + int layer_index, + const CoglMatrix *matrix) +{ + cogl_pipeline_set_layer_matrix (COGL_PIPELINE (material), + layer_index, matrix); +} + +const GList * +cogl_material_get_layers (CoglMaterial *material) +{ + return _cogl_pipeline_get_layers (COGL_PIPELINE (material)); +} + +int +cogl_material_get_n_layers (CoglMaterial *material) +{ + return cogl_pipeline_get_n_layers (COGL_PIPELINE (material)); +} + +CoglMaterialLayerType +cogl_material_layer_get_type (CoglMaterialLayer *layer) +{ + return COGL_MATERIAL_LAYER_TYPE_TEXTURE; +} + +CoglHandle +cogl_material_layer_get_texture (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_texture (COGL_PIPELINE_LAYER (layer)); +} + +CoglMaterialFilter +cogl_material_layer_get_min_filter (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_min_filter (COGL_PIPELINE_LAYER (layer)); +} + +CoglMaterialFilter +cogl_material_layer_get_mag_filter (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_mag_filter (COGL_PIPELINE_LAYER (layer)); +} + +void +cogl_material_set_layer_filters (CoglMaterial *material, + int layer_index, + CoglMaterialFilter min_filter, + CoglMaterialFilter mag_filter) +{ + cogl_pipeline_set_layer_filters (COGL_PIPELINE (material), + layer_index, + min_filter, + mag_filter); +} + +CoglBool +cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index, + CoglBool enable, + CoglError **error) +{ + CoglPipeline *pipeline = COGL_PIPELINE (material); + return cogl_pipeline_set_layer_point_sprite_coords_enabled (pipeline, + layer_index, + enable, + error); +} + +CoglBool +cogl_material_get_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index) +{ + CoglPipeline *pipeline = COGL_PIPELINE (material); + return cogl_pipeline_get_layer_point_sprite_coords_enabled (pipeline, + layer_index); +} + +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_s (CoglMaterial *material, + int layer_index) +{ + return cogl_pipeline_get_layer_wrap_mode_s (COGL_PIPELINE (material), + layer_index); +} + +void +cogl_material_set_layer_wrap_mode_s (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode) +{ + cogl_pipeline_set_layer_wrap_mode_s (COGL_PIPELINE (material), layer_index, + mode); +} + +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_t (CoglMaterial *material, + int layer_index) +{ + return cogl_pipeline_get_layer_wrap_mode_t (COGL_PIPELINE (material), + layer_index); +} + +void +cogl_material_set_layer_wrap_mode_t (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode) +{ + cogl_pipeline_set_layer_wrap_mode_t (COGL_PIPELINE (material), layer_index, + mode); +} + +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_p (CoglMaterial *material, + int layer_index) +{ + return cogl_pipeline_get_layer_wrap_mode_p (COGL_PIPELINE (material), + layer_index); +} + +void +cogl_material_set_layer_wrap_mode_p (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode) +{ + cogl_pipeline_set_layer_wrap_mode_p (COGL_PIPELINE (material), layer_index, + mode); +} + +void +cogl_material_set_layer_wrap_mode (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode) +{ + cogl_pipeline_set_layer_wrap_mode (COGL_PIPELINE (material), layer_index, + mode); +} + +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_s (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_wrap_mode_s (COGL_PIPELINE_LAYER (layer)); +} + +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_t (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_wrap_mode_t (COGL_PIPELINE_LAYER (layer)); +} + +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_p (CoglMaterialLayer *layer) +{ + return _cogl_pipeline_layer_get_wrap_mode_p (COGL_PIPELINE_LAYER (layer)); +} + +void +cogl_material_foreach_layer (CoglMaterial *material, + CoglMaterialLayerCallback callback, + void *user_data) +{ + cogl_pipeline_foreach_layer (COGL_PIPELINE (material), + (CoglPipelineLayerCallback)callback, user_data); +} + +CoglBool +cogl_material_set_depth_state (CoglMaterial *material, + const CoglDepthState *state, + CoglError **error) +{ + return cogl_pipeline_set_depth_state (COGL_PIPELINE (material), + state, error); +} + +void +cogl_material_get_depth_state (CoglMaterial *material, + CoglDepthState *state_out) +{ + cogl_pipeline_get_depth_state (COGL_PIPELINE (material), state_out); +} + diff --git a/cogl/cogl/deprecated/cogl-material-compat.h b/cogl/cogl/deprecated/cogl-material-compat.h new file mode 100644 index 000000000..88d3ac335 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-material-compat.h @@ -0,0 +1,1391 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_MATERIAL_H__ +#define __COGL_MATERIAL_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +/** + * SECTION:cogl-material + * @short_description: Fuctions for creating and manipulating materials + * + * COGL allows creating and manipulating materials used to fill in + * geometry. Materials may simply be lighting attributes (such as an + * ambient and diffuse colour) or might represent one or more textures + * blended together. + */ + +typedef struct _CoglMaterial CoglMaterial; +typedef struct _CoglMaterialLayer CoglMaterialLayer; + +#define COGL_MATERIAL(OBJECT) ((CoglMaterial *)OBJECT) + +/** + * CoglMaterialFilter: + * @COGL_MATERIAL_FILTER_NEAREST: Measuring in manhatten distance from the, + * current pixel center, use the nearest texture texel + * @COGL_MATERIAL_FILTER_LINEAR: Use the weighted average of the 4 texels + * nearest the current pixel center + * @COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose + * texel size most closely matches the current pixel, and use the + * %COGL_MATERIAL_FILTER_NEAREST criterion + * @COGL_MATERIAL_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose + * texel size most closely matches the current pixel, and use the + * %COGL_MATERIAL_FILTER_LINEAR criterion + * @COGL_MATERIAL_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels + * whose texel size most closely matches the current pixel, use + * the %COGL_MATERIAL_FILTER_NEAREST criterion on each one and take + * their weighted average + * @COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels + * whose texel size most closely matches the current pixel, use + * the %COGL_MATERIAL_FILTER_LINEAR criterion on each one and take + * their weighted average + * + * Texture filtering is used whenever the current pixel maps either to more + * than one texture element (texel) or less than one. These filter enums + * correspond to different strategies used to come up with a pixel color, by + * possibly referring to multiple neighbouring texels and taking a weighted + * average or simply using the nearest texel. + */ +typedef enum { + COGL_MATERIAL_FILTER_NEAREST = 0x2600, + COGL_MATERIAL_FILTER_LINEAR = 0x2601, + COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700, + COGL_MATERIAL_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701, + COGL_MATERIAL_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702, + COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703 +} CoglMaterialFilter; +/* NB: these values come from the equivalents in gl.h */ + +/** + * CoglMaterialWrapMode: + * @COGL_MATERIAL_WRAP_MODE_REPEAT: The texture will be repeated. This + * is useful for example to draw a tiled background. + * @COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the + * range 0→1 will sample copies of the edge pixels of the + * texture. This is useful to avoid artifacts if only one copy of + * the texture is being rendered. + * @COGL_MATERIAL_WRAP_MODE_AUTOMATIC: Cogl will try to automatically + * decide which of the above two to use. For cogl_rectangle(), it + * will use repeat mode if any of the texture coordinates are + * outside the range 0→1, otherwise it will use clamp to edge. For + * cogl_polygon() it will always use repeat mode. For + * cogl_vertex_buffer_draw() it will use repeat mode except for + * layers that have point sprite coordinate generation enabled. This + * is the default value. + * + * The wrap mode specifies what happens when texture coordinates + * outside the range 0→1 are used. Note that if the filter mode is + * anything but %COGL_MATERIAL_FILTER_NEAREST then texels outside the + * range 0→1 might be used even when the coordinate is exactly 0 or 1 + * because OpenGL will try to sample neighbouring pixels. For example + * if you are trying to render the full texture then you may get + * artifacts around the edges when the pixels from the other side are + * merged in if the wrap mode is set to repeat. + * + * Since: 1.4 + */ +/* GL_ALWAYS is just used here as a value that is known not to clash + * with any valid GL wrap modes + * + * XXX: keep the values in sync with the CoglMaterialWrapModeInternal + * enum so no conversion is actually needed. + */ +typedef enum { + COGL_MATERIAL_WRAP_MODE_REPEAT = 0x2901, + COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE = 0x812F, + COGL_MATERIAL_WRAP_MODE_AUTOMATIC = 0x0207 +} CoglMaterialWrapMode; +/* NB: these values come from the equivalents in gl.h */ + +/** + * cogl_material_new: + * + * Allocates and initializes a blank white material + * + * Return value: a pointer to a new #CoglMaterial + * Deprecated: 1.16: Use cogl_pipeline_new() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_new) +CoglMaterial * +cogl_material_new (void); + +/** + * cogl_material_copy: + * @source: a #CoglMaterial object to copy + * + * Creates a new material with the configuration copied from the + * source material. + * + * We would strongly advise developers to always aim to use + * cogl_material_copy() instead of cogl_material_new() whenever there will + * be any similarity between two materials. Copying a material helps Cogl + * keep track of a materials ancestry which we may use to help minimize GPU + * state changes. + * + * Returns: a pointer to the newly allocated #CoglMaterial + * + * Since: 1.2 + * Deprecated: 1.16: Use cogl_pipeline_copy() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_copy) +CoglMaterial * +cogl_material_copy (CoglMaterial *source); + +/** + * cogl_material_ref: + * @material: a #CoglMaterial object. + * + * Increment the reference count for a #CoglMaterial. + * + * Return value: the @material. + * + * Since: 1.0 + * + * Deprecated: 1.2: Use cogl_object_ref() instead + */ +COGL_DEPRECATED +CoglHandle +cogl_material_ref (CoglHandle material); + +/** + * cogl_material_unref: + * @material: a #CoglMaterial object. + * + * Decrement the reference count for a #CoglMaterial. + * + * Since: 1.0 + * + * Deprecated: 1.2: Use cogl_object_unref() instead + */ +COGL_DEPRECATED +void +cogl_material_unref (CoglHandle material); + +/** + * cogl_is_material: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing material object. + * + * Return value: %TRUE if the handle references a #CoglMaterial, + * %FALSE otherwise + * Deprecated: 1.16: Use cogl_is_pipeline() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_is_pipeline) +CoglBool +cogl_is_material (CoglHandle handle); + +/** + * cogl_material_set_color: + * @material: A #CoglMaterial object + * @color: The components of the color + * + * Sets the basic color of the material, used when no lighting is enabled. + * + * Note that if you don't add any layers to the material then the color + * will be blended unmodified with the destination; the default blend + * expects premultiplied colors: for example, use (0.5, 0.0, 0.0, 0.5) for + * semi-transparent red. See cogl_color_premultiply(). + * + * The default value is (1.0, 1.0, 1.0, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_color() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_color) +void +cogl_material_set_color (CoglMaterial *material, + const CoglColor *color); + +/** + * cogl_material_set_color4ub: + * @material: A #CoglMaterial object + * @red: The red component + * @green: The green component + * @blue: The blue component + * @alpha: The alpha component + * + * Sets the basic color of the material, used when no lighting is enabled. + * + * The default value is (0xff, 0xff, 0xff, 0xff) + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_color4ub() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_color4ub) +void +cogl_material_set_color4ub (CoglMaterial *material, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); + +/** + * cogl_material_set_color4f: + * @material: A #CoglMaterial object + * @red: The red component + * @green: The green component + * @blue: The blue component + * @alpha: The alpha component + * + * Sets the basic color of the material, used when no lighting is enabled. + * + * The default value is (1.0, 1.0, 1.0, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_color4f() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_color4f) +void +cogl_material_set_color4f (CoglMaterial *material, + float red, + float green, + float blue, + float alpha); + +/** + * cogl_material_get_color: + * @material: A #CoglMaterial object + * @color: (out): The location to store the color + * + * Retrieves the current material color. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_get_color() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_color) +void +cogl_material_get_color (CoglMaterial *material, + CoglColor *color); + +/** + * cogl_material_set_ambient: + * @material: A #CoglMaterial object + * @ambient: The components of the desired ambient color + * + * Sets the material's ambient color, in the standard OpenGL lighting + * model. The ambient color affects the overall color of the object. + * + * Since the diffuse color will be intense when the light hits the surface + * directly, the ambient will be most apparent where the light hits at a + * slant. + * + * The default value is (0.2, 0.2, 0.2, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_ambient (CoglMaterial *material, + const CoglColor *ambient); + +/** + * cogl_material_get_ambient: + * @material: A #CoglMaterial object + * @ambient: The location to store the ambient color + * + * Retrieves the current ambient color for @material + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_get_ambient (CoglMaterial *material, + CoglColor *ambient); + +/** + * cogl_material_set_diffuse: + * @material: A #CoglMaterial object + * @diffuse: The components of the desired diffuse color + * + * Sets the material's diffuse color, in the standard OpenGL lighting + * model. The diffuse color is most intense where the light hits the + * surface directly - perpendicular to the surface. + * + * The default value is (0.8, 0.8, 0.8, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_diffuse (CoglMaterial *material, + const CoglColor *diffuse); + +/** + * cogl_material_get_diffuse: + * @material: A #CoglMaterial object + * @diffuse: The location to store the diffuse color + * + * Retrieves the current diffuse color for @material + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_get_diffuse (CoglMaterial *material, + CoglColor *diffuse); + +/** + * cogl_material_set_ambient_and_diffuse: + * @material: A #CoglMaterial object + * @color: The components of the desired ambient and diffuse colors + * + * Conveniently sets the diffuse and ambient color of @material at the same + * time. See cogl_material_set_ambient() and cogl_material_set_diffuse(). + * + * The default ambient color is (0.2, 0.2, 0.2, 1.0) + * + * The default diffuse color is (0.8, 0.8, 0.8, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_ambient_and_diffuse (CoglMaterial *material, + const CoglColor *color); + +/** + * cogl_material_set_specular: + * @material: A #CoglMaterial object + * @specular: The components of the desired specular color + * + * Sets the material's specular color, in the standard OpenGL lighting + * model. The intensity of the specular color depends on the viewport + * position, and is brightest along the lines of reflection. + * + * The default value is (0.0, 0.0, 0.0, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_specular (CoglMaterial *material, + const CoglColor *specular); + +/** + * cogl_material_get_specular: + * @material: A #CoglMaterial object + * @specular: The location to store the specular color + * + * Retrieves the materials current specular color. + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_get_specular (CoglMaterial *material, + CoglColor *specular); + +/** + * cogl_material_set_shininess: + * @material: A #CoglMaterial object + * @shininess: The desired shininess; must be >= 0.0 + * + * Sets the shininess of the material, in the standard OpenGL lighting + * model, which determines the size of the specular highlights. A + * higher @shininess will produce smaller highlights which makes the + * object appear more shiny. + * + * The default value is 0.0 + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_shininess (CoglMaterial *material, + float shininess); + +/** + * cogl_material_get_shininess: + * @material: A #CoglMaterial object + * + * Retrieves the materials current emission color. + * + * Return value: The materials current shininess value + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +float +cogl_material_get_shininess (CoglMaterial *material); + +/** + * cogl_material_set_emission: + * @material: A #CoglMaterial object + * @emission: The components of the desired emissive color + * + * Sets the material's emissive color, in the standard OpenGL lighting + * model. It will look like the surface is a light source emitting this + * color. + * + * The default value is (0.0, 0.0, 0.0, 1.0) + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_emission (CoglMaterial *material, + const CoglColor *emission); + +/** + * cogl_material_get_emission: + * @material: A #CoglMaterial object + * @emission: The location to store the emission color + * + * Retrieves the materials current emission color. + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglSnippet shader api for lighting + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_get_emission (CoglMaterial *material, + CoglColor *emission); + +/** + * CoglMaterialAlphaFunc: + * @COGL_MATERIAL_ALPHA_FUNC_NEVER: Never let the fragment through. + * @COGL_MATERIAL_ALPHA_FUNC_LESS: Let the fragment through if the incoming + * alpha value is less than the reference alpha value + * @COGL_MATERIAL_ALPHA_FUNC_EQUAL: Let the fragment through if the incoming + * alpha value equals the reference alpha value + * @COGL_MATERIAL_ALPHA_FUNC_LEQUAL: Let the fragment through if the incoming + * alpha value is less than or equal to the reference alpha value + * @COGL_MATERIAL_ALPHA_FUNC_GREATER: Let the fragment through if the incoming + * alpha value is greater than the reference alpha value + * @COGL_MATERIAL_ALPHA_FUNC_NOTEQUAL: Let the fragment through if the incoming + * alpha value does not equal the reference alpha value + * @COGL_MATERIAL_ALPHA_FUNC_GEQUAL: Let the fragment through if the incoming + * alpha value is greater than or equal to the reference alpha value. + * @COGL_MATERIAL_ALPHA_FUNC_ALWAYS: Always let the fragment through. + * + * Alpha testing happens before blending primitives with the framebuffer and + * gives an opportunity to discard fragments based on a comparison with the + * incoming alpha value and a reference alpha value. The #CoglMaterialAlphaFunc + * determines how the comparison is done. + */ +typedef enum { + COGL_MATERIAL_ALPHA_FUNC_NEVER = 0x0200, + COGL_MATERIAL_ALPHA_FUNC_LESS = 0x0201, + COGL_MATERIAL_ALPHA_FUNC_EQUAL = 0x0202, + COGL_MATERIAL_ALPHA_FUNC_LEQUAL = 0x0203, + COGL_MATERIAL_ALPHA_FUNC_GREATER = 0x0204, + COGL_MATERIAL_ALPHA_FUNC_NOTEQUAL = 0x0205, + COGL_MATERIAL_ALPHA_FUNC_GEQUAL = 0x0206, + COGL_MATERIAL_ALPHA_FUNC_ALWAYS = 0x0207 +} CoglMaterialAlphaFunc; + +/** + * cogl_material_set_alpha_test_function: + * @material: A #CoglMaterial object + * @alpha_func: A @CoglMaterialAlphaFunc constant + * @alpha_reference: A reference point that the chosen alpha function uses + * to compare incoming fragments to. + * + * Before a primitive is blended with the framebuffer, it goes through an + * alpha test stage which lets you discard fragments based on the current + * alpha value. This function lets you change the function used to evaluate + * the alpha channel, and thus determine which fragments are discarded + * and which continue on to the blending stage. + * + * The default is %COGL_MATERIAL_ALPHA_FUNC_ALWAYS + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_alpha_test_function() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_alpha_test_function) +void +cogl_material_set_alpha_test_function (CoglMaterial *material, + CoglMaterialAlphaFunc alpha_func, + float alpha_reference); + +/** + * cogl_material_set_blend: + * @material: A #CoglMaterial object + * @blend_string: A Cogl blend string + * describing the desired blend function. + * @error: return location for a #CoglError that may report lack of driver + * support if you give separate blend string statements for the alpha + * channel and RGB channels since some drivers, or backends such as + * GLES 1.1, don't support this feature. May be %NULL, in which case a + * warning will be printed out using GLib's logging facilities if an + * error is encountered. + * + * If not already familiar; please refer here + * for an overview of what blend strings are, and their syntax. + * + * Blending occurs after the alpha test function, and combines fragments with + * the framebuffer. + + * Currently the only blend function Cogl exposes is ADD(). So any valid + * blend statements will be of the form: + * + * |[ + * <channel-mask>=ADD(SRC_COLOR*(<factor>), DST_COLOR*(<factor>)) + * ]| + * + * The brackets around blend factors are currently not + * optional! + * + * This is the list of source-names usable as blend factors: + * + * SRC_COLOR: The color of the in comming fragment + * DST_COLOR: The color of the framebuffer + * CONSTANT: The constant set via cogl_material_set_blend_constant() + * + * + * The source names can be used according to the + * color-source and factor syntax, + * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would + * "(CONSTANT[RGB])" + * + * These can also be used as factors: + * + * 0: (0, 0, 0, 0) + * 1: (1, 1, 1, 1) + * SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A]) + * + * + * Remember; all color components are normalized to the range [0, 1] + * before computing the result of blending. + * + * + * Blend Strings/1 + * Blend a non-premultiplied source over a destination with + * premultiplied alpha: + * + * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))" + * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + * + * + * + * Blend Strings/2 + * Blend a premultiplied source over a destination with + * premultiplied alpha + * + * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + * + * + * The default blend string is: + * |[ + * RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A])) + * ]| + * + * That gives normal alpha-blending when the calculated color for the material + * is in premultiplied form. + * + * Return value: %TRUE if the blend string was successfully parsed, and the + * described blending is supported by the underlying driver/hardware. If + * there was an error, %FALSE is returned and @error is set accordingly (if + * present). + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_blend() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_blend) +CoglBool +cogl_material_set_blend (CoglMaterial *material, + const char *blend_string, + CoglError **error); + +/** + * cogl_material_set_blend_constant: + * @material: A #CoglMaterial object + * @constant_color: The constant color you want + * + * When blending is setup to reference a CONSTANT blend factor then + * blending will depend on the constant set with this function. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_blend_constant() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_blend_constant) +void +cogl_material_set_blend_constant (CoglMaterial *material, + const CoglColor *constant_color); + +/** + * cogl_material_set_point_size: + * @material: a material. + * @point_size: the new point size. + * + * Changes the size of points drawn when %COGL_VERTICES_MODE_POINTS is + * used with the vertex buffer API. Note that typically the GPU will + * only support a limited minimum and maximum range of point sizes. If + * the chosen point size is outside that range then the nearest value + * within that range will be used instead. The size of a point is in + * screen space so it will be the same regardless of any + * transformations. The default point size is 1.0. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_point_size() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_point_size) +void +cogl_material_set_point_size (CoglMaterial *material, + float point_size); + +/** + * cogl_material_get_point_size: + * @material: a #CoglHandle to a material. + * + * Get the size of points drawn when %COGL_VERTICES_MODE_POINTS is + * used with the vertex buffer API. + * + * Return value: the point size of the material. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_get_point_size() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_point_size) +float +cogl_material_get_point_size (CoglMaterial *material); + +/** + * cogl_material_get_user_program: + * @material: a #CoglMaterial object. + * + * Queries what user program has been associated with the given + * @material using cogl_material_set_user_program(). + * + * Return value: (transfer none): The current user program + * or %COGL_INVALID_HANDLE. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglHandle +cogl_material_get_user_program (CoglMaterial *material); + +/** + * cogl_material_set_user_program: + * @material: a #CoglMaterial object. + * @program: A #CoglHandle to a linked CoglProgram + * + * Associates a linked CoglProgram with the given material so that the + * program can take full control of vertex and/or fragment processing. + * + * This is an example of how it can be used to associate an ARBfp + * program with a #CoglMaterial: + * |[ + * CoglHandle shader; + * CoglHandle program; + * CoglMaterial *material; + * + * shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); + * cogl_shader_source (shader, + * "!!ARBfp1.0\n" + * "MOV result.color,fragment.color;\n" + * "END\n"); + * cogl_shader_compile (shader); + * + * program = cogl_create_program (); + * cogl_program_attach_shader (program, shader); + * cogl_program_link (program); + * + * material = cogl_material_new (); + * cogl_material_set_user_program (material, program); + * + * cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + * cogl_rectangle (0, 0, 100, 100); + * ]| + * + * It is possibly worth keeping in mind that this API is not part of + * the long term design for how we want to expose shaders to Cogl + * developers (We are planning on deprecating the cogl_program and + * cogl_shader APIs in favour of a "snippet" framework) but in the + * meantime we hope this will handle most practical GLSL and ARBfp + * requirements. + * + * Also remember you need to check for either the + * %COGL_FEATURE_SHADERS_GLSL or %COGL_FEATURE_SHADERS_ARBFP before + * using the cogl_program or cogl_shader API. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_material_set_user_program (CoglMaterial *material, + CoglHandle program); + +/** + * cogl_material_set_layer: + * @material: A #CoglMaterial object + * @layer_index: the index of the layer + * @texture: a #CoglHandle for the layer object + * + * In addition to the standard OpenGL lighting model a Cogl material may have + * one or more layers comprised of textures that can be blended together in + * order, with a number of different texture combine modes. This function + * defines a new texture layer. + * + * The index values of multiple layers do not have to be consecutive; it is + * only their relative order that is important. + * + * In the future, we may define other types of material layers, such + * as purely GLSL based layers. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_layer() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer) +void +cogl_material_set_layer (CoglMaterial *material, + int layer_index, + CoglHandle texture); + +/** + * cogl_material_remove_layer: + * @material: A #CoglMaterial object + * @layer_index: Specifies the layer you want to remove + * + * This function removes a layer from your material + * Deprecated: 1.16: Use cogl_pipeline_remove_layer() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_remove_layer) +void +cogl_material_remove_layer (CoglMaterial *material, + int layer_index); + + +/** + * cogl_material_set_layer_combine: + * @material: A #CoglMaterial object + * @layer_index: Specifies the layer you want define a combine function for + * @blend_string: A Cogl blend string + * describing the desired texture combine function. + * @error: A #CoglError that may report parse errors or lack of GPU/driver + * support. May be %NULL, in which case a warning will be printed out if an + * error is encountered. + * + * If not already familiar; you can refer + * here for an overview of what blend + * strings are and there syntax. + * + * These are all the functions available for texture combining: + * + * REPLACE(arg0) = arg0 + * MODULATE(arg0, arg1) = arg0 x arg1 + * ADD(arg0, arg1) = arg0 + arg1 + * ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5 + * INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2) + * SUBTRACT(arg0, arg1) = arg0 - arg1 + * + * + * DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * + * + * DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * + * + * Refer to the + * color-source syntax for + * describing the arguments. The valid source names for texture combining + * are: + * + * + * TEXTURE + * Use the color from the current texture layer + * + * + * TEXTURE_0, TEXTURE_1, etc + * Use the color from the specified texture layer + * + * + * CONSTANT + * Use the color from the constant given with + * cogl_material_set_layer_constant() + * + * + * PRIMARY + * Use the color of the material as set with + * cogl_material_set_color() + * + * + * PREVIOUS + * Either use the texture color from the previous layer, or + * if this is layer 0, use the color of the material as set with + * cogl_material_set_color() + * + * + * + * + * Layer Combine Examples + * This is effectively what the default blending is: + * + * RGBA = MODULATE (PREVIOUS, TEXTURE) + * + * This could be used to cross-fade between two images, using + * the alpha component of a constant as the interpolator. The constant + * color is given by calling cogl_material_set_layer_constant. + * + * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A]) + * + * + * + * You can't give a multiplication factor for arguments as you can + * with blending. + * + * Return value: %TRUE if the blend string was successfully parsed, and the + * described texture combining is supported by the underlying driver and + * or hardware. On failure, %FALSE is returned and @error is set + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_combine() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_combine) +CoglBool +cogl_material_set_layer_combine (CoglMaterial *material, + int layer_index, + const char *blend_string, + CoglError **error); + +/** + * cogl_material_set_layer_combine_constant: + * @material: A #CoglMaterial object + * @layer_index: Specifies the layer you want to specify a constant used + * for texture combining + * @constant: The constant color you want + * + * When you are using the 'CONSTANT' color source in a layer combine + * description then you can use this function to define its value. + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_combine_constant() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_combine_constant) +void +cogl_material_set_layer_combine_constant (CoglMaterial *material, + int layer_index, + const CoglColor *constant); + +/** + * cogl_material_set_layer_matrix: + * @material: A #CoglMaterial object + * @layer_index: the index for the layer inside @material + * @matrix: the transformation matrix for the layer + * + * This function lets you set a matrix that can be used to e.g. translate + * and rotate a single layer of a material used to fill your geometry. + * Deprecated: 1.16: Use cogl_pipeline_set_layer_matrix() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_matrix) +void +cogl_material_set_layer_matrix (CoglMaterial *material, + int layer_index, + const CoglMatrix *matrix); + +/** + * cogl_material_get_layers: + * @material: A #CoglMaterial object + * + * This function lets you access a material's internal list of layers + * for iteration. + * + * You should avoid using this API if possible since it was only + * made public by mistake and will be deprecated when we have + * suitable alternative. + * + * It's important to understand that the list returned may not + * remain valid if you modify the material or any of the layers in any + * way and so you would have to re-get the list in that + * situation. + * + * Return value: (element-type CoglMaterialLayer) (transfer none): A + * list of #CoglMaterialLayer's that can be passed to the + * cogl_material_layer_* functions. The list is owned by Cogl and it + * should not be modified or freed + * Deprecated: 1.16: Use cogl_pipeline_get_layers() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_layers) +const GList * +cogl_material_get_layers (CoglMaterial *material); + +/** + * cogl_material_get_n_layers: + * @material: A #CoglMaterial object + * + * Retrieves the number of layers defined for the given @material + * + * Return value: the number of layers + * + * Since: 1.0 + * Deprecated: 1.16: Use cogl_pipeline_get_n_layers() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_n_layers) +int +cogl_material_get_n_layers (CoglMaterial *material); + +/** + * CoglMaterialLayerType: + * @COGL_MATERIAL_LAYER_TYPE_TEXTURE: The layer represents a + * texture + * + * Available types of layers for a #CoglMaterial. This enumeration + * might be expanded in later versions. + * + * Since: 1.0 + */ +typedef enum { + COGL_MATERIAL_LAYER_TYPE_TEXTURE +} CoglMaterialLayerType; + + +/** + * cogl_material_layer_get_type: + * @layer: A #CoglMaterialLayer object + * + * Retrieves the type of the layer + * + * Currently there is only one type of layer defined: + * %COGL_MATERIAL_LAYER_TYPE_TEXTURE, but considering we may add purely GLSL + * based layers in the future, you should write code that checks the type + * first. + * + * Return value: the type of the layer + * Deprecated: 1.16: No replacement + */ +COGL_DEPRECATED_IN_1_16 +CoglMaterialLayerType +cogl_material_layer_get_type (CoglMaterialLayer *layer); + +/** + * cogl_material_layer_get_texture: + * @layer: A #CoglMaterialLayer object + * + * Extracts a texture handle for a specific layer. + * + * In the future Cogl may support purely GLSL based layers; for those + * layers this function which will likely return %COGL_INVALID_HANDLE if you + * try to get the texture handle from them. Considering this scenario, you + * should call cogl_material_layer_get_type() first in order check it is of + * type %COGL_MATERIAL_LAYER_TYPE_TEXTURE before calling this function. + * + * Return value: (transfer none): a #CoglHandle for the texture inside the layer + * Deprecated: 1.16: No replacement + */ +COGL_DEPRECATED_IN_1_16 +CoglHandle +cogl_material_layer_get_texture (CoglMaterialLayer *layer); + +/** + * cogl_material_layer_get_min_filter: + * @layer: a #CoglHandle for a material layer + * + * Queries the currently set downscaling filter for a material layer + * + * Return value: the current downscaling filter + * Deprecated: 1.16: No replacement + */ +COGL_DEPRECATED_IN_1_16 +CoglMaterialFilter +cogl_material_layer_get_min_filter (CoglMaterialLayer *layer); + +/** + * cogl_material_layer_get_mag_filter: + * @layer: A #CoglMaterialLayer object + * + * Queries the currently set downscaling filter for a material later + * + * Return value: the current downscaling filter + * Deprecated: 1.16: No replacement + */ +COGL_DEPRECATED_IN_1_16 +CoglMaterialFilter +cogl_material_layer_get_mag_filter (CoglMaterialLayer *layer); + +/** + * cogl_material_set_layer_filters: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * @min_filter: the filter used when scaling a texture down. + * @mag_filter: the filter used when magnifying a texture. + * + * Changes the decimation and interpolation filters used when a texture is + * drawn at other scales than 100%. + * Deprecated: 1.16: Use cogl_pipeline_set_layer_filters() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_filters) +void +cogl_material_set_layer_filters (CoglMaterial *material, + int layer_index, + CoglMaterialFilter min_filter, + CoglMaterialFilter mag_filter); + +/** + * cogl_material_set_layer_point_sprite_coords_enabled: + * @material: a #CoglHandle to a material. + * @layer_index: the layer number to change. + * @enable: whether to enable point sprite coord generation. + * @error: A return location for a CoglError, or NULL to ignore errors. + * + * When rendering points, if @enable is %TRUE then the texture + * coordinates for this layer will be replaced with coordinates that + * vary from 0.0 to 1.0 across the primitive. The top left of the + * point will have the coordinates 0.0,0.0 and the bottom right will + * have 1.0,1.0. If @enable is %FALSE then the coordinates will be + * fixed for the entire point. + * + * This function will only work if %COGL_FEATURE_POINT_SPRITE is + * available. If the feature is not available then the function will + * return %FALSE and set @error. + * + * Return value: %TRUE if the function succeeds, %FALSE otherwise. + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_point_sprite_coords_enabled() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_point_sprite_coords_enabled) +CoglBool +cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index, + CoglBool enable, + CoglError **error); + +/** + * cogl_material_get_layer_point_sprite_coords_enabled: + * @material: a #CoglHandle to a material. + * @layer_index: the layer number to check. + * + * Gets whether point sprite coordinate generation is enabled for this + * texture layer. + * + * Return value: whether the texture coordinates will be replaced with + * point sprite coordinates. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_get_layer_point_sprite_coords_enabled() + * instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_layer_point_sprite_coords_enabled) +CoglBool +cogl_material_get_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index); + +/** + * cogl_material_get_layer_wrap_mode_s: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 's' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 's' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Deprecated: 1.16: Use cogl_pipeline_get_layer_wrap_mode_s() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_layer_wrap_mode_s) +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_s (CoglMaterial *material, + int layer_index); + +/** + * cogl_material_set_layer_wrap_mode_s: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 's' coordinate of texture lookups on this layer. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_wrap_mode_s() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_wrap_mode_s) +void +cogl_material_set_layer_wrap_mode_s (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode); + +/** + * cogl_material_get_layer_wrap_mode_t: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 't' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 't' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Deprecated: 1.16: Use cogl_pipeline_get_layer_wrap_mode_t() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_layer_wrap_mode_t) +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_t (CoglMaterial *material, + int layer_index); + + +/** + * cogl_material_set_layer_wrap_mode_t: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 't' coordinate of texture lookups on this layer. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_wrap_mode_t() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_wrap_mode_t) +void +cogl_material_set_layer_wrap_mode_t (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode); + +/** + * cogl_material_get_layer_wrap_mode_p: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * + * Returns the wrap mode for the 'p' coordinate of texture lookups on this + * layer. + * + * Return value: the wrap mode for the 'p' coordinate of texture lookups on + * this layer. + * + * Since: 1.6 + * Deprecated: 1.16: Use cogl_pipeline_get_layer_wrap_mode_p() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_layer_wrap_mode_p) +CoglMaterialWrapMode +cogl_material_get_layer_wrap_mode_p (CoglMaterial *material, + int layer_index); + +/** + * cogl_material_set_layer_wrap_mode_p: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for the 'p' coordinate of texture lookups on + * this layer. 'p' is the third coordinate. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_wrap_mode_p() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_wrap_mode_p) +void +cogl_material_set_layer_wrap_mode_p (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode); + +/** + * cogl_material_set_layer_wrap_mode: + * @material: A #CoglMaterial object + * @layer_index: the layer number to change. + * @mode: the new wrap mode + * + * Sets the wrap mode for all three coordinates of texture lookups on + * this layer. This is equivalent to calling + * cogl_material_set_layer_wrap_mode_s(), + * cogl_material_set_layer_wrap_mode_t() and + * cogl_material_set_layer_wrap_mode_p() separately. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_set_layer_wrap_mode() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_layer_wrap_mode) +void +cogl_material_set_layer_wrap_mode (CoglMaterial *material, + int layer_index, + CoglMaterialWrapMode mode); + +/** + * cogl_material_layer_get_wrap_mode_s: + * @layer: A #CoglMaterialLayer object + * + * Gets the wrap mode for the 's' coordinate of texture lookups on this layer. + * + * Return value: the wrap mode value for the s coordinate. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_layer_get_wrap_mode_s() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_layer_get_wrap_mode_s) +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_s (CoglMaterialLayer *layer); + +/** + * cogl_material_layer_get_wrap_mode_t: + * @layer: A #CoglMaterialLayer object + * + * Gets the wrap mode for the 't' coordinate of texture lookups on this layer. + * + * Return value: the wrap mode value for the t coordinate. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_layer_get_wrap_mode_t() instead + */ + +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_layer_get_wrap_mode_t) +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_t (CoglMaterialLayer *layer); + +/** + * cogl_material_layer_get_wrap_mode_p: + * @layer: A #CoglMaterialLayer object + * + * Gets the wrap mode for the 'p' coordinate of texture lookups on + * this layer. 'p' is the third coordinate. + * + * Return value: the wrap mode value for the p coordinate. + * + * Since: 1.4 + * Deprecated: 1.16: Use cogl_pipeline_layer_get_wrap_mode_p() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_layer_get_wrap_mode_p) +CoglMaterialWrapMode +cogl_material_layer_get_wrap_mode_p (CoglMaterialLayer *layer); + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * cogl_material_set_depth_state: + * @material: A #CoglMaterial object + * @state: A #CoglDepthState struct + * @error: A #CoglError to report failures to setup the given @state. + * + * This commits all the depth state configured in @state struct to the + * given @material. The configuration values are copied into the + * material so there is no requirement to keep the #CoglDepthState + * struct around if you don't need it any more. + * + * Note: Since some platforms do not support the depth range feature + * it is possible for this function to fail and report an @error. + * + * Returns: TRUE if the GPU supports all the given @state else %FALSE + * and returns an @error. + * + * Since: 1.8 + * Stability: Unstable + * Deprecated: 1.16: Use cogl_pipeline_set_depth_state() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_set_depth_state) +CoglBool +cogl_material_set_depth_state (CoglMaterial *material, + const CoglDepthState *state, + CoglError **error); + +/** + * cogl_material_get_depth_state: + * @material: A #CoglMaterial object + * @state_out: A destination #CoglDepthState struct + * + * Retrieves the current depth state configuration for the given + * @pipeline as previously set using cogl_pipeline_set_depth_state(). + * + * Since: 2.0 + * Stability: Unstable + * Deprecated: 1.16: Use cogl_pipeline_get_depth_state() instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_pipeline_get_depth_state) +void +cogl_material_get_depth_state (CoglMaterial *material, + CoglDepthState *state_out); + +/** + * CoglMaterialLayerCallback: + * @material: The #CoglMaterial whos layers are being iterated + * @layer_index: The current layer index + * @user_data: The private data passed to cogl_material_foreach_layer() + * + * The callback prototype used with cogl_material_foreach_layer() for + * iterating all the layers of a @material. + * + * Since: 1.4 + * Stability: Unstable + * Deprecated: 1.16 + */ +typedef CoglBool (*CoglMaterialLayerCallback) (CoglMaterial *material, + int layer_index, + void *user_data); + +/** + * cogl_material_foreach_layer: + * @material: A #CoglMaterial object + * @callback: A #CoglMaterialLayerCallback to be called for each layer + * index + * @user_data: Private data that will be passed to the callback + * + * Iterates all the layer indices of the given @material. + * + * Since: 1.4 + * Stability: Unstable + * Deprecated: 1.16: No replacement + */ +COGL_DEPRECATED_IN_1_16 +void +cogl_material_foreach_layer (CoglMaterial *material, + CoglMaterialLayerCallback callback, + void *user_data); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +G_END_DECLS + +#endif /* __COGL_MATERIAL_H__ */ diff --git a/cogl/cogl/deprecated/cogl-program-private.h b/cogl/cogl/deprecated/cogl-program-private.h new file mode 100644 index 000000000..64ed72c6e --- /dev/null +++ b/cogl/cogl/deprecated/cogl-program-private.h @@ -0,0 +1,88 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_PROGRAM_H +#define __COGL_PROGRAM_H + +#include "cogl-object-private.h" +#include "cogl-shader-private.h" + +typedef struct _CoglProgram CoglProgram; + +struct _CoglProgram +{ + CoglHandleObject _parent; + + GSList *attached_shaders; + + GArray *custom_uniforms; + + /* An age counter that changes whenever the list of shaders is modified */ + unsigned int age; +}; + +typedef struct _CoglProgramUniform CoglProgramUniform; + +struct _CoglProgramUniform +{ + char *name; + CoglBoxedValue value; + /* The cached GL location for this uniform. This is only valid + between calls to _cogl_program_dirty_all_uniforms */ + GLint location; + /* Whether we have a location yet */ + unsigned int location_valid : 1; + /* Whether the uniform value has changed since the last time the + uniforms were flushed */ + unsigned int dirty : 1; +}; + +/* Internal function to flush the custom uniforms for the given use + program. This assumes the target GL program is already bound. The + gl_program still needs to be passed so that CoglProgram can query + the uniform locations. gl_program_changed should be set to TRUE if + we are flushing the uniforms against a different GL program from + the last time it was flushed. This will cause it to requery all of + the locations and assume that all uniforms are dirty */ +void +_cogl_program_flush_uniforms (CoglProgram *program, + GLuint gl_program, + CoglBool gl_program_changed); + +CoglShaderLanguage +_cogl_program_get_language (CoglHandle handle); + +CoglBool +_cogl_program_has_fragment_shader (CoglHandle handle); + +CoglBool +_cogl_program_has_vertex_shader (CoglHandle handle); + +#endif /* __COGL_PROGRAM_H */ diff --git a/cogl/cogl/deprecated/cogl-program.c b/cogl/cogl/deprecated/cogl-program.c new file mode 100644 index 000000000..9b44d3538 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-program.c @@ -0,0 +1,503 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "cogl-util.h" +#include "cogl-util-gl-private.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" + +#include "cogl-shader-private.h" +#include "cogl-program-private.h" + +#include + +static void _cogl_program_free (CoglProgram *program); + +COGL_HANDLE_DEFINE (Program, program); +COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (program); + +/* A CoglProgram is effectively just a list of shaders that will be + used together and a set of values for the custom uniforms. No + actual GL program is created - instead this is the responsibility + of the GLSL material backend. The uniform values are collected in + an array and then flushed whenever the material backend requests + it. */ + +static void +_cogl_program_free (CoglProgram *program) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Unref all of the attached shaders */ + g_slist_foreach (program->attached_shaders, (GFunc) cogl_handle_unref, NULL); + /* Destroy the list */ + g_slist_free (program->attached_shaders); + + for (i = 0; i < program->custom_uniforms->len; i++) + { + CoglProgramUniform *uniform = + &g_array_index (program->custom_uniforms, CoglProgramUniform, i); + + g_free (uniform->name); + + if (uniform->value.count > 1) + g_free (uniform->value.v.array); + } + + g_array_free (program->custom_uniforms, TRUE); + + g_slice_free (CoglProgram, program); +} + +CoglHandle +cogl_create_program (void) +{ + CoglProgram *program; + + program = g_slice_new0 (CoglProgram); + + program->custom_uniforms = + g_array_new (FALSE, FALSE, sizeof (CoglProgramUniform)); + program->age = 0; + + return _cogl_program_handle_new (program); +} + +void +cogl_program_attach_shader (CoglHandle program_handle, + CoglHandle shader_handle) +{ + CoglProgram *program; + CoglShader *shader; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_is_program (program_handle) || !cogl_is_shader (shader_handle)) + return; + + program = program_handle; + shader = shader_handle; + + /* Only one shader is allowed if the type is ARBfp */ + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + _COGL_RETURN_IF_FAIL (program->attached_shaders == NULL); + else if (shader->language == COGL_SHADER_LANGUAGE_GLSL) + _COGL_RETURN_IF_FAIL (_cogl_program_get_language (program) == + COGL_SHADER_LANGUAGE_GLSL); + + program->attached_shaders + = g_slist_prepend (program->attached_shaders, + cogl_handle_ref (shader_handle)); + + program->age++; +} + +void +cogl_program_link (CoglHandle handle) +{ + /* There's no point in linking the program here because it will have + to be relinked with a different fixed functionality shader + whenever the settings change */ +} + +void +cogl_program_use (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (handle == COGL_INVALID_HANDLE || + cogl_is_program (handle)); + + if (ctx->current_program == 0 && handle != 0) + ctx->legacy_state_set++; + else if (handle == 0 && ctx->current_program != 0) + ctx->legacy_state_set--; + + if (handle != COGL_INVALID_HANDLE) + cogl_handle_ref (handle); + if (ctx->current_program != COGL_INVALID_HANDLE) + cogl_handle_unref (ctx->current_program); + ctx->current_program = handle; +} + +int +cogl_program_get_uniform_location (CoglHandle handle, + const char *uniform_name) +{ + int i; + CoglProgram *program; + CoglProgramUniform *uniform; + + if (!cogl_is_program (handle)) + return -1; + + program = handle; + + /* We can't just ask the GL program object for the uniform location + directly because it will change every time the program is linked + with a different shader. Instead we make our own mapping of + uniform numbers and cache the names */ + for (i = 0; i < program->custom_uniforms->len; i++) + { + uniform = &g_array_index (program->custom_uniforms, + CoglProgramUniform, i); + + if (!strcmp (uniform->name, uniform_name)) + return i; + } + + /* Create a new uniform with the given name */ + g_array_set_size (program->custom_uniforms, + program->custom_uniforms->len + 1); + uniform = &g_array_index (program->custom_uniforms, + CoglProgramUniform, + program->custom_uniforms->len - 1); + + uniform->name = g_strdup (uniform_name); + memset (&uniform->value, 0, sizeof (CoglBoxedValue)); + uniform->dirty = TRUE; + uniform->location_valid = FALSE; + + return program->custom_uniforms->len - 1; +} + +static CoglProgramUniform * +cogl_program_modify_uniform (CoglProgram *program, + int uniform_no) +{ + CoglProgramUniform *uniform; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_program (program), NULL); + _COGL_RETURN_VAL_IF_FAIL (uniform_no >= 0 && + uniform_no < program->custom_uniforms->len, + NULL); + + uniform = &g_array_index (program->custom_uniforms, + CoglProgramUniform, uniform_no); + uniform->dirty = TRUE; + + return uniform; +} + +void +cogl_program_uniform_1f (int uniform_no, + float value) +{ + CoglProgramUniform *uniform; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no); + _cogl_boxed_value_set_1f (&uniform->value, value); +} + +void +cogl_program_set_uniform_1f (CoglHandle handle, + int uniform_location, + float value) +{ + CoglProgramUniform *uniform; + + uniform = cogl_program_modify_uniform (handle, uniform_location); + _cogl_boxed_value_set_1f (&uniform->value, value); +} + +void +cogl_program_uniform_1i (int uniform_no, + int value) +{ + CoglProgramUniform *uniform; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no); + _cogl_boxed_value_set_1i (&uniform->value, value); +} + +void +cogl_program_set_uniform_1i (CoglHandle handle, + int uniform_location, + int value) +{ + CoglProgramUniform *uniform; + + uniform = cogl_program_modify_uniform (handle, uniform_location); + _cogl_boxed_value_set_1i (&uniform->value, value); +} + +void +cogl_program_uniform_float (int uniform_no, + int size, + int count, + const float *value) +{ + CoglProgramUniform *uniform; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no); + _cogl_boxed_value_set_float (&uniform->value, size, count, value); +} + +void +cogl_program_set_uniform_float (CoglHandle handle, + int uniform_location, + int n_components, + int count, + const float *value) +{ + CoglProgramUniform *uniform; + + uniform = cogl_program_modify_uniform (handle, uniform_location); + _cogl_boxed_value_set_float (&uniform->value, n_components, count, value); +} + +void +cogl_program_uniform_int (int uniform_no, + int size, + int count, + const int *value) +{ + CoglProgramUniform *uniform; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no); + _cogl_boxed_value_set_int (&uniform->value, size, count, value); +} + +void +cogl_program_set_uniform_int (CoglHandle handle, + int uniform_location, + int n_components, + int count, + const int *value) +{ + CoglProgramUniform *uniform; + + uniform = cogl_program_modify_uniform (handle, uniform_location); + _cogl_boxed_value_set_int (&uniform->value, n_components, count, value); +} + +void +cogl_program_set_uniform_matrix (CoglHandle handle, + int uniform_location, + int dimensions, + int count, + CoglBool transpose, + const float *value) +{ + CoglProgramUniform *uniform; + + uniform = cogl_program_modify_uniform (handle, uniform_location); + _cogl_boxed_value_set_matrix (&uniform->value, + dimensions, + count, + transpose, + value); +} + +void +cogl_program_uniform_matrix (int uniform_no, + int size, + int count, + CoglBool transpose, + const float *value) +{ + CoglProgramUniform *uniform; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no); + _cogl_boxed_value_set_matrix (&uniform->value, size, count, transpose, value); +} + +/* ARBfp local parameters can be referenced like: + * + * "program.local[5]" + * ^14char offset (after whitespace is stripped) + */ +static int +get_local_param_index (const char *uniform_name) +{ + char *input = g_strdup (uniform_name); + int i; + char *p = input; + char *endptr; + int _index; + + for (i = 0; input[i] != '\0'; i++) + if (input[i] != '_' && input[i] != '\t') + *p++ = input[i]; + input[i] = '\0'; + + _COGL_RETURN_VAL_IF_FAIL (strncmp ("program.local[", input, 14) == 0, -1); + + _index = g_ascii_strtoull (input + 14, &endptr, 10); + _COGL_RETURN_VAL_IF_FAIL (endptr != input + 14, -1); + _COGL_RETURN_VAL_IF_FAIL (*endptr == ']', -1); + + _COGL_RETURN_VAL_IF_FAIL (_index >= 0, -1); + + g_free (input); + + return _index; +} + +#ifdef HAVE_COGL_GL + +static void +_cogl_program_flush_uniform_arbfp (GLint location, + CoglBoxedValue *value) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (value->type != COGL_BOXED_NONE) + { + _COGL_RETURN_IF_FAIL (value->type == COGL_BOXED_FLOAT); + _COGL_RETURN_IF_FAIL (value->size == 4); + _COGL_RETURN_IF_FAIL (value->count == 1); + + GE( ctx, glProgramLocalParameter4fv (GL_FRAGMENT_PROGRAM_ARB, location, + value->v.float_value) ); + } +} + +#endif /* HAVE_COGL_GL */ + +void +_cogl_program_flush_uniforms (CoglProgram *program, + GLuint gl_program, + CoglBool gl_program_changed) +{ + CoglProgramUniform *uniform; + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _COGL_RETURN_IF_FAIL (ctx->driver != COGL_DRIVER_GLES1); + + for (i = 0; i < program->custom_uniforms->len; i++) + { + uniform = &g_array_index (program->custom_uniforms, + CoglProgramUniform, i); + + if (gl_program_changed || uniform->dirty) + { + if (gl_program_changed || !uniform->location_valid) + { + if (_cogl_program_get_language (program) == + COGL_SHADER_LANGUAGE_GLSL) + uniform->location = + ctx->glGetUniformLocation (gl_program, uniform->name); + else + uniform->location = + get_local_param_index (uniform->name); + + uniform->location_valid = TRUE; + } + + /* If the uniform isn't really in the program then there's + no need to actually set it */ + if (uniform->location != -1) + { + switch (_cogl_program_get_language (program)) + { + case COGL_SHADER_LANGUAGE_GLSL: + _cogl_boxed_value_set_uniform (ctx, + uniform->location, + &uniform->value); + break; + + case COGL_SHADER_LANGUAGE_ARBFP: +#ifdef HAVE_COGL_GL + _cogl_program_flush_uniform_arbfp (uniform->location, + &uniform->value); +#endif + break; + } + } + + uniform->dirty = FALSE; + } + } +} + +CoglShaderLanguage +_cogl_program_get_language (CoglHandle handle) +{ + CoglProgram *program = handle; + + /* Use the language of the first shader */ + + if (program->attached_shaders) + { + CoglShader *shader = program->attached_shaders->data; + return shader->language; + } + else + return COGL_SHADER_LANGUAGE_GLSL; +} + +static CoglBool +_cogl_program_has_shader_type (CoglProgram *program, + CoglShaderType type) +{ + GSList *l; + + for (l = program->attached_shaders; l; l = l->next) + { + CoglShader *shader = l->data; + + if (shader->type == type) + return TRUE; + } + + return FALSE; +} + +CoglBool +_cogl_program_has_fragment_shader (CoglHandle handle) +{ + return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_FRAGMENT); +} + +CoglBool +_cogl_program_has_vertex_shader (CoglHandle handle) +{ + return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_VERTEX); +} diff --git a/cogl/cogl/deprecated/cogl-shader-private.h b/cogl/cogl/deprecated/cogl-shader-private.h new file mode 100644 index 000000000..dcc16b924 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-shader-private.h @@ -0,0 +1,72 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_SHADER_H +#define __COGL_SHADER_H + +#include "cogl-object-private.h" +#include "cogl-shader.h" +#include "cogl-gl-header.h" +#include "cogl-pipeline.h" + +typedef struct _CoglShader CoglShader; + +typedef enum +{ + COGL_SHADER_LANGUAGE_GLSL, + COGL_SHADER_LANGUAGE_ARBFP +} CoglShaderLanguage; + +struct _CoglShader +{ + CoglHandleObject _parent; + GLuint gl_handle; + CoglPipeline *compilation_pipeline; + CoglShaderType type; + CoglShaderLanguage language; + char *source; +}; + +void +_cogl_shader_compile_real (CoglHandle handle, + CoglPipeline *pipeline); + +CoglShaderLanguage +_cogl_program_get_language (CoglHandle handle); + +void +_cogl_shader_set_source_with_boilerplate (GLuint shader_gl_handle, + GLenum shader_gl_type, + int n_tex_coord_attribs, + GLsizei count_in, + const char **strings_in, + const GLint *lengths_in); + +#endif /* __COGL_SHADER_H */ diff --git a/cogl/cogl/deprecated/cogl-shader.c b/cogl/cogl/deprecated/cogl-shader.c new file mode 100644 index 000000000..08dcb82c5 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-shader.c @@ -0,0 +1,377 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-shader-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-glsl-shader-private.h" +#include "cogl-glsl-shader-boilerplate.h" + +#include + +#include + +static void _cogl_shader_free (CoglShader *shader); + +COGL_HANDLE_DEFINE (Shader, shader); +COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (shader); + +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif + +static void +_cogl_shader_free (CoglShader *shader) +{ + /* Frees shader resources but its handle is not + released! Do that separately before this! */ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + +#ifdef HAVE_COGL_GL + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + { + if (shader->gl_handle) + GE (ctx, glDeletePrograms (1, &shader->gl_handle)); + } + else +#endif + if (shader->gl_handle) + GE (ctx, glDeleteShader (shader->gl_handle)); + + g_slice_free (CoglShader, shader); +} + +CoglHandle +cogl_create_shader (CoglShaderType type) +{ + CoglShader *shader; + + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + switch (type) + { + case COGL_SHADER_TYPE_VERTEX: + case COGL_SHADER_TYPE_FRAGMENT: + break; + default: + g_warning ("Unexpected shader type (0x%08lX) given to " + "cogl_create_shader", (unsigned long) type); + return COGL_INVALID_HANDLE; + } + + shader = g_slice_new (CoglShader); + shader->language = COGL_SHADER_LANGUAGE_GLSL; + shader->gl_handle = 0; + shader->compilation_pipeline = NULL; + shader->type = type; + + return _cogl_shader_handle_new (shader); +} + +static void +delete_shader (CoglShader *shader) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + +#ifdef HAVE_COGL_GL + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + { + if (shader->gl_handle) + GE (ctx, glDeletePrograms (1, &shader->gl_handle)); + } + else +#endif + { + if (shader->gl_handle) + GE (ctx, glDeleteShader (shader->gl_handle)); + } + + shader->gl_handle = 0; + + if (shader->compilation_pipeline) + { + cogl_object_unref (shader->compilation_pipeline); + shader->compilation_pipeline = NULL; + } +} + +void +cogl_shader_source (CoglHandle handle, + const char *source) +{ + CoglShader *shader; + CoglShaderLanguage language; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_is_shader (handle)) + return; + + shader = handle; + +#ifdef HAVE_COGL_GL + if (strncmp (source, "!!ARBfp1.0", 10) == 0) + language = COGL_SHADER_LANGUAGE_ARBFP; + else +#endif + language = COGL_SHADER_LANGUAGE_GLSL; + + /* Delete the old object if the language is changing... */ + if (G_UNLIKELY (language != shader->language) && + shader->gl_handle) + delete_shader (shader); + + shader->source = g_strdup (source); + + shader->language = language; +} + +void +cogl_shader_compile (CoglHandle handle) +{ + CoglShader *shader; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_is_shader (handle)) + return; + +#ifdef HAVE_COGL_GL + shader = handle; + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + _cogl_shader_compile_real (handle, NULL); +#endif + + /* XXX: For GLSL we don't actually compile anything until the shader + * gets used so we have an opportunity to add some boilerplate to + * the shader. + * + * At the end of the day this is obviously a badly designed API + * given that we are having to lie to the user. It was a mistake to + * so thinly wrap the OpenGL shader API and the current plan is to + * replace it with a pipeline snippets API. */ +} + +void +_cogl_shader_compile_real (CoglHandle handle, + CoglPipeline *pipeline) +{ + CoglShader *shader = handle; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + +#ifdef HAVE_COGL_GL + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + { +#ifdef COGL_GL_DEBUG + GLenum gl_error; +#endif + + if (shader->gl_handle) + return; + + GE (ctx, glGenPrograms (1, &shader->gl_handle)); + + GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, shader->gl_handle)); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) + g_message ("user ARBfp program:\n%s", shader->source); + +#ifdef COGL_GL_DEBUG + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; +#endif + ctx->glProgramString (GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + strlen (shader->source), + shader->source); +#ifdef COGL_GL_DEBUG + gl_error = ctx->glGetError (); + if (gl_error != GL_NO_ERROR) + { + g_warning ("%s: GL error (%d): Failed to compile ARBfp:\n%s\n%s", + G_STRLOC, + gl_error, + shader->source, + ctx->glGetString (GL_PROGRAM_ERROR_STRING_ARB)); + } +#endif + } + else +#endif + { + GLenum gl_type; + GLint status; + + if (shader->gl_handle) + { + CoglPipeline *prev = shader->compilation_pipeline; + + /* XXX: currently the only things that will affect the + * boilerplate for user shaders, apart from driver features, + * are the pipeline layer-indices and texture-unit-indices + */ + if (pipeline == prev || + _cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline)) + return; + } + + if (shader->gl_handle) + delete_shader (shader); + + switch (shader->type) + { + case COGL_SHADER_TYPE_VERTEX: + gl_type = GL_VERTEX_SHADER; + break; + case COGL_SHADER_TYPE_FRAGMENT: + gl_type = GL_FRAGMENT_SHADER; + break; + default: + g_assert_not_reached (); + break; + } + + shader->gl_handle = ctx->glCreateShader (gl_type); + + _cogl_glsl_shader_set_source_with_boilerplate (ctx, + shader->gl_handle, + gl_type, + pipeline, + 1, + (const char **) + &shader->source, + NULL); + + GE (ctx, glCompileShader (shader->gl_handle)); + + shader->compilation_pipeline = cogl_object_ref (pipeline); + + GE (ctx, glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status)); + if (!status) + { + char buffer[512]; + int len = 0; + + ctx->glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer); + buffer[len] = '\0'; + + g_warning ("Failed to compile GLSL program:\n" + "src:\n%s\n" + "error:\n%s\n", + shader->source, + buffer); + } + } +} + +char * +cogl_shader_get_info_log (CoglHandle handle) +{ + if (!cogl_is_shader (handle)) + return NULL; + + /* XXX: This API doesn't really do anything! + * + * This API is purely for compatibility + * + * The reason we don't do anything is because a shader needs to + * be associated with a CoglPipeline for Cogl to be able to + * compile and link anything. + * + * The way this API was originally designed as a very thin wrapper + * over the GL api was a mistake and it's now very difficult to + * make the API work in a meaningful way given how the rest of Cogl + * has evolved. + * + * The CoglShader API is mostly deprecated by CoglSnippets and so + * these days we do the bare minimum to support the existing users + * of it until they are able to migrate to the snippets api. + */ + + return g_strdup (""); +} + +CoglShaderType +cogl_shader_get_type (CoglHandle handle) +{ + CoglShader *shader; + + _COGL_GET_CONTEXT (ctx, COGL_SHADER_TYPE_VERTEX); + + if (!cogl_is_shader (handle)) + { + g_warning ("Non shader handle type passed to cogl_shader_get_type"); + return COGL_SHADER_TYPE_VERTEX; + } + + shader = handle; + return shader->type; +} + +CoglBool +cogl_shader_is_compiled (CoglHandle handle) +{ +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES2) + if (!cogl_is_shader (handle)) + return FALSE; + + /* XXX: This API doesn't really do anything! + * + * This API is purely for compatibility and blatantly lies to the + * user about whether their shader has been compiled. + * + * I suppose we could say we're stretching the definition of + * "compile" and are deferring any related errors to be "linker" + * errors. + * + * The reason we don't do anything is because a shader needs to + * be associated with a CoglPipeline for Cogl to be able to + * compile and link anything. + * + * The CoglShader API is mostly deprecated by CoglSnippets and so + * these days we do the bare minimum to support the existing users + * of it until they are able to migrate to the snippets api. + */ + + return TRUE; + +#else + return FALSE; +#endif +} diff --git a/cogl/cogl/deprecated/cogl-shader.h b/cogl/cogl/deprecated/cogl-shader.h new file mode 100644 index 000000000..af225e878 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-shader.h @@ -0,0 +1,704 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_SHADER_H__ +#define __COGL_SHADER_H__ + +#include +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-shaders + * @short_description: Fuctions for accessing the programmable GL pipeline + * + * Cogl allows accessing the GL programmable pipeline in order to create + * vertex and fragment shaders. + * + * The shader source code can either be GLSL or ARBfp. If the source + * code is ARBfp, it must begin with the string “!!ARBfp1.0”. The + * application should check for the %COGL_FEATURE_SHADERS_GLSL or + * %COGL_FEATURE_SHADERS_ARBFP features before using shaders. + * + * When using GLSL Cogl provides replacement names for most of the + * builtin varyings and uniforms. It is recommended to use these names + * wherever possible to increase portability between OpenGL 2.0 and + * GLES 2.0. GLES 2.0 does not have most of the builtins under their + * original names so they will only work with the Cogl names. + * + * For use in all GLSL shaders, the Cogl builtins are as follows: + * + * + * + * + * uniform mat4 + * cogl_modelview_matrix + * + * The current modelview matrix. This is equivalent to + * #gl_ModelViewMatrix. + * + * + * + * uniform mat4 + * cogl_projection_matrix + * + * The current projection matrix. This is equivalent to + * #gl_ProjectionMatrix. + * + * + * + * uniform mat4 + * cogl_modelview_projection_matrix + * + * The combined modelview and projection matrix. A vertex shader + * would typically use this to transform the incoming vertex + * position. The separate modelview and projection matrices are + * usually only needed for lighting calculations. This is + * equivalent to #gl_ModelViewProjectionMatrix. + * + * + * + * uniform mat4 + * cogl_texture_matrix[] + * + * An array of matrices for transforming the texture + * coordinates. This is equivalent to #gl_TextureMatrix. + * + * + * + * + * + * In a vertex shader, the following are also available: + * + * + * + * + * attribute vec4 + * cogl_position_in + * + * The incoming vertex position. This is equivalent to #gl_Vertex. + * + * + * + * attribute vec4 + * cogl_color_in + * + * The incoming vertex color. This is equivalent to #gl_Color. + * + * + * + * attribute vec4 + * cogl_tex_coord_in + * + * The texture coordinate for the first texture unit. This is + * equivalent to #gl_MultiTexCoord0. + * + * + * + * attribute vec4 + * cogl_tex_coord0_in + * + * The texture coordinate for the first texture unit. This is + * equivalent to #gl_MultiTexCoord0. There is also + * #cogl_tex_coord1_in and so on. + * + * + * + * attribute vec3 + * cogl_normal_in + * + * The normal of the vertex. This is equivalent to #gl_Normal. + * + * + * + * vec4 + * cogl_position_out + * + * The calculated position of the vertex. This must be written to + * in all vertex shaders. This is equivalent to #gl_Position. + * + * + * + * float + * cogl_point_size_out + * + * The calculated size of a point. This is equivalent to #gl_PointSize. + * + * + * + * varying vec4 + * cogl_color_out + * + * The calculated color of a vertex. This is equivalent to #gl_FrontColor. + * + * + * + * varying vec4 + * cogl_tex_coord_out[] + * + * An array of calculated texture coordinates for a vertex. This is + * equivalent to #gl_TexCoord. + * + * + * + * + * + * In a fragment shader, the following are also available: + * + * + * + * + * varying vec4 cogl_color_in + * + * The calculated color of a vertex. This is equivalent to #gl_FrontColor. + * + * + * + * varying vec4 + * cogl_tex_coord_in[] + * + * An array of calculated texture coordinates for a vertex. This is + * equivalent to #gl_TexCoord. + * + * + * + * vec4 cogl_color_out + * + * The final calculated color of the fragment. All fragment shaders + * must write to this variable. This is equivalent to + * #gl_FrontColor. + * + * + * + * float cogl_depth_out + * + * An optional output variable specifying the depth value to use + * for this fragment. This is equivalent to #gl_FragDepth. + * + * + * + * bool cogl_front_facing + * + * A readonly variable that will be true if the current primitive + * is front facing. This can be used to implement two-sided + * coloring algorithms. This is equivalent to #gl_FrontFacing. + * + * + * + * + * + * It's worth nothing that this API isn't what Cogl would like to have + * in the long term and it may be removed in Cogl 2.0. The + * experimental #CoglShader API is the proposed replacement. + */ + +/** + * CoglShaderType: + * @COGL_SHADER_TYPE_VERTEX: A program for proccessing vertices + * @COGL_SHADER_TYPE_FRAGMENT: A program for processing fragments + * + * Types of shaders + * + * Since: 1.0 + */ +typedef enum { + COGL_SHADER_TYPE_VERTEX, + COGL_SHADER_TYPE_FRAGMENT +} CoglShaderType; + +/** + * cogl_create_shader: + * @shader_type: COGL_SHADER_TYPE_VERTEX or COGL_SHADER_TYPE_FRAGMENT. + * + * Create a new shader handle, use cogl_shader_source() to set the + * source code to be used on it. + * + * Returns: a new shader handle. + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglHandle +cogl_create_shader (CoglShaderType shader_type); + +/** + * cogl_shader_ref: + * @handle: A #CoglHandle to a shader. + * + * Add an extra reference to a shader. + * + * Returns: @handle + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglHandle +cogl_shader_ref (CoglHandle handle); + +/** + * cogl_shader_unref: + * @handle: A #CoglHandle to a shader. + * + * Removes a reference to a shader. If it was the last reference the + * shader object will be destroyed. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_shader_unref (CoglHandle handle); + +/** + * cogl_is_shader: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing shader object. + * + * Returns: %TRUE if the handle references a shader, + * %FALSE otherwise + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglBool +cogl_is_shader (CoglHandle handle); + +/** + * cogl_shader_source: + * @shader: #CoglHandle for a shader. + * @source: Shader source. + * + * Replaces the current source associated with a shader with a new + * one. + * + * Please see above + * for a description of the recommended format for the shader code. + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_shader_source (CoglHandle shader, + const char *source); + +/** + * cogl_shader_compile: + * @handle: #CoglHandle for a shader. + * + * Compiles the shader, no return value, but the shader is now ready + * for linking into a program. Note that calling this function is + * optional. If it is not called then the shader will be automatically + * compiled when it is linked. + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_shader_compile (CoglHandle handle); + +/** + * cogl_shader_get_info_log: + * @handle: #CoglHandle for a shader. + * + * Retrieves the information log for a coglobject, can be used in conjunction + * with cogl_shader_get_parameteriv() to retrieve the compiler warnings/error + * messages that caused a shader to not compile correctly, mainly useful for + * debugging purposes. + * + * Return value: a newly allocated string containing the info log. Use + * g_free() to free it + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +char * +cogl_shader_get_info_log (CoglHandle handle); + +/** + * cogl_shader_get_type: + * @handle: #CoglHandle for a shader. + * + * Retrieves the type of a shader #CoglHandle + * + * Return value: %COGL_SHADER_TYPE_VERTEX if the shader is a vertex processor + * or %COGL_SHADER_TYPE_FRAGMENT if the shader is a frament processor + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglShaderType +cogl_shader_get_type (CoglHandle handle); + +/** + * cogl_shader_is_compiled: + * @handle: #CoglHandle for a shader. + * + * Retrieves whether a shader #CoglHandle has been compiled + * + * Return value: %TRUE if the shader object has sucessfully be compiled + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglBool +cogl_shader_is_compiled (CoglHandle handle); + +/** + * cogl_create_program: + * + * Create a new cogl program object that can be used to replace parts of the GL + * rendering pipeline with custom code. + * + * Returns: a new cogl program. + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglHandle +cogl_create_program (void); + +/** + * cogl_program_ref: + * @handle: A #CoglHandle to a program. + * + * Add an extra reference to a program. + * + * Deprecated: 1.0: Please use cogl_object_ref() instead. + * + * Returns: @handle + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglHandle +cogl_program_ref (CoglHandle handle); + +/** + * cogl_program_unref: + * @handle: A #CoglHandle to a program. + * + * Removes a reference to a program. If it was the last reference the + * program object will be destroyed. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_unref (CoglHandle handle); + +/** + * cogl_is_program: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing program object. + * + * Returns: %TRUE if the handle references a program, + * %FALSE otherwise + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +CoglBool +cogl_is_program (CoglHandle handle); + +/** + * cogl_program_attach_shader: + * @program_handle: a #CoglHandle for a shdaer program. + * @shader_handle: a #CoglHandle for a vertex of fragment shader. + * + * Attaches a shader to a program object. A program can have multiple + * vertex or fragment shaders but only one of them may provide a + * main() function. It is allowed to use a program with only a vertex + * shader or only a fragment shader. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_attach_shader (CoglHandle program_handle, + CoglHandle shader_handle); + +/** + * cogl_program_link: + * @handle: a #CoglHandle for a shader program. + * + * Links a program making it ready for use. Note that calling this + * function is optional. If it is not called the program will + * automatically be linked the first time it is used. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_link (CoglHandle handle); + +/** + * cogl_program_use: + * @handle: a #CoglHandle for a shader program or %COGL_INVALID_HANDLE. + * + * Activate a specific shader program replacing that part of the GL + * rendering pipeline, if passed in %COGL_INVALID_HANDLE the default + * behavior of GL is reinstated. + * + * This function affects the global state of the current Cogl + * context. It is much more efficient to attach the shader to a + * specific material used for rendering instead by calling + * cogl_material_set_user_program(). + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_use (CoglHandle handle); + +/** + * cogl_program_get_uniform_location: + * @handle: a #CoglHandle for a shader program. + * @uniform_name: the name of a uniform. + * + * Retrieve the location (offset) of a uniform variable in a shader program, + * a uniform is a variable that is constant for all vertices/fragments for a + * shader object and is possible to modify as an external parameter. + * + * Return value: the offset of a uniform in a specified program. + * This uniform can be set using cogl_program_uniform_1f() when the + * program is in use. + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +int +cogl_program_get_uniform_location (CoglHandle handle, + const char *uniform_name); + +/** + * cogl_program_set_uniform_1f: + * @program: A #CoglHandle for a linked program + * @uniform_location: the uniform location retrieved from + * cogl_program_get_uniform_location(). + * @value: the new value of the uniform. + * + * Changes the value of a floating point uniform for the given linked + * @program. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_set_uniform_1f (CoglHandle program, + int uniform_location, + float value); + +/** + * cogl_program_set_uniform_1i: + * @program: A #CoglHandle for a linked program + * @uniform_location: the uniform location retrieved from + * cogl_program_get_uniform_location(). + * @value: the new value of the uniform. + * + * Changes the value of an integer uniform for the given linked + * @program. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_set_uniform_1i (CoglHandle program, + int uniform_location, + int value); + +/** + * cogl_program_set_uniform_float: + * @program: A #CoglHandle for a linked program + * @uniform_location: the uniform location retrieved from + * cogl_program_get_uniform_location(). + * @n_components: The number of components for the uniform. For + * example with glsl you'd use 3 for a vec3 or 4 for a vec4. + * @count: For uniform arrays this is the array length otherwise just + * pass 1 + * @value: (array length=count): the new value of the uniform[s]. + * + * Changes the value of a float vector uniform, or uniform array for + * the given linked @program. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_set_uniform_float (CoglHandle program, + int uniform_location, + int n_components, + int count, + const float *value); + +/** + * cogl_program_set_uniform_int: + * @program: A #CoglHandle for a linked program + * @uniform_location: the uniform location retrieved from + * cogl_program_get_uniform_location(). + * @n_components: The number of components for the uniform. For + * example with glsl you'd use 3 for a vec3 or 4 for a vec4. + * @count: For uniform arrays this is the array length otherwise just + * pass 1 + * @value: (array length=count): the new value of the uniform[s]. + * + * Changes the value of a int vector uniform, or uniform array for + * the given linked @program. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_set_uniform_int (CoglHandle program, + int uniform_location, + int n_components, + int count, + const int *value); + +/** + * cogl_program_set_uniform_matrix: + * @program: A #CoglHandle for a linked program + * @uniform_location: the uniform location retrieved from + * cogl_program_get_uniform_location(). + * @dimensions: The dimensions of the matrix. So for for example pass + * 2 for a 2x2 matrix or 3 for 3x3. + * @count: For uniform arrays this is the array length otherwise just + * pass 1 + * @transpose: Whether to transpose the matrix when setting the uniform. + * @value: (array length=count): the new value of the uniform. + * + * Changes the value of a matrix uniform, or uniform array in the + * given linked @program. + * + * Since: 1.4 + * Deprecated: 1.16: Use #CoglSnippet api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_set_uniform_matrix (CoglHandle program, + int uniform_location, + int dimensions, + int count, + CoglBool transpose, + const float *value); + +/** + * cogl_program_uniform_1f: + * @uniform_no: the uniform to set. + * @value: the new value of the uniform. + * + * Changes the value of a floating point uniform in the currently + * used (see cogl_program_use()) shader program. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_uniform_1f (int uniform_no, + float value); + +/** + * cogl_program_uniform_1i: + * @uniform_no: the uniform to set. + * @value: the new value of the uniform. + * + * Changes the value of an integer uniform in the currently + * used (see cogl_program_use()) shader program. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_uniform_1i (int uniform_no, + int value); + +/** + * cogl_program_uniform_float: + * @uniform_no: the uniform to set. + * @size: Size of float vector. + * @count: Size of array of uniforms. + * @value: (array length=count): the new value of the uniform. + * + * Changes the value of a float vector uniform, or uniform array in the + * currently used (see cogl_program_use()) shader program. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_uniform_float (int uniform_no, + int size, + int count, + const float *value); + +/** + * cogl_program_uniform_int: + * @uniform_no: the uniform to set. + * @size: Size of int vector. + * @count: Size of array of uniforms. + * @value: (array length=count): the new value of the uniform. + * + * Changes the value of a int vector uniform, or uniform array in the + * currently used (see cogl_program_use()) shader program. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_uniform_int (int uniform_no, + int size, + int count, + const int *value); + +/** + * cogl_program_uniform_matrix: + * @uniform_no: the uniform to set. + * @size: Size of matrix. + * @count: Size of array of uniforms. + * @transpose: Whether to transpose the matrix when setting the uniform. + * @value: (array length=count): the new value of the uniform. + * + * Changes the value of a matrix uniform, or uniform array in the + * currently used (see cogl_program_use()) shader program. The @size + * parameter is used to determine the square size of the matrix. + * + * Deprecated: 1.16: Use #CoglSnippet api + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_snippet_) +void +cogl_program_uniform_matrix (int uniform_no, + int size, + int count, + CoglBool transpose, + const float *value); + +COGL_END_DECLS + +#endif /* __COGL_SHADER_H__ */ diff --git a/cogl/cogl/deprecated/cogl-texture-deprecated.c b/cogl/cogl/deprecated/cogl-texture-deprecated.c new file mode 100644 index 000000000..d18a01377 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-texture-deprecated.c @@ -0,0 +1,85 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#include + +#include "cogl-types.h" +#include "cogl-texture.h" +#include "cogl-texture-private.h" +#include "cogl-object-private.h" +#include "deprecated/cogl-texture-deprecated.h" + +CoglPixelFormat +cogl_texture_get_format (CoglTexture *texture) +{ + return _cogl_texture_get_format (texture); +} + +unsigned int +cogl_texture_get_rowstride (CoglTexture *texture) +{ + CoglPixelFormat format = cogl_texture_get_format (texture); + /* FIXME: This function should go away. It previously just returned + the rowstride that was used to upload the data as far as I can + tell. This is not helpful */ + + /* Just guess at a suitable rowstride */ + return (_cogl_pixel_format_get_bytes_per_pixel (format) + * cogl_texture_get_width (texture)); +} + +void * +cogl_texture_ref (void *object) +{ + if (!cogl_is_texture (object)) + return NULL; + + _COGL_OBJECT_DEBUG_REF (CoglTexture, object); + + cogl_object_ref (object); + + return object; +} + +void +cogl_texture_unref (void *object) +{ + if (!cogl_is_texture (object)) + { + g_warning (G_STRINGIFY (cogl_texture_unref) + ": Ignoring unref of CoglObject " + "due to type mismatch"); + return; + } + + _COGL_OBJECT_DEBUG_UNREF (CoglTexture, object); + + cogl_object_unref (object); +} diff --git a/cogl/cogl/deprecated/cogl-texture-deprecated.h b/cogl/cogl/deprecated/cogl-texture-deprecated.h new file mode 100644 index 000000000..4ab824df4 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-texture-deprecated.h @@ -0,0 +1,105 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_DEPRECATED_H__ +#define __COGL_TEXTURE_DEPRECATED_H__ + +/** + * cogl_texture_get_format: + * @texture: a #CoglTexture pointer. + * + * Queries the #CoglPixelFormat of a cogl texture. + * + * Return value: the #CoglPixelFormat of the GPU side texture + * Deprecated: 1.18: This api is misleading + */ +COGL_DEPRECATED_IN_1_18 +CoglPixelFormat +cogl_texture_get_format (CoglTexture *texture); + +/** + * cogl_texture_get_rowstride: + * @texture a #CoglTexture pointer. + * + * Determines the bytes-per-pixel for the #CoglPixelFormat retrieved + * from cogl_texture_get_format() and multiplies that by the texture's + * width. + * + * It's very unlikely that anyone would need to use this API to + * query the internal rowstride of a #CoglTexture which can just be + * considered an implementation detail. Actually it's not even useful + * internally since underlying drivers are free to use a different + * format + * + * This API is only here for backwards compatibility and + * shouldn't be used in new code. In particular please don't be + * mislead to pass the returned value to cogl_texture_get_data() for + * the rowstride, since you should be passing the rowstride you desire + * for your destination buffer not the rowstride of the source + * texture. + * + * Return value: The bytes-per-pixel for the current format + * multiplied by the texture's width + * + * Deprecated: 1.10: There's no replacement for the API but there's + * also no known need for API either. It was just + * a mistake that it was ever published. + */ +COGL_DEPRECATED_IN_1_10 +unsigned int +cogl_texture_get_rowstride (CoglTexture *texture); + +/** + * cogl_texture_ref: (skip) + * @texture: a #CoglTexture. + * + * Increment the reference count for a cogl texture. + * + * Deprecated: 1.2: Use cogl_object_ref() instead + * + * Return value: the @texture pointer. + */ +COGL_DEPRECATED_FOR (cogl_object_ref) +void * +cogl_texture_ref (void *texture); + +/** + * cogl_texture_unref: (skip) + * @texture: a #CoglTexture. + * + * Decrement the reference count for a cogl texture. + * + * Deprecated: 1.2: Use cogl_object_unref() instead + */ +COGL_DEPRECATED_FOR (cogl_object_unref) +void +cogl_texture_unref (void *texture); + +#endif /* __COGL_TEXTURE_DEPRECATED_H__ */ diff --git a/cogl/cogl/deprecated/cogl-type-casts.h b/cogl/cogl/deprecated/cogl-type-casts.h new file mode 100644 index 000000000..ae716e7dd --- /dev/null +++ b/cogl/cogl/deprecated/cogl-type-casts.h @@ -0,0 +1,53 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_TYPE_CASTS_H__ +#define __COGL_TYPE_CASTS_H__ + +/* The various interface types in Cogl used to be more strongly typed + * which required lots type casting by developers. We provided + * macros for performing these casts following a widely used Gnome + * coding style. Since we now consistently typedef these interfaces + * as void for the public C api and use runtime type checking to + * catch programming errors the casts have become redundant and + * so these macros are only kept for compatibility... + */ + +#define COGL_FRAMEBUFFER(X) (X) +#define COGL_BUFFER(X) (X) +#define COGL_TEXTURE(X) (X) +#define COGL_META_TEXTURE(X) (X) +#define COGL_PRIMITIVE_TEXTURE(X) (X) + +#endif /* __COGL_TYPE_CASTS_H__ */ diff --git a/cogl/cogl/deprecated/cogl-vertex-buffer-private.h b/cogl/cogl/deprecated/cogl-vertex-buffer-private.h new file mode 100644 index 000000000..f803bbba6 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-vertex-buffer-private.h @@ -0,0 +1,165 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_VERTEX_BUFFER_H +#define __COGL_VERTEX_BUFFER_H + +#include "cogl-object-private.h" + +#include "cogl-primitive.h" + +#include + +/* Note we put quite a bit into the flags here to help keep + * the down size of the CoglVertexBufferAttrib struct below. */ +typedef enum _CoglVertexBufferAttribFlags +{ + /* Types */ + /* NB: update the _TYPE_MASK below if these are changed */ + COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY = 1<<0, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY = 1<<1, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY = 1<<2, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY = 1<<3, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY = 1<<4, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID = 1<<5, + + COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMALIZED = 1<<6, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED = 1<<7, + + /* Usage hints */ + /* FIXME - flatten into one flag, since its used as a boolean */ + COGL_VERTEX_BUFFER_ATTRIB_FLAG_INFREQUENT_RESUBMIT = 1<<8, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT = 1<<9, + + /* GL Data types */ + /* NB: Update the _GL_TYPE_MASK below if these are changed */ + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_BYTE = 1<<10, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_BYTE = 1<<11, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_SHORT = 1<<12, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_SHORT = 1<<13, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_INT = 1<<14, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_INT = 1<<15, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_FLOAT = 1<<16, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_DOUBLE = 1<<17, + + COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED = 1<<18, + COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED = 1<<19 + + /* XXX NB: If we need > 24 bits then look at changing the layout + * of struct _CoglVertexBufferAttrib below */ +} CoglVertexBufferAttribFlags; + +#define COGL_VERTEX_BUFFER_ATTRIB_FLAG_TYPE_MASK \ + (COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY \ + | COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY \ + | COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY \ + | COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY \ + | COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY \ + | COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID) + +typedef struct _CoglVertexBufferAttrib +{ + /* TODO: look at breaking up the flags into seperate + * bitfields and seperate enums */ + CoglVertexBufferAttribFlags flags:24; + uint8_t id; + GQuark name; + char *name_without_detail; + union _u + { + const void *pointer; + size_t vbo_offset; + } u; + CoglAttributeType type; + size_t span_bytes; + uint16_t stride; + uint8_t n_components; + uint8_t texture_unit; + + int attribute_first; + CoglAttribute *attribute; + +} CoglVertexBufferAttrib; + +typedef enum _CoglVertexBufferVBOFlags +{ + COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED = 1<<0, + COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK = 1<<1, + + /* FIXME - flatten into one flag, since its used as a boolean */ + COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT = 1<<3, + COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT = 1<<4, + + COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED = 1<<5 +} CoglVertexBufferVBOFlags; + +/* + * A CoglVertexBufferVBO represents one or more attributes in a single + * buffer object + */ +typedef struct _CoglVertexBufferVBO +{ + CoglVertexBufferVBOFlags flags; + + CoglAttributeBuffer *attribute_buffer; + size_t buffer_bytes; + + GList *attributes; +} CoglVertexBufferVBO; + +typedef struct _CoglVertexBufferIndices +{ + CoglHandleObject _parent; + + CoglIndices *indices; +} CoglVertexBufferIndices; + +typedef struct _CoglVertexBuffer +{ + CoglHandleObject _parent; + + int n_vertices; /*!< The number of vertices in the buffer */ + GList *submitted_vbos; /* The VBOs currently submitted to the GPU */ + + /* Note: new_attributes is normally NULL and only valid while + * modifying a buffer. */ + GList *new_attributes; /*!< attributes pending submission */ + + CoglBool dirty_attributes; + + CoglPrimitive *primitive; + +} CoglVertexBuffer; + +#endif /* __COGL_VERTEX_BUFFER_H */ + diff --git a/cogl/cogl/deprecated/cogl-vertex-buffer.c b/cogl/cogl/deprecated/cogl-vertex-buffer.c new file mode 100644 index 000000000..b51db13bb --- /dev/null +++ b/cogl/cogl/deprecated/cogl-vertex-buffer.c @@ -0,0 +1,1795 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +/* XXX: For an overview of the functionality implemented here, please + * see cogl-vertex-buffer.h, which contains the gtk-doc section overview + * for the Vertex Buffers API. + */ + +/* + * TODO: We need to do a better job of minimizing when we call glVertexPointer + * and pals in enable_state_for_drawing_buffer + * + * We should have an internal 2-tuple cache of (VBO, offset) for each of them + * so we can avoid some GL calls. We could have cogl wrappers for the + * gl*Pointer funcs that look like this: + * + * cogl_vertex_pointer (n_components, gl_type, stride, vbo, offset); + * cogl_color_pointer (n_components, gl_type, stride, vbo, offset); + * + * They would also accept NULL for the VBO handle to support old style vertex + * arrays. + * + * TODO: + * Actually hook this up to the cogl shaders infrastructure. The vertex + * buffer API has been designed to allow adding of arbitrary attributes for use + * with shaders, but this has yet to be actually plumbed together and tested. + * The bits we are missing: + * - cogl_program_use doesn't currently record within ctx-> which program + * is currently in use so a.t.m only Clutter knows the current shader. + * - We don't query the current shader program for the generic vertex indices + * (using glGetAttribLocation) so that we can call glEnableVertexAttribArray + * with those indices. + * (currently we just make up consecutive indices) + * - some dirty flag mechanims to know when the shader program has changed + * so we don't need to re-query it each time we draw a buffer. + * + * TODO + * Expose API that lets developers get back a buffer handle for a particular + * polygon so they may add custom attributes to them. + * - It should be possible to query/modify attributes efficiently, in place, + * avoiding copies. It would not be acceptable to simply require that + * developers must query back the n_vertices of a buffer and then the + * n_components, type and stride etc of each attribute since there + * would be too many combinations to realistically handle. + * + * - In practice, some cases might be best solved with a higher level + * EditableMesh API, (see futher below) but for many cases I think an + * API like this might be appropriate: + * + * cogl_vertex_buffer_foreach_vertex (buffer_handle, + * (AttributesBufferIteratorFunc)callback, + * "gl_Vertex", "gl_Color", NULL); + * static void callback (CoglVertexBufferVertex *vert) + * { + * GLfloat *pos = vert->attrib[0]; + * GLubyte *color = vert->attrib[1]; + * GLfloat *new_attrib = buf[vert->index]; + * + * new_attrib = pos*color; + * } + * + * TODO + * Think about a higher level Mesh API for building/modifying attribute buffers + * - E.g. look at Blender for inspiration here. They can build a mesh from + * "MVert", "MFace" and "MEdge" primitives. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-vertex-buffer-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline.h" +#include "cogl-pipeline-private.h" +#include "cogl-primitives.h" +#include "cogl-framebuffer-private.h" +#include "cogl-primitive-private.h" +#include "cogl-journal-private.h" +#include "cogl1-context.h" +#include "cogl-vertex-buffer.h" + +#define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \ + (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1))) + +static void _cogl_vertex_buffer_free (CoglVertexBuffer *buffer); +static void _cogl_vertex_buffer_indices_free (CoglVertexBufferIndices *buffer_indices); +static CoglUserDataKey _cogl_vertex_buffer_pipeline_priv_key; + +COGL_HANDLE_DEFINE (VertexBuffer, vertex_buffer); +COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (vertex_buffer); +COGL_HANDLE_DEFINE (VertexBufferIndices, vertex_buffer_indices); + +CoglHandle +cogl_vertex_buffer_new (unsigned int n_vertices) +{ + CoglVertexBuffer *buffer = g_slice_alloc (sizeof (CoglVertexBuffer)); + + buffer->n_vertices = n_vertices; + + buffer->submitted_vbos = NULL; + buffer->new_attributes = NULL; + buffer->primitive = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, + n_vertices, NULL); + + /* return COGL_INVALID_HANDLE; */ + return _cogl_vertex_buffer_handle_new (buffer); +} + +unsigned int +cogl_vertex_buffer_get_n_vertices (CoglHandle handle) +{ + CoglVertexBuffer *buffer; + + if (!cogl_is_vertex_buffer (handle)) + return 0; + + buffer = handle; + + return buffer->n_vertices; +} + +/* There are a number of standard OpenGL attributes that we deal with + * specially. These attributes are all namespaced with a "gl_" prefix + * so we should catch any typos instead of silently adding a custom + * attribute. + */ +static CoglVertexBufferAttribFlags +validate_gl_attribute (const char *gl_attribute, + uint8_t n_components, + uint8_t *texture_unit) +{ + CoglVertexBufferAttribFlags type; + char *detail_seperator = NULL; + int name_len; + + detail_seperator = strstr (gl_attribute, "::"); + if (detail_seperator) + name_len = detail_seperator - gl_attribute; + else + name_len = strlen (gl_attribute); + + if (strncmp (gl_attribute, "Vertex", name_len) == 0) + { + if (G_UNLIKELY (n_components == 1)) + g_critical ("glVertexPointer doesn't allow 1 component vertex " + "positions so we currently only support \"gl_Vertex\" " + "attributes where n_components == 2, 3 or 4"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY; + } + else if (strncmp (gl_attribute, "Color", name_len) == 0) + { + if (G_UNLIKELY (n_components != 3 && n_components != 4)) + g_critical ("glColorPointer expects 3 or 4 component colors so we " + "currently only support \"gl_Color\" attributes where " + "n_components == 3 or 4"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY; + } + else if (strncmp (gl_attribute, + "MultiTexCoord", + strlen ("MultiTexCoord")) == 0) + { + unsigned int unit; + + if (sscanf (gl_attribute, "MultiTexCoord%u", &unit) != 1) + { + g_warning ("gl_MultiTexCoord attributes should include a\n" + "texture unit number, E.g. gl_MultiTexCoord0\n"); + unit = 0; + } + /* FIXME: validate any '::' delimiter for this case */ + *texture_unit = unit; + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY; + } + else if (strncmp (gl_attribute, "Normal", name_len) == 0) + { + if (G_UNLIKELY (n_components != 3)) + g_critical ("glNormalPointer expects 3 component normals so we " + "currently only support \"gl_Normal\" attributes where " + "n_components == 3"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY; + } + else + { + g_warning ("Unknown gl_* attribute name gl_%s\n", gl_attribute); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID; + } + + return type; +} + +/* There are a number of standard OpenGL attributes that we deal with + * specially. These attributes are all namespaced with a "gl_" prefix + * so we should catch any typos instead of silently adding a custom + * attribute. + */ +static CoglVertexBufferAttribFlags +validate_cogl_attribute (const char *cogl_attribute, + uint8_t n_components, + uint8_t *texture_unit) +{ + CoglVertexBufferAttribFlags type; + char *detail_seperator = NULL; + int name_len; + + detail_seperator = strstr (cogl_attribute, "::"); + if (detail_seperator) + name_len = detail_seperator - cogl_attribute; + else + name_len = strlen (cogl_attribute); + + if (strncmp (cogl_attribute, "position_in", name_len) == 0) + { + if (G_UNLIKELY (n_components == 1)) + g_critical ("glVertexPointer doesn't allow 1 component vertex " + "positions so we currently only support " + "\"cogl_position_in\" attributes where " + "n_components == 2, 3 or 4"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY; + } + else if (strncmp (cogl_attribute, "color_in", name_len) == 0) + { + if (G_UNLIKELY (n_components != 3 && n_components != 4)) + g_critical ("glColorPointer expects 3 or 4 component colors so we " + "currently only support \"cogl_color_in\" attributes " + "where n_components == 3 or 4"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY; + } + else if (strncmp (cogl_attribute, + "cogl_tex_coord", + strlen ("cogl_tex_coord")) == 0) + { + unsigned int unit; + + if (strcmp (cogl_attribute, "cogl_tex_coord_in") == 0) + unit = 0; + else if (sscanf (cogl_attribute, "cogl_tex_coord%u_in", &unit) != 1) + { + g_warning ("texture coordinate attributes should either be " + "referenced as \"cogl_tex_coord_in\" or with a" + "texture unit number like \"cogl_tex_coord1_in\""); + unit = 0; + } + /* FIXME: validate any '::' delimiter for this case */ + *texture_unit = unit; + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY; + } + else if (strncmp (cogl_attribute, "normal_in", name_len) == 0) + { + if (G_UNLIKELY (n_components != 3)) + g_critical ("glNormalPointer expects 3 component normals so we " + "currently only support \"cogl_normal_in\" attributes " + "where n_components == 3"); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY; + } + else + { + g_warning ("Unknown cogl_* attribute name cogl_%s\n", cogl_attribute); + type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID; + } + + return type; +} + +/* This validates that a custom attribute name is a valid GLSL variable name + * + * NB: attribute names may have a detail component delimited using '::' E.g. + * custom_attrib::foo or custom_attrib::bar + * + * maybe I should hang a compiled regex somewhere to handle this + */ +static CoglBool +validate_custom_attribute_name (const char *attribute_name) +{ + char *detail_seperator = NULL; + int name_len; + int i; + + detail_seperator = strstr (attribute_name, "::"); + if (detail_seperator) + name_len = detail_seperator - attribute_name; + else + name_len = strlen (attribute_name); + + if (name_len == 0 + || !g_ascii_isalpha (attribute_name[0]) + || attribute_name[0] != '_') + return FALSE; + + for (i = 1; i < name_len; i++) + if (!g_ascii_isalnum (attribute_name[i]) || attribute_name[i] != '_') + return FALSE; + + return TRUE; +} + +/* Iterates the CoglVertexBufferVBOs of a buffer and creates a flat list + * of all the submitted attributes + * + * Note: The CoglVertexBufferAttrib structs are deep copied, except the + * internal CoglAttribute pointer is set to NULL. + */ +static GList * +copy_submitted_attributes_list (CoglVertexBuffer *buffer) +{ + GList *tmp; + GList *submitted_attributes = NULL; + + for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *attribute = tmp2->data; + CoglVertexBufferAttrib *copy = + g_slice_alloc (sizeof (CoglVertexBufferAttrib)); + *copy = *attribute; + copy->name_without_detail = + g_strdup (attribute->name_without_detail); + copy->attribute = NULL; + submitted_attributes = g_list_prepend (submitted_attributes, copy); + } + } + return submitted_attributes; +} + +static size_t +sizeof_attribute_type (CoglAttributeType type) +{ + switch (type) + { + case COGL_ATTRIBUTE_TYPE_BYTE: + return 1; + case COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE: + return 1; + case COGL_ATTRIBUTE_TYPE_SHORT: + return 2; + case COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT: + return 2; + case COGL_ATTRIBUTE_TYPE_FLOAT: + return 4; + } + g_return_val_if_reached (0); +} + +static size_t +strideof (CoglAttributeType type, int n_components) +{ + return sizeof_attribute_type (type) * n_components; +} + +static char * +canonize_attribute_name (const char *attribute_name) +{ + char *detail_seperator = NULL; + int name_len; + + if (strncmp (attribute_name, "gl_", 3) != 0) + return g_strdup (attribute_name); + + /* skip past the "gl_" */ + attribute_name += 3; + + detail_seperator = strstr (attribute_name, "::"); + if (detail_seperator) + name_len = detail_seperator - attribute_name; + else + { + name_len = strlen (attribute_name); + detail_seperator = ""; + } + + if (strncmp (attribute_name, "Vertex", name_len) == 0) + return g_strconcat ("cogl_position_in", detail_seperator, NULL); + else if (strncmp (attribute_name, "Color", name_len) == 0) + return g_strconcat ("cogl_color_in", detail_seperator, NULL); + else if (strncmp (attribute_name, + "MultiTexCoord", + strlen ("MultiTexCoord")) == 0) + { + unsigned int unit; + + if (sscanf (attribute_name, "MultiTexCoord%u", &unit) != 1) + { + g_warning ("gl_MultiTexCoord attributes should include a\n" + "texture unit number, E.g. gl_MultiTexCoord0\n"); + unit = 0; + } + return g_strdup_printf ("cogl_tex_coord%u_in%s", + unit, detail_seperator); + } + else if (strncmp (attribute_name, "Normal", name_len) == 0) + return g_strconcat ("cogl_normal_in", detail_seperator, NULL); + else + { + g_warning ("Unknown gl_* attribute name gl_%s\n", attribute_name); + return g_strdup (attribute_name); + } +} + +void +cogl_vertex_buffer_add (CoglHandle handle, + const char *attribute_name, + uint8_t n_components, + CoglAttributeType type, + CoglBool normalized, + uint16_t stride, + const void *pointer) +{ + CoglVertexBuffer *buffer; + char *cogl_attribute_name; + GQuark name_quark; + CoglBool modifying_an_attrib = FALSE; + CoglVertexBufferAttrib *attribute; + CoglVertexBufferAttribFlags flags = 0; + uint8_t texture_unit = 0; + GList *tmp; + char *detail; + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + buffer->dirty_attributes = TRUE; + + cogl_attribute_name = canonize_attribute_name (attribute_name); + name_quark = g_quark_from_string (cogl_attribute_name); + + /* The submit function works by diffing between submitted_attributes + * and new_attributes to minimize the upload bandwidth + cost of + * allocating new VBOs, so if there isn't already a list of new_attributes + * we create one: */ + if (!buffer->new_attributes) + buffer->new_attributes = copy_submitted_attributes_list (buffer); + + /* Note: we first look for an existing attribute that we are modifying + * so we may skip needing to validate the name */ + for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *submitted_attribute = tmp->data; + if (submitted_attribute->name == name_quark) + { + modifying_an_attrib = TRUE; + + attribute = submitted_attribute; + + /* since we will skip validate_gl/cogl_attribute in this case, we + * need to pluck out the attribute type before overwriting the + * flags: */ + flags |= + attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_TYPE_MASK; + break; + } + } + + if (!modifying_an_attrib) + { + /* Validate the attribute name, is suitable as a variable name */ + if (strncmp (attribute_name, "gl_", 3) == 0) + { + /* Note: we pass the original attribute name here so that + * any warning messages correspond to the users original + * attribute name... */ + flags |= validate_gl_attribute (attribute_name + 3, + n_components, + &texture_unit); + if (flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID) + return; + } + else if (strncmp (attribute_name, "cogl_", 5) == 0) + { + flags |= validate_cogl_attribute (attribute_name + 5, + n_components, + &texture_unit); + if (flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID) + return; + } + else + { + flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY; + if (validate_custom_attribute_name (attribute_name)) + return; + } + + attribute = g_slice_alloc0 (sizeof (CoglVertexBufferAttrib)); + } + + attribute->name = name_quark; + detail = strstr (cogl_attribute_name, "::"); + if (detail) + attribute->name_without_detail = g_strndup (cogl_attribute_name, + detail - cogl_attribute_name); + else + attribute->name_without_detail = g_strdup (cogl_attribute_name); + attribute->type = type; + attribute->n_components = n_components; + if (stride == 0) + stride = strideof (type, n_components); + attribute->stride = stride; + attribute->u.pointer = pointer; + attribute->texture_unit = texture_unit; + attribute->attribute = NULL; + + flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED; + + /* Note: We currently just assume, if an attribute is *ever* updated + * then it should be taged as frequently changing. */ + if (modifying_an_attrib) + flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT; + else + flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_INFREQUENT_RESUBMIT; + + if (normalized) + flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMALIZED; + attribute->flags = flags; + + attribute->span_bytes = buffer->n_vertices * attribute->stride; + + if (!modifying_an_attrib) + buffer->new_attributes = + g_list_prepend (buffer->new_attributes, attribute); + + g_free (cogl_attribute_name); +} + +static void +_cogl_vertex_buffer_attrib_free (CoglVertexBufferAttrib *attribute) +{ + if (attribute->attribute) + cogl_object_unref (attribute->attribute); + g_free (attribute->name_without_detail); + g_slice_free (CoglVertexBufferAttrib, attribute); +} + +void +cogl_vertex_buffer_delete (CoglHandle handle, + const char *attribute_name) +{ + CoglVertexBuffer *buffer; + char *cogl_attribute_name = canonize_attribute_name (attribute_name); + GQuark name = g_quark_from_string (cogl_attribute_name); + GList *tmp; + + g_free (cogl_attribute_name); + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + buffer->dirty_attributes = TRUE; + + /* The submit function works by diffing between submitted_attributes + * and new_attributes to minimize the upload bandwidth + cost of + * allocating new VBOs, so if there isn't already a list of new_attributes + * we create one: */ + if (!buffer->new_attributes) + buffer->new_attributes = copy_submitted_attributes_list (buffer); + + for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *submitted_attribute = tmp->data; + if (submitted_attribute->name == name) + { + buffer->new_attributes = + g_list_delete_link (buffer->new_attributes, tmp); + _cogl_vertex_buffer_attrib_free (submitted_attribute); + return; + } + } + + g_warning ("Failed to find an attribute named %s to delete\n", + attribute_name); +} + +static void +set_attribute_enable (CoglHandle handle, + const char *attribute_name, + CoglBool state) +{ + CoglVertexBuffer *buffer; + char *cogl_attribute_name = canonize_attribute_name (attribute_name); + GQuark name_quark = g_quark_from_string (cogl_attribute_name); + GList *tmp; + + g_free (cogl_attribute_name); + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + buffer->dirty_attributes = TRUE; + + /* NB: If a buffer is currently being edited, then there can be two seperate + * lists of attributes; those that are currently submitted and a new list yet + * to be submitted, we need to modify both. */ + + for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + if (attribute->name == name_quark) + { + if (state) + attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED; + else + attribute->flags &= ~COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED; + break; + } + } + + for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *attribute = tmp2->data; + if (attribute->name == name_quark) + { + if (state) + attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED; + else + attribute->flags &= ~COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED; + return; + } + } + } + + g_warning ("Failed to %s attribute named %s/%s\n", + state == TRUE ? "enable" : "disable", + attribute_name, cogl_attribute_name); +} + +void +cogl_vertex_buffer_enable (CoglHandle handle, + const char *attribute_name) +{ + set_attribute_enable (handle, attribute_name, TRUE); +} + +void +cogl_vertex_buffer_disable (CoglHandle handle, + const char *attribute_name) +{ + set_attribute_enable (handle, attribute_name, FALSE); +} + +/* Given an attribute that we know has already been submitted before, this + * function looks for the existing VBO that contains it. + * + * Note: It will free redundant attribute struct once the corresponding + * VBO has been found. + */ +static void +filter_already_submitted_attribute (CoglVertexBufferAttrib *attribute, + GList **reuse_vbos, + GList **submitted_vbos) +{ + GList *tmp; + + /* First check the cogl_vbos we already know are being reused since we + * are more likley to get a match here */ + for (tmp = *reuse_vbos; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *vbo_attribute = tmp2->data; + + if (vbo_attribute->name == attribute->name) + { + vbo_attribute->flags &= + ~COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED; + /* Note: we don't free the redundant attribute here, since it + * will be freed after all filtering in + * cogl_vertex_buffer_submit */ + return; + } + } + } + + for (tmp = *submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferVBO *cogl_vbo = tmp->data; + CoglVertexBufferAttrib *reuse_attribute = NULL; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *vbo_attribute = tmp2->data; + if (vbo_attribute->name == attribute->name) + { + reuse_attribute = vbo_attribute; + /* Note: we don't free the redundant attribute here, since it + * will be freed after all filtering in + * cogl_vertex_buffer_submit */ + + *submitted_vbos = g_list_remove_link (*submitted_vbos, tmp); + tmp->next = *reuse_vbos; + *reuse_vbos = tmp; + break; + } + } + + if (!reuse_attribute) + continue; + + /* Mark all but the matched attribute as UNUSED, so that when we + * finish filtering all our attributes any attrributes still + * marked as UNUSED can be removed from their cogl_vbo */ + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *vbo_attribute = tmp2->data; + if (vbo_attribute != reuse_attribute) + vbo_attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED; + } + + return; + } + + g_critical ("Failed to find the cogl vbo that corresponds to an\n" + "attribute that had apparently already been submitted!"); +} + +/* When we first mark a CoglVertexBufferVBO to be reused, we mark the + * attributes as unsed, so that when filtering of attributes into VBOs is done + * we can then prune the now unsed attributes. */ +static void +remove_unused_attributes (CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + GList *next; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + next = tmp->next; + + if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED) + { + cogl_vbo->attributes = + g_list_delete_link (cogl_vbo->attributes, tmp); + g_slice_free (CoglVertexBufferAttrib, attribute); + } + } +} + +/* Give a newly added, strided, attribute, this function looks for a + * CoglVertexBufferVBO that the attribute is interleved with. If it can't + * find one then a new CoglVertexBufferVBO is allocated and added to the + * list of new_strided_vbos. + */ +static void +filter_strided_attribute (CoglVertexBufferAttrib *attribute, + GList **new_vbos) +{ + GList *tmp; + CoglVertexBufferVBO *new_cogl_vbo; + + for (tmp = *new_vbos; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferVBO *cogl_vbo = tmp->data; + GList *tmp2; + + if (!(cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED)) + continue; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *vbo_attribute = tmp2->data; + const char *attribute_start = attribute->u.pointer; + const char *vbo_attribute_start = vbo_attribute->u.pointer; + + /* NB: All attributes have buffer->n_vertices values which + * simplifies determining which attributes are interleved + * since we assume they will start no farther than +- a + * stride away from each other: + */ + if (attribute_start <= (vbo_attribute_start - vbo_attribute->stride) + || attribute_start + >= (vbo_attribute_start + vbo_attribute->stride)) + continue; /* Not interleved */ + + cogl_vbo->attributes = + g_list_prepend (cogl_vbo->attributes, attribute); + + if (attribute->flags & + COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT) + { + cogl_vbo->flags &= + ~COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT; + cogl_vbo->flags |= + COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT; + } + return; + } + } + new_cogl_vbo = g_slice_alloc (sizeof (CoglVertexBufferVBO)); + new_cogl_vbo->attributes = NULL; + new_cogl_vbo->attributes = + g_list_prepend (new_cogl_vbo->attributes, attribute); + /* Any one of the interleved attributes will have the same span_bytes */ + new_cogl_vbo->attribute_buffer = NULL; + new_cogl_vbo->buffer_bytes = attribute->span_bytes; + new_cogl_vbo->flags = COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED; + + if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_INFREQUENT_RESUBMIT) + new_cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT; + else + new_cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT; + + *new_vbos = g_list_prepend (*new_vbos, new_cogl_vbo); + return; +} + +/* This iterates through the list of submitted VBOs looking for one that + * contains attribute. If found the list *link* is removed and returned */ +static GList * +unlink_submitted_vbo_containing_attribute (GList **submitted_vbos, + CoglVertexBufferAttrib *attribute) +{ + GList *tmp; + GList *next = NULL; + + for (tmp = *submitted_vbos; tmp != NULL; tmp = next) + { + CoglVertexBufferVBO *submitted_vbo = tmp->data; + GList *tmp2; + + next = tmp->next; + + for (tmp2 = submitted_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *submitted_attribute = tmp2->data; + + if (submitted_attribute->name == attribute->name) + { + *submitted_vbos = g_list_remove_link (*submitted_vbos, tmp); + return tmp; + } + } + } + + return NULL; +} + +/* Unlinks all the submitted VBOs that conflict with the new cogl_vbo and + * returns them as a list. */ +static GList * +get_submitted_vbo_conflicts (GList **submitted_vbos, + CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + GList *conflicts = NULL; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + GList *link = + unlink_submitted_vbo_containing_attribute (submitted_vbos, + tmp->data); + if (link) + { + /* prepend the link to the list of conflicts: */ + link->next = conflicts; + conflicts = link; + } + } + return conflicts; +} + +/* Any attributes in cogl_vbo gets removed from conflict_vbo */ +static void +disassociate_conflicting_attributes (CoglVertexBufferVBO *conflict_vbo, + CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + + /* NB: The attributes list in conflict_vbo will be shrinking so + * we iterate those in the inner loop. */ + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + GList *tmp2; + for (tmp2 = conflict_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglVertexBufferAttrib *conflict_attribute = tmp2->data; + + if (conflict_attribute->name == attribute->name) + { + _cogl_vertex_buffer_attrib_free (conflict_attribute); + conflict_vbo->attributes = + g_list_delete_link (conflict_vbo->attributes, tmp2); + break; + } + } + } +} + +static void +cogl_vertex_buffer_vbo_free (CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + _cogl_vertex_buffer_attrib_free (tmp->data); + g_list_free (cogl_vbo->attributes); + + if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED) + cogl_object_unref (cogl_vbo->attribute_buffer); + + g_slice_free (CoglVertexBufferVBO, cogl_vbo); +} + +/* This figures out the lowest attribute client pointer. (This pointer is used + * to upload all the interleved attributes). + * + * In the process it also replaces the client pointer with the attributes + * offset, and marks the attribute as submitted. + */ +static const void * +prep_strided_vbo_for_upload (CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + const char *lowest_pointer = NULL; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + const char *client_pointer = attribute->u.pointer; + + if (!lowest_pointer || client_pointer < lowest_pointer) + lowest_pointer = client_pointer; + } + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + const char *client_pointer = attribute->u.pointer; + attribute->u.vbo_offset = client_pointer - lowest_pointer; + attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED; + } + + return lowest_pointer; +} + +static CoglBool +upload_multipack_vbo_via_map_buffer (CoglVertexBufferVBO *cogl_vbo) +{ + GList *tmp; + unsigned int offset = 0; + uint8_t *buf; + + _COGL_GET_CONTEXT (ctx, FALSE); + + buf = cogl_buffer_map (COGL_BUFFER (cogl_vbo->attribute_buffer), + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + if (!buf) + return FALSE; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + gsize attribute_size = attribute->span_bytes; + gsize type_size = sizeof_attribute_type (attribute->type); + + PAD_FOR_ALIGNMENT (offset, type_size); + + memcpy (buf + offset, attribute->u.pointer, attribute_size); + + attribute->u.vbo_offset = offset; + attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED; + offset += attribute_size; + } + + cogl_buffer_unmap (COGL_BUFFER (cogl_vbo->attribute_buffer)); + + return TRUE; +} + +static void +upload_multipack_vbo_via_buffer_sub_data (CoglVertexBufferVBO *cogl_vbo) +{ + GList *l; + unsigned int offset = 0; + + for (l = cogl_vbo->attributes; l != NULL; l = l->next) + { + CoglVertexBufferAttrib *attribute = l->data; + gsize attribute_size = attribute->span_bytes; + gsize type_size = sizeof_attribute_type (attribute->type); + + PAD_FOR_ALIGNMENT (offset, type_size); + + cogl_buffer_set_data (COGL_BUFFER (cogl_vbo->attribute_buffer), + offset, + attribute->u.pointer, + attribute_size); + + attribute->u.vbo_offset = offset; + attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED; + offset += attribute_size; + } +} + +static void +upload_attributes (CoglVertexBufferVBO *cogl_vbo) +{ + CoglBufferUpdateHint usage; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT) + usage = COGL_BUFFER_UPDATE_HINT_DYNAMIC; + else + usage = COGL_BUFFER_UPDATE_HINT_STATIC; + cogl_buffer_set_update_hint (COGL_BUFFER (cogl_vbo->attribute_buffer), usage); + + if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED) + { + const void *pointer = prep_strided_vbo_for_upload (cogl_vbo); + cogl_buffer_set_data (COGL_BUFFER (cogl_vbo->attribute_buffer), + 0, /* offset */ + pointer, + cogl_vbo->buffer_bytes); + } + else /* MULTIPACK */ + { + /* I think it might depend on the specific driver/HW whether its better + * to use glMapBuffer here or glBufferSubData here. There is even a good + * thread about this topic here: + * http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg35004.html + * For now I have gone with glMapBuffer, but the jury is still out. + */ + + if (!upload_multipack_vbo_via_map_buffer (cogl_vbo)) + upload_multipack_vbo_via_buffer_sub_data (cogl_vbo); + } + + cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED; +} + +/* Note: although there ends up being quite a few inner loops involved with + * resolving buffers, the number of attributes will be low so I don't expect + * them to cause a problem. */ +static void +cogl_vertex_buffer_vbo_resolve (CoglVertexBuffer *buffer, + CoglVertexBufferVBO *new_cogl_vbo, + GList **final_vbos) +{ + GList *conflicts; + GList *tmp; + GList *next; + CoglBool found_target_vbo = FALSE; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + conflicts = + get_submitted_vbo_conflicts (&buffer->submitted_vbos, new_cogl_vbo); + + for (tmp = conflicts; tmp != NULL; tmp = next) + { + CoglVertexBufferVBO *conflict_vbo = tmp->data; + + next = tmp->next; + + disassociate_conflicting_attributes (conflict_vbo, new_cogl_vbo); + + if (!conflict_vbo->attributes) + { + /* See if we can re-use this now empty VBO: */ + + if (!found_target_vbo + && conflict_vbo->buffer_bytes == new_cogl_vbo->buffer_bytes) + { + found_target_vbo = TRUE; + new_cogl_vbo->attribute_buffer = + cogl_object_ref (conflict_vbo->attribute_buffer); + cogl_vertex_buffer_vbo_free (conflict_vbo); + + upload_attributes (new_cogl_vbo); + + *final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo); + } + else + cogl_vertex_buffer_vbo_free (conflict_vbo); + } + else + { + /* Relink the VBO back into buffer->submitted_vbos since it may + * be involved in other conflicts later */ + tmp->next = buffer->submitted_vbos; + tmp->prev = NULL; + buffer->submitted_vbos = tmp; + } + } + + if (!found_target_vbo) + { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + new_cogl_vbo->attribute_buffer = + cogl_attribute_buffer_new (ctx, new_cogl_vbo->buffer_bytes, NULL); + + upload_attributes (new_cogl_vbo); + *final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo); + } +} + +static void +update_primitive_attributes (CoglVertexBuffer *buffer) +{ + GList *l; + int n_attributes = 0; + CoglAttribute **attributes; + int i; + + if (!buffer->dirty_attributes) + return; + + buffer->dirty_attributes = FALSE; + + for (l = buffer->submitted_vbos; l; l = l->next) + { + CoglVertexBufferVBO *cogl_vbo = l->data; + GList *l2; + + for (l2 = cogl_vbo->attributes; l2; l2 = l2->next, n_attributes++) + ; + } + + _COGL_RETURN_IF_FAIL (n_attributes > 0); + + attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); + + i = 0; + for (l = buffer->submitted_vbos; l; l = l->next) + { + CoglVertexBufferVBO *cogl_vbo = l->data; + GList *l2; + + for (l2 = cogl_vbo->attributes; l2; l2 = l2->next) + { + CoglVertexBufferAttrib *attribute = l2->data; + if (G_LIKELY (attribute->flags & + COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED)) + { + if (G_UNLIKELY (!attribute->attribute)) + { + attribute->attribute = + cogl_attribute_new (cogl_vbo->attribute_buffer, + attribute->name_without_detail, + attribute->stride, + attribute->u.vbo_offset, + attribute->n_components, + attribute->type); + } + + attributes[i++] = attribute->attribute; + } + } + } + + cogl_primitive_set_attributes (buffer->primitive, attributes, i); +} + +static void +cogl_vertex_buffer_submit_real (CoglVertexBuffer *buffer) +{ + GList *tmp; + CoglVertexBufferVBO *new_multipack_vbo; + GList *new_multipack_vbo_link; + GList *new_vbos = NULL; + GList *reuse_vbos = NULL; + GList *final_vbos = NULL; + + if (!buffer->new_attributes) + goto done; + + /* The objective now is to copy the attribute data supplied by the client + * into buffer objects, but it's important to minimize the number of + * redundant data uploads. + * + * We obviously aim to group together the attributes that are interleved so + * that they can be delivered in one go to the driver. + * All BOs for interleved data are created as STATIC_DRAW_ARB. + * + * Non interleved attributes tagged as INFREQUENT_RESUBMIT will be grouped + * together back to back in a single BO created as STATIC_DRAW_ARB + * + * Non interleved attributes tagged as FREQUENT_RESUBMIT will be copied into + * individual buffer objects, and the BO itself created DYNAMIC_DRAW_ARB + * + * If we are modifying a previously submitted CoglVertexBuffer then we are + * carefull not to needlesly delete OpenGL buffer objects and replace with + * new ones, instead we upload new data to the existing buffers. + */ + + /* NB: We must forget attribute->pointer after submitting since the user + * is free to re-use that memory for other purposes now. */ + + /* Pseudo code: + * + * Broadly speaking we start with a list of unsorted attributes, and filter + * those into 'new' and 're-use' CoglVertexBufferVBO (CBO) lists. We then + * take the list of new CBO structs and compare with the CBOs that have + * already been submitted to the GPU (but ignoring those we already know will + * be re-used) to determine what other CBOs can be re-used, due to being + * superseded, and what new GL VBOs need to be created. + * + * We have two kinds of CBOs: + * - Multi Pack CBOs + * These contain multiple attributes tightly packed back to back) + * - Strided CBOs + * These typically contain multiple interleved sets of attributes, + * though they can contain just one attribute with a stride + * + * First create a new-CBOs entry "new-multipack-CBO" + * Tag "new-multipack-CBO" as MULTIPACK + INFREQUENT_RESUBMIT + * For each unsorted attrib: + * if already marked as submitted: + * iterate reuse-CBOs: + * if we find one that contains this attribute: + * free redundant unsorted attrib struct + * remove the UNUSED flag from the attrib found in the reuse-CBO + * continue to next unsorted attrib + * iterate submitted VBOs: + * if we find one that contains this attribute: + * free redundant unsorted attrib struct + * unlink the vbo and move it to the list of reuse-CBOs + * mark all attributes except the one just matched as UNUSED + * assert (found) + * continue to next unsorted attrib + * if strided: + * iterate the new, strided, CBOs, to see if the attribute is + * interleved with one of them, if found: + * add to the matched CBO + * else if not found: + * create a new-CBOs entry tagged STRIDED + INFREQUENT_RESUBMIT + * else if unstrided && tagged with FREQUENT_RESUBMIT: + * create a new-CBOs entry tagged MULTIPACK + FREQUENT_RESUBMIT + * else + * add to the new-multipack-CBO + * free list of unsorted-attribs + * + * Next compare the new list of CBOs with the submitted set and try to + * minimize the memory bandwidth required to upload the attributes and the + * overhead of creating new GL-BOs. + * + * We deal with four sets of CBOs: + * - The "new" CBOs + * (as determined above during filtering) + * - The "re-use" CBOs + * (as determined above during filtering) + * - The "submitted" CBOs + * (I.e. ones currently submitted to the GPU) + * - The "final" CBOs + * (The result of resolving the differences between the above sets) + * + * The re-use CBOs are dealt with first, and we simply delete any remaining + * attributes in these that are still marked as UNUSED, and move them + * to the list of final CBOs. + * + * Next we iterate through the "new" CBOs, searching for conflicts + * with the "submitted" CBOs and commit our decision to the "final" CBOs + * + * When searching for submitted entries we always unlink items from the + * submitted list once we make matches (before we make descisions + * based on the matches). If the CBO node is superseded it is freed, + * if it is modified but may be needed for more descisions later it is + * relinked back into the submitted list and if it's identical to a new + * CBO it will be linked into the final list. + * + * At the end the list of submitted CBOs represents the attributes that were + * deleted from the buffer. + * + * Iterate re-use-CBOs: + * Iterate attribs for each: + * if attrib UNUSED: + * remove the attrib from the CBO + free + * |Note: we could potentially mark this as a re-useable gap + * |if needs be later. + * add re-use CBO to the final-CBOs + * Iterate new-CBOs: + * List submitted CBOs conflicting with the this CBO (Unlinked items) + * found-target-BO=FALSE + * Iterate conflicting CBOs: + * Disassociate conflicting attribs from conflicting CBO struct + * If no attribs remain: + * If found-target-BO!=TRUE + * _AND_ If the total size of the conflicting CBO is compatible: + * |Note: We don't currently consider re-using oversized buffers + * found-target-BO=TRUE + * upload replacement data + * free submitted CBO struct + * add new CBO struct to final-CBOs + * else: + * delete conflict GL-BO + * delete conflict CBO struct + * else: + * relink CBO back into submitted-CBOs + * + * if found-target-BO == FALSE: + * create a new GL-BO + * upload data + * add new CBO struct to final-BOs + * + * Iterate through the remaining "submitted" CBOs: + * delete the submitted GL-BO + * free the submitted CBO struct + */ + + new_multipack_vbo = g_slice_alloc (sizeof (CoglVertexBufferVBO)); + new_multipack_vbo->attribute_buffer = NULL; + new_multipack_vbo->buffer_bytes = 0; + new_multipack_vbo->flags = + COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK + | COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT; + new_multipack_vbo->attributes = NULL; + new_vbos = g_list_prepend (new_vbos, new_multipack_vbo); + /* We save the link pointer here, just so we can do a fast removal later if + * no attributes get added to this vbo. */ + new_multipack_vbo_link = new_vbos; + + /* Start with a list of unsorted attributes, and filter those into + * potential new Cogl BO structs + */ + for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglVertexBufferAttrib *attribute = tmp->data; + + if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED) + { + /* If the attribute is already marked as submitted, then we need + * to find the existing VBO that contains it so we dont delete it. + * + * NB: this also frees the attribute struct since it's implicitly + * redundant in this case. + */ + filter_already_submitted_attribute (attribute, + &reuse_vbos, + &buffer->submitted_vbos); + } + else if (attribute->stride) + { + /* look for a CoglVertexBufferVBO that the attribute is + * interleved with. If one can't be found then a new + * CoglVertexBufferVBO is allocated and added to the list of + * new_vbos: */ + filter_strided_attribute (attribute, &new_vbos); + } + else if (attribute->flags & + COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT) + { + CoglVertexBufferVBO *cogl_vbo = + g_slice_alloc (sizeof (CoglVertexBufferVBO)); + + /* attributes we expect will be frequently resubmitted are placed + * in their own VBO so that updates don't impact other attributes + */ + + cogl_vbo->flags = + COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK + | COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT; + cogl_vbo->attributes = NULL; + cogl_vbo->attributes = g_list_prepend (cogl_vbo->attributes, + attribute); + cogl_vbo->attribute_buffer = NULL; + cogl_vbo->buffer_bytes = attribute->span_bytes; + new_vbos = g_list_prepend (new_vbos, cogl_vbo); + } + else + { + gsize type_size = sizeof_attribute_type (attribute->flags); + + /* Infrequently updated attributes just get packed back to back + * in a single VBO: */ + new_multipack_vbo->attributes = + g_list_prepend (new_multipack_vbo->attributes, + attribute); + + /* Note: we have to ensure that each run of attributes is + * naturally aligned according to its data type, which may + * require some padding bytes: */ + + /* XXX: We also have to be sure that the attributes aren't + * reorderd before being uploaded because the alignment padding + * is based on the adjacent attribute. + */ + + PAD_FOR_ALIGNMENT (new_multipack_vbo->buffer_bytes, type_size); + + new_multipack_vbo->buffer_bytes += attribute->span_bytes; + } + } + + /* At this point all buffer->new_attributes have been filtered into + * CoglVertexBufferVBOs... */ + g_list_free (buffer->new_attributes); + buffer->new_attributes = NULL; + + /* If the multipack vbo wasn't needed: */ + if (new_multipack_vbo->attributes == NULL) + { + new_vbos = g_list_delete_link (new_vbos, new_multipack_vbo_link); + g_slice_free (CoglVertexBufferVBO, new_multipack_vbo); + } + + for (tmp = reuse_vbos; tmp != NULL; tmp = tmp->next) + remove_unused_attributes (tmp->data); + final_vbos = g_list_concat (final_vbos, reuse_vbos); + + for (tmp = new_vbos; tmp != NULL; tmp = tmp->next) + cogl_vertex_buffer_vbo_resolve (buffer, tmp->data, &final_vbos); + + /* Anything left corresponds to deleted attributes: */ + for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next) + cogl_vertex_buffer_vbo_free (tmp->data); + g_list_free (buffer->submitted_vbos); + g_list_free (new_vbos); + + buffer->submitted_vbos = final_vbos; + +done: + update_primitive_attributes (buffer); +} + +void +cogl_vertex_buffer_submit (CoglHandle handle) +{ + CoglVertexBuffer *buffer; + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + + cogl_vertex_buffer_submit_real (buffer); +} + +typedef struct +{ + /* We have a ref-count on this private structure because we need to + refer to it both from the private data on a pipeline and any weak + pipelines that we create from it. If we didn't have the ref count + then we would depend on the order of destruction of a + CoglPipeline and the weak materials to avoid a crash */ + unsigned int ref_count; + + CoglPipeline *real_source; +} VertexBufferMaterialPrivate; + +static void +unref_pipeline_priv (VertexBufferMaterialPrivate *priv) +{ + if (--priv->ref_count < 1) + g_slice_free (VertexBufferMaterialPrivate, priv); +} + +static void +weak_override_source_destroyed_cb (CoglPipeline *pipeline, + void *user_data) +{ + VertexBufferMaterialPrivate *pipeline_priv = user_data; + /* Unref the weak pipeline copy since it is no longer valid - probably because + * one of its ancestors has been changed. */ + cogl_object_unref (pipeline_priv->real_source); + pipeline_priv->real_source = NULL; + /* A reference was added when we copied the weak material so we need + to unref it here */ + unref_pipeline_priv (pipeline_priv); +} + +static CoglBool +validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + VertexBufferMaterialPrivate *pipeline_priv = user_data; + CoglPipeline *source = pipeline_priv->real_source; + + if (!cogl_pipeline_get_layer_point_sprite_coords_enabled (source, + layer_index)) + { + CoglPipelineWrapMode wrap_s; + CoglPipelineWrapMode wrap_t; + CoglPipelineWrapMode wrap_p; + CoglBool need_override_source = FALSE; + + /* By default COGL_PIPELINE_WRAP_MODE_AUTOMATIC becomes + * GL_CLAMP_TO_EDGE but we want GL_REPEAT to maintain + * compatibility with older versions of Cogl so we'll override + * it. We don't want to do this for point sprites because in + * that case the whole texture is drawn so you would usually + * want clamp-to-edge. + */ + wrap_s = cogl_pipeline_get_layer_wrap_mode_s (source, layer_index); + if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + need_override_source = TRUE; + wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT; + } + wrap_t = cogl_pipeline_get_layer_wrap_mode_t (source, layer_index); + if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + need_override_source = TRUE; + wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT; + } + wrap_p = cogl_pipeline_get_layer_wrap_mode_p (source, layer_index); + if (wrap_p == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + need_override_source = TRUE; + wrap_p = COGL_PIPELINE_WRAP_MODE_REPEAT; + } + + if (need_override_source) + { + if (pipeline_priv->real_source == pipeline) + { + pipeline_priv->ref_count++; + pipeline_priv->real_source = source = + _cogl_pipeline_weak_copy (pipeline, + weak_override_source_destroyed_cb, + pipeline_priv); + } + + cogl_pipeline_set_layer_wrap_mode_s (source, layer_index, wrap_s); + cogl_pipeline_set_layer_wrap_mode_t (source, layer_index, wrap_t); + cogl_pipeline_set_layer_wrap_mode_p (source, layer_index, wrap_p); + } + } + + return TRUE; +} + +static void +destroy_pipeline_priv_cb (void *user_data) +{ + unref_pipeline_priv (user_data); +} + +static void +update_primitive_and_draw (CoglVertexBuffer *buffer, + CoglVerticesMode mode, + int first, + int count, + CoglVertexBufferIndices *buffer_indices) +{ + VertexBufferMaterialPrivate *pipeline_priv; + CoglPipeline *users_source; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_primitive_set_mode (buffer->primitive, mode); + cogl_primitive_set_first_vertex (buffer->primitive, first); + cogl_primitive_set_n_vertices (buffer->primitive, count); + + if (buffer_indices) + cogl_primitive_set_indices (buffer->primitive, buffer_indices->indices, count); + else + cogl_primitive_set_indices (buffer->primitive, NULL, count); + + cogl_vertex_buffer_submit_real (buffer); + + users_source = cogl_get_source (); + pipeline_priv = + cogl_object_get_user_data (COGL_OBJECT (users_source), + &_cogl_vertex_buffer_pipeline_priv_key); + if (G_UNLIKELY (!pipeline_priv)) + { + pipeline_priv = g_slice_new0 (VertexBufferMaterialPrivate); + pipeline_priv->ref_count = 1; + cogl_object_set_user_data (COGL_OBJECT (users_source), + &_cogl_vertex_buffer_pipeline_priv_key, + pipeline_priv, + destroy_pipeline_priv_cb); + } + + if (G_UNLIKELY (!pipeline_priv->real_source)) + { + pipeline_priv->real_source = users_source; + cogl_pipeline_foreach_layer (pipeline_priv->real_source, + validate_layer_cb, + pipeline_priv); + } + + /* XXX: although this may seem redundant, we need to do this since + * CoglVertexBuffers can be used with legacy state and its the source stack + * which track whether legacy state is enabled. + * + * (We only have a CoglDrawFlag to disable legacy state not one + * to enable it) */ + cogl_push_source (pipeline_priv->real_source); + + _cogl_primitive_draw (buffer->primitive, + cogl_get_draw_framebuffer (), + pipeline_priv->real_source, + 0 /* no draw flags */); + + cogl_pop_source (); +} + +void +cogl_vertex_buffer_draw (CoglHandle handle, + CoglVerticesMode mode, + int first, + int count) +{ + CoglVertexBuffer *buffer; + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + + update_primitive_and_draw (buffer, mode, first, count, NULL); +} + +static CoglHandle +_cogl_vertex_buffer_indices_new_real (CoglIndices *indices) +{ + CoglVertexBufferIndices *buffer_indices = + g_slice_alloc (sizeof (CoglVertexBufferIndices)); + buffer_indices->indices = indices; + + return _cogl_vertex_buffer_indices_handle_new (buffer_indices); +} + +CoglHandle +cogl_vertex_buffer_indices_new (CoglIndicesType indices_type, + const void *indices_array, + int indices_len) +{ + CoglIndices *indices; + + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + indices = cogl_indices_new (ctx, indices_type, indices_array, indices_len); + return _cogl_vertex_buffer_indices_new_real (indices); +} + +CoglIndicesType +cogl_vertex_buffer_indices_get_type (CoglHandle indices_handle) +{ + CoglVertexBufferIndices *buffer_indices = NULL; + + if (!cogl_is_vertex_buffer_indices (indices_handle)) + return COGL_INDICES_TYPE_UNSIGNED_SHORT; + + buffer_indices = indices_handle; + + return cogl_indices_get_type (buffer_indices->indices); +} + +void +_cogl_vertex_buffer_indices_free (CoglVertexBufferIndices *buffer_indices) +{ + cogl_object_unref (buffer_indices->indices); + g_slice_free (CoglVertexBufferIndices, buffer_indices); +} + +void +cogl_vertex_buffer_draw_elements (CoglHandle handle, + CoglVerticesMode mode, + CoglHandle indices_handle, + int min_index, + int max_index, + int indices_offset, + int count) +{ + CoglVertexBuffer *buffer; + CoglVertexBufferIndices *buffer_indices; + + if (!cogl_is_vertex_buffer (handle)) + return; + + buffer = handle; + + if (!cogl_is_vertex_buffer_indices (indices_handle)) + return; + + buffer_indices = indices_handle; + + update_primitive_and_draw (buffer, mode, indices_offset, count, + buffer_indices); +} + +static void +_cogl_vertex_buffer_free (CoglVertexBuffer *buffer) +{ + GList *tmp; + + for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next) + cogl_vertex_buffer_vbo_free (tmp->data); + g_list_free (buffer->submitted_vbos); + + for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next) + _cogl_vertex_buffer_attrib_free (tmp->data); + g_list_free (buffer->new_attributes); + + if (buffer->primitive) + cogl_object_unref (buffer->primitive); + + g_slice_free (CoglVertexBuffer, buffer); +} + +CoglHandle +cogl_vertex_buffer_indices_get_for_quads (unsigned int n_indices) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + if (n_indices <= 256 / 4 * 6) + { + if (ctx->quad_buffer_indices_byte == COGL_INVALID_HANDLE) + { + /* NB: cogl_get_quad_indices takes n_quads not n_indices... */ + CoglIndices *indices = cogl_get_rectangle_indices (ctx, 256 / 4); + cogl_object_ref (indices); + ctx->quad_buffer_indices_byte = + _cogl_vertex_buffer_indices_new_real (indices); + } + + return ctx->quad_buffer_indices_byte; + } + else + { + if (ctx->quad_buffer_indices && + ctx->quad_buffer_indices_len < n_indices) + { + cogl_handle_unref (ctx->quad_buffer_indices); + ctx->quad_buffer_indices = COGL_INVALID_HANDLE; + } + + if (ctx->quad_buffer_indices == COGL_INVALID_HANDLE) + { + /* NB: cogl_get_quad_indices takes n_quads not n_indices... */ + CoglIndices *indices = + cogl_get_rectangle_indices (ctx, n_indices / 6); + cogl_object_ref (indices); + ctx->quad_buffer_indices = + _cogl_vertex_buffer_indices_new_real (indices); + } + + ctx->quad_buffer_indices_len = n_indices; + + return ctx->quad_buffer_indices; + } + + g_return_val_if_reached (NULL); +} + diff --git a/cogl/cogl/deprecated/cogl-vertex-buffer.h b/cogl/cogl/deprecated/cogl-vertex-buffer.h new file mode 100644 index 000000000..3b3a8df62 --- /dev/null +++ b/cogl/cogl/deprecated/cogl-vertex-buffer.h @@ -0,0 +1,451 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_VERTEX_BUFFER_H__ +#define __COGL_VERTEX_BUFFER_H__ + +#include +#include +#include +#include + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-vertex-buffer + * @short_description: An API for submitting extensible arrays of vertex + * attributes to be mapped into the GPU for fast drawing. + * + * For example to describe a textured triangle, you could create a new cogl + * vertex buffer with 3 vertices, and then you might add 2 attributes for each + * vertex: + * + * + * a "gl_Position" describing the (x,y,z) position for each vertex. + * + * + * a "gl_MultiTexCoord0" describing the (tx,ty) texture coordinates for each + * vertex. + * + * + * + * The Vertex Buffer API is designed to be a fairly raw mechanism for + * developers to be able to submit geometry to Cogl in a format that can be + * directly consumed by an OpenGL driver and mapped into your GPU for fast + * re-use. It is designed to avoid repeated validation of the attributes by the + * driver; to minimize transport costs (e.g. considering indirect GLX + * use-cases) and to potentially avoid repeated format conversions when + * attributes are supplied in a format that is not natively supported by the + * GPU. + * + * Although this API does allow you to modify attributes after they have been + * submitted to the GPU you should be aware that modification is not that + * cheap, since it implies validating the new data and potentially the + * OpenGL driver will need to reformat it for the GPU. + * + * If at all possible think of tricks that let you re-use static attributes, + * and if you do need to repeatedly update attributes (e.g. for some kind of + * morphing geometry) then only update and re-submit the specific attributes + * that have changed. + */ + +/** + * cogl_vertex_buffer_new: + * @n_vertices: The number of vertices that your attributes will correspond to. + * + * Creates a new vertex buffer that you can use to add attributes. + * + * Return value: a new #CoglHandle + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglHandle +cogl_vertex_buffer_new (unsigned int n_vertices); + +/** + * cogl_vertex_buffer_get_n_vertices: + * @handle: A vertex buffer handle + * + * Retrieves the number of vertices that @handle represents + * + * Return value: the number of vertices + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +unsigned int +cogl_vertex_buffer_get_n_vertices (CoglHandle handle); + +/** + * cogl_vertex_buffer_add: + * @handle: A vertex buffer handle + * @attribute_name: The name of your attribute. It should be a valid GLSL + * variable name and standard attribute types must use one of following + * built-in names: (Note: they correspond to the built-in names of GLSL) + * + * "gl_Color" + * "gl_Normal" + * "gl_MultiTexCoord0, gl_MultiTexCoord1, ..." + * "gl_Vertex" + * + * To support adding multiple variations of the same attribute the name + * can have a detail component, E.g. "gl_Color::active" or + * "gl_Color::inactive" + * @n_components: The number of components per attribute and must be 1, 2, + * 3 or 4 + * @type: a #CoglAttributeType specifying the data type of each component. + * @normalized: If %TRUE, this specifies that values stored in an integer + * format should be mapped into the range [-1.0, 1.0] or [0.0, 1.0] + * for unsigned values. If %FALSE they are converted to floats + * directly. + * @stride: This specifies the number of bytes from the start of one attribute + * value to the start of the next value (for the same attribute). So, for + * example, with a position interleved with color like this: + * XYRGBAXYRGBAXYRGBA, then if each letter represents a byte, the + * stride for both attributes is 6. The special value 0 means the + * values are stored sequentially in memory. + * @pointer: This addresses the first attribute in the vertex array. This + * must remain valid until you either call cogl_vertex_buffer_submit() or + * issue a draw call. + * + * Adds an attribute to a buffer, or replaces a previously added + * attribute with the same name. + * + * You either can use one of the built-in names such as "gl_Vertex", or + * "gl_MultiTexCoord0" to add standard attributes, like positions, colors + * and normals, or you can add custom attributes for use in shaders. + * + * The number of vertices declared when calling cogl_vertex_buffer_new() + * determines how many attribute values will be read from the supplied + * @pointer. + * + * The data for your attribute isn't copied anywhere until you call + * cogl_vertex_buffer_submit(), or issue a draw call which automatically + * submits pending attribute changes. so the supplied pointer must remain + * valid until then. If you are updating an existing attribute (done by + * re-adding it) then you still need to re-call cogl_vertex_buffer_submit() + * to commit the changes to the GPU. Be carefull to minimize the number + * of calls to cogl_vertex_buffer_submit(), though. + * + * If you are interleving attributes it is assumed that each interleaved + * attribute starts no farther than +- stride bytes from the other attributes + * it is interleved with. I.e. this is ok: + * + * |-0-0-0-0-0-0-0-0-0-0| + * + * This is not ok: + * + * |- - - - -0-0-0-0-0-0 0 0 0 0| + * + * (Though you can have multiple groups of interleved attributes) + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_add (CoglHandle handle, + const char *attribute_name, + uint8_t n_components, + CoglAttributeType type, + CoglBool normalized, + uint16_t stride, + const void *pointer); + +/** + * cogl_vertex_buffer_delete: + * @handle: A vertex buffer handle + * @attribute_name: The name of a previously added attribute + * + * Deletes an attribute from a buffer. You will need to call + * cogl_vertex_buffer_submit() or issue a draw call to commit this + * change to the GPU. + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_delete (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_vertex_buffer_submit: + * @handle: A vertex buffer handle + * + * Submits all the user added attributes to the GPU; once submitted, the + * attributes can be used for drawing. + * + * You should aim to minimize calls to this function since it implies + * validating your data; it potentially incurs a transport cost (especially if + * you are using GLX indirect rendering) and potentially a format conversion + * cost if the GPU doesn't natively support any of the given attribute formats. + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_submit (CoglHandle handle); + +/** + * cogl_vertex_buffer_disable: + * @handle: A vertex buffer handle + * @attribute_name: The name of the attribute you want to disable + * + * Disables a previosuly added attribute. + * + * Since it can be costly to add and remove new attributes to buffers; to make + * individual buffers more reuseable it is possible to enable and disable + * attributes before using a buffer for drawing. + * + * You don't need to call cogl_vertex_buffer_submit() after using this + * function. + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_disable (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_vertex_buffer_enable: + * @handle: A vertex buffer handle + * @attribute_name: The name of the attribute you want to enable + * + * Enables a previosuly disabled attribute. + * + * Since it can be costly to add and remove new attributes to buffers; to make + * individual buffers more reuseable it is possible to enable and disable + * attributes before using a buffer for drawing. + * + * You don't need to call cogl_vertex_buffer_submit() after using this function + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_enable (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_vertex_buffer_draw: + * @handle: A vertex buffer handle + * @mode: A #CoglVerticesMode specifying how the vertices should be + * interpreted. + * @first: Specifies the index of the first vertex you want to draw with + * @count: Specifies the number of vertices you want to draw. + * + * Allows you to draw geometry using all or a subset of the + * vertices in a vertex buffer. + * + * Any un-submitted attribute changes are automatically submitted before + * drawing. + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_draw (CoglHandle handle, + CoglVerticesMode mode, + int first, + int count); + +/** + * cogl_vertex_buffer_indices_new: + * @indices_type: a #CoglIndicesType specifying the data type used for + * the indices. + * @indices_array: (array length=indices_len): Specifies the address of + * your array of indices + * @indices_len: The number of indices in indices_array + * + * Depending on how much geometry you are submitting it can be worthwhile + * optimizing the number of redundant vertices you submit. Using an index + * array allows you to reference vertices multiple times, for example + * during triangle strips. + * + * Return value: A CoglHandle for the indices which you can pass to + * cogl_vertex_buffer_draw_elements(). + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglHandle +cogl_vertex_buffer_indices_new (CoglIndicesType indices_type, + const void *indices_array, + int indices_len); + +/** + * cogl_vertex_buffer_indices_get_type: + * @indices: An indices handle + * + * Queries back the data type used for the given indices + * + * Returns: The CoglIndicesType used + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglIndicesType +cogl_vertex_buffer_indices_get_type (CoglHandle indices); + +/** + * cogl_vertex_buffer_draw_elements: + * @handle: A vertex buffer handle + * @mode: A #CoglVerticesMode specifying how the vertices should be + * interpreted. + * @indices: A CoglHandle for a set of indices allocated via + * cogl_vertex_buffer_indices_new () + * @min_index: Specifies the minimum vertex index contained in indices + * @max_index: Specifies the maximum vertex index contained in indices + * @indices_offset: An offset into named indices. The offset marks the first + * index to use for drawing. + * @count: Specifies the number of vertices you want to draw. + * + * This function lets you use an array of indices to specify the vertices + * within your vertex buffer that you want to draw. The indices themselves + * are created by calling cogl_vertex_buffer_indices_new () + * + * Any un-submitted attribute changes are automatically submitted before + * drawing. + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +void +cogl_vertex_buffer_draw_elements (CoglHandle handle, + CoglVerticesMode mode, + CoglHandle indices, + int min_index, + int max_index, + int indices_offset, + int count); + +/** + * cogl_vertex_buffer_ref: + * @handle: a @CoglHandle. + * + * Increment the reference count for a vertex buffer + * + * Return value: the @handle. + * + * Deprecated: 1.2: Use cogl_object_ref() instead + */ +COGL_DEPRECATED_FOR (cogl_object_ref) +CoglHandle +cogl_vertex_buffer_ref (CoglHandle handle); + +/** + * cogl_vertex_buffer_unref: + * @handle: a @CoglHandle. + * + * Decrement the reference count for a vertex buffer + * + * Deprecated: 1.2: Use cogl_object_unref() instead + */ +COGL_DEPRECATED_FOR (cogl_object_unref) +void +cogl_vertex_buffer_unref (CoglHandle handle); + +/** + * cogl_vertex_buffer_indices_get_for_quads: + * @n_indices: the number of indices in the vertex buffer. + * + * Creates a vertex buffer containing the indices needed to draw pairs + * of triangles from a list of vertices grouped as quads. There will + * be at least @n_indices entries in the buffer (but there may be + * more). + * + * The indices will follow this pattern: + * + * 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7 ... etc + * + * For example, if you submit vertices for a quad like like that shown + * in then you can request 6 + * indices to render two triangles like those shown in . + * + *
+ * Example of vertices submitted to form a quad + * + *
+ * + *
+ * Illustration of the triangle indices that will be generated + * + *
+ * + * Returns: A %CoglHandle containing the indices. The handled is + * owned by Cogl and should not be modified or unref'd. + * + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglHandle +cogl_vertex_buffer_indices_get_for_quads (unsigned int n_indices); + +/** + * cogl_is_vertex_buffer: + * @handle: a #CoglHandle for a vertex buffer object + * + * Checks whether @handle is a Vertex Buffer Object + * + * Return value: %TRUE if the handle is a VBO, and %FALSE + * otherwise + * + * Since: 1.0 + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglBool +cogl_is_vertex_buffer (CoglHandle handle); + +/** + * cogl_is_vertex_buffer_indices: + * @handle: a #CoglHandle + * + * Checks whether @handle is a handle to the indices for a vertex + * buffer object + * + * Return value: %TRUE if the handle is indices, and %FALSE + * otherwise + * + * Since: 1.4 + * Deprecated: 1.16: Use the #CoglPrimitive api instead + */ +COGL_DEPRECATED_IN_1_16_FOR (cogl_primitive_API) +CoglBool +cogl_is_vertex_buffer_indices (CoglHandle handle); +COGL_END_DECLS + +#endif /* __COGL_VERTEX_BUFFER_H__ */ diff --git a/cogl/cogl/driver/gl/cogl-attribute-gl-private.h b/cogl/cogl/driver/gl/cogl-attribute-gl-private.h new file mode 100644 index 000000000..efb3c0ea2 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-attribute-gl-private.h @@ -0,0 +1,53 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_ATTRIBUTE_GL_PRIVATE_H_ +#define _COGL_ATTRIBUTE_GL_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-framebuffer.h" +#include "cogl-attribute.h" +#include "cogl-attribute-private.h" + +void +_cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglFlushLayerState *layers_state, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes); + +void +_cogl_gl_disable_all_attributes (CoglContext *ctx); + +#endif /* _COGL_ATTRIBUTE_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-attribute-gl.c b/cogl/cogl/driver/gl/cogl-attribute-gl.c new file mode 100644 index 000000000..34ddb5592 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-attribute-gl.c @@ -0,0 +1,541 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-error-private.h" +#include "cogl-context-private.h" +#include "cogl-attribute.h" +#include "cogl-attribute-private.h" +#include "cogl-attribute-gl-private.h" +#include "cogl-pipeline-progend-glsl-private.h" +#include "cogl-buffer-gl-private.h" + +typedef struct _ForeachChangedBitState +{ + CoglContext *context; + const CoglBitmask *new_bits; + CoglPipeline *pipeline; +} ForeachChangedBitState; + +static CoglBool +toggle_builtin_attribute_enabled_cb (int bit_num, void *user_data) +{ + ForeachChangedBitState *state = user_data; + CoglContext *context = state->context; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_GL_FIXED), + FALSE); + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + { + CoglBool enabled = _cogl_bitmask_get (state->new_bits, bit_num); + GLenum cap; + + switch (bit_num) + { + case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: + cap = GL_COLOR_ARRAY; + break; + case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY: + cap = GL_VERTEX_ARRAY; + break; + case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY: + cap = GL_NORMAL_ARRAY; + break; + default: + g_assert_not_reached (); + } + if (enabled) + GE (context, glEnableClientState (cap)); + else + GE (context, glDisableClientState (cap)); + } +#endif + + return TRUE; +} + +static CoglBool +toggle_texcood_attribute_enabled_cb (int bit_num, void *user_data) +{ + ForeachChangedBitState *state = user_data; + CoglContext *context = state->context; + + _COGL_RETURN_VAL_IF_FAIL (_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_GL_FIXED), + FALSE); + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + { + CoglBool enabled = _cogl_bitmask_get (state->new_bits, bit_num); + + GE( context, glClientActiveTexture (GL_TEXTURE0 + bit_num) ); + + if (enabled) + GE( context, glEnableClientState (GL_TEXTURE_COORD_ARRAY) ); + else + GE( context, glDisableClientState (GL_TEXTURE_COORD_ARRAY) ); + } +#endif + + return TRUE; +} + +static CoglBool +toggle_custom_attribute_enabled_cb (int bit_num, void *user_data) +{ + ForeachChangedBitState *state = user_data; + CoglBool enabled = _cogl_bitmask_get (state->new_bits, bit_num); + CoglContext *context = state->context; + + if (enabled) + GE( context, glEnableVertexAttribArray (bit_num) ); + else + GE( context, glDisableVertexAttribArray (bit_num) ); + + return TRUE; +} + +static void +foreach_changed_bit_and_save (CoglContext *context, + CoglBitmask *current_bits, + const CoglBitmask *new_bits, + CoglBitmaskForeachFunc callback, + ForeachChangedBitState *state) +{ + /* Get the list of bits that are different */ + _cogl_bitmask_clear_all (&context->changed_bits_tmp); + _cogl_bitmask_set_bits (&context->changed_bits_tmp, current_bits); + _cogl_bitmask_xor_bits (&context->changed_bits_tmp, new_bits); + + /* Iterate over each bit to change */ + state->new_bits = new_bits; + _cogl_bitmask_foreach (&context->changed_bits_tmp, + callback, + state); + + /* Store the new values */ + _cogl_bitmask_clear_all (current_bits); + _cogl_bitmask_set_bits (current_bits, new_bits); +} + +#ifdef COGL_PIPELINE_PROGEND_GLSL + +static void +setup_generic_buffered_attribute (CoglContext *context, + CoglPipeline *pipeline, + CoglAttribute *attribute, + uint8_t *base) +{ + int name_index = attribute->name_state->name_index; + int attrib_location = + _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); + + if (attrib_location == -1) + return; + + GE( context, glVertexAttribPointer (attrib_location, + attribute->d.buffered.n_components, + attribute->d.buffered.type, + attribute->normalized, + attribute->d.buffered.stride, + base + attribute->d.buffered.offset) ); + _cogl_bitmask_set (&context->enable_custom_attributes_tmp, + attrib_location, TRUE); +} + +static void +setup_generic_const_attribute (CoglContext *context, + CoglPipeline *pipeline, + CoglAttribute *attribute) +{ + int name_index = attribute->name_state->name_index; + int attrib_location = + _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); + int columns; + int i; + + if (attrib_location == -1) + return; + + if (attribute->d.constant.boxed.type == COGL_BOXED_MATRIX) + columns = attribute->d.constant.boxed.size; + else + columns = 1; + + /* Note: it's ok to access a COGL_BOXED_FLOAT as a matrix with only + * one column... */ + + switch (attribute->d.constant.boxed.size) + { + case 1: + GE( context, glVertexAttrib1fv (attrib_location, + attribute->d.constant.boxed.v.matrix)); + break; + case 2: + for (i = 0; i < columns; i++) + GE( context, glVertexAttrib2fv (attrib_location + i, + attribute->d.constant.boxed.v.matrix)); + break; + case 3: + for (i = 0; i < columns; i++) + GE( context, glVertexAttrib3fv (attrib_location + i, + attribute->d.constant.boxed.v.matrix)); + break; + case 4: + for (i = 0; i < columns; i++) + GE( context, glVertexAttrib4fv (attrib_location + i, + attribute->d.constant.boxed.v.matrix)); + break; + default: + g_warn_if_reached (); + } +} + +#endif /* COGL_PIPELINE_PROGEND_GLSL */ + +static void +setup_legacy_buffered_attribute (CoglContext *ctx, + CoglPipeline *pipeline, + CoglAttribute *attribute, + uint8_t *base) +{ + switch (attribute->name_state->name_id) + { + case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: + _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp, + COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY, TRUE); + GE (ctx, glColorPointer (attribute->d.buffered.n_components, + attribute->d.buffered.type, + attribute->d.buffered.stride, + base + attribute->d.buffered.offset)); + break; + case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY: + _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp, + COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY, TRUE); + GE (ctx, glNormalPointer (attribute->d.buffered.type, + attribute->d.buffered.stride, + base + attribute->d.buffered.offset)); + break; + case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY: + { + int layer_number = attribute->name_state->layer_number; + const CoglPipelineGetLayerFlags flags = + COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *layer = + _cogl_pipeline_get_layer_with_flags (pipeline, layer_number, flags); + + if (layer) + { + int unit = _cogl_pipeline_layer_get_unit_index (layer); + + _cogl_bitmask_set (&ctx->enable_texcoord_attributes_tmp, + unit, + TRUE); + + GE (ctx, glClientActiveTexture (GL_TEXTURE0 + unit)); + GE (ctx, glTexCoordPointer (attribute->d.buffered.n_components, + attribute->d.buffered.type, + attribute->d.buffered.stride, + base + attribute->d.buffered.offset)); + } + break; + } + case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY: + _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp, + COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY, TRUE); + GE (ctx, glVertexPointer (attribute->d.buffered.n_components, + attribute->d.buffered.type, + attribute->d.buffered.stride, + base + attribute->d.buffered.offset)); + break; + case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY: +#ifdef COGL_PIPELINE_PROGEND_GLSL + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE)) + setup_generic_buffered_attribute (ctx, pipeline, attribute, base); +#endif + break; + default: + g_warn_if_reached (); + } +} + +static void +setup_legacy_const_attribute (CoglContext *ctx, + CoglPipeline *pipeline, + CoglAttribute *attribute) +{ +#ifdef COGL_PIPELINE_PROGEND_GLSL + if (attribute->name_state->name_id == COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY) + { + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE)) + setup_generic_const_attribute (ctx, pipeline, attribute); + } + else +#endif + { + float vector[4] = { 0, 0, 0, 1 }; + float *boxed = attribute->d.constant.boxed.v.float_value; + int n_components = attribute->d.constant.boxed.size; + int i; + + for (i = 0; i < n_components; i++) + vector[i] = boxed[i]; + + switch (attribute->name_state->name_id) + { + case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: + GE (ctx, glColor4f (vector[0], vector[1], vector[2], vector[3])); + break; + case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY: + GE (ctx, glNormal3f (vector[0], vector[1], vector[2])); + break; + case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY: + { + int layer_number = attribute->name_state->layer_number; + const CoglPipelineGetLayerFlags flags = + COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *layer = + _cogl_pipeline_get_layer_with_flags (pipeline, + layer_number, + flags); + + if (layer) + { + int unit = _cogl_pipeline_layer_get_unit_index (layer); + + GE (ctx, glClientActiveTexture (GL_TEXTURE0 + unit)); + + GE (ctx, glMultiTexCoord4f (vector[0], + vector[1], + vector[2], + vector[3])); + } + break; + } + case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY: + GE (ctx, glVertex4f (vector[0], vector[1], vector[2], vector[3])); + break; + default: + g_warn_if_reached (); + } + } +} + +static void +apply_attribute_enable_updates (CoglContext *context, + CoglPipeline *pipeline) +{ + ForeachChangedBitState changed_bits_state; + + changed_bits_state.context = context; + changed_bits_state.new_bits = &context->enable_builtin_attributes_tmp; + changed_bits_state.pipeline = pipeline; + + foreach_changed_bit_and_save (context, + &context->enabled_builtin_attributes, + &context->enable_builtin_attributes_tmp, + toggle_builtin_attribute_enabled_cb, + &changed_bits_state); + + changed_bits_state.new_bits = &context->enable_texcoord_attributes_tmp; + foreach_changed_bit_and_save (context, + &context->enabled_texcoord_attributes, + &context->enable_texcoord_attributes_tmp, + toggle_texcood_attribute_enabled_cb, + &changed_bits_state); + + changed_bits_state.new_bits = &context->enable_custom_attributes_tmp; + foreach_changed_bit_and_save (context, + &context->enabled_custom_attributes, + &context->enable_custom_attributes_tmp, + toggle_custom_attribute_enabled_cb, + &changed_bits_state); +} + +void +_cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglFlushLayerState *layers_state, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes) +{ + CoglContext *ctx = framebuffer->context; + int i; + CoglBool with_color_attrib = FALSE; + CoglBool unknown_color_alpha = FALSE; + CoglPipeline *copy = NULL; + + /* Iterate the attributes to see if we have a color attribute which + * may affect our decision to enable blending or not. + * + * We need to do this before flushing the pipeline. */ + for (i = 0; i < n_attributes; i++) + switch (attributes[i]->name_state->name_id) + { + case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: + if ((flags & COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE) == 0 && + _cogl_attribute_get_n_components (attributes[i]) == 4) + unknown_color_alpha = TRUE; + with_color_attrib = TRUE; + break; + + default: + break; + } + + if (G_UNLIKELY (layers_state->options.flags)) + { + /* If we haven't already created a derived pipeline... */ + if (!copy) + { + copy = cogl_pipeline_copy (pipeline); + pipeline = copy; + } + _cogl_pipeline_apply_overrides (pipeline, &layers_state->options); + + /* TODO: + * overrides = cogl_pipeline_get_data (pipeline, + * last_overrides_key); + * if (overrides) + * { + * age = cogl_pipeline_get_age (pipeline); + * XXX: actually we also need to check for legacy_state + * and blending overrides for use of glColorPointer... + * if (overrides->ags != age || + * memcmp (&overrides->options, &options, + * sizeof (options) != 0) + * { + * cogl_object_unref (overrides->weak_pipeline); + * g_slice_free (Overrides, overrides); + * overrides = NULL; + * } + * } + * if (!overrides) + * { + * overrides = g_slice_new (Overrides); + * overrides->weak_pipeline = + * cogl_pipeline_weak_copy (pipeline); + * _cogl_pipeline_apply_overrides (overrides->weak_pipeline, + * &options); + * + * cogl_pipeline_set_data (pipeline, last_overrides_key, + * weak_overrides, + * free_overrides_cb, + * NULL); + * } + * pipeline = overrides->weak_pipeline; + */ + } + + _cogl_pipeline_flush_gl_state (ctx, + pipeline, + framebuffer, + with_color_attrib, + unknown_color_alpha); + + _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp); + _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp); + _cogl_bitmask_clear_all (&ctx->enable_custom_attributes_tmp); + + /* Bind the attribute pointers. We need to do this after the + * pipeline is flushed because when using GLSL that is the only + * point when we can determine the attribute locations */ + + for (i = 0; i < n_attributes; i++) + { + CoglAttribute *attribute = attributes[i]; + CoglAttributeBuffer *attribute_buffer; + CoglBuffer *buffer; + uint8_t *base; + + if (attribute->is_buffered) + { + attribute_buffer = cogl_attribute_get_buffer (attribute); + buffer = COGL_BUFFER (attribute_buffer); + + /* Note: we don't try and catch errors with binding buffers + * here since OOM errors at this point indicate that nothing + * has yet been uploaded to attribute buffer which we + * consider to be a programmer error. + */ + base = + _cogl_buffer_gl_bind (buffer, + COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, + NULL); + + if (pipeline->progend == COGL_PIPELINE_PROGEND_GLSL) + setup_generic_buffered_attribute (ctx, pipeline, attribute, base); + else + setup_legacy_buffered_attribute (ctx, pipeline, attribute, base); + + _cogl_buffer_gl_unbind (buffer); + } + else + { + if (pipeline->progend == COGL_PIPELINE_PROGEND_GLSL) + setup_generic_const_attribute (ctx, pipeline, attribute); + else + setup_legacy_const_attribute (ctx, pipeline, attribute); + } + } + + apply_attribute_enable_updates (ctx, pipeline); + + if (copy) + cogl_object_unref (copy); +} + +void +_cogl_gl_disable_all_attributes (CoglContext *ctx) +{ + _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp); + _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp); + _cogl_bitmask_clear_all (&ctx->enable_custom_attributes_tmp); + + /* XXX: we can pass a NULL source pipeline here because we know a + * source pipeline only needs to be referenced when enabling + * attributes. */ + apply_attribute_enable_updates (ctx, NULL); +} diff --git a/cogl/cogl/driver/gl/cogl-buffer-gl-private.h b/cogl/cogl/driver/gl/cogl-buffer-gl-private.h new file mode 100644 index 000000000..b8f0435b3 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-buffer-gl-private.h @@ -0,0 +1,74 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_BUFFER_GL_PRIVATE_H_ +#define _COGL_BUFFER_GL_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context.h" +#include "cogl-buffer.h" +#include "cogl-buffer-private.h" + +void +_cogl_buffer_gl_create (CoglBuffer *buffer); + +void +_cogl_buffer_gl_destroy (CoglBuffer *buffer); + +void * +_cogl_buffer_gl_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error); + +void +_cogl_buffer_gl_unmap (CoglBuffer *buffer); + +CoglBool +_cogl_buffer_gl_set_data (CoglBuffer *buffer, + unsigned int offset, + const void *data, + unsigned int size, + CoglError **error); + +void * +_cogl_buffer_gl_bind (CoglBuffer *buffer, + CoglBufferBindTarget target, + CoglError **error); + +void +_cogl_buffer_gl_unbind (CoglBuffer *buffer); + +#endif /* _COGL_BUFFER_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-buffer-gl.c b/cogl/cogl/driver/gl/cogl-buffer-gl.c new file mode 100644 index 000000000..0f984064e --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-buffer-gl.c @@ -0,0 +1,442 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011,2012,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Damien Lespiau + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-buffer-gl-private.h" +#include "cogl-error-private.h" +#include "cogl-util-gl-private.h" + +/* + * GL/GLES compatibility defines for the buffer API: + */ + +#ifndef GL_PIXEL_PACK_BUFFER +#define GL_PIXEL_PACK_BUFFER 0x88EB +#endif +#ifndef GL_PIXEL_UNPACK_BUFFER +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#endif +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8893 +#endif +#ifndef GL_READ_ONLY +#define GL_READ_ONLY 0x88B8 +#endif +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 +#endif +#ifndef GL_READ_WRITE +#define GL_READ_WRITE 0x88BA +#endif +#ifndef GL_MAP_READ_BIT +#define GL_MAP_READ_BIT 0x0001 +#endif +#ifndef GL_MAP_WRITE_BIT +#define GL_MAP_WRITE_BIT 0x0002 +#endif +#ifndef GL_MAP_INVALIDATE_RANGE_BIT +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#endif +#ifndef GL_MAP_INVALIDATE_BUFFER_BIT +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#endif + +void +_cogl_buffer_gl_create (CoglBuffer *buffer) +{ + CoglContext *ctx = buffer->context; + + GE (ctx, glGenBuffers (1, &buffer->gl_handle)); +} + +void +_cogl_buffer_gl_destroy (CoglBuffer *buffer) +{ + GE( buffer->context, glDeleteBuffers (1, &buffer->gl_handle) ); +} + +static GLenum +update_hints_to_gl_enum (CoglBuffer *buffer) +{ + /* usage hint is always DRAW for now */ + switch (buffer->update_hint) + { + case COGL_BUFFER_UPDATE_HINT_STATIC: + return GL_STATIC_DRAW; + case COGL_BUFFER_UPDATE_HINT_DYNAMIC: + return GL_DYNAMIC_DRAW; + + case COGL_BUFFER_UPDATE_HINT_STREAM: + /* OpenGL ES 1.1 only knows about STATIC_DRAW and DYNAMIC_DRAW */ +#if defined(HAVE_COGL_GL) || defined(HAVE_COGL_GLES2) + if (buffer->context->driver != COGL_DRIVER_GLES1) + return GL_STREAM_DRAW; +#else + return GL_DYNAMIC_DRAW; +#endif + } + + g_assert_not_reached (); +} + +static GLenum +convert_bind_target_to_gl_target (CoglBufferBindTarget target) +{ + switch (target) + { + case COGL_BUFFER_BIND_TARGET_PIXEL_PACK: + return GL_PIXEL_PACK_BUFFER; + case COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK: + return GL_PIXEL_UNPACK_BUFFER; + case COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER: + return GL_ARRAY_BUFFER; + case COGL_BUFFER_BIND_TARGET_INDEX_BUFFER: + return GL_ELEMENT_ARRAY_BUFFER; + default: + g_return_val_if_reached (COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK); + } +} + +static CoglBool +recreate_store (CoglBuffer *buffer, + CoglError **error) +{ + CoglContext *ctx = buffer->context; + GLenum gl_target; + GLenum gl_enum; + GLenum gl_error; + + /* This assumes the buffer is already bound */ + + gl_target = convert_bind_target_to_gl_target (buffer->last_target); + gl_enum = update_hints_to_gl_enum (buffer); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glBufferData (gl_target, + buffer->size, + NULL, + gl_enum); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + return FALSE; + + buffer->store_created = TRUE; + return TRUE; +} + +GLenum +_cogl_buffer_access_to_gl_enum (CoglBufferAccess access) +{ + if ((access & COGL_BUFFER_ACCESS_READ_WRITE) == COGL_BUFFER_ACCESS_READ_WRITE) + return GL_READ_WRITE; + else if (access & COGL_BUFFER_ACCESS_WRITE) + return GL_WRITE_ONLY; + else + return GL_READ_ONLY; +} + +static void * +_cogl_buffer_bind_no_create (CoglBuffer *buffer, + CoglBufferBindTarget target) +{ + CoglContext *ctx = buffer->context; + + _COGL_RETURN_VAL_IF_FAIL (buffer != NULL, NULL); + + /* Don't allow binding the buffer to multiple targets at the same time */ + _COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[buffer->last_target] != buffer, + NULL); + + /* Don't allow nesting binds to the same target */ + _COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[target] == NULL, NULL); + + buffer->last_target = target; + ctx->current_buffer[target] = buffer; + + if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) + { + GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target); + GE( ctx, glBindBuffer (gl_target, buffer->gl_handle) ); + return NULL; + } + else + return buffer->data; +} + +void * +_cogl_buffer_gl_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints, + CoglError **error) +{ + uint8_t *data; + CoglBufferBindTarget target; + GLenum gl_target; + CoglContext *ctx = buffer->context; + GLenum gl_error; + + if (((access & COGL_BUFFER_ACCESS_READ) && + !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ)) || + ((access & COGL_BUFFER_ACCESS_WRITE) && + !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Tried to map a buffer with unsupported access mode"); + return NULL; + } + + target = buffer->last_target; + _cogl_buffer_bind_no_create (buffer, target); + + gl_target = convert_bind_target_to_gl_target (target); + + if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) && + offset == 0 && size >= buffer->size) + hints |= COGL_BUFFER_MAP_HINT_DISCARD; + + /* If the map buffer range extension is supported then we will + * always use it even if we are mapping the full range because the + * normal mapping function doesn't support passing the discard + * hints */ + if (ctx->glMapBufferRange) + { + GLbitfield gl_access = 0; + CoglBool should_recreate_store = !buffer->store_created; + + if ((access & COGL_BUFFER_ACCESS_READ)) + gl_access |= GL_MAP_READ_BIT; + if ((access & COGL_BUFFER_ACCESS_WRITE)) + gl_access |= GL_MAP_WRITE_BIT; + + if ((hints & COGL_BUFFER_MAP_HINT_DISCARD)) + { + /* glMapBufferRange generates an error if you pass the + * discard hint along with asking for read access. However + * it can make sense to ask for both if write access is also + * requested so that the application can immediately read + * back what it just wrote. To work around the restriction + * in GL we just recreate the buffer storage in that case + * which is an alternative way to indicate that the buffer + * contents can be discarded. */ + if ((access & COGL_BUFFER_ACCESS_READ)) + should_recreate_store = TRUE; + else + gl_access |= GL_MAP_INVALIDATE_BUFFER_BIT; + } + else if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) && + !(access & COGL_BUFFER_ACCESS_READ)) + gl_access |= GL_MAP_INVALIDATE_RANGE_BIT; + + if (should_recreate_store) + { + if (!recreate_store (buffer, error)) + { + _cogl_buffer_gl_unbind (buffer); + return NULL; + } + } + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + data = ctx->glMapBufferRange (gl_target, + offset, + size, + gl_access); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + _cogl_buffer_gl_unbind (buffer); + return NULL; + } + + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + } + else + { + /* create an empty store if we don't have one yet. creating the store + * lazily allows the user of the CoglBuffer to set a hint before the + * store is created. */ + if (!buffer->store_created || + (hints & COGL_BUFFER_MAP_HINT_DISCARD)) + { + if (!recreate_store (buffer, error)) + { + _cogl_buffer_gl_unbind (buffer); + return NULL; + } + } + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + data = ctx->glMapBuffer (gl_target, + _cogl_buffer_access_to_gl_enum (access)); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + _cogl_buffer_gl_unbind (buffer); + return NULL; + } + + _COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL); + + data += offset; + } + + if (data) + buffer->flags |= COGL_BUFFER_FLAG_MAPPED; + + _cogl_buffer_gl_unbind (buffer); + + return data; +} + +void +_cogl_buffer_gl_unmap (CoglBuffer *buffer) +{ + CoglContext *ctx = buffer->context; + + _cogl_buffer_bind_no_create (buffer, buffer->last_target); + + GE( ctx, glUnmapBuffer (convert_bind_target_to_gl_target + (buffer->last_target)) ); + buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED; + + _cogl_buffer_gl_unbind (buffer); +} + +CoglBool +_cogl_buffer_gl_set_data (CoglBuffer *buffer, + unsigned int offset, + const void *data, + unsigned int size, + CoglError **error) +{ + CoglBufferBindTarget target; + GLenum gl_target; + CoglContext *ctx = buffer->context; + GLenum gl_error; + CoglBool status = TRUE; + CoglError *internal_error = NULL; + + target = buffer->last_target; + + _cogl_buffer_gl_bind (buffer, target, &internal_error); + + /* NB: _cogl_buffer_gl_bind() may return NULL in non-error + * conditions so we have to explicity check internal_error + * to see if an exception was thrown. + */ + if (internal_error) + { + _cogl_propagate_error (error, internal_error); + return FALSE; + } + + gl_target = convert_bind_target_to_gl_target (target); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glBufferSubData (gl_target, offset, size, data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_buffer_gl_unbind (buffer); + + return status; +} + +void * +_cogl_buffer_gl_bind (CoglBuffer *buffer, + CoglBufferBindTarget target, + CoglError **error) +{ + void *ret; + + ret = _cogl_buffer_bind_no_create (buffer, target); + + /* create an empty store if we don't have one yet. creating the store + * lazily allows the user of the CoglBuffer to set a hint before the + * store is created. */ + if ((buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) && + !buffer->store_created) + { + if (!recreate_store (buffer, error)) + { + _cogl_buffer_gl_unbind (buffer); + return NULL; + } + } + + return ret; +} + +void +_cogl_buffer_gl_unbind (CoglBuffer *buffer) +{ + CoglContext *ctx = buffer->context; + + _COGL_RETURN_IF_FAIL (buffer != NULL); + + /* the unbind should pair up with a previous bind */ + _COGL_RETURN_IF_FAIL (ctx->current_buffer[buffer->last_target] == buffer); + + if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) + { + GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target); + GE( ctx, glBindBuffer (gl_target, 0) ); + } + + ctx->current_buffer[buffer->last_target] = NULL; +} diff --git a/cogl/cogl/driver/gl/cogl-clip-stack-gl-private.h b/cogl/cogl/driver/gl/cogl-clip-stack-gl-private.h new file mode 100644 index 000000000..ff22d2660 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-clip-stack-gl-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#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_ */ diff --git a/cogl/cogl/driver/gl/cogl-clip-stack-gl.c b/cogl/cogl/driver/gl/cogl-clip-stack-gl.c new file mode 100644 index 000000000..ea1ae620a --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-clip-stack-gl.c @@ -0,0 +1,627 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010,2011,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-primitives-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-clip-stack-gl-private.h" +#include "cogl-primitive-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: + case COGL_DRIVER_GL3: + 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, ctx->stencil_pipeline, + framebuffer, FALSE, FALSE); + + 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_primitive_silhouette (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + void *user_data) +{ + _cogl_primitive_draw (user_data, + framebuffer, + pipeline, + 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 && + (ctx->needs_viewport_scissor_workaround == FALSE || + (framebuffer->viewport_age == + framebuffer->viewport_age_for_scissor_workaround && + ctx->viewport_scissor_workaround_framebuffer == + framebuffer))) + 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 = + _cogl_has_private_feature (ctx, 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 + * + * See comment below about ctx->needs_viewport_scissor_workaround + */ + if (stack == NULL && !ctx->needs_viewport_scissor_workaround) + { + 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); + + /* XXX: ONGOING BUG: Intel viewport scissor + * + * Intel gen6 drivers don't correctly handle offset viewports, since + * primitives aren't clipped within the bounds of the viewport. To + * workaround this we push our own clip for the viewport that will + * use scissoring to ensure we clip as expected. + * + * TODO: file a bug upstream! + */ + if (ctx->needs_viewport_scissor_workaround) + { + _cogl_util_scissor_intersect (framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_x + + framebuffer->viewport_width, + framebuffer->viewport_y + + framebuffer->viewport_height, + &scissor_x0, &scissor_y0, + &scissor_x1, &scissor_y1); + framebuffer->viewport_age_for_scissor_workaround = + framebuffer->viewport_age; + ctx->viewport_scissor_workaround_framebuffer = + framebuffer; + } + + /* 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_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; +} diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h new file mode 100644 index 000000000..47a1c1c96 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h @@ -0,0 +1,102 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_FRAMEBUFFER_GL_PRIVATE_H__ +#define __COGL_FRAMEBUFFER_GL_PRIVATE_H__ + +CoglBool +_cogl_offscreen_gl_allocate (CoglOffscreen *offscreen, + CoglError **error); + +void +_cogl_offscreen_gl_free (CoglOffscreen *offscreen); + +void +_cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state); + +void +_cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha); + +void +_cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits); + +void +_cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers); + +void +_cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target); + +void +_cogl_framebuffer_gl_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +void +_cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +CoglBool +_cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error); + +#endif /* __COGL_FRAMEBUFFER_GL_PRIVATE_H__ */ + + diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c new file mode 100644 index 000000000..a30ccc1f8 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c @@ -0,0 +1,1624 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-framebuffer-gl-private.h" +#include "cogl-buffer-gl-private.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-texture-private.h" + +#include +#include + +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D00 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif +#ifndef GL_DEPTH24_STENCIL8 +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#endif +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif +#ifndef GL_TEXTURE_SAMPLES_IMG +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif +#ifndef GL_PACK_INVERT_MESA +#define GL_PACK_INVERT_MESA 0x8758 +#endif +#ifndef GL_BACK_LEFT +#define GL_BACK_LEFT 0x0402 +#endif +#ifndef GL_BACK_RIGHT +#define GL_BACK_RIGHT 0x0403 +#endif + +#ifndef GL_COLOR +#define GL_COLOR 0x1800 +#endif +#ifndef GL_DEPTH +#define GL_DEPTH 0x1801 +#endif +#ifndef GL_STENCIL +#define GL_STENCIL 0x1802 +#endif + + +static void +_cogl_framebuffer_gl_flush_viewport_state (CoglFramebuffer *framebuffer) +{ + float gl_viewport_y; + + g_assert (framebuffer->viewport_width >=0 && + framebuffer->viewport_height >=0); + + /* Convert the Cogl viewport y offset to an OpenGL viewport y offset + * NB: OpenGL defines its window and viewport origins to be bottom + * left, while Cogl defines them to be top left. + * NB: We render upside down to offscreen framebuffers so we don't + * need to convert the y offset in this case. */ + if (cogl_is_offscreen (framebuffer)) + gl_viewport_y = framebuffer->viewport_y; + else + gl_viewport_y = framebuffer->height - + (framebuffer->viewport_y + framebuffer->viewport_height); + + COGL_NOTE (OPENGL, "Calling glViewport(%f, %f, %f, %f)", + framebuffer->viewport_x, + gl_viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height); + + GE (framebuffer->context, + glViewport (framebuffer->viewport_x, + gl_viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height)); +} + +static void +_cogl_framebuffer_gl_flush_clip_state (CoglFramebuffer *framebuffer) +{ + _cogl_clip_stack_flush (framebuffer->clip_stack, framebuffer); +} + +static void +_cogl_framebuffer_gl_flush_dither_state (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + if (ctx->current_gl_dither_enabled != framebuffer->dither_enabled) + { + if (framebuffer->dither_enabled) + GE (ctx, glEnable (GL_DITHER)); + else + GE (ctx, glDisable (GL_DITHER)); + ctx->current_gl_dither_enabled = framebuffer->dither_enabled; + } +} + +static void +_cogl_framebuffer_gl_flush_modelview_state (CoglFramebuffer *framebuffer) +{ + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + _cogl_context_set_current_modelview_entry (framebuffer->context, + modelview_entry); +} + +static void +_cogl_framebuffer_gl_flush_projection_state (CoglFramebuffer *framebuffer) +{ + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + _cogl_context_set_current_projection_entry (framebuffer->context, + projection_entry); +} + +static void +_cogl_framebuffer_gl_flush_color_mask_state (CoglFramebuffer *framebuffer) +{ + CoglContext *context = framebuffer->context; + + /* The color mask state is really owned by a CoglPipeline so to + * ensure the color mask is updated the next time we draw something + * we need to make sure the logic ops for the pipeline are + * re-flushed... */ + context->current_pipeline_changes_since_flush |= + COGL_PIPELINE_STATE_LOGIC_OPS; + context->current_pipeline_age--; +} + +static void +_cogl_framebuffer_gl_flush_front_face_winding_state (CoglFramebuffer *framebuffer) +{ + CoglContext *context = framebuffer->context; + CoglPipelineCullFaceMode mode; + + /* NB: The face winding state is actually owned by the current + * CoglPipeline. + * + * If we don't have a current pipeline then we can just assume that + * when we later do flush a pipeline we will check the current + * framebuffer to know how to setup the winding */ + if (!context->current_pipeline) + return; + + mode = cogl_pipeline_get_cull_face_mode (context->current_pipeline); + + /* If the current CoglPipeline has a culling mode that doesn't care + * about the winding we can avoid forcing an update of the state and + * bail out. */ + if (mode == COGL_PIPELINE_CULL_FACE_MODE_NONE || + mode == COGL_PIPELINE_CULL_FACE_MODE_BOTH) + return; + + /* Since the winding state is really owned by the current pipeline + * the way we "flush" an updated winding is to dirty the pipeline + * state... */ + context->current_pipeline_changes_since_flush |= + COGL_PIPELINE_STATE_CULL_FACE; + context->current_pipeline_age--; +} + +static void +_cogl_framebuffer_gl_flush_stereo_mode_state (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + GLenum draw_buffer = GL_BACK; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN) + return; + + if (!ctx->glDrawBuffer) + return; + + /* The one-shot default draw buffer setting in _cogl_framebuffer_gl_bind + * must have already happened. If not it would override what we set here. */ + g_assert (ctx->was_bound_to_onscreen); + + switch (framebuffer->stereo_mode) + { + case COGL_STEREO_BOTH: + draw_buffer = GL_BACK; + break; + case COGL_STEREO_LEFT: + draw_buffer = GL_BACK_LEFT; + break; + case COGL_STEREO_RIGHT: + draw_buffer = GL_BACK_RIGHT; + break; + } + + if (ctx->current_gl_draw_buffer != draw_buffer) + { + GE (ctx, glDrawBuffer (draw_buffer)); + ctx->current_gl_draw_buffer = draw_buffer; + } +} + +void +_cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target) +{ + CoglContext *ctx = framebuffer->context; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN) + { + CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer); + GE (ctx, glBindFramebuffer (target, + offscreen->gl_framebuffer.fbo_handle)); + } + else + { + const CoglWinsysVtable *winsys = + _cogl_framebuffer_get_winsys (framebuffer); + winsys->onscreen_bind (COGL_ONSCREEN (framebuffer)); + /* glBindFramebuffer is an an extension with OpenGL ES 1.1 */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + GE (ctx, glBindFramebuffer (target, 0)); + + /* Initialise the glDrawBuffer state the first time the context + * is bound to the default framebuffer. If the winsys is using a + * surfaceless context for the initial make current then the + * default draw buffer will be GL_NONE so we need to correct + * that. We can't do it any earlier because binding GL_BACK when + * there is no default framebuffer won't work */ + if (!ctx->was_bound_to_onscreen) + { + if (ctx->glDrawBuffer) + { + GE (ctx, glDrawBuffer (GL_BACK)); + } + else if (ctx->glDrawBuffers) + { + /* glDrawBuffer isn't available on GLES 3.0 so we need + * to be able to use glDrawBuffers as well. On GLES 2 + * neither is available but the state should always be + * GL_BACK anyway so we don't need to set anything. On + * desktop GL this must be GL_BACK_LEFT instead of + * GL_BACK but as this code path will only be hit for + * GLES we can just use GL_BACK. */ + static const GLenum buffers[] = { GL_BACK }; + + GE (ctx, glDrawBuffers (G_N_ELEMENTS (buffers), buffers)); + } + + ctx->was_bound_to_onscreen = TRUE; + } + } +} + +void +_cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state) +{ + CoglContext *ctx = draw_buffer->context; + unsigned long differences; + int bit; + + /* We can assume that any state that has changed for the current + * framebuffer is different to the currently flushed value. */ + differences = ctx->current_draw_buffer_changes; + + /* Any state of the current framebuffer that hasn't already been + * flushed is assumed to be unknown so we will always flush that + * state if asked. */ + differences |= ~ctx->current_draw_buffer_state_flushed; + + /* We only need to consider the state we've been asked to flush */ + differences &= state; + + if (ctx->current_draw_buffer != draw_buffer) + { + /* If the previous draw buffer is NULL then we'll assume + everything has changed. This can happen if a framebuffer is + destroyed while it is the last flushed draw buffer. In that + case the framebuffer destructor will set + ctx->current_draw_buffer to NULL */ + if (ctx->current_draw_buffer == NULL) + differences |= state; + else + /* NB: we only need to compare the state we're being asked to flush + * and we don't need to compare the state we've already decided + * we will definitely flush... */ + differences |= _cogl_framebuffer_compare (ctx->current_draw_buffer, + draw_buffer, + state & ~differences); + + /* NB: we don't take a reference here, to avoid a circular + * reference. */ + ctx->current_draw_buffer = draw_buffer; + ctx->current_draw_buffer_state_flushed = 0; + } + + if (ctx->current_read_buffer != read_buffer && + state & COGL_FRAMEBUFFER_STATE_BIND) + { + differences |= COGL_FRAMEBUFFER_STATE_BIND; + /* NB: we don't take a reference here, to avoid a circular + * reference. */ + ctx->current_read_buffer = read_buffer; + } + + if (!differences) + return; + + /* Lazily ensure the framebuffers have been allocated */ + if (G_UNLIKELY (!draw_buffer->allocated)) + cogl_framebuffer_allocate (draw_buffer, NULL); + if (G_UNLIKELY (!read_buffer->allocated)) + cogl_framebuffer_allocate (read_buffer, NULL); + + /* We handle buffer binding separately since the method depends on whether + * we are binding the same buffer for read and write or not unlike all + * other state that only relates to the draw_buffer. */ + if (differences & COGL_FRAMEBUFFER_STATE_BIND) + { + if (draw_buffer == read_buffer) + _cogl_framebuffer_gl_bind (draw_buffer, GL_FRAMEBUFFER); + else + { + /* NB: Currently we only take advantage of binding separate + * read/write buffers for offscreen framebuffer blit + * purposes. */ + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); + _COGL_RETURN_IF_FAIL (draw_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN); + _COGL_RETURN_IF_FAIL (read_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN); + + _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); + _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); + } + + differences &= ~COGL_FRAMEBUFFER_STATE_BIND; + } + + COGL_FLAGS_FOREACH_START (&differences, 1, bit) + { + /* XXX: We considered having an array of callbacks for each state index + * that we'd call here but decided that this way the compiler is more + * likely going to be able to in-line the flush functions and use the + * index to jump straight to the required code. */ + switch (bit) + { + case COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT: + _cogl_framebuffer_gl_flush_viewport_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_CLIP: + _cogl_framebuffer_gl_flush_clip_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_DITHER: + _cogl_framebuffer_gl_flush_dither_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW: + _cogl_framebuffer_gl_flush_modelview_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION: + _cogl_framebuffer_gl_flush_projection_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_COLOR_MASK: + _cogl_framebuffer_gl_flush_color_mask_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING: + _cogl_framebuffer_gl_flush_front_face_winding_state (draw_buffer); + break; + case COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE: + /* Nothing to do for depth write state change; the state will always + * be taken into account when flushing the pipeline's depth state. */ + break; + case COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE: + _cogl_framebuffer_gl_flush_stereo_mode_state (draw_buffer); + break; + default: + g_warn_if_reached (); + } + } + COGL_FLAGS_FOREACH_END; + + ctx->current_draw_buffer_state_flushed |= state; + ctx->current_draw_buffer_changes &= ~state; +} + +static CoglTexture * +create_depth_texture (CoglContext *ctx, + int width, + int height) +{ + CoglTexture2D *depth_texture = + cogl_texture_2d_new_with_size (ctx, width, height); + + cogl_texture_set_components (COGL_TEXTURE (depth_texture), + COGL_TEXTURE_COMPONENTS_DEPTH); + + return COGL_TEXTURE (depth_texture); +} + +static CoglTexture * +attach_depth_texture (CoglContext *ctx, + CoglTexture *depth_texture, + CoglOffscreenAllocateFlags flags) +{ + GLuint tex_gl_handle; + GLenum tex_gl_target; + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) + { + /* attach a GL_DEPTH_STENCIL texture to the GL_DEPTH_ATTACHMENT and + * GL_STENCIL_ATTACHMENT attachement points */ + g_assert (_cogl_texture_get_format (depth_texture) == + COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8); + + cogl_texture_get_gl_texture (depth_texture, + &tex_gl_handle, &tex_gl_target); + + GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + tex_gl_target, tex_gl_handle, + 0)); + GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + tex_gl_target, tex_gl_handle, + 0)); + } + else if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) + { + /* attach a newly created GL_DEPTH_COMPONENT16 texture to the + * GL_DEPTH_ATTACHMENT attachement point */ + g_assert (_cogl_texture_get_format (depth_texture) == + COGL_PIXEL_FORMAT_DEPTH_16); + + cogl_texture_get_gl_texture (COGL_TEXTURE (depth_texture), + &tex_gl_handle, &tex_gl_target); + + GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + tex_gl_target, tex_gl_handle, + 0)); + } + + return COGL_TEXTURE (depth_texture); +} + +static GList * +try_creating_renderbuffers (CoglContext *ctx, + int width, + int height, + CoglOffscreenAllocateFlags flags, + int n_samples) +{ + GList *renderbuffers = NULL; + GLuint gl_depth_stencil_handle; + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) + { + GLenum format; + + /* WebGL adds a GL_DEPTH_STENCIL_ATTACHMENT and requires that we + * use the GL_DEPTH_STENCIL format. */ +#ifdef HAVE_COGL_WEBGL + format = GL_DEPTH_STENCIL; +#else + /* Although GL_OES_packed_depth_stencil is mostly equivalent to + * GL_EXT_packed_depth_stencil, one notable difference is that + * GL_OES_packed_depth_stencil doesn't allow GL_DEPTH_STENCIL to + * be passed as an internal format to glRenderbufferStorage. + */ + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL)) + format = GL_DEPTH_STENCIL; + else + { + _COGL_RETURN_VAL_IF_FAIL ( + _cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL), + NULL); + format = GL_DEPTH24_STENCIL8; + } +#endif + + /* Create a renderbuffer for depth and stenciling */ + GE (ctx, glGenRenderbuffers (1, &gl_depth_stencil_handle)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle)); + if (n_samples) + GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, + n_samples, + format, + width, height)); + else + GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, format, + width, height)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); + + +#ifdef HAVE_COGL_WEBGL + GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + gl_depth_stencil_handle)); +#else + GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + gl_depth_stencil_handle)); + GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + gl_depth_stencil_handle)); +#endif + renderbuffers = + g_list_prepend (renderbuffers, + GUINT_TO_POINTER (gl_depth_stencil_handle)); + } + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) + { + GLuint gl_depth_handle; + + GE (ctx, glGenRenderbuffers (1, &gl_depth_handle)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle)); + /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's + * available under GLES */ + if (n_samples) + GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, + n_samples, + GL_DEPTH_COMPONENT16, + width, height)); + else + GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + width, height)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); + GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, gl_depth_handle)); + renderbuffers = + g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_handle)); + } + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL) + { + GLuint gl_stencil_handle; + + GE (ctx, glGenRenderbuffers (1, &gl_stencil_handle)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle)); + if (n_samples) + GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, + n_samples, + GL_STENCIL_INDEX8, + width, height)); + else + GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, + width, height)); + GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); + GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_stencil_handle)); + renderbuffers = + g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_stencil_handle)); + } + + return renderbuffers; +} + +static void +delete_renderbuffers (CoglContext *ctx, GList *renderbuffers) +{ + GList *l; + + for (l = renderbuffers; l; l = l->next) + { + GLuint renderbuffer = GPOINTER_TO_UINT (l->data); + GE (ctx, glDeleteRenderbuffers (1, &renderbuffer)); + } + + g_list_free (renderbuffers); +} + +/* + * NB: This function may be called with a standalone GLES2 context + * bound so we can create a shadow framebuffer that wraps the same + * CoglTexture as the given CoglOffscreen. This function shouldn't + * modify anything in + */ +static CoglBool +try_creating_fbo (CoglContext *ctx, + CoglTexture *texture, + int texture_level, + int texture_level_width, + int texture_level_height, + CoglTexture *depth_texture, + CoglFramebufferConfig *config, + CoglOffscreenAllocateFlags flags, + CoglGLFramebuffer *gl_framebuffer) +{ + GLuint tex_gl_handle; + GLenum tex_gl_target; + GLenum status; + int n_samples; + + if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target)) + return FALSE; + + if (tex_gl_target != GL_TEXTURE_2D +#ifdef HAVE_COGL_GL + && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB +#endif + ) + return FALSE; + + if (config->samples_per_pixel) + { + if (!ctx->glFramebufferTexture2DMultisampleIMG) + return FALSE; + n_samples = config->samples_per_pixel; + } + else + n_samples = 0; + + /* We are about to generate and bind a new fbo, so we pretend to + * change framebuffer state so that the old framebuffer will be + * rebound again before drawing. */ + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND; + + /* Generate framebuffer */ + ctx->glGenFramebuffers (1, &gl_framebuffer->fbo_handle); + GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, gl_framebuffer->fbo_handle)); + + if (n_samples) + { + GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, + n_samples, + texture_level)); + } + else + GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, + texture_level)); + + /* attach either a depth/stencil texture, a depth texture or render buffers + * depending on what we've been asked to provide */ + + if (depth_texture && + flags & (COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL | + COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH)) + { + attach_depth_texture (ctx, depth_texture, flags); + + /* Let's clear the flags that are now fulfilled as we might need to + * create renderbuffers (for the ALLOCATE_FLAG_DEPTH | + * ALLOCATE_FLAG_STENCIL case) */ + flags &= ~(COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL | + COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH); + } + + if (flags) + { + gl_framebuffer->renderbuffers = + try_creating_renderbuffers (ctx, + texture_level_width, + texture_level_height, + flags, + n_samples); + } + + /* Make sure it's complete */ + status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + GE (ctx, glDeleteFramebuffers (1, &gl_framebuffer->fbo_handle)); + + delete_renderbuffers (ctx, gl_framebuffer->renderbuffers); + gl_framebuffer->renderbuffers = NULL; + + return FALSE; + } + + /* Update the real number of samples_per_pixel now that we have a + * complete framebuffer */ + if (n_samples) + { + GLenum attachment = GL_COLOR_ATTACHMENT0; + GLenum pname = GL_TEXTURE_SAMPLES_IMG; + int texture_samples; + + GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, + attachment, + pname, + &texture_samples) ); + gl_framebuffer->samples_per_pixel = texture_samples; + } + + return TRUE; +} + +CoglBool +_cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx, + CoglTexture *texture, + int texture_level, + int texture_level_width, + int texture_level_height, + CoglTexture *depth_texture, + CoglFramebufferConfig *config, + CoglOffscreenAllocateFlags flags, + CoglGLFramebuffer *gl_framebuffer) +{ + return try_creating_fbo (ctx, + texture, + texture_level, + texture_level_width, + texture_level_height, + depth_texture, + config, + flags, + gl_framebuffer); +} + +CoglBool +_cogl_offscreen_gl_allocate (CoglOffscreen *offscreen, + CoglError **error) +{ + CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen); + CoglContext *ctx = fb->context; + CoglOffscreenAllocateFlags flags; + CoglGLFramebuffer *gl_framebuffer = &offscreen->gl_framebuffer; + int level_width; + int level_height; + + _COGL_RETURN_VAL_IF_FAIL (offscreen->texture_level < + _cogl_texture_get_n_levels (offscreen->texture), + FALSE); + + _cogl_texture_get_level_size (offscreen->texture, + offscreen->texture_level, + &level_width, + &level_height, + NULL); + + if (fb->config.depth_texture_enabled && + offscreen->depth_texture == NULL) + { + offscreen->depth_texture = + create_depth_texture (ctx, + level_width, + level_height); + + if (!cogl_texture_allocate (offscreen->depth_texture, error)) + { + cogl_object_unref (offscreen->depth_texture); + offscreen->depth_texture = NULL; + return FALSE; + } + + _cogl_texture_associate_framebuffer (offscreen->depth_texture, fb); + } + + /* XXX: The framebuffer_object spec isn't clear in defining whether attaching + * a texture as a renderbuffer with mipmap filtering enabled while the + * mipmaps have not been uploaded should result in an incomplete framebuffer + * object. (different drivers make different decisions) + * + * To avoid an error with drivers that do consider this a problem we + * explicitly set non mipmapped filters here. These will later be reset when + * the texture is actually used for rendering according to the filters set on + * the corresponding CoglPipeline. + */ + _cogl_texture_gl_flush_legacy_texobj_filters (offscreen->texture, + GL_NEAREST, GL_NEAREST); + + if (((offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL) && + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = 0, + gl_framebuffer)) || + + (ctx->have_last_offscreen_allocate_flags && + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = ctx->last_offscreen_allocate_flags, + gl_framebuffer)) || + + ( + /* NB: WebGL introduces a DEPTH_STENCIL_ATTACHMENT and doesn't + * need an extension to handle _FLAG_DEPTH_STENCIL */ +#ifndef HAVE_COGL_WEBGL + (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || + _cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) && +#endif + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL, + gl_framebuffer)) || + + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH | + COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, + gl_framebuffer) || + + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, + gl_framebuffer) || + + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH, + gl_framebuffer) || + + try_creating_fbo (ctx, + offscreen->texture, + offscreen->texture_level, + level_width, + level_height, + offscreen->depth_texture, + &fb->config, + flags = 0, + gl_framebuffer)) + { + fb->samples_per_pixel = gl_framebuffer->samples_per_pixel; + + if (!offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL) + { + /* Record that the last set of flags succeeded so that we can + try that set first next time */ + ctx->last_offscreen_allocate_flags = flags; + ctx->have_last_offscreen_allocate_flags = TRUE; + } + + /* Save the flags we managed to successfully allocate the + * renderbuffers with in case we need to make renderbuffers for a + * GLES2 context later */ + offscreen->allocation_flags = flags; + + return TRUE; + } + else + { + _cogl_set_error (error, COGL_FRAMEBUFFER_ERROR, + COGL_FRAMEBUFFER_ERROR_ALLOCATE, + "Failed to create an OpenGL framebuffer object"); + return FALSE; + } +} + +void +_cogl_offscreen_gl_free (CoglOffscreen *offscreen) +{ + CoglContext *ctx = COGL_FRAMEBUFFER (offscreen)->context; + + delete_renderbuffers (ctx, offscreen->gl_framebuffer.renderbuffers); + + GE (ctx, glDeleteFramebuffers (1, &offscreen->gl_framebuffer.fbo_handle)); +} + +void +_cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha) +{ + CoglContext *ctx = framebuffer->context; + GLbitfield gl_buffers = 0; + + if (buffers & COGL_BUFFER_BIT_COLOR) + { + GE( ctx, glClearColor (red, green, blue, alpha) ); + gl_buffers |= GL_COLOR_BUFFER_BIT; + + if (ctx->current_gl_color_mask != framebuffer->color_mask) + { + CoglColorMask color_mask = framebuffer->color_mask; + GE( ctx, glColorMask (!!(color_mask & COGL_COLOR_MASK_RED), + !!(color_mask & COGL_COLOR_MASK_GREEN), + !!(color_mask & COGL_COLOR_MASK_BLUE), + !!(color_mask & COGL_COLOR_MASK_ALPHA))); + ctx->current_gl_color_mask = color_mask; + /* Make sure the ColorMask is updated when the next primitive is drawn */ + ctx->current_pipeline_changes_since_flush |= + COGL_PIPELINE_STATE_LOGIC_OPS; + ctx->current_pipeline_age--; + } + } + + if (buffers & COGL_BUFFER_BIT_DEPTH) + { + gl_buffers |= GL_DEPTH_BUFFER_BIT; + + if (ctx->depth_writing_enabled_cache != framebuffer->depth_writing_enabled) + { + GE( ctx, glDepthMask (framebuffer->depth_writing_enabled)); + + ctx->depth_writing_enabled_cache = framebuffer->depth_writing_enabled; + + /* Make sure the DepthMask is updated when the next primitive is drawn */ + ctx->current_pipeline_changes_since_flush |= + COGL_PIPELINE_STATE_DEPTH; + ctx->current_pipeline_age--; + } + } + + if (buffers & COGL_BUFFER_BIT_STENCIL) + gl_buffers |= GL_STENCIL_BUFFER_BIT; + + + GE (ctx, glClear (gl_buffers)); +} + +static inline void +_cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + if (G_LIKELY (!framebuffer->dirty_bitmasks)) + return; + + cogl_framebuffer_allocate (framebuffer, NULL); + + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + +#ifdef HAVE_COGL_GL + if ((ctx->driver == COGL_DRIVER_GL3 && + framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) || + (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS) && + framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)) + { + gboolean is_offscreen = framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN; + const struct { + GLenum attachment, pname; + size_t offset; + } params[] = { + { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, + offsetof (CoglFramebufferBits, red) }, + { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + offsetof (CoglFramebufferBits, green) }, + { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + offsetof (CoglFramebufferBits, blue) }, + { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + offsetof (CoglFramebufferBits, alpha) }, + { is_offscreen ? GL_DEPTH_ATTACHMENT : GL_DEPTH, + GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + offsetof (CoglFramebufferBits, depth) }, + { is_offscreen ? GL_STENCIL_ATTACHMENT : GL_STENCIL, + GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + offsetof (CoglFramebufferBits, stencil) }, + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (params); i++) + { + int *value = + (int *) ((uint8_t *) &framebuffer->bits + params[i].offset); + GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, + params[i].attachment, + params[i].pname, + value) ); + } + } + else +#endif /* HAVE_COGL_GL */ + { + GE( ctx, glGetIntegerv (GL_RED_BITS, &framebuffer->bits.red) ); + GE( ctx, glGetIntegerv (GL_GREEN_BITS, &framebuffer->bits.green) ); + GE( ctx, glGetIntegerv (GL_BLUE_BITS, &framebuffer->bits.blue) ); + GE( ctx, glGetIntegerv (GL_ALPHA_BITS, &framebuffer->bits.alpha) ); + GE( ctx, glGetIntegerv (GL_DEPTH_BITS, &framebuffer->bits.depth) ); + GE( ctx, glGetIntegerv (GL_STENCIL_BITS, &framebuffer->bits.stencil) ); + } + + /* If we don't have alpha textures then the alpha bits are actually + * stored in the red component */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && + framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN && + framebuffer->internal_format == COGL_PIXEL_FORMAT_A_8) + { + framebuffer->bits.alpha = framebuffer->bits.red; + framebuffer->bits.red = 0; + } + + COGL_NOTE (OFFSCREEN, + "RGBA/D/S Bits for framebuffer[%p, %s]: %d, %d, %d, %d, %d, %d", + framebuffer, + framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN + ? "offscreen" + : "onscreen", + framebuffer->bits.red, + framebuffer->bits.blue, + framebuffer->bits.green, + framebuffer->bits.alpha, + framebuffer->bits.depth, + framebuffer->bits.stencil); + + framebuffer->dirty_bitmasks = FALSE; +} + +void +_cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits) +{ + _cogl_framebuffer_init_bits (framebuffer); + + /* TODO: cache these in some driver specific location not + * directly as part of CoglFramebuffer. */ + *bits = framebuffer->bits; +} + +void +_cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer) +{ + GE (framebuffer->context, glFinish ()); +} + +void +_cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers) +{ + CoglContext *ctx = framebuffer->context; + + if (ctx->glDiscardFramebuffer) + { + GLenum attachments[3]; + int i = 0; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + { + if (buffers & COGL_BUFFER_BIT_COLOR) + attachments[i++] = GL_COLOR; + if (buffers & COGL_BUFFER_BIT_DEPTH) + attachments[i++] = GL_DEPTH; + if (buffers & COGL_BUFFER_BIT_STENCIL) + attachments[i++] = GL_STENCIL; + } + else + { + if (buffers & COGL_BUFFER_BIT_COLOR) + attachments[i++] = GL_COLOR_ATTACHMENT0; + if (buffers & COGL_BUFFER_BIT_DEPTH) + attachments[i++] = GL_DEPTH_ATTACHMENT; + if (buffers & COGL_BUFFER_BIT_STENCIL) + attachments[i++] = GL_STENCIL_ATTACHMENT; + } + + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + GE (ctx, glDiscardFramebuffer (GL_FRAMEBUFFER, i, attachments)); + } +} + +void +_cogl_framebuffer_gl_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ + _cogl_flush_attributes_state (framebuffer, pipeline, flags, + attributes, n_attributes); + + GE (framebuffer->context, + glDrawArrays ((GLenum)mode, first_vertex, n_vertices)); +} + +static size_t +sizeof_index_type (CoglIndicesType type) +{ + switch (type) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + return 1; + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + return 2; + case COGL_INDICES_TYPE_UNSIGNED_INT: + return 4; + } + g_return_val_if_reached (0); +} + +void +_cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ + CoglBuffer *buffer; + uint8_t *base; + size_t buffer_offset; + size_t index_size; + GLenum indices_gl_type = 0; + + _cogl_flush_attributes_state (framebuffer, pipeline, flags, + attributes, n_attributes); + + buffer = COGL_BUFFER (cogl_indices_get_buffer (indices)); + + /* Note: we don't try and catch errors with binding the index buffer + * here since OOM errors at this point indicate that nothing has yet + * been uploaded to the indices buffer which we consider to be a + * programmer error. + */ + base = _cogl_buffer_gl_bind (buffer, + COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, NULL); + buffer_offset = cogl_indices_get_offset (indices); + index_size = sizeof_index_type (cogl_indices_get_type (indices)); + + switch (cogl_indices_get_type (indices)) + { + case COGL_INDICES_TYPE_UNSIGNED_BYTE: + indices_gl_type = GL_UNSIGNED_BYTE; + break; + case COGL_INDICES_TYPE_UNSIGNED_SHORT: + indices_gl_type = GL_UNSIGNED_SHORT; + break; + case COGL_INDICES_TYPE_UNSIGNED_INT: + indices_gl_type = GL_UNSIGNED_INT; + break; + } + + GE (framebuffer->context, + glDrawElements ((GLenum)mode, + n_vertices, + indices_gl_type, + base + buffer_offset + index_size * first_vertex)); + + _cogl_buffer_gl_unbind (buffer); +} + +static CoglBool +mesa_46631_slow_read_pixels_workaround (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error) +{ + CoglContext *ctx; + CoglPixelFormat format; + CoglBitmap *pbo; + int width; + int height; + CoglBool res; + uint8_t *dst; + const uint8_t *src; + + ctx = cogl_framebuffer_get_context (framebuffer); + + width = cogl_bitmap_get_width (bitmap); + height = cogl_bitmap_get_height (bitmap); + format = cogl_bitmap_get_format (bitmap); + + pbo = cogl_bitmap_new_with_size (ctx, width, height, format); + + /* Read into the pbo. We need to disable the flipping because the + blit fast path in the driver does not work with + GL_PACK_INVERT_MESA is set */ + res = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, + source | + COGL_READ_PIXELS_NO_FLIP, + pbo, + error); + if (!res) + { + cogl_object_unref (pbo); + return FALSE; + } + + /* Copy the pixels back into application's buffer */ + dst = _cogl_bitmap_map (bitmap, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + error); + if (!dst) + { + cogl_object_unref (pbo); + return FALSE; + } + + src = _cogl_bitmap_map (pbo, + COGL_BUFFER_ACCESS_READ, + 0, /* hints */ + error); + if (src) + { + int src_rowstride = cogl_bitmap_get_rowstride (pbo); + int dst_rowstride = cogl_bitmap_get_rowstride (bitmap); + int to_copy = + _cogl_pixel_format_get_bytes_per_pixel (format) * width; + int y; + + /* If the framebuffer is onscreen we need to flip the + data while copying */ + if (!cogl_is_offscreen (framebuffer)) + { + src += src_rowstride * (height - 1); + src_rowstride = -src_rowstride; + } + + for (y = 0; y < height; y++) + { + memcpy (dst, src, to_copy); + dst += dst_rowstride; + src += src_rowstride; + } + + _cogl_bitmap_unmap (pbo); + } + else + res = FALSE; + + _cogl_bitmap_unmap (bitmap); + + cogl_object_unref (pbo); + + return res; +} + +CoglBool +_cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error) +{ + CoglContext *ctx = framebuffer->context; + int framebuffer_height = cogl_framebuffer_get_height (framebuffer); + int width = cogl_bitmap_get_width (bitmap); + int height = cogl_bitmap_get_height (bitmap); + CoglPixelFormat format = cogl_bitmap_get_format (bitmap); + CoglPixelFormat required_format; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + CoglBool pack_invert_set; + int status = FALSE; + + /* Workaround for cases where its faster to read into a temporary + * PBO. This is only worth doing if: + * + * • The GPU is an Intel GPU. In that case there is a known + * fast-path when reading into a PBO that will use the blitter + * instead of the Mesa fallback code. The driver bug will only be + * set if this is the case. + * • We're not already reading into a PBO. + * • The target format is BGRA. The fast-path blit does not get hit + * otherwise. + * • The size of the data is not trivially small. This isn't a + * requirement to hit the fast-path blit but intuitively it feels + * like if the amount of data is too small then the cost of + * allocating a PBO will outweigh the cost of temporarily + * converting the data to floats. + */ + if ((ctx->gpu.driver_bugs & + COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS) && + (width > 8 || height > 8) && + (format & ~COGL_PREMULT_BIT) == COGL_PIXEL_FORMAT_BGRA_8888 && + cogl_bitmap_get_buffer (bitmap) == NULL) + { + CoglError *ignore_error = NULL; + + if (mesa_46631_slow_read_pixels_workaround (framebuffer, + x, y, + source, + bitmap, + &ignore_error)) + return TRUE; + else + cogl_error_free (ignore_error); + } + + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + + /* The y co-ordinate should be given in OpenGL's coordinate system + * so 0 is the bottom row + * + * NB: all offscreen rendering is done upside down so no conversion + * is necissary in this case. + */ + if (!cogl_is_offscreen (framebuffer)) + y = framebuffer_height - y - height; + + required_format = ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_intformat, + &gl_format, + &gl_type); + + /* NB: All offscreen rendering is done upside down so there is no need + * to flip in this case... */ + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_MESA_PACK_INVERT) && + (source & COGL_READ_PIXELS_NO_FLIP) == 0 && + !cogl_is_offscreen (framebuffer)) + { + GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, TRUE)); + pack_invert_set = TRUE; + } + else + pack_invert_set = FALSE; + + /* Under GLES only GL_RGBA with GL_UNSIGNED_BYTE as well as an + implementation specific format under + GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES and + GL_IMPLEMENTATION_COLOR_READ_TYPE_OES is supported. We could try + to be more clever and check if the requested type matches that + but we would need some reliable functions to convert from GL + types to Cogl types. For now, lets just always read in + GL_RGBA/GL_UNSIGNED_BYTE and convert if necessary. We also need + to use this intermediate buffer if the rowstride has padding + because GLES does not support setting GL_ROW_LENGTH */ + if ((!_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT) && + (gl_format != GL_RGBA || gl_type != GL_UNSIGNED_BYTE || + cogl_bitmap_get_rowstride (bitmap) != 4 * width)) || + (required_format & ~COGL_PREMULT_BIT) != (format & ~COGL_PREMULT_BIT)) + { + CoglBitmap *tmp_bmp; + CoglPixelFormat read_format; + int bpp, rowstride; + uint8_t *tmp_data; + CoglBool succeeded; + + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT)) + read_format = required_format; + else + { + read_format = COGL_PIXEL_FORMAT_RGBA_8888; + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + } + + if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (read_format)) + read_format = ((read_format & ~COGL_PREMULT_BIT) | + (framebuffer->internal_format & COGL_PREMULT_BIT)); + + tmp_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, + width, height, + read_format, + error); + if (!tmp_bmp) + goto EXIT; + + bpp = _cogl_pixel_format_get_bytes_per_pixel (read_format); + rowstride = cogl_bitmap_get_rowstride (tmp_bmp); + + ctx->texture_driver->prep_gl_for_pixels_download (ctx, + rowstride, + width, + bpp); + + /* Note: we don't worry about catching errors here since we know + * we won't be lazily allocating storage for this buffer so it + * won't fail due to lack of memory. */ + tmp_data = _cogl_bitmap_gl_bind (tmp_bmp, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD, + NULL); + + GE( ctx, glReadPixels (x, y, width, height, + gl_format, gl_type, + tmp_data) ); + + _cogl_bitmap_gl_unbind (tmp_bmp); + + succeeded = _cogl_bitmap_convert_into_bitmap (tmp_bmp, bitmap, error); + + cogl_object_unref (tmp_bmp); + + if (!succeeded) + goto EXIT; + } + else + { + CoglBitmap *shared_bmp; + CoglPixelFormat bmp_format; + int bpp, rowstride; + CoglBool succeeded = FALSE; + uint8_t *pixels; + CoglError *internal_error = NULL; + + rowstride = cogl_bitmap_get_rowstride (bitmap); + + /* We match the premultiplied state of the target buffer to the + * premultiplied state of the framebuffer so that it will get + * converted to the right format below */ + if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) + bmp_format = ((format & ~COGL_PREMULT_BIT) | + (framebuffer->internal_format & COGL_PREMULT_BIT)); + else + bmp_format = format; + + if (bmp_format != format) + shared_bmp = _cogl_bitmap_new_shared (bitmap, + bmp_format, + width, height, + rowstride); + else + shared_bmp = cogl_object_ref (bitmap); + + bpp = _cogl_pixel_format_get_bytes_per_pixel (bmp_format); + + ctx->texture_driver->prep_gl_for_pixels_download (ctx, + rowstride, + width, + bpp); + + pixels = _cogl_bitmap_gl_bind (shared_bmp, + COGL_BUFFER_ACCESS_WRITE, + 0, /* hints */ + &internal_error); + /* NB: _cogl_bitmap_gl_bind() can return NULL in sucessfull + * cases so we have to explicitly check the cogl error pointer + * to know if there was a problem */ + if (internal_error) + { + cogl_object_unref (shared_bmp); + _cogl_propagate_error (error, internal_error); + goto EXIT; + } + + GE( ctx, glReadPixels (x, y, + width, height, + gl_format, gl_type, + pixels) ); + + _cogl_bitmap_gl_unbind (shared_bmp); + + /* Convert to the premult format specified by the caller + in-place. This will do nothing if the premult status is already + correct. */ + if (_cogl_bitmap_convert_premult_status (shared_bmp, format, error)) + succeeded = TRUE; + + cogl_object_unref (shared_bmp); + + if (!succeeded) + goto EXIT; + } + + /* NB: All offscreen rendering is done upside down so there is no need + * to flip in this case... */ + if (!cogl_is_offscreen (framebuffer) && + (source & COGL_READ_PIXELS_NO_FLIP) == 0 && + !pack_invert_set) + { + uint8_t *temprow; + int rowstride; + uint8_t *pixels; + + rowstride = cogl_bitmap_get_rowstride (bitmap); + pixels = _cogl_bitmap_map (bitmap, + COGL_BUFFER_ACCESS_READ | + COGL_BUFFER_ACCESS_WRITE, + 0, /* hints */ + error); + + if (pixels == NULL) + goto EXIT; + + temprow = g_alloca (rowstride * sizeof (uint8_t)); + + /* vertically flip the buffer in-place */ + for (y = 0; y < height / 2; y++) + { + if (y != height - y - 1) /* skip center row */ + { + memcpy (temprow, + pixels + y * rowstride, rowstride); + memcpy (pixels + y * rowstride, + pixels + (height - y - 1) * rowstride, rowstride); + memcpy (pixels + (height - y - 1) * rowstride, + temprow, + rowstride); + } + } + + _cogl_bitmap_unmap (bitmap); + } + + status = TRUE; + +EXIT: + + /* Currently this function owns the pack_invert state and we don't want this + * to interfere with other Cogl components so all other code can assume that + * we leave the pack_invert state off. */ + if (pack_invert_set) + GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, FALSE)); + + return status; +} diff --git a/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed-private.h b/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed-private.h new file mode 100644 index 000000000..6f259f998 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed-private.h @@ -0,0 +1,42 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_FRAGEND_FIXED_PRIVATE_H +#define __COGL_PIPELINE_FRAGEND_FIXED_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineFragend _cogl_pipeline_fixed_fragend; + +#endif /* __COGL_PIPELINE_FRAGEND_FIXED_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed.c b/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed.c new file mode 100644 index 000000000..55b095687 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed.c @@ -0,0 +1,435 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-pipeline-opengl-private.h" + +#ifdef COGL_PIPELINE_FRAGEND_FIXED + +#include "cogl-context-private.h" +#include "cogl-object-private.h" + +#include "cogl-texture-private.h" +#include "cogl-blend-string.h" +#include "cogl-profile.h" +#include "cogl-program-private.h" + +#include +#include +#include + +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#endif + +const CoglPipelineFragend _cogl_pipeline_fixed_fragend; + +static void +_cogl_disable_texture_unit (int unit_index) +{ + CoglTextureUnit *unit; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + unit = &g_array_index (ctx->texture_units, CoglTextureUnit, unit_index); + + if (unit->enabled_gl_target) + { + _cogl_set_active_texture_unit (unit_index); + GE (ctx, glDisable (unit->enabled_gl_target)); + unit->enabled_gl_target = 0; + } +} + +static int +get_max_texture_units (void) +{ + _COGL_GET_CONTEXT (ctx, 0); + + /* This function is called quite often so we cache the value to + avoid too many GL calls */ + if (ctx->max_texture_units == -1) + { + ctx->max_texture_units = 1; + GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_UNITS, + &ctx->max_texture_units)); + } + + return ctx->max_texture_units; +} + +static void +_cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); +} + +static void +translate_sources (CoglPipeline *pipeline, + int n_sources, + CoglPipelineCombineSource *source_in, + GLenum *source_out) +{ + int i; + + /* The texture source numbers specified in the layer combine are the + layer numbers so we need to map these to unit indices */ + + for (i = 0; i < n_sources; i++) + switch (source_in[i]) + { + case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: + source_out[i] = GL_TEXTURE; + break; + + case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: + source_out[i] = GL_CONSTANT; + break; + + case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: + source_out[i] = GL_PRIMARY_COLOR; + break; + + case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: + source_out[i] = GL_PREVIOUS; + break; + + default: + { + int layer_num = source_in[i] - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; + CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *layer = + _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); + + if (layer == NULL) + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + { + g_warning ("The application is trying to use a texture " + "combine with a layer number that does not exist"); + warning_seen = TRUE; + } + source_out[i] = GL_PREVIOUS; + } + else + source_out[i] = (_cogl_pipeline_layer_get_unit_index (layer) + + GL_TEXTURE0); + } + } +} + +static CoglBool +_cogl_pipeline_fragend_fixed_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference) +{ + CoglTextureUnit *unit = + _cogl_get_texture_unit (_cogl_pipeline_layer_get_unit_index (layer)); + int unit_index = unit->index; + int n_rgb_func_args; + int n_alpha_func_args; + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* XXX: Beware that since we are changing the active texture unit we + * must make sure we don't call into other Cogl components that may + * temporarily bind texture objects to query/modify parameters since + * they will end up binding texture unit 1. See + * _cogl_bind_gl_texture_transient for more details. + */ + _cogl_set_active_texture_unit (unit_index); + + if (G_UNLIKELY (unit_index >= get_max_texture_units ())) + { + _cogl_disable_texture_unit (unit_index); + /* TODO: although this isn't considered an error that + * warrants falling back to a different backend we + * should print a warning here. */ + return TRUE; + } + + /* Handle enabling or disabling the right texture type */ + if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE) + { + CoglTextureType texture_type = + _cogl_pipeline_layer_get_texture_type (layer); + GLenum gl_target; + + switch (texture_type) + { + case COGL_TEXTURE_TYPE_2D: + gl_target = GL_TEXTURE_2D; + break; + + case COGL_TEXTURE_TYPE_3D: + gl_target = GL_TEXTURE_3D; + break; + + case COGL_TEXTURE_TYPE_RECTANGLE: + gl_target = GL_TEXTURE_RECTANGLE_ARB; + break; + + default: + g_assert_not_reached (); + } + + _cogl_set_active_texture_unit (unit_index); + + /* The common GL code handles binding the right texture so we + just need to handle enabling and disabling it */ + + if (unit->enabled_gl_target != gl_target) + { + /* Disable the previous target if it's still enabled */ + if (unit->enabled_gl_target) + GE (ctx, glDisable (unit->enabled_gl_target)); + + /* Enable the new target */ + if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) + { + GE (ctx, glEnable (gl_target)); + unit->enabled_gl_target = gl_target; + } + } + } + else + { + /* Even though there may be no difference between the last flushed + * texture state and the current layers texture state it may be that the + * texture unit has been disabled for some time so we need to assert that + * it's enabled now. + */ + if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING)) && + unit->enabled_gl_target == 0) + { + _cogl_set_active_texture_unit (unit_index); + GE (ctx, glEnable (unit->gl_target)); + unit->enabled_gl_target = unit->gl_target; + } + } + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE) + { + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_COMBINE); + CoglPipelineLayerBigState *big_state = authority->big_state; + GLenum sources[3]; + + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE)); + + /* Set the combiner functions... */ + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, + GL_COMBINE_RGB, + big_state->texture_combine_rgb_func)); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, + GL_COMBINE_ALPHA, + big_state->texture_combine_alpha_func)); + + /* + * Setup the function arguments... + */ + + /* For the RGB components... */ + n_rgb_func_args = + _cogl_get_n_args_for_combine_func (big_state->texture_combine_rgb_func); + + translate_sources (pipeline, + n_rgb_func_args, + big_state->texture_combine_rgb_src, + sources); + + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, + sources[0])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, + big_state->texture_combine_rgb_op[0])); + if (n_rgb_func_args > 1) + { + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, + sources[1])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, + big_state->texture_combine_rgb_op[1])); + } + if (n_rgb_func_args > 2) + { + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_RGB, + sources[2])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB, + big_state->texture_combine_rgb_op[2])); + } + + /* For the Alpha component */ + n_alpha_func_args = + _cogl_get_n_args_for_combine_func (big_state->texture_combine_alpha_func); + + translate_sources (pipeline, + n_alpha_func_args, + big_state->texture_combine_alpha_src, + sources); + + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, + sources[0])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, + big_state->texture_combine_alpha_op[0])); + if (n_alpha_func_args > 1) + { + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, + sources[1])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, + big_state->texture_combine_alpha_op[1])); + } + if (n_alpha_func_args > 2) + { + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_ALPHA, + sources[2])); + GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, + big_state->texture_combine_alpha_op[2])); + } + } + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) + { + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority + (layer, COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT); + CoglPipelineLayerBigState *big_state = authority->big_state; + + GE (ctx, glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, + big_state->texture_combine_constant)); + } + + return TRUE; +} + +static CoglBool +get_highest_unit_index_cb (CoglPipelineLayer *layer, + void *user_data) +{ + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + int *highest_index = user_data; + + *highest_index = unit_index; + + return TRUE; +} + +static CoglBool +_cogl_pipeline_fragend_fixed_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + int highest_unit_index = -1; + int i; + + _COGL_GET_CONTEXT (ctx, FALSE); + + _cogl_pipeline_foreach_layer_internal (pipeline, + get_highest_unit_index_cb, + &highest_unit_index); + + /* Disable additional texture units that may have previously been in use.. */ + for (i = highest_unit_index + 1; i < ctx->texture_units->len; i++) + _cogl_disable_texture_unit (i); + + if (pipelines_difference & COGL_PIPELINE_STATE_FOG) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_FOG); + CoglPipelineFogState *fog_state = &authority->big_state->fog_state; + + if (fog_state->enabled) + { + GLfloat fogColor[4]; + GLenum gl_mode = GL_LINEAR; + + fogColor[0] = cogl_color_get_red_float (&fog_state->color); + fogColor[1] = cogl_color_get_green_float (&fog_state->color); + fogColor[2] = cogl_color_get_blue_float (&fog_state->color); + fogColor[3] = cogl_color_get_alpha_float (&fog_state->color); + + GE (ctx, glEnable (GL_FOG)); + + GE (ctx, glFogfv (GL_FOG_COLOR, fogColor)); + + if (ctx->driver == COGL_DRIVER_GLES1) + switch (fog_state->mode) + { + case COGL_FOG_MODE_LINEAR: + gl_mode = GL_LINEAR; + break; + case COGL_FOG_MODE_EXPONENTIAL: + gl_mode = GL_EXP; + break; + case COGL_FOG_MODE_EXPONENTIAL_SQUARED: + gl_mode = GL_EXP2; + break; + } + /* TODO: support other modes for GLES2 */ + + /* NB: GLES doesn't have glFogi */ + GE (ctx, glFogf (GL_FOG_MODE, gl_mode)); + GE (ctx, glHint (GL_FOG_HINT, GL_NICEST)); + + GE (ctx, glFogf (GL_FOG_DENSITY, fog_state->density)); + GE (ctx, glFogf (GL_FOG_START, fog_state->z_near)); + GE (ctx, glFogf (GL_FOG_END, fog_state->z_far)); + } + else + GE (ctx, glDisable (GL_FOG)); + } + + return TRUE; +} + +const CoglPipelineFragend _cogl_pipeline_fixed_fragend = +{ + _cogl_pipeline_fragend_fixed_start, + _cogl_pipeline_fragend_fixed_add_layer, + NULL, /* passthrough */ + _cogl_pipeline_fragend_fixed_end, + NULL, /* pipeline_change_notify */ + NULL, /* pipeline_set_parent_notify */ + NULL, /* layer_change_notify */ +}; + +#endif /* COGL_PIPELINE_FRAGEND_FIXED */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl-private.h b/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl-private.h new file mode 100644 index 000000000..72f5928a8 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H +#define __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineFragend _cogl_pipeline_glsl_fragend; + +GLuint +_cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline); + +#endif /* __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl.c b/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl.c new file mode 100644 index 000000000..6fdb3a127 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl.c @@ -0,0 +1,1149 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-layer-private.h" +#include "cogl-blend-string.h" +#include "cogl-snippet-private.h" +#include "cogl-list.h" + +#ifdef COGL_PIPELINE_FRAGEND_GLSL + +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-shader-private.h" +#include "cogl-program-private.h" +#include "cogl-pipeline-cache.h" +#include "cogl-pipeline-fragend-glsl-private.h" +#include "cogl-glsl-shader-private.h" + +#include + +/* + * GL/GLES compatability defines for pipeline thingies: + */ + +/* This might not be defined on GLES */ +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +const CoglPipelineFragend _cogl_pipeline_glsl_backend; + +typedef struct _UnitState +{ + unsigned int sampled:1; + unsigned int combine_constant_used:1; +} UnitState; + +typedef struct _LayerData +{ + CoglList link; + + /* Layer index for the for the previous layer. This isn't + necessarily the same as this layer's index - 1 because the + indices can have gaps. If this is the first layer then it will be + -1 */ + int previous_layer_index; + + CoglPipelineLayer *layer; +} LayerData; + +typedef struct +{ + int ref_count; + + GLuint gl_shader; + GString *header, *source; + UnitState *unit_state; + + /* List of layers that we haven't generated code for yet. These are + in reverse order. As soon as we're about to generate code for + layer we'll remove it from the list so we don't generate it + again */ + CoglList layers; + + CoglPipelineCacheEntry *cache_entry; +} CoglPipelineShaderState; + +static CoglUserDataKey shader_state_key; + +static void +ensure_layer_generated (CoglPipeline *pipeline, + int layer_num); + +static CoglPipelineShaderState * +shader_state_new (int n_layers, + CoglPipelineCacheEntry *cache_entry) +{ + CoglPipelineShaderState *shader_state; + + shader_state = g_slice_new0 (CoglPipelineShaderState); + shader_state->ref_count = 1; + shader_state->unit_state = g_new0 (UnitState, n_layers); + shader_state->cache_entry = cache_entry; + + return shader_state; +} + +static CoglPipelineShaderState * +get_shader_state (CoglPipeline *pipeline) +{ + return cogl_object_get_user_data (COGL_OBJECT (pipeline), &shader_state_key); +} + +static void +destroy_shader_state (void *user_data, + void *instance) +{ + CoglPipelineShaderState *shader_state = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != instance) + shader_state->cache_entry->usage_count--; + + if (--shader_state->ref_count == 0) + { + if (shader_state->gl_shader) + GE( ctx, glDeleteShader (shader_state->gl_shader) ); + + g_free (shader_state->unit_state); + + g_slice_free (CoglPipelineShaderState, shader_state); + } +} + +static void +set_shader_state (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) +{ + if (shader_state) + { + shader_state->ref_count++; + + /* If we're not setting the state on the template pipeline then + * mark it as a usage of the pipeline cache entry */ + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != pipeline) + shader_state->cache_entry->usage_count++; + } + + _cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + shader_state, + destroy_shader_state); +} + +static void +dirty_shader_state (CoglPipeline *pipeline) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + NULL, + NULL); +} + +GLuint +_cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + + if (shader_state) + return shader_state->gl_shader; + else + return 0; +} + +static CoglPipelineSnippetList * +get_fragment_snippets (CoglPipeline *pipeline) +{ + pipeline = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS); + + return &pipeline->big_state->fragment_snippets; +} + +static CoglPipelineSnippetList * +get_layer_fragment_snippets (CoglPipelineLayer *layer) +{ + unsigned long state = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; + layer = _cogl_pipeline_layer_get_authority (layer, state); + + return &layer->big_state->fragment_snippets; +} + +static CoglBool +has_replace_hook (CoglPipelineLayer *layer, + CoglSnippetHook hook) +{ + GList *l; + + for (l = get_layer_fragment_snippets (layer)->entries; l; l = l->next) + { + CoglSnippet *snippet = l->data; + + if (snippet->hook == hook && snippet->replace) + return TRUE; + } + + return FALSE; +} + +static CoglBool +add_layer_declaration_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineShaderState *shader_state = user_data; + CoglTextureType texture_type = + _cogl_pipeline_layer_get_texture_type (layer); + const char *target_string; + + _cogl_gl_util_get_texture_target_string (texture_type, &target_string, NULL); + + g_string_append_printf (shader_state->header, + "uniform sampler%s cogl_sampler%i;\n", + target_string, + layer->index); + + return TRUE; +} + +static void +add_layer_declarations (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + /* We always emit sampler uniforms in case there will be custom + * layer snippets that want to sample arbitrary layers. */ + + _cogl_pipeline_foreach_layer_internal (pipeline, + add_layer_declaration_cb, + shader_state); +} + +static void +add_global_declarations (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + CoglSnippetHook hook = COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS; + CoglPipelineSnippetList *snippets = get_fragment_snippets (pipeline); + + /* Add the global data hooks. All of the code in these snippets is + * always added and only the declarations data is used */ + + _cogl_pipeline_snippet_generate_declarations (shader_state->header, + hook, + snippets); +} + +static void +_cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state; + CoglPipeline *authority; + CoglPipelineCacheEntry *cache_entry = NULL; + CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Now lookup our glsl backend private state */ + shader_state = get_shader_state (pipeline); + + if (shader_state == NULL) + { + /* If we don't have an associated glsl shader yet then find the + * glsl-authority (the oldest ancestor whose state will result in + * the same shader being generated as for this pipeline). + * + * We always make sure to associate new shader with the + * glsl-authority to maximize the chance that other pipelines can + * share it. + */ + authority = _cogl_pipeline_find_equivalent_parent + (pipeline, + _cogl_pipeline_get_state_for_fragment_codegen (ctx) & + ~COGL_PIPELINE_STATE_LAYERS, + _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx)); + + shader_state = get_shader_state (authority); + + /* If we don't have an existing program associated with the + * glsl-authority then start generating code for a new shader... + */ + if (shader_state == NULL) + { + /* Check if there is already a similar cached pipeline whose + shader state we can share */ + if (G_LIKELY (!(COGL_DEBUG_ENABLED + (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) + { + cache_entry = + _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache, + authority); + + shader_state = get_shader_state (cache_entry->pipeline); + } + + if (shader_state) + shader_state->ref_count++; + else + shader_state = shader_state_new (n_layers, cache_entry); + + set_shader_state (authority, shader_state); + + shader_state->ref_count--; + + if (cache_entry) + set_shader_state (cache_entry->pipeline, shader_state); + } + + /* If the pipeline isn't actually its own glsl-authority + * then take a reference to the program state associated + * with the glsl-authority... */ + if (authority != pipeline) + set_shader_state (pipeline, shader_state); + } + + if (user_program) + { + /* If the user program contains a fragment shader then we don't need + to generate one */ + if (_cogl_program_has_fragment_shader (user_program)) + { + if (shader_state->gl_shader) + { + GE( ctx, glDeleteShader (shader_state->gl_shader) ); + shader_state->gl_shader = 0; + } + return; + } + } + + if (shader_state->gl_shader) + return; + + /* If we make it here then we have a glsl_shader_state struct + without a gl_shader either because this is the first time we've + encountered it or because the user program has changed */ + + /* We reuse two grow-only GStrings for code-gen. One string + contains the uniform and attribute declarations while the + other contains the main function. We need two strings + because we need to dynamically declare attributes as the + add_layer callback is invoked */ + g_string_set_size (ctx->codegen_header_buffer, 0); + g_string_set_size (ctx->codegen_source_buffer, 0); + shader_state->header = ctx->codegen_header_buffer; + shader_state->source = ctx->codegen_source_buffer; + _cogl_list_init (&shader_state->layers); + + add_layer_declarations (pipeline, shader_state); + add_global_declarations (pipeline, shader_state); + + g_string_append (shader_state->source, + "void\n" + "cogl_generated_source ()\n" + "{\n"); + + for (i = 0; i < n_layers; i++) + { + shader_state->unit_state[i].sampled = FALSE; + shader_state->unit_state[i].combine_constant_used = FALSE; + } +} + +static void +add_constant_lookup (CoglPipelineShaderState *shader_state, + CoglPipeline *pipeline, + CoglPipelineLayer *layer, + const char *swizzle) +{ + g_string_append_printf (shader_state->header, + "_cogl_layer_constant_%i.%s", + layer->index, swizzle); +} + +static void +ensure_texture_lookup_generated (CoglPipelineShaderState *shader_state, + CoglPipeline *pipeline, + CoglPipelineLayer *layer) +{ + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + CoglPipelineSnippetData snippet_data; + CoglTextureType texture_type; + const char *target_string, *tex_coord_swizzle; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (shader_state->unit_state[unit_index].sampled) + return; + + texture_type = + _cogl_pipeline_layer_get_texture_type (layer); + _cogl_gl_util_get_texture_target_string (texture_type, + &target_string, + &tex_coord_swizzle); + + shader_state->unit_state[unit_index].sampled = TRUE; + + g_string_append_printf (shader_state->header, + "vec4 cogl_texel%i;\n", + layer->index); + + g_string_append_printf (shader_state->source, + " cogl_texel%i = cogl_texture_lookup%i (" + "cogl_sampler%i, ", + layer->index, + layer->index, + layer->index); + + if (cogl_pipeline_get_layer_point_sprite_coords_enabled (pipeline, + layer->index)) + g_string_append_printf (shader_state->source, + "vec4 (cogl_point_coord, 0.0, 1.0)"); + else + g_string_append_printf (shader_state->source, + "cogl_tex_coord%i_in", + layer->index); + + g_string_append (shader_state->source, ");\n"); + + /* There's no need to generate the real texture lookup if it's going + to be replaced */ + if (!has_replace_hook (layer, COGL_SNIPPET_HOOK_TEXTURE_LOOKUP)) + { + g_string_append_printf (shader_state->header, + "vec4\n" + "cogl_real_texture_lookup%i (sampler%s tex,\n" + " vec4 coords)\n" + "{\n" + " return ", + layer->index, + target_string); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) + g_string_append (shader_state->header, + "vec4 (1.0, 1.0, 1.0, 1.0);\n"); + else + g_string_append_printf (shader_state->header, + "texture%s (tex, coords.%s);\n", + target_string, tex_coord_swizzle); + + g_string_append (shader_state->header, "}\n"); + } + + /* Wrap the texture lookup in any snippets that have been hooked */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = get_layer_fragment_snippets (layer); + snippet_data.hook = COGL_SNIPPET_HOOK_TEXTURE_LOOKUP; + snippet_data.chain_function = g_strdup_printf ("cogl_real_texture_lookup%i", + layer->index); + snippet_data.final_name = g_strdup_printf ("cogl_texture_lookup%i", + layer->index); + snippet_data.function_prefix = g_strdup_printf ("cogl_texture_lookup_hook%i", + layer->index); + snippet_data.return_type = "vec4"; + snippet_data.return_variable = "cogl_texel"; + snippet_data.arguments = "cogl_sampler, cogl_tex_coord"; + snippet_data.argument_declarations = + g_strdup_printf ("sampler%s cogl_sampler, vec4 cogl_tex_coord", + target_string); + snippet_data.source_buf = shader_state->header; + + _cogl_pipeline_snippet_generate_code (&snippet_data); + + g_free ((char *) snippet_data.chain_function); + g_free ((char *) snippet_data.final_name); + g_free ((char *) snippet_data.function_prefix); + g_free ((char *) snippet_data.argument_declarations); +} + +static void +add_arg (CoglPipelineShaderState *shader_state, + CoglPipeline *pipeline, + CoglPipelineLayer *layer, + int previous_layer_index, + CoglPipelineCombineSource src, + CoglPipelineCombineOp operand, + const char *swizzle) +{ + GString *shader_source = shader_state->header; + char alpha_swizzle[5] = "aaaa"; + + g_string_append_c (shader_source, '('); + + if (operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR || + operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA) + g_string_append_printf (shader_source, + "vec4(1.0, 1.0, 1.0, 1.0).%s - ", + swizzle); + + /* If the operand is reading from the alpha then replace the swizzle + with the same number of copies of the alpha */ + if (operand == COGL_PIPELINE_COMBINE_OP_SRC_ALPHA || + operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA) + { + alpha_swizzle[strlen (swizzle)] = '\0'; + swizzle = alpha_swizzle; + } + + switch (src) + { + case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: + g_string_append_printf (shader_source, + "cogl_texel%i.%s", + layer->index, + swizzle); + break; + + case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: + add_constant_lookup (shader_state, + pipeline, + layer, + swizzle); + break; + + case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: + if (previous_layer_index >= 0) + { + g_string_append_printf (shader_source, + "cogl_layer%i.%s", + previous_layer_index, + swizzle); + break; + } + /* flow through */ + case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: + g_string_append_printf (shader_source, "cogl_color_in.%s", swizzle); + break; + + default: + { + int layer_num = src - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; + CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *other_layer = + _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); + + if (other_layer == NULL) + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + { + g_warning ("The application is trying to use a texture " + "combine with a layer number that does not exist"); + warning_seen = TRUE; + } + g_string_append_printf (shader_source, + "vec4 (1.0, 1.0, 1.0, 1.0).%s", + swizzle); + } + else + g_string_append_printf (shader_source, + "cogl_texel%i.%s", + other_layer->index, + swizzle); + } + break; + } + + g_string_append_c (shader_source, ')'); +} + +static void +ensure_arg_generated (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + int previous_layer_index, + CoglPipelineCombineSource src) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + + switch (src) + { + case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: + /* This doesn't involve any other layers */ + break; + + case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + /* Create a sampler uniform for this layer if we haven't already */ + if (!shader_state->unit_state[unit_index].combine_constant_used) + { + g_string_append_printf (shader_state->header, + "uniform vec4 _cogl_layer_constant_%i;\n", + layer->index); + shader_state->unit_state[unit_index].combine_constant_used = TRUE; + } + } + break; + + case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: + if (previous_layer_index >= 0) + ensure_layer_generated (pipeline, previous_layer_index); + break; + + case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: + ensure_texture_lookup_generated (shader_state, + pipeline, + layer); + break; + + default: + if (src >= COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0) + { + int layer_num = src - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; + CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *other_layer = + _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); + + if (other_layer) + ensure_texture_lookup_generated (shader_state, + pipeline, + other_layer); + } + break; + } +} + +static void +ensure_args_for_func (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + int previous_layer_index, + CoglPipelineCombineFunc function, + CoglPipelineCombineSource *src) +{ + int n_args = _cogl_get_n_args_for_combine_func (function); + int i; + + for (i = 0; i < n_args; i++) + ensure_arg_generated (pipeline, layer, previous_layer_index, src[i]); +} + +static void +append_masked_combine (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + int previous_layer_index, + const char *swizzle, + CoglPipelineCombineFunc function, + CoglPipelineCombineSource *src, + CoglPipelineCombineOp *op) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + GString *shader_source = shader_state->header; + + g_string_append_printf (shader_state->header, + " cogl_layer.%s = ", + swizzle); + + switch (function) + { + case COGL_PIPELINE_COMBINE_FUNC_REPLACE: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + break; + + case COGL_PIPELINE_COMBINE_FUNC_MODULATE: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + g_string_append (shader_source, " * "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], swizzle); + break; + + case COGL_PIPELINE_COMBINE_FUNC_ADD: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + g_string_append (shader_source, " + "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], swizzle); + break; + + case COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + g_string_append (shader_source, " + "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], swizzle); + g_string_append_printf (shader_source, + " - vec4(0.5, 0.5, 0.5, 0.5).%s", + swizzle); + break; + + case COGL_PIPELINE_COMBINE_FUNC_SUBTRACT: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + g_string_append (shader_source, " - "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], swizzle); + break; + + case COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE: + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], swizzle); + g_string_append (shader_source, " * "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[2], op[2], swizzle); + g_string_append (shader_source, " + "); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], swizzle); + g_string_append_printf (shader_source, + " * (vec4(1.0, 1.0, 1.0, 1.0).%s - ", + swizzle); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[2], op[2], swizzle); + g_string_append_c (shader_source, ')'); + break; + + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB: + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA: + g_string_append (shader_source, "vec4(4.0 * (("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], "r"); + g_string_append (shader_source, " - 0.5) * ("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], "r"); + g_string_append (shader_source, " - 0.5) + ("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], "g"); + g_string_append (shader_source, " - 0.5) * ("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], "g"); + g_string_append (shader_source, " - 0.5) + ("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[0], op[0], "b"); + g_string_append (shader_source, " - 0.5) * ("); + add_arg (shader_state, pipeline, layer, previous_layer_index, + src[1], op[1], "b"); + g_string_append_printf (shader_source, " - 0.5))).%s", swizzle); + break; + } + + g_string_append_printf (shader_source, ";\n"); +} + +static void +ensure_layer_generated (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + CoglPipelineLayer *combine_authority; + CoglPipelineLayerBigState *big_state; + CoglPipelineLayer *layer; + CoglPipelineSnippetData snippet_data; + LayerData *layer_data; + + /* Find the layer that corresponds to this layer_num */ + _cogl_list_for_each (layer_data, &shader_state->layers, link) + { + layer = layer_data->layer; + + if (layer->index == layer_index) + goto found; + } + + /* If we didn't find it then we can assume the layer has already + been generated */ + return; + + found: + + /* Remove the layer from the list so we don't generate it again */ + _cogl_list_remove (&layer_data->link); + + combine_authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_COMBINE); + big_state = combine_authority->big_state; + + /* Make a global variable for the result of the layer code */ + g_string_append_printf (shader_state->header, + "vec4 cogl_layer%i;\n", + layer_index); + + /* Skip the layer generation if there is a snippet that replaces the + default layer code. This is important because generating this + code may cause the code for other layers to be generated and + stored in the global variable. If this code isn't actually used + then the global variables would be uninitialised and they may be + used from other layers */ + if (!has_replace_hook (layer, COGL_SNIPPET_HOOK_LAYER_FRAGMENT)) + { + ensure_args_for_func (pipeline, + layer, + layer_data->previous_layer_index, + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src); + ensure_args_for_func (pipeline, + layer, + layer_data->previous_layer_index, + big_state->texture_combine_alpha_func, + big_state->texture_combine_alpha_src); + + g_string_append_printf (shader_state->header, + "vec4\n" + "cogl_real_generate_layer%i ()\n" + "{\n" + " vec4 cogl_layer;\n", + layer_index); + + if (!_cogl_pipeline_layer_needs_combine_separate (combine_authority) || + /* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function + * since if you use it, it overrides your ALPHA function... + */ + big_state->texture_combine_rgb_func == + COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA) + append_masked_combine (pipeline, + layer, + layer_data->previous_layer_index, + "rgba", + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src, + big_state->texture_combine_rgb_op); + else + { + append_masked_combine (pipeline, + layer, + layer_data->previous_layer_index, + "rgb", + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src, + big_state->texture_combine_rgb_op); + append_masked_combine (pipeline, + layer, + layer_data->previous_layer_index, + "a", + big_state->texture_combine_alpha_func, + big_state->texture_combine_alpha_src, + big_state->texture_combine_alpha_op); + } + + g_string_append (shader_state->header, + " return cogl_layer;\n" + "}\n"); + } + + /* Wrap the layer code in any snippets that have been hooked */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = get_layer_fragment_snippets (layer); + snippet_data.hook = COGL_SNIPPET_HOOK_LAYER_FRAGMENT; + snippet_data.chain_function = g_strdup_printf ("cogl_real_generate_layer%i", + layer_index); + snippet_data.final_name = g_strdup_printf ("cogl_generate_layer%i", + layer_index); + snippet_data.function_prefix = g_strdup_printf ("cogl_generate_layer%i", + layer_index); + snippet_data.return_type = "vec4"; + snippet_data.return_variable = "cogl_layer"; + snippet_data.source_buf = shader_state->header; + + _cogl_pipeline_snippet_generate_code (&snippet_data); + + g_free ((char *) snippet_data.chain_function); + g_free ((char *) snippet_data.final_name); + g_free ((char *) snippet_data.function_prefix); + + g_string_append_printf (shader_state->source, + " cogl_layer%i = cogl_generate_layer%i ();\n", + layer_index, + layer_index); + + g_slice_free (LayerData, layer_data); +} + +static CoglBool +_cogl_pipeline_fragend_glsl_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + LayerData *layer_data; + + if (!shader_state->source) + return TRUE; + + /* Store the layers in reverse order */ + layer_data = g_slice_new (LayerData); + layer_data->layer = layer; + + if (_cogl_list_empty (&shader_state->layers)) + { + layer_data->previous_layer_index = -1; + } + else + { + LayerData *first = + _cogl_container_of (shader_state->layers.next, LayerData, link); + layer_data->previous_layer_index = first->layer->index; + } + + _cogl_list_insert (&shader_state->layers, &layer_data->link); + + return TRUE; +} + +/* GLES2 and GL3 don't have alpha testing so we need to implement it + in the shader */ + +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + +static void +add_alpha_test_snippet (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + CoglPipelineAlphaFunc alpha_func; + + alpha_func = cogl_pipeline_get_alpha_test_function (pipeline); + + if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_ALWAYS) + /* Do nothing */ + return; + + if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_NEVER) + { + /* Always discard the fragment */ + g_string_append (shader_state->source, + " discard;\n"); + return; + } + + /* For all of the other alpha functions we need a uniform for the + reference */ + + g_string_append (shader_state->header, + "uniform float _cogl_alpha_test_ref;\n"); + + g_string_append (shader_state->source, + " if (cogl_color_out.a "); + + switch (alpha_func) + { + case COGL_PIPELINE_ALPHA_FUNC_LESS: + g_string_append (shader_state->source, ">="); + break; + case COGL_PIPELINE_ALPHA_FUNC_EQUAL: + g_string_append (shader_state->source, "!="); + break; + case COGL_PIPELINE_ALPHA_FUNC_LEQUAL: + g_string_append (shader_state->source, ">"); + break; + case COGL_PIPELINE_ALPHA_FUNC_GREATER: + g_string_append (shader_state->source, "<="); + break; + case COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL: + g_string_append (shader_state->source, "=="); + break; + case COGL_PIPELINE_ALPHA_FUNC_GEQUAL: + g_string_append (shader_state->source, "< "); + break; + + case COGL_PIPELINE_ALPHA_FUNC_ALWAYS: + case COGL_PIPELINE_ALPHA_FUNC_NEVER: + g_assert_not_reached (); + break; + } + + g_string_append (shader_state->source, + " _cogl_alpha_test_ref)\n discard;\n"); +} + +#endif /* HAVE_COGL_GLES2 */ + +static CoglBool +_cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (shader_state->source) + { + const char *source_strings[2]; + GLint lengths[2]; + GLint compile_status; + GLuint shader; + CoglPipelineSnippetData snippet_data; + + COGL_STATIC_COUNTER (fragend_glsl_compile_counter, + "glsl fragment compile counter", + "Increments each time a new GLSL " + "fragment shader is compiled", + 0 /* no application private data */); + COGL_COUNTER_INC (_cogl_uprof_context, fragend_glsl_compile_counter); + + /* We only need to generate code to calculate the fragment value + for the last layer. If the value of this layer depends on any + previous layers then it will recursively generate the code + for those layers */ + if (!_cogl_list_empty (&shader_state->layers)) + { + CoglPipelineLayer *last_layer; + LayerData *layer_data, *tmp; + + layer_data = _cogl_container_of (shader_state->layers.next, + LayerData, + link); + last_layer = layer_data->layer; + + ensure_layer_generated (pipeline, last_layer->index); + g_string_append_printf (shader_state->source, + " cogl_color_out = cogl_layer%i;\n", + last_layer->index); + + _cogl_list_for_each_safe (layer_data, + tmp, + &shader_state->layers, + link) + g_slice_free (LayerData, layer_data); + } + else + g_string_append (shader_state->source, + " cogl_color_out = cogl_color_in;\n"); + +#if defined(HAVE_COGL_GLES2) || defined (HAVE_COGL_GL) + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEST)) + add_alpha_test_snippet (pipeline, shader_state); +#endif + + /* Close the function surrounding the generated fragment processing */ + g_string_append (shader_state->source, "}\n"); + + /* Add all of the hooks for fragment processing */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = get_fragment_snippets (pipeline); + snippet_data.hook = COGL_SNIPPET_HOOK_FRAGMENT; + snippet_data.chain_function = "cogl_generated_source"; + snippet_data.final_name = "main"; + snippet_data.function_prefix = "cogl_fragment_hook"; + snippet_data.source_buf = shader_state->source; + _cogl_pipeline_snippet_generate_code (&snippet_data); + + GE_RET( shader, ctx, glCreateShader (GL_FRAGMENT_SHADER) ); + + lengths[0] = shader_state->header->len; + source_strings[0] = shader_state->header->str; + lengths[1] = shader_state->source->len; + source_strings[1] = shader_state->source->str; + + _cogl_glsl_shader_set_source_with_boilerplate (ctx, + shader, GL_FRAGMENT_SHADER, + pipeline, + 2, /* count */ + source_strings, lengths); + + GE( ctx, glCompileShader (shader) ); + GE( ctx, glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); + + if (!compile_status) + { + GLint len = 0; + char *shader_log; + + GE( ctx, glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); + shader_log = g_alloca (len); + GE( ctx, glGetShaderInfoLog (shader, len, &len, shader_log) ); + g_warning ("Shader compilation failed:\n%s", shader_log); + } + + shader_state->header = NULL; + shader_state->source = NULL; + shader_state->gl_shader = shader; + } + + return TRUE; +} + +static void +_cogl_pipeline_fragend_glsl_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & _cogl_pipeline_get_state_for_fragment_codegen (ctx))) + dirty_shader_state (pipeline); +} + +/* NB: layers are considered immutable once they have any dependants + * so although multiple pipelines can end up depending on a single + * static layer, we can guarantee that if a layer is being *changed* + * then it can only have one pipeline depending on it. + * + * XXX: Don't forget this is *pre* change, we can't read the new value + * yet! + */ +static void +_cogl_pipeline_fragend_glsl_layer_pre_change_notify ( + CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx))) + { + dirty_shader_state (owner); + return; + } + + /* TODO: we could be saving snippets of texture combine code along + * with each layer and then when a layer changes we would just free + * the snippet. */ +} + +const CoglPipelineFragend _cogl_pipeline_glsl_fragend = +{ + _cogl_pipeline_fragend_glsl_start, + _cogl_pipeline_fragend_glsl_add_layer, + NULL, /* passthrough */ + _cogl_pipeline_fragend_glsl_end, + _cogl_pipeline_fragend_glsl_pre_change_notify, + NULL, /* pipeline_set_parent_notify */ + _cogl_pipeline_fragend_glsl_layer_pre_change_notify +}; + +#endif /* COGL_PIPELINE_FRAGEND_GLSL */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-opengl-private.h b/cogl/cogl/driver/gl/cogl-pipeline-opengl-private.h new file mode 100644 index 000000000..0a374be49 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-opengl-private.h @@ -0,0 +1,158 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_OPENGL_PRIVATE_H +#define __COGL_PIPELINE_OPENGL_PRIVATE_H + +#include "cogl-pipeline-private.h" +#include "cogl-matrix-stack.h" + +/* + * cogl-pipeline.c owns the GPU's texture unit state so we have some + * private structures for describing the current state of a texture + * unit that we track in a per context array (ctx->texture_units) that + * grows according to the largest texture unit used so far... + * + * Roughly speaking the members in this structure are of two kinds: + * either they are a low level reflection of the state we send to + * OpenGL or they are for high level meta data assoicated with the + * texture unit when flushing CoglPipelineLayers that is typically + * used to optimize subsequent re-flushing of the same layer. + * + * The low level members are at the top, and the high level members + * start with the .layer member. + */ +typedef struct _CoglTextureUnit +{ + /* The base 0 texture unit index which can be used with + * glActiveTexture () */ + int index; + + /* The GL target currently glEnabled or 0 if nothing is + * enabled. This is only used by the fixed pipeline fragend */ + GLenum enabled_gl_target; + + /* The raw GL texture object name for which we called glBindTexture when + * we flushed the last layer. (NB: The CoglTexture associated + * with a layer may represent more than one GL texture) */ + GLuint gl_texture; + /* The target of the GL texture object. This is just used so that we + * can quickly determine the intended target to flush when + * dirty_gl_texture == TRUE */ + GLenum gl_target; + + /* Foreign textures are those not created or deleted by Cogl. If we ever + * call glBindTexture for a foreign texture then the next time we are + * asked to glBindTexture we can't try and optimize a redundant state + * change because we don't know if the original texture name was deleted + * and now we are being asked to bind a recycled name. */ + CoglBool is_foreign; + + /* We have many components in Cogl that need to temporarily bind arbitrary + * textures e.g. to query texture object parameters and since we don't + * want that to result in too much redundant reflushing of layer state + * when all that's needed is to re-bind the layer's gl_texture we use this + * to track when the unit->gl_texture state is out of sync with the GL + * texture object really bound too (GL_TEXTURE0+unit->index). + * + * XXX: as a further optimization cogl-pipeline.c uses a convention + * of always using texture unit 1 for these transient bindings so we + * can assume this is only ever TRUE for unit 1. + */ + CoglBool dirty_gl_texture; + + /* A matrix stack giving us the means to associate a texture + * transform matrix with the texture unit. */ + CoglMatrixStack *matrix_stack; + + /* + * Higher level layer state associated with the unit... + */ + + /* The CoglPipelineLayer whos state was flushed to update this + * texture unit last. + * + * This will be set to NULL if the layer is modified or freed which + * means when we come to flush a layer; if this pointer is still + * valid and == to the layer being flushed we don't need to update + * any texture unit state. */ + CoglPipelineLayer *layer; + + /* To help minimize the state changes required we track the + * difference flags associated with the layer whos state was last + * flushed to update this texture unit. + * + * Note: we track this explicitly because .layer may get invalidated + * if that layer is modified or deleted. Even if the layer is + * invalidated though these flags can be used to optimize the state + * flush of the next layer + */ + unsigned long layer_changes_since_flush; + + /* Whenever a CoglTexture's internal GL texture storage changes + * cogl-pipeline.c is notified with a call to + * _cogl_pipeline_texture_storage_change_notify which inturn sets + * this to TRUE for each texture unit that it is currently bound + * too. When we later come to flush some pipeline state then we will + * always check this to potentially force an update of the texture + * state even if the pipeline hasn't changed. */ + CoglBool texture_storage_changed; + +} CoglTextureUnit; + +CoglTextureUnit * +_cogl_get_texture_unit (int index_); + +void +_cogl_destroy_texture_units (void); + +void +_cogl_set_active_texture_unit (int unit_index); + +void +_cogl_bind_gl_texture_transient (GLenum gl_target, + GLuint gl_texture, + CoglBool is_foreign); + +void +_cogl_delete_gl_texture (GLuint gl_texture); + +void +_cogl_pipeline_flush_gl_state (CoglContext *context, + CoglPipeline *pipeline, + CoglFramebuffer *framebuffer, + CoglBool skip_gl_state, + CoglBool unknown_color_alpha); + +#endif /* __COGL_PIPELINE_OPENGL_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-opengl.c b/cogl/cogl/driver/gl/cogl-pipeline-opengl.c new file mode 100644 index 000000000..c7b44ee73 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-opengl.c @@ -0,0 +1,1484 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#include "config.h" + +#include "cogl-debug.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-context-private.h" +#include "cogl-texture-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-offscreen.h" +#include "cogl-texture-gl-private.h" + +#include "cogl-pipeline-progend-glsl-private.h" + +#include + +#include +#include + +/* + * GL/GLES compatability defines for pipeline thingies: + */ + +/* These aren't defined in the GLES headers */ +#ifndef GL_POINT_SPRITE +#define GL_POINT_SPRITE 0x8861 +#endif +#ifndef GL_COORD_REPLACE +#define GL_COORD_REPLACE 0x8862 +#endif +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812d +#endif +#ifndef GL_PROGRAM_POINT_SIZE +#define GL_PROGRAM_POINT_SIZE 0x8642 +#endif + +static void +texture_unit_init (CoglContext *ctx, + CoglTextureUnit *unit, + int index_) +{ + unit->index = index_; + unit->enabled_gl_target = 0; + unit->gl_texture = 0; + unit->gl_target = 0; + unit->is_foreign = FALSE; + unit->dirty_gl_texture = FALSE; + unit->matrix_stack = cogl_matrix_stack_new (ctx); + + unit->layer = NULL; + unit->layer_changes_since_flush = 0; + unit->texture_storage_changed = FALSE; +} + +static void +texture_unit_free (CoglTextureUnit *unit) +{ + if (unit->layer) + cogl_object_unref (unit->layer); + cogl_object_unref (unit->matrix_stack); +} + +CoglTextureUnit * +_cogl_get_texture_unit (int index_) +{ + _COGL_GET_CONTEXT (ctx, NULL); + + if (ctx->texture_units->len < (index_ + 1)) + { + int i; + int prev_len = ctx->texture_units->len; + ctx->texture_units = g_array_set_size (ctx->texture_units, index_ + 1); + for (i = prev_len; i <= index_; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + + texture_unit_init (ctx, unit, i); + } + } + + return &g_array_index (ctx->texture_units, CoglTextureUnit, index_); +} + +void +_cogl_destroy_texture_units (void) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (i = 0; i < ctx->texture_units->len; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + texture_unit_free (unit); + } + g_array_free (ctx->texture_units, TRUE); +} + +void +_cogl_set_active_texture_unit (int unit_index) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->active_texture_unit != unit_index) + { + GE (ctx, glActiveTexture (GL_TEXTURE0 + unit_index)); + ctx->active_texture_unit = unit_index; + } +} + +/* Note: _cogl_bind_gl_texture_transient conceptually has slightly + * different semantics to OpenGL's glBindTexture because Cogl never + * cares about tracking multiple textures bound to different targets + * on the same texture unit. + * + * glBindTexture lets you bind multiple textures to a single texture + * unit if they are bound to different targets. So it does something + * like: + * unit->current_texture[target] = texture; + * + * Cogl only lets you associate one texture with the currently active + * texture unit, so the target is basically a redundant parameter + * that's implicitly set on that texture. + * + * Technically this is just a thin wrapper around glBindTexture so + * actually it does have the GL semantics but it seems worth + * mentioning the conceptual difference in case anyone wonders why we + * don't associate the gl_texture with a gl_target in the + * CoglTextureUnit. + */ +void +_cogl_bind_gl_texture_transient (GLenum gl_target, + GLuint gl_texture, + CoglBool is_foreign) +{ + CoglTextureUnit *unit; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* We choose to always make texture unit 1 active for transient + * binds so that in the common case where multitexturing isn't used + * we can simply ignore the state of this texture unit. Notably we + * didn't use a large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1) + * in case the driver doesn't have a sparse data structure for + * texture units. + */ + _cogl_set_active_texture_unit (1); + unit = _cogl_get_texture_unit (1); + + /* NB: If we have previously bound a foreign texture to this texture + * unit we don't know if that texture has since been deleted and we + * are seeing the texture name recycled */ + if (unit->gl_texture == gl_texture && + !unit->dirty_gl_texture && + !unit->is_foreign) + return; + + GE (ctx, glBindTexture (gl_target, gl_texture)); + + unit->dirty_gl_texture = TRUE; + unit->is_foreign = is_foreign; +} + +void +_cogl_delete_gl_texture (GLuint gl_texture) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (i = 0; i < ctx->texture_units->len; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + + if (unit->gl_texture == gl_texture) + { + unit->gl_texture = 0; + unit->gl_target = 0; + unit->dirty_gl_texture = FALSE; + } + } + + GE (ctx, glDeleteTextures (1, &gl_texture)); +} + +/* Whenever the underlying GL texture storage of a CoglTexture is + * changed (e.g. due to migration out of a texture atlas) then we are + * notified. This lets us ensure that we reflush that texture's state + * if it is reused again with the same texture unit. + */ +void +_cogl_pipeline_texture_storage_change_notify (CoglTexture *texture) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (i = 0; i < ctx->texture_units->len; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + + if (unit->layer && + _cogl_pipeline_layer_get_texture (unit->layer) == texture) + unit->texture_storage_changed = TRUE; + + /* NB: the texture may be bound to multiple texture units so + * we continue to check the rest */ + } +} + +static void +set_glsl_program (GLuint gl_program) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_gl_program != gl_program) + { + GLenum gl_error; + + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + ctx->glUseProgram (gl_program); + if (ctx->glGetError () == GL_NO_ERROR) + ctx->current_gl_program = gl_program; + else + { + GE( ctx, glUseProgram (0) ); + ctx->current_gl_program = 0; + } + } +} + +void +_cogl_use_fragment_program (GLuint gl_program, CoglPipelineProgramType type) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If we're changing program type... */ + if (type != ctx->current_fragment_program_type) + { + /* ... disable the old type */ + switch (ctx->current_fragment_program_type) + { + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + /* If the program contains a vertex shader then we shouldn't + disable it */ + if (ctx->current_vertex_program_type != + COGL_PIPELINE_PROGRAM_TYPE_GLSL) + set_glsl_program (0); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: +#ifdef HAVE_COGL_GL + GE( ctx, glDisable (GL_FRAGMENT_PROGRAM_ARB) ); +#endif + break; + + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + + /* ... and enable the new type */ + switch (type) + { + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: +#ifdef HAVE_COGL_GL + GE( ctx, glEnable (GL_FRAGMENT_PROGRAM_ARB) ); +#endif + break; + + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + } + + if (type == COGL_PIPELINE_PROGRAM_TYPE_GLSL) + { +#ifdef COGL_PIPELINE_FRAGEND_GLSL + set_glsl_program (gl_program); + +#else + + g_warning ("Unexpected use of GLSL fragend!"); + +#endif /* COGL_PIPELINE_FRAGEND_GLSL */ + } +#ifndef COGL_PIPELINE_FRAGEND_ARBFP + else if (type == COGL_PIPELINE_PROGRAM_TYPE_ARBFP) + g_warning ("Unexpected use of ARBFP fragend!"); +#endif /* COGL_PIPELINE_FRAGEND_ARBFP */ + + ctx->current_fragment_program_type = type; +} + +void +_cogl_use_vertex_program (GLuint gl_program, CoglPipelineProgramType type) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If we're changing program type... */ + if (type != ctx->current_vertex_program_type) + { + /* ... disable the old type */ + switch (ctx->current_vertex_program_type) + { + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + /* If the program contains a fragment shader then we shouldn't + disable it */ + if (ctx->current_fragment_program_type != + COGL_PIPELINE_PROGRAM_TYPE_GLSL) + set_glsl_program (0); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: + /* It doesn't make sense to enable ARBfp for the vertex program */ + g_assert_not_reached (); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + + /* ... and enable the new type */ + switch (type) + { + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: + /* It doesn't make sense to enable ARBfp for the vertex program */ + g_assert_not_reached (); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + } + + if (type == COGL_PIPELINE_PROGRAM_TYPE_GLSL) + { +#ifdef COGL_PIPELINE_VERTEND_GLSL + set_glsl_program (gl_program); + +#else + + g_warning ("Unexpected use of GLSL vertend!"); + +#endif /* COGL_PIPELINE_VERTEND_GLSL */ + } +#ifndef COGL_PIPELINE_VERTEND_ARBFP + else if (type == COGL_PIPELINE_PROGRAM_TYPE_ARBFP) + g_warning ("Unexpected use of ARBFP vertend!"); +#endif /* COGL_PIPELINE_VERTEND_ARBFP */ + + ctx->current_vertex_program_type = type; +} + +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + +static CoglBool +blend_factor_uses_constant (GLenum blend_factor) +{ + return (blend_factor == GL_CONSTANT_COLOR || + blend_factor == GL_ONE_MINUS_CONSTANT_COLOR || + blend_factor == GL_CONSTANT_ALPHA || + blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA); +} + +#endif + +static void +flush_depth_state (CoglContext *ctx, + CoglDepthState *depth_state) +{ + CoglBool depth_writing_enabled = depth_state->write_enabled; + + if (ctx->current_draw_buffer) + depth_writing_enabled &= ctx->current_draw_buffer->depth_writing_enabled; + + if (ctx->depth_test_enabled_cache != depth_state->test_enabled) + { + if (depth_state->test_enabled == TRUE) + GE (ctx, glEnable (GL_DEPTH_TEST)); + else + GE (ctx, glDisable (GL_DEPTH_TEST)); + ctx->depth_test_enabled_cache = depth_state->test_enabled; + } + + if (ctx->depth_test_function_cache != depth_state->test_function && + depth_state->test_enabled == TRUE) + { + GE (ctx, glDepthFunc (depth_state->test_function)); + ctx->depth_test_function_cache = depth_state->test_function; + } + + if (ctx->depth_writing_enabled_cache != depth_writing_enabled) + { + GE (ctx, glDepthMask (depth_writing_enabled ? + GL_TRUE : GL_FALSE)); + ctx->depth_writing_enabled_cache = depth_writing_enabled; + } + + if (ctx->driver != COGL_DRIVER_GLES1 && + (ctx->depth_range_near_cache != depth_state->range_near || + ctx->depth_range_far_cache != depth_state->range_far)) + { + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED)) + GE (ctx, glDepthRangef (depth_state->range_near, + depth_state->range_far)); + else + GE (ctx, glDepthRange (depth_state->range_near, + depth_state->range_far)); + + ctx->depth_range_near_cache = depth_state->range_near; + ctx->depth_range_far_cache = depth_state->range_far; + } +} + +UNIT_TEST (check_gl_blend_enable, + 0 /* no requirements */, + 0 /* no failure cases */) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + + /* By default blending should be disabled */ + g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); + _cogl_framebuffer_flush_journal (test_fb); + + /* After drawing an opaque rectangle blending should still be + * disabled */ + g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); + + cogl_pipeline_set_color4f (pipeline, 0, 0, 0, 0); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); + _cogl_framebuffer_flush_journal (test_fb); + + /* After drawing a transparent rectangle blending should be enabled */ + g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 1); + + cogl_pipeline_set_blend (pipeline, "RGBA=ADD(SRC_COLOR, 0)", NULL); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); + _cogl_framebuffer_flush_journal (test_fb); + + /* After setting a blend string that effectively disables blending + * then blending should be disabled */ + g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); +} + +static void +_cogl_pipeline_flush_color_blend_alpha_depth_state ( + CoglPipeline *pipeline, + unsigned long pipelines_difference, + CoglBool with_color_attrib) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* On GLES2 we'll flush the color later */ + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED) && + !with_color_attrib) + { + if ((pipelines_difference & COGL_PIPELINE_STATE_COLOR) || + /* Assume if we were previously told to skip the color, then + * the current color needs updating... */ + ctx->current_pipeline_with_color_attrib) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); + GE (ctx, glColor4ub (cogl_color_get_red_byte (&authority->color), + cogl_color_get_green_byte (&authority->color), + cogl_color_get_blue_byte (&authority->color), + cogl_color_get_alpha_byte (&authority->color))); + } + } + + if (pipelines_difference & COGL_PIPELINE_STATE_BLEND) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND); + CoglPipelineBlendState *blend_state = + &authority->big_state->blend_state; + + /* GLES 1 only has glBlendFunc */ + if (ctx->driver == COGL_DRIVER_GLES1) + { + GE (ctx, glBlendFunc (blend_state->blend_src_factor_rgb, + blend_state->blend_dst_factor_rgb)); + } +#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) + else + { + if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) || + blend_factor_uses_constant (blend_state + ->blend_src_factor_alpha) || + blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) || + blend_factor_uses_constant (blend_state->blend_dst_factor_alpha)) + { + float red = + cogl_color_get_red_float (&blend_state->blend_constant); + float green = + cogl_color_get_green_float (&blend_state->blend_constant); + float blue = + cogl_color_get_blue_float (&blend_state->blend_constant); + float alpha = + cogl_color_get_alpha_float (&blend_state->blend_constant); + + + GE (ctx, glBlendColor (red, green, blue, alpha)); + } + + if (ctx->glBlendEquationSeparate && + blend_state->blend_equation_rgb != + blend_state->blend_equation_alpha) + GE (ctx, + glBlendEquationSeparate (blend_state->blend_equation_rgb, + blend_state->blend_equation_alpha)); + else + GE (ctx, glBlendEquation (blend_state->blend_equation_rgb)); + + if (ctx->glBlendFuncSeparate && + (blend_state->blend_src_factor_rgb != + blend_state->blend_src_factor_alpha || + (blend_state->blend_dst_factor_rgb != + blend_state->blend_dst_factor_alpha))) + GE (ctx, glBlendFuncSeparate (blend_state->blend_src_factor_rgb, + blend_state->blend_dst_factor_rgb, + blend_state->blend_src_factor_alpha, + blend_state->blend_dst_factor_alpha)); + else + GE (ctx, glBlendFunc (blend_state->blend_src_factor_rgb, + blend_state->blend_dst_factor_rgb)); + } +#endif + } + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEST)) + { + /* Under GLES2 the alpha function is implemented as part of the + fragment shader */ + if (pipelines_difference & (COGL_PIPELINE_STATE_ALPHA_FUNC | + COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE)) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_ALPHA_FUNC); + CoglPipelineAlphaFuncState *alpha_state = + &authority->big_state->alpha_state; + + /* NB: Currently the Cogl defines are compatible with the GL ones: */ + GE (ctx, glAlphaFunc (alpha_state->alpha_func, + alpha_state->alpha_func_reference)); + } + + /* Under GLES2 the lighting parameters are implemented as uniforms + in the progend */ + if (pipelines_difference & COGL_PIPELINE_STATE_LIGHTING) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_LIGHTING); + CoglPipelineLightingState *lighting_state = + &authority->big_state->lighting_state; + + GE (ctx, glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, + lighting_state->ambient)); + GE (ctx, glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, + lighting_state->diffuse)); + GE (ctx, glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, + lighting_state->specular)); + GE (ctx, glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, + lighting_state->emission)); + GE (ctx, glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, + &lighting_state->shininess)); + } + } + +#endif + + if (pipelines_difference & COGL_PIPELINE_STATE_DEPTH) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_DEPTH); + CoglDepthState *depth_state = &authority->big_state->depth_state; + + flush_depth_state (ctx, depth_state); + } + + if (pipelines_difference & COGL_PIPELINE_STATE_LOGIC_OPS) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LOGIC_OPS); + CoglPipelineLogicOpsState *logic_ops_state = &authority->big_state->logic_ops_state; + CoglColorMask color_mask = logic_ops_state->color_mask; + + if (ctx->current_draw_buffer) + color_mask &= ctx->current_draw_buffer->color_mask; + + GE (ctx, glColorMask (!!(color_mask & COGL_COLOR_MASK_RED), + !!(color_mask & COGL_COLOR_MASK_GREEN), + !!(color_mask & COGL_COLOR_MASK_BLUE), + !!(color_mask & COGL_COLOR_MASK_ALPHA))); + ctx->current_gl_color_mask = color_mask; + } + + if (pipelines_difference & COGL_PIPELINE_STATE_CULL_FACE) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_CULL_FACE); + CoglPipelineCullFaceState *cull_face_state + = &authority->big_state->cull_face_state; + + if (cull_face_state->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) + GE( ctx, glDisable (GL_CULL_FACE) ); + else + { + CoglBool invert_winding; + + GE( ctx, glEnable (GL_CULL_FACE) ); + + switch (cull_face_state->mode) + { + case COGL_PIPELINE_CULL_FACE_MODE_NONE: + g_assert_not_reached (); + + case COGL_PIPELINE_CULL_FACE_MODE_FRONT: + GE( ctx, glCullFace (GL_FRONT) ); + break; + + case COGL_PIPELINE_CULL_FACE_MODE_BACK: + GE( ctx, glCullFace (GL_BACK) ); + break; + + case COGL_PIPELINE_CULL_FACE_MODE_BOTH: + GE( ctx, glCullFace (GL_FRONT_AND_BACK) ); + break; + } + + /* If we are painting to an offscreen framebuffer then we + need to invert the winding of the front face because + everything is painted upside down */ + invert_winding = cogl_is_offscreen (ctx->current_draw_buffer); + + switch (cull_face_state->front_winding) + { + case COGL_WINDING_CLOCKWISE: + GE( ctx, glFrontFace (invert_winding ? GL_CCW : GL_CW) ); + break; + + case COGL_WINDING_COUNTER_CLOCKWISE: + GE( ctx, glFrontFace (invert_winding ? GL_CW : GL_CCW) ); + break; + } + } + } + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE) && + (pipelines_difference & COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE)) + { + unsigned long state = COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE; + CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, state); + + if (authority->big_state->per_vertex_point_size) + GE( ctx, glEnable (GL_PROGRAM_POINT_SIZE) ); + else + GE( ctx, glDisable (GL_PROGRAM_POINT_SIZE) ); + } +#endif + + if (pipeline->real_blend_enable != ctx->gl_blend_enable_cache) + { + if (pipeline->real_blend_enable) + GE (ctx, glEnable (GL_BLEND)); + else + GE (ctx, glDisable (GL_BLEND)); + /* XXX: we shouldn't update any other blend state if blending + * is disabled! */ + ctx->gl_blend_enable_cache = pipeline->real_blend_enable; + } +} + +static int +get_max_activateable_texture_units (void) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (G_UNLIKELY (ctx->max_activateable_texture_units == -1)) + { + GLint values[3]; + int n_values = 0; + int i; + +#ifdef HAVE_COGL_GL + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED)) + { + /* GL_MAX_TEXTURE_COORDS is provided for both GLSL and ARBfp. It + defines the number of texture coordinates that can be + uploaded (but doesn't necessarily relate to how many texture + images can be sampled) */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_GLSL) || + cogl_has_feature (ctx, COGL_FEATURE_ID_ARBFP)) + /* Previously this code subtracted the value by one but there + was no explanation for why it did this and it doesn't seem + to make sense so it has been removed */ + GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_COORDS, + values + n_values++)); + + /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is defined for GLSL but + not ARBfp */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_GLSL)) + GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, + values + n_values++)); + } +#endif /* HAVE_COGL_GL */ + +#ifdef HAVE_COGL_GLES2 + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED) && + _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE)) + { + GE (ctx, glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, values + n_values)); + /* Two of the vertex attribs need to be used for the position + and color */ + values[n_values++] -= 2; + + GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, + values + n_values++)); + } +#endif + +#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) + { + /* GL_MAX_TEXTURE_UNITS defines the number of units that are + usable from the fixed function pipeline, therefore it isn't + available in GLES2. These are also tied to the number of + texture coordinates that can be uploaded so it should be less + than that available from the shader extensions */ + GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_UNITS, + values + n_values++)); + + } +#endif + + g_assert (n_values <= G_N_ELEMENTS (values) && + n_values > 0); + + /* Use the maximum value */ + ctx->max_activateable_texture_units = values[0]; + for (i = 1; i < n_values; i++) + ctx->max_activateable_texture_units = + MAX (values[i], ctx->max_activateable_texture_units); + } + + return ctx->max_activateable_texture_units; +} + +typedef struct +{ + int i; + unsigned long *layer_differences; +} CoglPipelineFlushLayerState; + +static CoglBool +flush_layers_common_gl_state_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglPipelineFlushLayerState *flush_state = user_data; + int unit_index = flush_state->i; + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + unsigned long layers_difference = + flush_state->layer_differences[unit_index]; + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* There may not be enough texture units so we can bail out if + * that's the case... + */ + if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ())) + { + static CoglBool shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("Your hardware does not have enough texture units" + "to handle this many texture layers"); + shown_warning = TRUE; + } + return FALSE; + } + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) + { + CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer); + GLuint gl_texture; + GLenum gl_target; + + if (texture == NULL) + switch (_cogl_pipeline_layer_get_texture_type (layer)) + { + case COGL_TEXTURE_TYPE_2D: + texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); + break; + case COGL_TEXTURE_TYPE_3D: + texture = COGL_TEXTURE (ctx->default_gl_texture_3d_tex); + break; + case COGL_TEXTURE_TYPE_RECTANGLE: + texture = COGL_TEXTURE (ctx->default_gl_texture_rect_tex); + break; + } + + cogl_texture_get_gl_texture (texture, + &gl_texture, + &gl_target); + + _cogl_set_active_texture_unit (unit_index); + + /* NB: There are several Cogl components and some code in + * Clutter that will temporarily bind arbitrary GL textures to + * query and modify texture object parameters. If you look at + * _cogl_bind_gl_texture_transient() you can see we make sure + * that such code always binds to texture unit 1 which means we + * can't rely on the unit->gl_texture state if unit->index == 1. + * + * Because texture unit 1 is a bit special we actually defer any + * necessary glBindTexture for it until the end of + * _cogl_pipeline_flush_gl_state(). + * + * NB: we get notified whenever glDeleteTextures is used (see + * _cogl_delete_gl_texture()) where we invalidate + * unit->gl_texture references to deleted textures so it's safe + * to compare unit->gl_texture with gl_texture. (Without the + * hook it would be possible to delete a GL texture and create a + * new one with the same name and comparing unit->gl_texture and + * gl_texture wouldn't detect that.) + * + * NB: for foreign textures we don't know how the deletion of + * the GL texture objects correspond to the deletion of the + * CoglTextures so if there was previously a foreign texture + * associated with the texture unit then we can't assume that we + * aren't seeing a recycled texture name so we have to bind. + */ + if (unit->gl_texture != gl_texture || unit->is_foreign) + { + if (unit_index == 1) + unit->dirty_gl_texture = TRUE; + else + GE (ctx, glBindTexture (gl_target, gl_texture)); + unit->gl_texture = gl_texture; + unit->gl_target = gl_target; + } + + unit->is_foreign = _cogl_texture_is_foreign (texture); + + /* The texture_storage_changed boolean indicates if the + * CoglTexture's underlying GL texture storage has changed since + * it was flushed to the texture unit. We've just flushed the + * latest state so we can reset this. */ + unit->texture_storage_changed = FALSE; + } + + if ((layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER) && + _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + { + const CoglSamplerCacheEntry *sampler_state; + + sampler_state = _cogl_pipeline_layer_get_sampler_state (layer); + + GE( ctx, glBindSampler (unit_index, sampler_state->sampler_object) ); + } + + /* FIXME: If using GLSL the progend we will use gl_PointCoord + * instead of us needing to replace the texture coordinates but at + * this point we can't currently tell if we are using the fixed or + * glsl progend. + */ +#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GL) + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED) && + (layers_difference & COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS)) + { + CoglPipelineState change = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, change); + CoglPipelineLayerBigState *big_state = authority->big_state; + + _cogl_set_active_texture_unit (unit_index); + + GE (ctx, glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE, + big_state->point_sprite_coords)); + } +#endif + + cogl_object_ref (layer); + if (unit->layer != NULL) + cogl_object_unref (unit->layer); + + unit->layer = layer; + unit->layer_changes_since_flush = 0; + + flush_state->i++; + + return TRUE; +} + +static void +_cogl_pipeline_flush_common_gl_state (CoglPipeline *pipeline, + unsigned long pipelines_difference, + unsigned long *layer_differences, + CoglBool with_color_attrib) +{ + CoglPipelineFlushLayerState state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_pipeline_flush_color_blend_alpha_depth_state (pipeline, + pipelines_difference, + with_color_attrib); + + state.i = 0; + state.layer_differences = layer_differences; + _cogl_pipeline_foreach_layer_internal (pipeline, + flush_layers_common_gl_state_cb, + &state); +} + +/* Re-assert the layer's wrap modes on the given CoglTexture. + * + * Note: we don't simply forward the wrap modes to layer->texture + * since the actual texture being used may have been overridden. + */ +static void +_cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer, + CoglTexture *texture) +{ + CoglSamplerCacheWrapMode wrap_mode_s, wrap_mode_t, wrap_mode_p; + GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p; + + if (texture == NULL) + return; + + _cogl_pipeline_layer_get_wrap_modes (layer, + &wrap_mode_s, + &wrap_mode_t, + &wrap_mode_p); + + /* Update the wrap mode on the texture object. The texture backend + should cache the value so that it will be a no-op if the object + already has the same wrap mode set. The backend is best placed to + do this because it knows how many of the coordinates will + actually be used (ie, a 1D texture only cares about the 's' + coordinate but a 3D texture would use all three). GL uses the + wrap mode as part of the texture object state but we are + pretending it's part of the per-layer environment state. This + will break if the application tries to use different modes in + different layers using the same texture. */ + + if (wrap_mode_s == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) + gl_wrap_mode_s = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_s = wrap_mode_s; + + if (wrap_mode_t == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) + gl_wrap_mode_t = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_t = wrap_mode_t; + + if (wrap_mode_p == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) + gl_wrap_mode_p = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_p = wrap_mode_p; + + _cogl_texture_gl_flush_legacy_texobj_wrap_modes (texture, + gl_wrap_mode_s, + gl_wrap_mode_t, + gl_wrap_mode_p); +} + +/* OpenGL associates the min/mag filters and repeat modes with the + * texture object not the texture unit so we always have to re-assert + * the filter and repeat modes whenever we use a texture since it may + * be referenced by multiple pipelines with different modes. + * + * This function is bypassed in favour of sampler objects if + * GL_ARB_sampler_objects is advertised. This fallback won't work if + * the same texture is bound to multiple layers with different sampler + * state. + */ +static void +foreach_texture_unit_update_filter_and_wrap_modes (void) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (i = 0; i < ctx->texture_units->len; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + + if (unit->layer) + { + CoglTexture *texture = _cogl_pipeline_layer_get_texture (unit->layer); + + if (texture != NULL) + { + CoglPipelineFilter min; + CoglPipelineFilter mag; + + _cogl_pipeline_layer_get_filters (unit->layer, &min, &mag); + _cogl_texture_gl_flush_legacy_texobj_filters (texture, min, mag); + + _cogl_pipeline_layer_forward_wrap_modes (unit->layer, texture); + } + } + } +} + +typedef struct +{ + int i; + unsigned long *layer_differences; +} CoglPipelineCompareLayersState; + +static CoglBool +compare_layer_differences_cb (CoglPipelineLayer *layer, void *user_data) +{ + CoglPipelineCompareLayersState *state = user_data; + CoglTextureUnit *unit = _cogl_get_texture_unit (state->i); + + if (unit->layer == layer) + state->layer_differences[state->i] = unit->layer_changes_since_flush; + else if (unit->layer) + { + state->layer_differences[state->i] = unit->layer_changes_since_flush; + state->layer_differences[state->i] |= + _cogl_pipeline_layer_compare_differences (layer, unit->layer); + } + else + state->layer_differences[state->i] = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; + + /* XXX: There is always a possibility that a CoglTexture's + * underlying GL texture storage has been changed since it was last + * bound to a texture unit which is why we have a callback into + * _cogl_pipeline_texture_storage_change_notify whenever a textures + * underlying GL texture storage changes which will set the + * unit->texture_intern_changed flag. If we see that's been set here + * then we force an update of the texture state... + */ + if (unit->texture_storage_changed) + state->layer_differences[state->i] |= + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA; + + state->i++; + + return TRUE; +} + +typedef struct +{ + CoglFramebuffer *framebuffer; + const CoglPipelineVertend *vertend; + const CoglPipelineFragend *fragend; + CoglPipeline *pipeline; + unsigned long *layer_differences; + CoglBool error_adding_layer; + CoglBool added_layer; +} CoglPipelineAddLayerState; + +static CoglBool +vertend_add_layer_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineAddLayerState *state = user_data; + const CoglPipelineVertend *vertend = state->vertend; + CoglPipeline *pipeline = state->pipeline; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + + /* Either generate per layer code snippets or setup the + * fixed function glTexEnv for each layer... */ + if (G_LIKELY (vertend->add_layer (pipeline, + layer, + state->layer_differences[unit_index], + state->framebuffer))) + state->added_layer = TRUE; + else + { + state->error_adding_layer = TRUE; + return FALSE; + } + + return TRUE; +} + +static CoglBool +fragend_add_layer_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineAddLayerState *state = user_data; + const CoglPipelineFragend *fragend = state->fragend; + CoglPipeline *pipeline = state->pipeline; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + + /* Either generate per layer code snippets or setup the + * fixed function glTexEnv for each layer... */ + if (G_LIKELY (fragend->add_layer (pipeline, + layer, + state->layer_differences[unit_index]))) + state->added_layer = TRUE; + else + { + state->error_adding_layer = TRUE; + return FALSE; + } + + return TRUE; +} + +/* + * _cogl_pipeline_flush_gl_state: + * + * Details of override options: + * ->fallback_mask: is a bitmask of the pipeline layers that need to be + * replaced with the default, fallback textures. The fallback textures are + * fully transparent textures so they hopefully wont contribute to the + * texture combining. + * + * The intention of fallbacks is to try and preserve + * the number of layers the user is expecting so that texture coordinates + * they gave will mostly still correspond to the textures they intended, and + * have a fighting chance of looking close to their originally intended + * result. + * + * ->disable_mask: is a bitmask of the pipeline layers that will simply have + * texturing disabled. It's only really intended for disabling all layers + * > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB + * and at some point the remaining bits flip to 1. It might work to disable + * arbitrary layers; though I'm not sure a.t.m how OpenGL would take to + * that. + * + * The intention of the disable_mask is for emitting geometry when the user + * hasn't supplied enough texture coordinates for all the layers and it's + * not possible to auto generate default texture coordinates for those + * layers. + * + * ->layer0_override_texture: forcibly tells us to bind this GL texture name for + * layer 0 instead of plucking the gl_texture from the CoglTexture of layer + * 0. + * + * The intention of this is for any primitives that supports sliced textures. + * The code will can iterate each of the slices and re-flush the pipeline + * forcing the GL texture of each slice in turn. + * + * ->wrap_mode_overrides: overrides the wrap modes set on each + * layer. This is used to implement the automatic wrap mode. + * + * XXX: It might also help if we could specify a texture matrix for code + * dealing with slicing that would be multiplied with the users own matrix. + * + * Normaly texture coords in the range [0, 1] refer to the extents of the + * texture, but when your GL texture represents a slice of the real texture + * (from the users POV) then a texture matrix would be a neat way of + * transforming the mapping for each slice. + * + * Currently for textured rectangles we manually calculate the texture + * coords for each slice based on the users given coords, but this solution + * isn't ideal, and can't be used with CoglVertexBuffers. + */ +void +_cogl_pipeline_flush_gl_state (CoglContext *ctx, + CoglPipeline *pipeline, + CoglFramebuffer *framebuffer, + CoglBool with_color_attrib, + CoglBool unknown_color_alpha) +{ + CoglPipeline *current_pipeline = ctx->current_pipeline; + unsigned long pipelines_difference; + int n_layers; + unsigned long *layer_differences; + int i; + CoglTextureUnit *unit1; + const CoglPipelineProgend *progend; + + COGL_STATIC_TIMER (pipeline_flush_timer, + "Mainloop", /* parent */ + "Material Flush", + "The time spent flushing material state", + 0 /* no application private data */); + + COGL_TIMER_START (_cogl_uprof_context, pipeline_flush_timer); + + /* Bail out asap if we've been asked to re-flush the already current + * pipeline and we can see the pipeline hasn't changed */ + if (current_pipeline == pipeline && + ctx->current_pipeline_age == pipeline->age && + ctx->current_pipeline_with_color_attrib == with_color_attrib && + ctx->current_pipeline_unknown_color_alpha == unknown_color_alpha) + goto done; + else + { + /* Update derived state (currently just the 'real_blend_enable' + * state) and determine a mask of state that differs between the + * current pipeline and the one we are flushing. + * + * Note updating the derived state is done before doing any + * pipeline comparisons so that we can correctly compare the + * 'real_blend_enable' state itself. + */ + + if (current_pipeline == pipeline) + { + pipelines_difference = ctx->current_pipeline_changes_since_flush; + + if (pipelines_difference & COGL_PIPELINE_STATE_AFFECTS_BLENDING || + pipeline->unknown_color_alpha != unknown_color_alpha) + { + CoglBool save_real_blend_enable = pipeline->real_blend_enable; + + _cogl_pipeline_update_real_blend_enable (pipeline, + unknown_color_alpha); + + if (save_real_blend_enable != pipeline->real_blend_enable) + pipelines_difference |= COGL_PIPELINE_STATE_REAL_BLEND_ENABLE; + } + } + else if (current_pipeline) + { + pipelines_difference = ctx->current_pipeline_changes_since_flush; + + _cogl_pipeline_update_real_blend_enable (pipeline, + unknown_color_alpha); + + pipelines_difference |= + _cogl_pipeline_compare_differences (ctx->current_pipeline, + pipeline); + } + else + { + _cogl_pipeline_update_real_blend_enable (pipeline, + unknown_color_alpha); + + pipelines_difference = COGL_PIPELINE_STATE_ALL; + } + } + + /* Get a layer_differences mask for each layer to be flushed */ + n_layers = cogl_pipeline_get_n_layers (pipeline); + if (n_layers) + { + CoglPipelineCompareLayersState state; + layer_differences = g_alloca (sizeof (unsigned long) * n_layers); + memset (layer_differences, 0, sizeof (unsigned long) * n_layers); + state.i = 0; + state.layer_differences = layer_differences; + _cogl_pipeline_foreach_layer_internal (pipeline, + compare_layer_differences_cb, + &state); + } + else + layer_differences = NULL; + + /* First flush everything that's the same regardless of which + * pipeline backend is being used... + * + * 1) top level state: + * glColor (or skip if a vertex attribute is being used for color) + * blend state + * alpha test state (except for GLES 2.0) + * + * 2) then foreach layer: + * determine gl_target/gl_texture + * bind texture + * + * Note: After _cogl_pipeline_flush_common_gl_state you can expect + * all state of the layers corresponding texture unit to be + * updated. + */ + _cogl_pipeline_flush_common_gl_state (pipeline, + pipelines_difference, + layer_differences, + with_color_attrib); + + /* Now flush the fragment, vertex and program state according to the + * current progend backend. + * + * Note: Some backends may not support the current pipeline + * configuration and in that case it will report and error and we + * will look for a different backend. + * + * NB: if pipeline->progend != COGL_PIPELINE_PROGEND_UNDEFINED then + * we have previously managed to successfully flush this pipeline + * with the given progend so we will simply use that to avoid + * fallback code paths. + */ + if (pipeline->progend == COGL_PIPELINE_PROGEND_UNDEFINED) + _cogl_pipeline_set_progend (pipeline, COGL_PIPELINE_PROGEND_DEFAULT); + + for (i = pipeline->progend; + i < COGL_PIPELINE_N_PROGENDS; + i++, _cogl_pipeline_set_progend (pipeline, i)) + { + const CoglPipelineVertend *vertend; + const CoglPipelineFragend *fragend; + CoglPipelineAddLayerState state; + + progend = _cogl_pipeline_progends[i]; + + if (G_UNLIKELY (!progend->start (pipeline))) + continue; + + vertend = _cogl_pipeline_vertends[progend->vertend]; + + vertend->start (pipeline, + n_layers, + pipelines_difference); + + state.framebuffer = framebuffer; + state.vertend = vertend; + state.pipeline = pipeline; + state.layer_differences = layer_differences; + state.error_adding_layer = FALSE; + state.added_layer = FALSE; + + _cogl_pipeline_foreach_layer_internal (pipeline, + vertend_add_layer_cb, + &state); + + if (G_UNLIKELY (state.error_adding_layer)) + continue; + + if (G_UNLIKELY (!vertend->end (pipeline, pipelines_difference))) + continue; + + /* Now prepare the fragment processing state (fragend) + * + * NB: We can't combine the setup of the vertend and fragend + * since the backends that do code generation share + * ctx->codegen_source_buffer as a scratch buffer. + */ + + fragend = _cogl_pipeline_fragends[progend->fragend]; + state.fragend = fragend; + + fragend->start (pipeline, + n_layers, + pipelines_difference); + + _cogl_pipeline_foreach_layer_internal (pipeline, + fragend_add_layer_cb, + &state); + + if (G_UNLIKELY (state.error_adding_layer)) + continue; + + if (!state.added_layer) + { + if (fragend->passthrough && + G_UNLIKELY (!fragend->passthrough (pipeline))) + continue; + } + + if (G_UNLIKELY (!fragend->end (pipeline, pipelines_difference))) + continue; + + if (progend->end) + progend->end (pipeline, pipelines_difference); + break; + } + + /* FIXME: This reference is actually resulting in lots of + * copy-on-write reparenting because one-shot pipelines end up + * living for longer than necessary and so any later modification of + * the parent will cause a copy-on-write. + * + * XXX: The issue should largely go away when we switch to using + * weak pipelines for overrides. + */ + cogl_object_ref (pipeline); + if (ctx->current_pipeline != NULL) + cogl_object_unref (ctx->current_pipeline); + ctx->current_pipeline = pipeline; + ctx->current_pipeline_changes_since_flush = 0; + ctx->current_pipeline_with_color_attrib = with_color_attrib; + ctx->current_pipeline_unknown_color_alpha = unknown_color_alpha; + ctx->current_pipeline_age = pipeline->age; + +done: + + progend = _cogl_pipeline_progends[pipeline->progend]; + + /* We can't assume the color will be retained between flushes when + * using the glsl progend because the generic attribute values are + * not stored as part of the program object so they could be + * overridden by any attribute changes in another program */ + if (pipeline->progend == COGL_PIPELINE_PROGEND_GLSL && !with_color_attrib) + { + int attribute; + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); + int name_index = COGL_ATTRIBUTE_COLOR_NAME_INDEX; + + attribute = + _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); + if (attribute != -1) + GE (ctx, + glVertexAttrib4f (attribute, + cogl_color_get_red_float (&authority->color), + cogl_color_get_green_float (&authority->color), + cogl_color_get_blue_float (&authority->color), + cogl_color_get_alpha_float (&authority->color))); + } + + /* Give the progend a chance to update any uniforms that might not + * depend on the material state. This is used on GLES2 to update the + * matrices */ + if (progend->pre_paint) + progend->pre_paint (pipeline, framebuffer); + + /* Handle the fact that OpenGL associates texture filter and wrap + * modes with the texture objects not the texture units... */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + foreach_texture_unit_update_filter_and_wrap_modes (); + + /* If this pipeline has more than one layer then we always need + * to make sure we rebind the texture for unit 1. + * + * NB: various components of Cogl may temporarily bind arbitrary + * textures to texture unit 1 so they can query and modify texture + * object parameters. cogl-pipeline.c (See + * _cogl_bind_gl_texture_transient) + */ + unit1 = _cogl_get_texture_unit (1); + if (cogl_pipeline_get_n_layers (pipeline) > 1 && unit1->dirty_gl_texture) + { + _cogl_set_active_texture_unit (1); + GE (ctx, glBindTexture (unit1->gl_target, unit1->gl_texture)); + unit1->dirty_gl_texture = FALSE; + } + + COGL_TIMER_STOP (_cogl_uprof_context, pipeline_flush_timer); +} + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed-private.h b/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed-private.h new file mode 100644 index 000000000..f97e16a99 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed-private.h @@ -0,0 +1,42 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H +#define __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineProgend _cogl_pipeline_fixed_progend; + +#endif /* __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed.c b/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed.c new file mode 100644 index 000000000..99f71bff1 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-progend-fixed.c @@ -0,0 +1,112 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-state-private.h" + +#ifdef COGL_PIPELINE_PROGEND_FIXED + +#include "cogl-context.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" + +static CoglBool +_cogl_pipeline_progend_fixed_start (CoglPipeline *pipeline) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED))) + return FALSE; + + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) + return FALSE; + + /* Vertex snippets are only supported in the GLSL fragend */ + if (_cogl_pipeline_has_vertex_snippets (pipeline)) + return FALSE; + + /* Fragment snippets are only supported in the GLSL fragend */ + if (_cogl_pipeline_has_fragment_snippets (pipeline)) + return FALSE; + + /* If there is a user program then the appropriate backend for that + * language should handle it. */ + if (cogl_pipeline_get_user_program (pipeline)) + return FALSE; + + /* The fixed progend can't handle the per-vertex point size + * attribute */ + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + return FALSE; + + return TRUE; +} + +static void +_cogl_pipeline_progend_fixed_pre_paint (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + if (ctx->current_projection_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_projection_entry, + COGL_MATRIX_PROJECTION, + framebuffer, + FALSE /* enable flip */); + if (ctx->current_modelview_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_modelview_entry, + COGL_MATRIX_MODELVIEW, + framebuffer, + FALSE /* enable flip */); +} + +const CoglPipelineProgend _cogl_pipeline_fixed_progend = + { + COGL_PIPELINE_VERTEND_FIXED, + COGL_PIPELINE_FRAGEND_FIXED, + _cogl_pipeline_progend_fixed_start, + NULL, /* end */ + NULL, /* pre_change_notify */ + NULL, /* layer_pre_change_notify */ + _cogl_pipeline_progend_fixed_pre_paint + }; + +#endif /* COGL_PIPELINE_PROGEND_FIXED */ diff --git a/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl-private.h b/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl-private.h new file mode 100644 index 000000000..d57519b80 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl-private.h @@ -0,0 +1,47 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H +#define __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H + +#include "cogl-pipeline-private.h" +#include "cogl-attribute-private.h" + +extern const CoglPipelineProgend _cogl_pipeline_glsl_progend; + +int +_cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline, + int name_index); + +#endif /* __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl.c b/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl.c new file mode 100644 index 000000000..8884ce669 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl.c @@ -0,0 +1,1074 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-util.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-offscreen.h" + +#ifdef COGL_PIPELINE_PROGEND_GLSL + +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-program-private.h" +#include "cogl-pipeline-fragend-glsl-private.h" +#include "cogl-pipeline-vertend-glsl-private.h" +#include "cogl-pipeline-cache.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-attribute-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-pipeline-progend-glsl-private.h" + +/* These are used to generalise updating some uniforms that are + required when building for drivers missing some fixed function + state that we use */ + +typedef void (* UpdateUniformFunc) (CoglPipeline *pipeline, + int uniform_location, + void *getter_func); + +static void update_float_uniform (CoglPipeline *pipeline, + int uniform_location, + void *getter_func); + +typedef struct +{ + const char *uniform_name; + void *getter_func; + UpdateUniformFunc update_func; + CoglPipelineState change; + + /* This builtin is only necessary if the following private feature + * is not implemented in the driver */ + CoglPrivateFeature feature_replacement; +} BuiltinUniformData; + +static BuiltinUniformData builtin_uniforms[] = + { + { "cogl_point_size_in", + cogl_pipeline_get_point_size, update_float_uniform, + COGL_PIPELINE_STATE_POINT_SIZE, + COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM }, + { "_cogl_alpha_test_ref", + cogl_pipeline_get_alpha_test_reference, update_float_uniform, + COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE, + COGL_PRIVATE_FEATURE_ALPHA_TEST } + }; + +const CoglPipelineProgend _cogl_pipeline_glsl_progend; + +typedef struct _UnitState +{ + unsigned int dirty_combine_constant:1; + unsigned int dirty_texture_matrix:1; + + GLint combine_constant_uniform; + + GLint texture_matrix_uniform; +} UnitState; + +typedef struct +{ + unsigned int ref_count; + + /* Age that the user program had last time we generated a GL + program. If it's different then we need to relink the program */ + unsigned int user_program_age; + + GLuint program; + + unsigned long dirty_builtin_uniforms; + GLint builtin_uniform_locations[G_N_ELEMENTS (builtin_uniforms)]; + + GLint modelview_uniform; + GLint projection_uniform; + GLint mvp_uniform; + + CoglMatrixEntryCache projection_cache; + CoglMatrixEntryCache modelview_cache; + + /* We need to track the last pipeline that the program was used with + * so know if we need to update all of the uniforms */ + CoglPipeline *last_used_for_pipeline; + + /* Array of GL uniform locations indexed by Cogl's uniform + location. We are careful only to allocated this array if a custom + uniform is actually set */ + GArray *uniform_locations; + + /* Array of attribute locations. */ + GArray *attribute_locations; + + /* The 'flip' uniform is used to flip the geometry upside-down when + the framebuffer requires it only when there are vertex + snippets. Otherwise this is acheived using the projection + matrix */ + GLint flip_uniform; + int flushed_flip_state; + + UnitState *unit_state; + + CoglPipelineCacheEntry *cache_entry; +} CoglPipelineProgramState; + +static CoglUserDataKey program_state_key; + +static CoglPipelineProgramState * +get_program_state (CoglPipeline *pipeline) +{ + return cogl_object_get_user_data (COGL_OBJECT (pipeline), &program_state_key); +} + +#define UNIFORM_LOCATION_UNKNOWN -2 + +#define ATTRIBUTE_LOCATION_UNKNOWN -2 + +/* Under GLES2 the vertex attribute API needs to query the attribute + numbers because it can't used the fixed function API to set the + builtin attributes. We cache the attributes here because the + progend knows when the program is changed so it can clear the + cache. This should always be called after the pipeline is flushed + so they can assert that the gl program is valid */ + +/* All attributes names get internally mapped to a global set of + * sequential indices when they are setup which we need to need to + * then be able to map to a GL attribute location once we have + * a linked GLSL program */ + +int +_cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline, + int name_index) +{ + CoglPipelineProgramState *program_state = get_program_state (pipeline); + int *locations; + + _COGL_GET_CONTEXT (ctx, -1); + + _COGL_RETURN_VAL_IF_FAIL (program_state != NULL, -1); + _COGL_RETURN_VAL_IF_FAIL (program_state->program != 0, -1); + + if (G_UNLIKELY (program_state->attribute_locations == NULL)) + program_state->attribute_locations = + g_array_new (FALSE, FALSE, sizeof (int)); + + if (G_UNLIKELY (program_state->attribute_locations->len <= name_index)) + { + int i = program_state->attribute_locations->len; + g_array_set_size (program_state->attribute_locations, name_index + 1); + for (; i < program_state->attribute_locations->len; i++) + g_array_index (program_state->attribute_locations, int, i) + = ATTRIBUTE_LOCATION_UNKNOWN; + } + + locations = &g_array_index (program_state->attribute_locations, int, 0); + + if (locations[name_index] == ATTRIBUTE_LOCATION_UNKNOWN) + { + CoglAttributeNameState *name_state = + g_array_index (ctx->attribute_name_index_map, + CoglAttributeNameState *, name_index); + + _COGL_RETURN_VAL_IF_FAIL (name_state != NULL, 0); + + GE_RET( locations[name_index], + ctx, glGetAttribLocation (program_state->program, + name_state->name) ); + } + + return locations[name_index]; +} + +static void +clear_attribute_cache (CoglPipelineProgramState *program_state) +{ + if (program_state->attribute_locations) + { + g_array_free (program_state->attribute_locations, TRUE); + program_state->attribute_locations = NULL; + } +} + +static void +clear_flushed_matrix_stacks (CoglPipelineProgramState *program_state) +{ + _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); + _cogl_matrix_entry_cache_init (&program_state->projection_cache); + _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); + _cogl_matrix_entry_cache_init (&program_state->modelview_cache); +} + +static CoglPipelineProgramState * +program_state_new (int n_layers, + CoglPipelineCacheEntry *cache_entry) +{ + CoglPipelineProgramState *program_state; + + program_state = g_slice_new (CoglPipelineProgramState); + program_state->ref_count = 1; + program_state->program = 0; + program_state->unit_state = g_new (UnitState, n_layers); + program_state->uniform_locations = NULL; + program_state->attribute_locations = NULL; + program_state->cache_entry = cache_entry; + _cogl_matrix_entry_cache_init (&program_state->modelview_cache); + _cogl_matrix_entry_cache_init (&program_state->projection_cache); + + return program_state; +} + +static void +destroy_program_state (void *user_data, + void *instance) +{ + CoglPipelineProgramState *program_state = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If the program state was last used for this pipeline then clear + it so that if same address gets used again for a new pipeline + then we won't think it's the same pipeline and avoid updating the + uniforms */ + if (program_state->last_used_for_pipeline == instance) + program_state->last_used_for_pipeline = NULL; + + if (program_state->cache_entry && + program_state->cache_entry->pipeline != instance) + program_state->cache_entry->usage_count--; + + if (--program_state->ref_count == 0) + { + clear_attribute_cache (program_state); + + _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); + _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); + + if (program_state->program) + GE( ctx, glDeleteProgram (program_state->program) ); + + g_free (program_state->unit_state); + + if (program_state->uniform_locations) + g_array_free (program_state->uniform_locations, TRUE); + + g_slice_free (CoglPipelineProgramState, program_state); + } +} + +static void +set_program_state (CoglPipeline *pipeline, + CoglPipelineProgramState *program_state) +{ + if (program_state) + { + program_state->ref_count++; + + /* If we're not setting the state on the template pipeline then + * mark it as a usage of the pipeline cache entry */ + if (program_state->cache_entry && + program_state->cache_entry->pipeline != pipeline) + program_state->cache_entry->usage_count++; + } + + _cogl_object_set_user_data (COGL_OBJECT (pipeline), + &program_state_key, + program_state, + destroy_program_state); +} + +static void +dirty_program_state (CoglPipeline *pipeline) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &program_state_key, + NULL, + NULL); +} + +static void +link_program (GLint gl_program) +{ + GLint link_status; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GE( ctx, glLinkProgram (gl_program) ); + + GE( ctx, glGetProgramiv (gl_program, GL_LINK_STATUS, &link_status) ); + + if (!link_status) + { + GLint log_length; + GLsizei out_log_length; + char *log; + + GE( ctx, glGetProgramiv (gl_program, GL_INFO_LOG_LENGTH, &log_length) ); + + log = g_malloc (log_length); + + GE( ctx, glGetProgramInfoLog (gl_program, log_length, + &out_log_length, log) ); + + g_warning ("Failed to link GLSL program:\n%.*s\n", + log_length, log); + + g_free (log); + } +} + +typedef struct +{ + int unit; + GLuint gl_program; + CoglBool update_all; + CoglPipelineProgramState *program_state; +} UpdateUniformsState; + +static CoglBool +get_uniform_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + UpdateUniformsState *state = user_data; + CoglPipelineProgramState *program_state = state->program_state; + UnitState *unit_state = &program_state->unit_state[state->unit]; + GLint uniform_location; + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* We can reuse the source buffer to create the uniform name because + the program has now been linked */ + g_string_set_size (ctx->codegen_source_buffer, 0); + g_string_append_printf (ctx->codegen_source_buffer, + "cogl_sampler%i", layer_index); + + GE_RET( uniform_location, + ctx, glGetUniformLocation (state->gl_program, + ctx->codegen_source_buffer->str) ); + + /* We can set the uniform immediately because the samplers are the + unit index not the texture object number so it will never + change. Unfortunately GL won't let us use a constant instead of a + uniform */ + if (uniform_location != -1) + GE( ctx, glUniform1i (uniform_location, state->unit) ); + + g_string_set_size (ctx->codegen_source_buffer, 0); + g_string_append_printf (ctx->codegen_source_buffer, + "_cogl_layer_constant_%i", layer_index); + + GE_RET( uniform_location, + ctx, glGetUniformLocation (state->gl_program, + ctx->codegen_source_buffer->str) ); + + unit_state->combine_constant_uniform = uniform_location; + + g_string_set_size (ctx->codegen_source_buffer, 0); + g_string_append_printf (ctx->codegen_source_buffer, + "cogl_texture_matrix[%i]", layer_index); + + GE_RET( uniform_location, + ctx, glGetUniformLocation (state->gl_program, + ctx->codegen_source_buffer->str) ); + + unit_state->texture_matrix_uniform = uniform_location; + + state->unit++; + + return TRUE; +} + +static CoglBool +update_constants_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + UpdateUniformsState *state = user_data; + CoglPipelineProgramState *program_state = state->program_state; + UnitState *unit_state = &program_state->unit_state[state->unit++]; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (unit_state->combine_constant_uniform != -1 && + (state->update_all || unit_state->dirty_combine_constant)) + { + float constant[4]; + _cogl_pipeline_get_layer_combine_constant (pipeline, + layer_index, + constant); + GE (ctx, glUniform4fv (unit_state->combine_constant_uniform, + 1, constant)); + unit_state->dirty_combine_constant = FALSE; + } + + if (unit_state->texture_matrix_uniform != -1 && + (state->update_all || unit_state->dirty_texture_matrix)) + { + const CoglMatrix *matrix; + const float *array; + + matrix = _cogl_pipeline_get_layer_matrix (pipeline, layer_index); + array = cogl_matrix_get_array (matrix); + GE (ctx, glUniformMatrix4fv (unit_state->texture_matrix_uniform, + 1, FALSE, array)); + unit_state->dirty_texture_matrix = FALSE; + } + + return TRUE; +} + +static void +update_builtin_uniforms (CoglContext *context, + CoglPipeline *pipeline, + GLuint gl_program, + CoglPipelineProgramState *program_state) +{ + int i; + + if (program_state->dirty_builtin_uniforms == 0) + return; + + for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) + if (!_cogl_has_private_feature (context, + builtin_uniforms[i].feature_replacement) && + (program_state->dirty_builtin_uniforms & (1 << i)) && + program_state->builtin_uniform_locations[i] != -1) + builtin_uniforms[i].update_func (pipeline, + program_state + ->builtin_uniform_locations[i], + builtin_uniforms[i].getter_func); + + program_state->dirty_builtin_uniforms = 0; +} + +typedef struct +{ + CoglPipelineProgramState *program_state; + unsigned long *uniform_differences; + int n_differences; + CoglContext *ctx; + const CoglBoxedValue *values; + int value_index; +} FlushUniformsClosure; + +static CoglBool +flush_uniform_cb (int uniform_num, void *user_data) +{ + FlushUniformsClosure *data = user_data; + + if (COGL_FLAGS_GET (data->uniform_differences, uniform_num)) + { + GArray *uniform_locations; + GLint uniform_location; + + if (data->program_state->uniform_locations == NULL) + data->program_state->uniform_locations = + g_array_new (FALSE, FALSE, sizeof (GLint)); + + uniform_locations = data->program_state->uniform_locations; + + if (uniform_locations->len <= uniform_num) + { + unsigned int old_len = uniform_locations->len; + + g_array_set_size (uniform_locations, uniform_num + 1); + + while (old_len <= uniform_num) + { + g_array_index (uniform_locations, GLint, old_len) = + UNIFORM_LOCATION_UNKNOWN; + old_len++; + } + } + + uniform_location = g_array_index (uniform_locations, GLint, uniform_num); + + if (uniform_location == UNIFORM_LOCATION_UNKNOWN) + { + const char *uniform_name = + g_ptr_array_index (data->ctx->uniform_names, uniform_num); + + uniform_location = + data->ctx->glGetUniformLocation (data->program_state->program, + uniform_name); + g_array_index (uniform_locations, GLint, uniform_num) = + uniform_location; + } + + if (uniform_location != -1) + _cogl_boxed_value_set_uniform (data->ctx, + uniform_location, + data->values + data->value_index); + + data->n_differences--; + COGL_FLAGS_SET (data->uniform_differences, uniform_num, FALSE); + } + + data->value_index++; + + return data->n_differences > 0; +} + +static void +_cogl_pipeline_progend_glsl_flush_uniforms (CoglPipeline *pipeline, + CoglPipelineProgramState * + program_state, + GLuint gl_program, + CoglBool program_changed) +{ + CoglPipelineUniformsState *uniforms_state; + FlushUniformsClosure data; + int n_uniform_longs; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) + uniforms_state = &pipeline->big_state->uniforms_state; + else + uniforms_state = NULL; + + data.program_state = program_state; + data.ctx = ctx; + + n_uniform_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names); + + data.uniform_differences = g_newa (unsigned long, n_uniform_longs); + + /* Try to find a common ancestor for the values that were already + flushed on the pipeline that this program state was last used for + so we can avoid flushing those */ + + if (program_changed || program_state->last_used_for_pipeline == NULL) + { + if (program_changed) + { + /* The program has changed so all of the uniform locations + are invalid */ + if (program_state->uniform_locations) + g_array_set_size (program_state->uniform_locations, 0); + } + + /* We need to flush everything so mark all of the uniforms as + dirty */ + memset (data.uniform_differences, 0xff, + n_uniform_longs * sizeof (unsigned long)); + data.n_differences = G_MAXINT; + } + else if (program_state->last_used_for_pipeline) + { + int i; + + memset (data.uniform_differences, 0, + n_uniform_longs * sizeof (unsigned long)); + _cogl_pipeline_compare_uniform_differences + (data.uniform_differences, + program_state->last_used_for_pipeline, + pipeline); + + /* We need to be sure to flush any uniforms that have changed + since the last flush */ + if (uniforms_state) + _cogl_bitmask_set_flags (&uniforms_state->changed_mask, + data.uniform_differences); + + /* Count the number of differences. This is so we can stop early + when we've flushed all of them */ + data.n_differences = 0; + + for (i = 0; i < n_uniform_longs; i++) + data.n_differences += + _cogl_util_popcountl (data.uniform_differences[i]); + } + + while (pipeline && data.n_differences > 0) + { + if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) + { + const CoglPipelineUniformsState *parent_uniforms_state = + &pipeline->big_state->uniforms_state; + + data.values = parent_uniforms_state->override_values; + data.value_index = 0; + + _cogl_bitmask_foreach (&parent_uniforms_state->override_mask, + flush_uniform_cb, + &data); + } + + pipeline = _cogl_pipeline_get_parent (pipeline); + } + + if (uniforms_state) + _cogl_bitmask_clear_all (&uniforms_state->changed_mask); +} + +static CoglBool +_cogl_pipeline_progend_glsl_start (CoglPipeline *pipeline) +{ + CoglHandle user_program; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLSL)) + return FALSE; + + user_program = cogl_pipeline_get_user_program (pipeline); + if (user_program && + _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_GLSL) + return FALSE; + + return TRUE; +} + +static void +_cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + CoglPipelineProgramState *program_state; + GLuint gl_program; + CoglBool program_changed = FALSE; + UpdateUniformsState state; + CoglProgram *user_program; + CoglPipelineCacheEntry *cache_entry = NULL; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + program_state = get_program_state (pipeline); + + user_program = cogl_pipeline_get_user_program (pipeline); + + if (program_state == NULL) + { + CoglPipeline *authority; + + /* Get the authority for anything affecting program state. This + should include both fragment codegen state and vertex codegen + state */ + authority = _cogl_pipeline_find_equivalent_parent + (pipeline, + (_cogl_pipeline_get_state_for_vertex_codegen (ctx) | + _cogl_pipeline_get_state_for_fragment_codegen (ctx)) & + ~COGL_PIPELINE_STATE_LAYERS, + _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); + + program_state = get_program_state (authority); + + if (program_state == NULL) + { + /* Check if there is already a similar cached pipeline whose + program state we can share */ + if (G_LIKELY (!(COGL_DEBUG_ENABLED + (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) + { + cache_entry = + _cogl_pipeline_cache_get_combined_template (ctx->pipeline_cache, + authority); + + program_state = get_program_state (cache_entry->pipeline); + } + + if (program_state) + program_state->ref_count++; + else + program_state + = program_state_new (cogl_pipeline_get_n_layers (authority), + cache_entry); + + set_program_state (authority, program_state); + + program_state->ref_count--; + + if (cache_entry) + set_program_state (cache_entry->pipeline, program_state); + } + + if (authority != pipeline) + set_program_state (pipeline, program_state); + } + + /* If the program has changed since the last link then we do + * need to relink */ + if (program_state->program && user_program && + user_program->age != program_state->user_program_age) + { + GE( ctx, glDeleteProgram (program_state->program) ); + program_state->program = 0; + } + + if (program_state->program == 0) + { + GLuint backend_shader; + GSList *l; + + GE_RET( program_state->program, ctx, glCreateProgram () ); + + /* Attach all of the shader from the user program */ + if (user_program) + { + for (l = user_program->attached_shaders; l; l = l->next) + { + CoglShader *shader = l->data; + + _cogl_shader_compile_real (shader, pipeline); + + g_assert (shader->language == COGL_SHADER_LANGUAGE_GLSL); + + GE( ctx, glAttachShader (program_state->program, + shader->gl_handle) ); + } + + program_state->user_program_age = user_program->age; + } + + /* Attach any shaders from the GLSL backends */ + if ((backend_shader = _cogl_pipeline_fragend_glsl_get_shader (pipeline))) + GE( ctx, glAttachShader (program_state->program, backend_shader) ); + if ((backend_shader = _cogl_pipeline_vertend_glsl_get_shader (pipeline))) + GE( ctx, glAttachShader (program_state->program, backend_shader) ); + + /* XXX: OpenGL as a special case requires the vertex position to + * be bound to generic attribute 0 so for simplicity we + * unconditionally bind the cogl_position_in attribute here... + */ + GE( ctx, glBindAttribLocation (program_state->program, + 0, "cogl_position_in")); + + link_program (program_state->program); + + program_changed = TRUE; + } + + gl_program = program_state->program; + + _cogl_use_fragment_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); + _cogl_use_vertex_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); + + state.unit = 0; + state.gl_program = gl_program; + state.program_state = program_state; + + if (program_changed) + { + cogl_pipeline_foreach_layer (pipeline, + get_uniform_cb, + &state); + clear_attribute_cache (program_state); + + GE_RET (program_state->flip_uniform, + ctx, glGetUniformLocation (gl_program, "_cogl_flip_vector")); + program_state->flushed_flip_state = -1; + } + + state.unit = 0; + state.update_all = (program_changed || + program_state->last_used_for_pipeline != pipeline); + + cogl_pipeline_foreach_layer (pipeline, + update_constants_cb, + &state); + + if (program_changed) + { + int i; + + clear_flushed_matrix_stacks (program_state); + + for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) + if (!_cogl_has_private_feature + (ctx, builtin_uniforms[i].feature_replacement)) + GE_RET( program_state->builtin_uniform_locations[i], ctx, + glGetUniformLocation (gl_program, + builtin_uniforms[i].uniform_name) ); + + GE_RET( program_state->modelview_uniform, ctx, + glGetUniformLocation (gl_program, + "cogl_modelview_matrix") ); + + GE_RET( program_state->projection_uniform, ctx, + glGetUniformLocation (gl_program, + "cogl_projection_matrix") ); + + GE_RET( program_state->mvp_uniform, ctx, + glGetUniformLocation (gl_program, + "cogl_modelview_projection_matrix") ); + } + + if (program_changed || + program_state->last_used_for_pipeline != pipeline) + program_state->dirty_builtin_uniforms = ~(unsigned long) 0; + + update_builtin_uniforms (ctx, pipeline, gl_program, program_state); + + _cogl_pipeline_progend_glsl_flush_uniforms (pipeline, + program_state, + gl_program, + program_changed); + + if (user_program) + _cogl_program_flush_uniforms (user_program, + gl_program, + program_changed); + + /* We need to track the last pipeline that the program was used with + * so know if we need to update all of the uniforms */ + program_state->last_used_for_pipeline = pipeline; +} + +static void +_cogl_pipeline_progend_glsl_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & (_cogl_pipeline_get_state_for_vertex_codegen (ctx) | + _cogl_pipeline_get_state_for_fragment_codegen (ctx)))) + { + dirty_program_state (pipeline); + } + else + { + int i; + + for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) + if (!_cogl_has_private_feature + (ctx, builtin_uniforms[i].feature_replacement) && + (change & builtin_uniforms[i].change)) + { + CoglPipelineProgramState *program_state + = get_program_state (pipeline); + if (program_state) + program_state->dirty_builtin_uniforms |= 1 << i; + return; + } + } +} + +/* NB: layers are considered immutable once they have any dependants + * so although multiple pipelines can end up depending on a single + * static layer, we can guarantee that if a layer is being *changed* + * then it can only have one pipeline depending on it. + * + * XXX: Don't forget this is *pre* change, we can't read the new value + * yet! + */ +static void +_cogl_pipeline_progend_glsl_layer_pre_change_notify ( + CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & (_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN))) + { + dirty_program_state (owner); + } + else if (change & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) + { + CoglPipelineProgramState *program_state = get_program_state (owner); + if (program_state) + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + program_state->unit_state[unit_index].dirty_combine_constant = TRUE; + } + } + else if (change & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) + { + CoglPipelineProgramState *program_state = get_program_state (owner); + if (program_state) + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + program_state->unit_state[unit_index].dirty_texture_matrix = TRUE; + } + } +} + +static void +_cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer) +{ + CoglBool needs_flip; + CoglMatrixEntry *projection_entry; + CoglMatrixEntry *modelview_entry; + CoglPipelineProgramState *program_state; + CoglBool modelview_changed; + CoglBool projection_changed; + CoglBool need_modelview; + CoglBool need_projection; + CoglMatrix modelview, projection; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + program_state = get_program_state (pipeline); + + projection_entry = ctx->current_projection_entry; + modelview_entry = ctx->current_modelview_entry; + + /* An initial pipeline is flushed while creating the context. At + this point there are no matrices selected so we can't do + anything */ + if (modelview_entry == NULL || projection_entry == NULL) + return; + + needs_flip = cogl_is_offscreen (ctx->current_draw_buffer); + + projection_changed = + _cogl_matrix_entry_cache_maybe_update (&program_state->projection_cache, + projection_entry, + (needs_flip && + program_state->flip_uniform == + -1)); + + modelview_changed = + _cogl_matrix_entry_cache_maybe_update (&program_state->modelview_cache, + modelview_entry, + /* never flip modelview */ + FALSE); + + if (modelview_changed || projection_changed) + { + if (program_state->mvp_uniform != -1) + need_modelview = need_projection = TRUE; + else + { + need_projection = (program_state->projection_uniform != -1 && + projection_changed); + need_modelview = (program_state->modelview_uniform != -1 && + modelview_changed); + } + + if (need_modelview) + cogl_matrix_entry_get (modelview_entry, &modelview); + if (need_projection) + { + if (needs_flip && program_state->flip_uniform == -1) + { + CoglMatrix tmp_matrix; + cogl_matrix_entry_get (projection_entry, &tmp_matrix); + cogl_matrix_multiply (&projection, + &ctx->y_flip_matrix, + &tmp_matrix); + } + else + cogl_matrix_entry_get (projection_entry, &projection); + } + + if (projection_changed && program_state->projection_uniform != -1) + GE (ctx, glUniformMatrix4fv (program_state->projection_uniform, + 1, /* count */ + FALSE, /* transpose */ + cogl_matrix_get_array (&projection))); + + if (modelview_changed && program_state->modelview_uniform != -1) + GE (ctx, glUniformMatrix4fv (program_state->modelview_uniform, + 1, /* count */ + FALSE, /* transpose */ + cogl_matrix_get_array (&modelview))); + + if (program_state->mvp_uniform != -1) + { + /* The journal usually uses an identity matrix for the + modelview so we can optimise this common case by + avoiding the matrix multiplication */ + if (cogl_matrix_entry_is_identity (modelview_entry)) + { + GE (ctx, + glUniformMatrix4fv (program_state->mvp_uniform, + 1, /* count */ + FALSE, /* transpose */ + cogl_matrix_get_array (&projection))); + } + else + { + CoglMatrix combined; + + cogl_matrix_multiply (&combined, + &projection, + &modelview); + GE (ctx, + glUniformMatrix4fv (program_state->mvp_uniform, + 1, /* count */ + FALSE, /* transpose */ + cogl_matrix_get_array (&combined))); + } + } + } + + if (program_state->flip_uniform != -1 + && program_state->flushed_flip_state != needs_flip) + { + static const float do_flip[4] = { 1.0f, -1.0f, 1.0f, 1.0f }; + static const float dont_flip[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + GE( ctx, glUniform4fv (program_state->flip_uniform, + 1, /* count */ + needs_flip ? do_flip : dont_flip) ); + program_state->flushed_flip_state = needs_flip; + } +} + +static void +update_float_uniform (CoglPipeline *pipeline, + int uniform_location, + void *getter_func) +{ + float (* float_getter_func) (CoglPipeline *) = getter_func; + float value; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + value = float_getter_func (pipeline); + GE( ctx, glUniform1f (uniform_location, value) ); +} + +const CoglPipelineProgend _cogl_pipeline_glsl_progend = + { + COGL_PIPELINE_VERTEND_GLSL, + COGL_PIPELINE_FRAGEND_GLSL, + _cogl_pipeline_progend_glsl_start, + _cogl_pipeline_progend_glsl_end, + _cogl_pipeline_progend_glsl_pre_change_notify, + _cogl_pipeline_progend_glsl_layer_pre_change_notify, + _cogl_pipeline_progend_glsl_pre_paint + }; + +#endif /* COGL_PIPELINE_PROGEND_GLSL */ diff --git a/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed-private.h b/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed-private.h new file mode 100644 index 000000000..97dee5479 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed-private.h @@ -0,0 +1,42 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H +#define __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineVertend _cogl_pipeline_fixed_vertend; + +#endif /* __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed.c b/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed.c new file mode 100644 index 000000000..f34a012a4 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed.c @@ -0,0 +1,121 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-framebuffer-private.h" + +#ifdef COGL_PIPELINE_VERTEND_FIXED + +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-program-private.h" + +const CoglPipelineVertend _cogl_pipeline_fixed_vertend; + +static void +_cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + _cogl_use_vertex_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); +} + +static CoglBool +_cogl_pipeline_vertend_fixed_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference, + CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) + { + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, state); + CoglMatrixEntry *matrix_entry; + + cogl_matrix_stack_set (unit->matrix_stack, + &authority->big_state->matrix); + + _cogl_set_active_texture_unit (unit_index); + + matrix_entry = unit->matrix_stack->last_entry; + _cogl_matrix_entry_flush_to_gl_builtins (ctx, matrix_entry, + COGL_MATRIX_TEXTURE, + framebuffer, + FALSE /* enable flip */); + } + + return TRUE; +} + +static CoglBool +_cogl_pipeline_vertend_fixed_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + if (pipelines_difference & COGL_PIPELINE_STATE_POINT_SIZE) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_POINT_SIZE); + + if (authority->big_state->point_size > 0.0f) + GE( ctx, glPointSize (authority->big_state->point_size) ); + } + + return TRUE; +} + +const CoglPipelineVertend _cogl_pipeline_fixed_vertend = +{ + _cogl_pipeline_vertend_fixed_start, + _cogl_pipeline_vertend_fixed_add_layer, + _cogl_pipeline_vertend_fixed_end, + NULL, /* pipeline_change_notify */ + NULL /* layer_change_notify */ +}; + +#endif /* COGL_PIPELINE_VERTEND_FIXED */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl-private.h b/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl-private.h new file mode 100644 index 000000000..4bd3823d0 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl-private.h @@ -0,0 +1,45 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H +#define __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineVertend _cogl_pipeline_glsl_vertend; + +GLuint +_cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline); + +#endif /* __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl.c b/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl.c new file mode 100644 index 000000000..c9e6a2824 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl.c @@ -0,0 +1,680 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" + +#ifdef COGL_PIPELINE_VERTEND_GLSL + +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-program-private.h" +#include "cogl-pipeline-vertend-glsl-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-glsl-shader-private.h" + +const CoglPipelineVertend _cogl_pipeline_glsl_vertend; + +typedef struct +{ + unsigned int ref_count; + + GLuint gl_shader; + GString *header, *source; + + CoglPipelineCacheEntry *cache_entry; +} CoglPipelineShaderState; + +static CoglUserDataKey shader_state_key; + +static CoglPipelineShaderState * +shader_state_new (CoglPipelineCacheEntry *cache_entry) +{ + CoglPipelineShaderState *shader_state; + + shader_state = g_slice_new0 (CoglPipelineShaderState); + shader_state->ref_count = 1; + shader_state->cache_entry = cache_entry; + + return shader_state; +} + +static CoglPipelineShaderState * +get_shader_state (CoglPipeline *pipeline) +{ + return cogl_object_get_user_data (COGL_OBJECT (pipeline), &shader_state_key); +} + +static void +destroy_shader_state (void *user_data, + void *instance) +{ + CoglPipelineShaderState *shader_state = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != instance) + shader_state->cache_entry->usage_count--; + + if (--shader_state->ref_count == 0) + { + if (shader_state->gl_shader) + GE( ctx, glDeleteShader (shader_state->gl_shader) ); + + g_slice_free (CoglPipelineShaderState, shader_state); + } +} + +static void +set_shader_state (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + if (shader_state) + { + shader_state->ref_count++; + + /* If we're not setting the state on the template pipeline then + * mark it as a usage of the pipeline cache entry */ + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != pipeline) + shader_state->cache_entry->usage_count++; + } + + _cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + shader_state, + destroy_shader_state); +} + +static void +dirty_shader_state (CoglPipeline *pipeline) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + NULL, + NULL); +} + +GLuint +_cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + + if (shader_state) + return shader_state->gl_shader; + else + return 0; +} + +static CoglPipelineSnippetList * +get_vertex_snippets (CoglPipeline *pipeline) +{ + pipeline = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_VERTEX_SNIPPETS); + + return &pipeline->big_state->vertex_snippets; +} + +static CoglPipelineSnippetList * +get_layer_vertex_snippets (CoglPipelineLayer *layer) +{ + unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; + layer = _cogl_pipeline_layer_get_authority (layer, state); + + return &layer->big_state->vertex_snippets; +} + +static CoglBool +add_layer_declaration_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineShaderState *shader_state = user_data; + CoglTextureType texture_type = + _cogl_pipeline_layer_get_texture_type (layer); + const char *target_string; + + _cogl_gl_util_get_texture_target_string (texture_type, &target_string, NULL); + + g_string_append_printf (shader_state->header, + "uniform sampler%s cogl_sampler%i;\n", + target_string, + layer->index); + + return TRUE; +} + +static void +add_layer_declarations (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + /* We always emit sampler uniforms in case there will be custom + * layer snippets that want to sample arbitrary layers. */ + + _cogl_pipeline_foreach_layer_internal (pipeline, + add_layer_declaration_cb, + shader_state); +} + +static void +add_global_declarations (CoglPipeline *pipeline, + CoglPipelineShaderState *shader_state) +{ + CoglSnippetHook hook = COGL_SNIPPET_HOOK_VERTEX_GLOBALS; + CoglPipelineSnippetList *snippets = get_vertex_snippets (pipeline); + + /* Add the global data hooks. All of the code in these snippets is + * always added and only the declarations data is used */ + + _cogl_pipeline_snippet_generate_declarations (shader_state->header, + hook, + snippets); +} + +static void +_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state; + CoglPipelineCacheEntry *cache_entry = NULL; + CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Now lookup our glsl backend private state (allocating if + * necessary) */ + shader_state = get_shader_state (pipeline); + + if (shader_state == NULL) + { + CoglPipeline *authority; + + /* Get the authority for anything affecting vertex shader + state */ + authority = _cogl_pipeline_find_equivalent_parent + (pipeline, + _cogl_pipeline_get_state_for_vertex_codegen (ctx) & + ~COGL_PIPELINE_STATE_LAYERS, + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); + + shader_state = get_shader_state (authority); + + if (shader_state == NULL) + { + /* Check if there is already a similar cached pipeline whose + shader state we can share */ + if (G_LIKELY (!(COGL_DEBUG_ENABLED + (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) + { + cache_entry = + _cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache, + authority); + + shader_state = get_shader_state (cache_entry->pipeline); + } + + if (shader_state) + shader_state->ref_count++; + else + shader_state = shader_state_new (cache_entry); + + set_shader_state (authority, shader_state); + + shader_state->ref_count--; + + if (cache_entry) + set_shader_state (cache_entry->pipeline, shader_state); + } + + if (authority != pipeline) + set_shader_state (pipeline, shader_state); + } + + if (user_program) + { + /* If the user program contains a vertex shader then we don't need + to generate one */ + if (_cogl_program_has_vertex_shader (user_program)) + { + if (shader_state->gl_shader) + { + GE( ctx, glDeleteShader (shader_state->gl_shader) ); + shader_state->gl_shader = 0; + } + return; + } + } + + if (shader_state->gl_shader) + return; + + /* If we make it here then we have a shader_state struct without a gl_shader + either because this is the first time we've encountered it or + because the user program has changed */ + + /* We reuse two grow-only GStrings for code-gen. One string + contains the uniform and attribute declarations while the + other contains the main function. We need two strings + because we need to dynamically declare attributes as the + add_layer callback is invoked */ + g_string_set_size (ctx->codegen_header_buffer, 0); + g_string_set_size (ctx->codegen_source_buffer, 0); + shader_state->header = ctx->codegen_header_buffer; + shader_state->source = ctx->codegen_source_buffer; + + add_layer_declarations (pipeline, shader_state); + add_global_declarations (pipeline, shader_state); + + g_string_append (shader_state->source, + "void\n" + "cogl_generated_source ()\n" + "{\n"); + + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + g_string_append (shader_state->header, + "attribute float cogl_point_size_in;\n"); + else if (!_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) + { + /* There is no builtin uniform for the point size on GLES2 so we + need to copy it from the custom uniform in the vertex shader + if we're not using per-vertex point sizes, however we'll only + do this if the point-size is non-zero. Toggle the point size + between zero and non-zero causes a state change which + generates a new program */ + if (cogl_pipeline_get_point_size (pipeline) > 0.0f) + { + g_string_append (shader_state->header, + "uniform float cogl_point_size_in;\n"); + g_string_append (shader_state->source, + " cogl_point_size_out = cogl_point_size_in;\n"); + } + } +} + +static CoglBool +_cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference, + CoglFramebuffer *framebuffer) +{ + CoglPipelineShaderState *shader_state; + CoglPipelineSnippetData snippet_data; + int layer_index = layer->index; + + _COGL_GET_CONTEXT (ctx, FALSE); + + shader_state = get_shader_state (pipeline); + + if (shader_state->source == NULL) + return TRUE; + + /* Transform the texture coordinates by the layer's user matrix. + * + * FIXME: this should avoid doing the transform if there is no user + * matrix set. This might need a separate layer state flag for + * whether there is a user matrix + * + * FIXME: we could be more clever here and try to detect if the + * fragment program is going to use the texture coordinates and + * avoid setting them if not + */ + + g_string_append_printf (shader_state->header, + "vec4\n" + "cogl_real_transform_layer%i (mat4 matrix, " + "vec4 tex_coord)\n" + "{\n" + " return matrix * tex_coord;\n" + "}\n", + layer_index); + + /* Wrap the layer code in any snippets that have been hooked */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = get_layer_vertex_snippets (layer); + snippet_data.hook = COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM; + snippet_data.chain_function = g_strdup_printf ("cogl_real_transform_layer%i", + layer_index); + snippet_data.final_name = g_strdup_printf ("cogl_transform_layer%i", + layer_index); + snippet_data.function_prefix = g_strdup_printf ("cogl_transform_layer%i", + layer_index); + snippet_data.return_type = "vec4"; + snippet_data.return_variable = "cogl_tex_coord"; + snippet_data.return_variable_is_argument = TRUE; + snippet_data.arguments = "cogl_matrix, cogl_tex_coord"; + snippet_data.argument_declarations = "mat4 cogl_matrix, vec4 cogl_tex_coord"; + snippet_data.source_buf = shader_state->header; + + _cogl_pipeline_snippet_generate_code (&snippet_data); + + g_free ((char *) snippet_data.chain_function); + g_free ((char *) snippet_data.final_name); + g_free ((char *) snippet_data.function_prefix); + + g_string_append_printf (shader_state->source, + " cogl_tex_coord%i_out = " + "cogl_transform_layer%i (cogl_texture_matrix%i,\n" + " " + " cogl_tex_coord%i_in);\n", + layer_index, + layer_index, + layer_index, + layer_index); + + return TRUE; +} + +static CoglBool +_cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state; + + _COGL_GET_CONTEXT (ctx, FALSE); + + shader_state = get_shader_state (pipeline); + + if (shader_state->source) + { + const char *source_strings[2]; + GLint lengths[2]; + GLint compile_status; + GLuint shader; + CoglPipelineSnippetData snippet_data; + CoglPipelineSnippetList *vertex_snippets; + CoglBool has_per_vertex_point_size = + cogl_pipeline_get_per_vertex_point_size (pipeline); + + COGL_STATIC_COUNTER (vertend_glsl_compile_counter, + "glsl vertex compile counter", + "Increments each time a new GLSL " + "vertex shader is compiled", + 0 /* no application private data */); + COGL_COUNTER_INC (_cogl_uprof_context, vertend_glsl_compile_counter); + + g_string_append (shader_state->header, + "void\n" + "cogl_real_vertex_transform ()\n" + "{\n" + " cogl_position_out = " + "cogl_modelview_projection_matrix * " + "cogl_position_in;\n" + "}\n"); + + g_string_append (shader_state->source, + " cogl_vertex_transform ();\n"); + + if (has_per_vertex_point_size) + { + g_string_append (shader_state->header, + "void\n" + "cogl_real_point_size_calculation ()\n" + "{\n" + " cogl_point_size_out = cogl_point_size_in;\n" + "}\n"); + g_string_append (shader_state->source, + " cogl_point_size_calculation ();\n"); + } + + g_string_append (shader_state->source, + " cogl_color_out = cogl_color_in;\n" + "}\n"); + + vertex_snippets = get_vertex_snippets (pipeline); + + /* Add hooks for the vertex transform part */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = vertex_snippets; + snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX_TRANSFORM; + snippet_data.chain_function = "cogl_real_vertex_transform"; + snippet_data.final_name = "cogl_vertex_transform"; + snippet_data.function_prefix = "cogl_vertex_transform"; + snippet_data.source_buf = shader_state->header; + _cogl_pipeline_snippet_generate_code (&snippet_data); + + /* Add hooks for the point size calculation part */ + if (has_per_vertex_point_size) + { + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = vertex_snippets; + snippet_data.hook = COGL_SNIPPET_HOOK_POINT_SIZE; + snippet_data.chain_function = "cogl_real_point_size_calculation"; + snippet_data.final_name = "cogl_point_size_calculation"; + snippet_data.function_prefix = "cogl_point_size_calculation"; + snippet_data.source_buf = shader_state->header; + _cogl_pipeline_snippet_generate_code (&snippet_data); + } + + /* Add all of the hooks for vertex processing */ + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = vertex_snippets; + snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX; + snippet_data.chain_function = "cogl_generated_source"; + snippet_data.final_name = "cogl_vertex_hook"; + snippet_data.function_prefix = "cogl_vertex_hook"; + snippet_data.source_buf = shader_state->source; + _cogl_pipeline_snippet_generate_code (&snippet_data); + + g_string_append (shader_state->source, + "void\n" + "main ()\n" + "{\n" + " cogl_vertex_hook ();\n"); + + /* If there are any snippets then we can't rely on the + projection matrix to flip the rendering for offscreen buffers + so we'll need to flip it using an extra statement and a + uniform */ + if (_cogl_pipeline_has_vertex_snippets (pipeline)) + { + g_string_append (shader_state->header, + "uniform vec4 _cogl_flip_vector;\n"); + g_string_append (shader_state->source, + " cogl_position_out *= _cogl_flip_vector;\n"); + } + + g_string_append (shader_state->source, + "}\n"); + + GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) ); + + lengths[0] = shader_state->header->len; + source_strings[0] = shader_state->header->str; + lengths[1] = shader_state->source->len; + source_strings[1] = shader_state->source->str; + + _cogl_glsl_shader_set_source_with_boilerplate (ctx, + shader, GL_VERTEX_SHADER, + pipeline, + 2, /* count */ + source_strings, lengths); + + GE( ctx, glCompileShader (shader) ); + GE( ctx, glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); + + if (!compile_status) + { + GLint len = 0; + char *shader_log; + + GE( ctx, glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); + shader_log = g_alloca (len); + GE( ctx, glGetShaderInfoLog (shader, len, &len, shader_log) ); + g_warning ("Shader compilation failed:\n%s", shader_log); + } + + shader_state->header = NULL; + shader_state->source = NULL; + shader_state->gl_shader = shader; + } + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM) && + (pipelines_difference & COGL_PIPELINE_STATE_POINT_SIZE)) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_POINT_SIZE); + + if (authority->big_state->point_size > 0.0f) + GE( ctx, glPointSize (authority->big_state->point_size) ); + } +#endif /* HAVE_COGL_GL */ + + return TRUE; +} + +static void +_cogl_pipeline_vertend_glsl_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & _cogl_pipeline_get_state_for_vertex_codegen (ctx))) + dirty_shader_state (pipeline); +} + +/* NB: layers are considered immutable once they have any dependants + * so although multiple pipelines can end up depending on a single + * static layer, we can guarantee that if a layer is being *changed* + * then it can only have one pipeline depending on it. + * + * XXX: Don't forget this is *pre* change, we can't read the new value + * yet! + */ +static void +_cogl_pipeline_vertend_glsl_layer_pre_change_notify ( + CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + CoglPipelineShaderState *shader_state; + + shader_state = get_shader_state (owner); + if (!shader_state) + return; + + if ((change & COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN)) + { + dirty_shader_state (owner); + return; + } + + /* TODO: we could be saving snippets of texture combine code along + * with each layer and then when a layer changes we would just free + * the snippet. */ +} + +const CoglPipelineVertend _cogl_pipeline_glsl_vertend = + { + _cogl_pipeline_vertend_glsl_start, + _cogl_pipeline_vertend_glsl_add_layer, + _cogl_pipeline_vertend_glsl_end, + _cogl_pipeline_vertend_glsl_pre_change_notify, + _cogl_pipeline_vertend_glsl_layer_pre_change_notify + }; + +UNIT_TEST (check_point_size_shader, + 0 /* no requirements */, + 0 /* no failure cases */) +{ + CoglPipeline *pipelines[4]; + CoglPipelineShaderState *shader_states[G_N_ELEMENTS (pipelines)]; + int i; + + /* Default pipeline with zero point size */ + pipelines[0] = cogl_pipeline_new (test_ctx); + + /* Point size 1 */ + pipelines[1] = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_point_size (pipelines[1], 1.0f); + + /* Point size 2 */ + pipelines[2] = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_point_size (pipelines[2], 2.0f); + + /* Same as the first pipeline, but reached by restoring the old + * state from a copy */ + pipelines[3] = cogl_pipeline_copy (pipelines[1]); + cogl_pipeline_set_point_size (pipelines[3], 0.0f); + + /* Draw something with all of the pipelines to make sure their state + * is flushed */ + for (i = 0; i < G_N_ELEMENTS (pipelines); i++) + cogl_framebuffer_draw_rectangle (test_fb, + pipelines[i], + 0.0f, 0.0f, + 10.0f, 10.0f); + cogl_framebuffer_finish (test_fb); + + /* Get all of the shader states. These might be NULL if the driver + * is not using GLSL */ + for (i = 0; i < G_N_ELEMENTS (pipelines); i++) + shader_states[i] = get_shader_state (pipelines[i]); + + /* If the first two pipelines are using GLSL then they should have + * the same shader unless there is no builtin uniform for the point + * size */ + if (shader_states[0]) + { + if (_cogl_has_private_feature + (test_ctx, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) + g_assert (shader_states[0] == shader_states[1]); + else + g_assert (shader_states[0] != shader_states[1]); + } + + /* The second and third pipelines should always have the same shader + * state because only toggling between zero and non-zero should + * change the shader */ + g_assert (shader_states[1] == shader_states[2]); + + /* The fourth pipeline should be exactly the same as the first */ + g_assert (shader_states[0] == shader_states[3]); +} + +#endif /* COGL_PIPELINE_VERTEND_GLSL */ diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h b/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h new file mode 100644 index 000000000..e5c658534 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h @@ -0,0 +1,119 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_TEXTURE_2D_GL_PRIVATE_H_ +#define _COGL_TEXTURE_2D_GL_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context-private.h" +#include "cogl-texture.h" + +void +_cogl_texture_2d_gl_free (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_gl_can_create (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format); + +void +_cogl_texture_2d_gl_init (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_gl_allocate (CoglTexture *tex, + CoglError **error); + +CoglTexture2D * +_cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error); + +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) +CoglTexture2D * +_cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx, + int width, + int height, + CoglPixelFormat format, + EGLImageKHR image, + CoglError **error); +#endif + +void +_cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter); + +void +_cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p); + +void +_cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level); + +unsigned int +_cogl_texture_2d_gl_get_gl_handle (CoglTexture2D *tex_2d); + +void +_cogl_texture_2d_gl_generate_mipmap (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bitmap, + int dst_x, + int dst_y, + int level, + CoglError **error); + +void +_cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d, + CoglPixelFormat format, + int rowstride, + uint8_t *data); + +#endif /* _COGL_TEXTURE_2D_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c new file mode 100644 index 000000000..8675f5205 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c @@ -0,0 +1,756 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2011,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#include + +#include + +#include "cogl-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-gl.h" +#include "cogl-texture-2d-gl-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-error-private.h" +#include "cogl-util-gl-private.h" + +void +_cogl_texture_2d_gl_free (CoglTexture2D *tex_2d) +{ + if (!tex_2d->is_foreign && tex_2d->gl_texture) + _cogl_delete_gl_texture (tex_2d->gl_texture); +} + +CoglBool +_cogl_texture_2d_gl_can_create (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format) +{ + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + (!_cogl_util_is_pot (width) || + !_cogl_util_is_pot (height))) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!ctx->texture_driver->size_supported (ctx, + GL_TEXTURE_2D, + gl_intformat, + gl_format, + gl_type, + width, + height)) + return FALSE; + + return TRUE; +} + +void +_cogl_texture_2d_gl_init (CoglTexture2D *tex_2d) +{ + tex_2d->gl_texture = 0; + + /* We default to GL_LINEAR for both filters */ + tex_2d->gl_legacy_texobj_min_filter = GL_LINEAR; + tex_2d->gl_legacy_texobj_mag_filter = GL_LINEAR; + + /* Wrap mode not yet set */ + tex_2d->gl_legacy_texobj_wrap_mode_s = GL_FALSE; + tex_2d->gl_legacy_texobj_wrap_mode_t = GL_FALSE; +} + +static CoglBool +allocate_with_size (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; + CoglContext *ctx = tex->context; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GLenum gl_error; + GLenum gl_texture; + + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (!_cogl_texture_2d_gl_can_create (ctx, + width, + height, + internal_format)) + { + _cogl_set_error (error, COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "Failed to create texture 2d due to size/format" + " constraints"); + return FALSE; + } + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); + + tex_2d->gl_internal_format = gl_intformat; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + gl_texture, + tex_2d->is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, + width, height, 0, gl_format, gl_type, NULL); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + GE( ctx, glDeleteTextures (1, &gl_texture) ); + return FALSE; + } + + tex_2d->gl_texture = gl_texture; + tex_2d->gl_internal_format = gl_intformat; + + tex_2d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_bitmap (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglBitmap *bmp = loader->src.bitmap.bitmap; + CoglContext *ctx = _cogl_bitmap_get_context (bmp); + CoglPixelFormat internal_format; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglBitmap *upload_bmp; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + internal_format = + _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); + + if (!_cogl_texture_2d_gl_can_create (ctx, + width, + height, + internal_format)) + { + _cogl_set_error (error, COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "Failed to create texture 2d due to size/format" + " constraints"); + return FALSE; + } + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + cogl_bitmap_get_format (upload_bmp), + NULL, /* internal format */ + &gl_format, + &gl_type); + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + NULL); + + /* Keep a copy of the first pixel so that if glGenerateMipmap isn't + supported we can fallback to using GL_GENERATE_MIPMAP */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + CoglError *ignore = NULL; + uint8_t *data = _cogl_bitmap_map (upload_bmp, + COGL_BUFFER_ACCESS_READ, 0, + &ignore); + CoglPixelFormat format = cogl_bitmap_get_format (upload_bmp); + + tex_2d->first_pixel.gl_format = gl_format; + tex_2d->first_pixel.gl_type = gl_type; + + if (data) + { + memcpy (tex_2d->first_pixel.data, data, + _cogl_pixel_format_get_bytes_per_pixel (format)); + _cogl_bitmap_unmap (upload_bmp); + } + else + { + g_warning ("Failed to read first pixel of bitmap for " + "glGenerateMipmap fallback"); + cogl_error_free (ignore); + memset (tex_2d->first_pixel.data, 0, + _cogl_pixel_format_get_bytes_per_pixel (format)); + } + } + + tex_2d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); + if (!ctx->texture_driver->upload_to_gl (ctx, + GL_TEXTURE_2D, + tex_2d->gl_texture, + FALSE, + upload_bmp, + gl_intformat, + gl_format, + gl_type, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + tex_2d->gl_internal_format = gl_intformat; + + cogl_object_unref (upload_bmp); + + tex_2d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) +static CoglBool +allocate_from_egl_image (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format = loader->src.egl_image.format; + GLenum gl_error; + + tex_2d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + FALSE); + + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, loader->src.egl_image.image); + if (ctx->glGetError () != GL_NO_ERROR) + { + _cogl_set_error (error, + COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_BAD_PARAMETER, + "Could not create a CoglTexture2D from a given " + "EGLImage"); + GE( ctx, glDeleteTextures (1, &tex_2d->gl_texture) ); + return FALSE; + } + + tex_2d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, + internal_format, + loader->src.egl_image.width, + loader->src.egl_image.height); + + return TRUE; +} +#endif + +static CoglBool +allocate_from_gl_foreign (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; + GLenum gl_error = 0; + GLint gl_compressed = GL_FALSE; + GLenum gl_int_format = 0; + + if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_2D)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Foreign GL_TEXTURE_2D textures are not " + "supported by your system"); + return FALSE; + } + + /* Make sure binding succeeds */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + loader->src.gl_foreign.gl_handle, TRUE); + if (ctx->glGetError () != GL_NO_ERROR) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Failed to bind foreign GL_TEXTURE_2D texture"); + return FALSE; + } + + /* Obtain texture parameters + (only level 0 we are interested in) */ + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS)) + { + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + { + GLint val; + + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &val) ); + + gl_int_format = val; + } + + /* If we can query GL for the actual pixel format then we'll ignore + the passed in format and use that. */ + if (!ctx->driver_vtable->pixel_format_from_gl_internal (ctx, + gl_int_format, + &format)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Unsupported internal format for foreign texture"); + return FALSE; + } + } + else +#endif + { + /* Otherwise we'll assume we can derive the GL format from the + passed in format */ + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_int_format, + NULL, + NULL); + } + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Compressed foreign textures aren't currently supported"); + return FALSE; + } + + /* Note: previously this code would query the texture object for + whether it has GL_GENERATE_MIPMAP enabled to determine whether to + auto-generate the mipmap. This doesn't make much sense any more + since Cogl switch to using glGenerateMipmap. Ideally I think + cogl_texture_2d_gl_new_from_foreign should take a flags parameter so + that the application can decide whether it wants + auto-mipmapping. To be compatible with existing code, Cogl now + disables its own auto-mipmapping but leaves the value of + GL_GENERATE_MIPMAP alone so that it would still work but without + the dirtiness tracking that Cogl would do. */ + + _cogl_texture_2d_set_auto_mipmap (COGL_TEXTURE (tex_2d), FALSE); + + /* Setup bitmap info */ + tex_2d->is_foreign = TRUE; + tex_2d->mipmaps_dirty = TRUE; + + tex_2d->gl_texture = loader->src.gl_foreign.gl_handle; + tex_2d->gl_internal_format = gl_int_format; + + /* Unknown filter */ + tex_2d->gl_legacy_texobj_min_filter = GL_FALSE; + tex_2d->gl_legacy_texobj_mag_filter = GL_FALSE; + + tex_2d->internal_format = format; + + _cogl_texture_set_allocated (tex, + format, + loader->src.gl_foreign.width, + loader->src.gl_foreign.height); + return TRUE; +} + +CoglBool +_cogl_texture_2d_gl_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_2d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_2d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) + return allocate_from_egl_image (tex_2d, loader, error); +#else + g_return_val_if_reached (FALSE); +#endif + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_2d, loader, error); + } + + g_return_val_if_reached (FALSE); +} + +void +_cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglContext *ctx = tex->context; + + if (min_filter == tex_2d->gl_legacy_texobj_min_filter + && mag_filter == tex_2d->gl_legacy_texobj_mag_filter) + return; + + /* Store new values */ + tex_2d->gl_legacy_texobj_min_filter = min_filter; + tex_2d->gl_legacy_texobj_mag_filter = mag_filter; + + /* Apply new filters to the texture */ + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + tex_2d->is_foreign); + GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) ); + GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) ); +} + +void +_cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglContext *ctx = tex->context; + + /* Only set the wrap mode if it's different from the current value + to avoid too many GL calls. Texture 2D doesn't make use of the r + coordinate so we can ignore its wrap mode */ + if (tex_2d->gl_legacy_texobj_wrap_mode_s != wrap_mode_s || + tex_2d->gl_legacy_texobj_wrap_mode_t != wrap_mode_t) + { + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + tex_2d->is_foreign); + GE( ctx, glTexParameteri (GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + wrap_mode_s) ); + GE( ctx, glTexParameteri (GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + wrap_mode_t) ); + + tex_2d->gl_legacy_texobj_wrap_mode_s = wrap_mode_s; + tex_2d->gl_legacy_texobj_wrap_mode_t = wrap_mode_t; + } +} + +CoglTexture2D * +cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + int width, + int height, + CoglPixelFormat format) +{ + CoglTextureLoader *loader; + + /* NOTE: width, height and internal format are not queriable + * in GLES, hence such a function prototype. + */ + + /* Note: We always trust the given width and height without querying + * the texture object because the user may be creating a Cogl + * texture for a texture_from_pixmap object where glTexImage2D may + * not have been called and the texture_from_pixmap spec doesn't + * clarify that it is reliable to query back the size from OpenGL. + */ + + /* Assert it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), FALSE); + + /* Validate width and height */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width; + loader->src.gl_foreign.height = height; + loader->src.gl_foreign.format = format; + + return _cogl_texture_2d_create_base (ctx, width, height, format, loader); +} + +void +_cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + + /* Make sure the current framebuffers are bound, though we don't need to + * flush the clip state here since we aren't going to draw to the + * framebuffer. */ + _cogl_framebuffer_flush_state (ctx->current_draw_buffer, + src_fb, + COGL_FRAMEBUFFER_STATE_ALL & + ~COGL_FRAMEBUFFER_STATE_CLIP); + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + tex_2d->is_foreign); + + ctx->glCopyTexSubImage2D (GL_TEXTURE_2D, + 0, /* level */ + dst_x, dst_y, + src_x, src_y, + width, height); +} + +unsigned int +_cogl_texture_2d_gl_get_gl_handle (CoglTexture2D *tex_2d) +{ + return tex_2d->gl_texture; +} + +void +_cogl_texture_2d_gl_generate_mipmap (CoglTexture2D *tex_2d) +{ + CoglContext *ctx = COGL_TEXTURE (tex_2d)->context; + + /* glGenerateMipmap is defined in the FBO extension. If it's not + available we'll fallback to temporarily enabling + GL_GENERATE_MIPMAP and reuploading the first pixel */ + if (cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + _cogl_texture_gl_generate_mipmaps (COGL_TEXTURE (tex_2d)); +#if defined(HAVE_COGL_GLES) || defined(HAVE_COGL_GL) + else + { + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + tex_2d->is_foreign); + + GE( ctx, glTexParameteri (GL_TEXTURE_2D, + GL_GENERATE_MIPMAP, + GL_TRUE) ); + GE( ctx, glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 1, 1, + tex_2d->first_pixel.gl_format, + tex_2d->first_pixel.gl_type, + tex_2d->first_pixel.data) ); + GE( ctx, glTexParameteri (GL_TEXTURE_2D, + GL_GENERATE_MIPMAP, + GL_FALSE) ); + } +#endif +} + +CoglBool +_cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bmp, + int dst_x, + int dst_y, + int level, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + CoglBitmap *upload_bmp; + CoglPixelFormat upload_format; + GLenum gl_format; + GLenum gl_type; + CoglBool status = TRUE; + + upload_bmp = + _cogl_bitmap_convert_for_upload (bmp, + _cogl_texture_get_format (tex), + FALSE, /* can't convert in place */ + error); + if (upload_bmp == NULL) + return FALSE; + + upload_format = cogl_bitmap_get_format (upload_bmp); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + upload_format, + NULL, /* internal format */ + &gl_format, + &gl_type); + + /* If this touches the first pixel then we'll update our copy */ + if (dst_x == 0 && dst_y == 0 && + !cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + CoglError *ignore = NULL; + uint8_t *data = + _cogl_bitmap_map (upload_bmp, COGL_BUFFER_ACCESS_READ, 0, &ignore); + CoglPixelFormat bpp = + _cogl_pixel_format_get_bytes_per_pixel (upload_format); + + tex_2d->first_pixel.gl_format = gl_format; + tex_2d->first_pixel.gl_type = gl_type; + + if (data) + { + memcpy (tex_2d->first_pixel.data, + (data + + cogl_bitmap_get_rowstride (upload_bmp) * src_y + + bpp * src_x), + bpp); + _cogl_bitmap_unmap (bmp); + } + else + { + g_warning ("Failed to read first bitmap pixel for " + "glGenerateMipmap fallback"); + cogl_error_free (ignore); + memset (tex_2d->first_pixel.data, 0, bpp); + } + } + + status = ctx->texture_driver->upload_subregion_to_gl (ctx, + tex, + FALSE, + src_x, src_y, + dst_x, dst_y, + width, height, + level, + upload_bmp, + gl_format, + gl_type, + error); + + cogl_object_unref (upload_bmp); + + _cogl_texture_gl_maybe_update_max_level (tex, level); + + return status; +} + +void +_cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + CoglContext *ctx = COGL_TEXTURE (tex_2d)->context; + int bpp; + int width = COGL_TEXTURE (tex_2d)->width; + GLenum gl_format; + GLenum gl_type; + + bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + NULL, /* internal format */ + &gl_format, + &gl_type); + + ctx->texture_driver->prep_gl_for_pixels_download (ctx, + rowstride, + width, + bpp); + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + tex_2d->gl_texture, + tex_2d->is_foreign); + + ctx->texture_driver->gl_get_tex_image (ctx, + GL_TEXTURE_2D, + gl_format, + gl_type, + data); +} diff --git a/cogl/cogl/driver/gl/cogl-texture-gl-private.h b/cogl/cogl/driver/gl/cogl-texture-gl-private.h new file mode 100644 index 000000000..b5baac7bf --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-texture-gl-private.h @@ -0,0 +1,66 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _COGL_TEXTURE_GL_PRIVATE_H_ +#define _COGL_TEXTURE_GL_PRIVATE_H_ + +#include "cogl-context.h" + +void +_cogl_texture_gl_prep_alignment_for_pixels_upload (CoglContext *ctx, + int pixels_rowstride); + +void +_cogl_texture_gl_prep_alignment_for_pixels_download (CoglContext *ctx, + int bpp, + int width, + int rowstride); + +void +_cogl_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *texture, + unsigned int wrap_mode_s, + unsigned int wrap_mode_t, + unsigned int wrap_mode_p); + +void +_cogl_texture_gl_flush_legacy_texobj_filters (CoglTexture *texture, + unsigned int min_filter, + unsigned int mag_filter); + +void +_cogl_texture_gl_maybe_update_max_level (CoglTexture *texture, + int max_level); + +void +_cogl_texture_gl_generate_mipmaps (CoglTexture *texture); + +GLenum +_cogl_texture_gl_get_format (CoglTexture *texture); + +#endif /* _COGL_TEXTURE_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-texture-gl.c b/cogl/cogl/driver/gl/cogl-texture-gl.c new file mode 100644 index 000000000..2e281c03b --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-texture-gl.c @@ -0,0 +1,158 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-texture-3d-private.h" +#include "cogl-util.h" +#include "cogl-pipeline-opengl-private.h" + +static inline int +calculate_alignment (int rowstride) +{ + int alignment = 1 << (_cogl_util_ffs (rowstride) - 1); + + return MIN (alignment, 8); +} + +void +_cogl_texture_gl_prep_alignment_for_pixels_upload (CoglContext *ctx, + int pixels_rowstride) +{ + GE( ctx, glPixelStorei (GL_UNPACK_ALIGNMENT, + calculate_alignment (pixels_rowstride)) ); +} + +void +_cogl_texture_gl_prep_alignment_for_pixels_download (CoglContext *ctx, + int bpp, + int width, + int rowstride) +{ + int alignment; + + /* If no padding is needed then we can always use an alignment of 1. + * We want to do this even though it is equivalent to the alignment + * of the rowstride because the Intel driver in Mesa currently has + * an optimisation when reading data into a PBO that only works if + * the alignment is exactly 1. + * + * https://bugs.freedesktop.org/show_bug.cgi?id=46632 + */ + + if (rowstride == bpp * width) + alignment = 1; + else + alignment = calculate_alignment (rowstride); + + GE( ctx, glPixelStorei (GL_PACK_ALIGNMENT, alignment) ); +} + +void +_cogl_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *texture, + unsigned int wrap_mode_s, + unsigned int wrap_mode_t, + unsigned int wrap_mode_p) +{ + texture->vtable->gl_flush_legacy_texobj_wrap_modes (texture, + wrap_mode_s, + wrap_mode_t, + wrap_mode_p); +} + +void +_cogl_texture_gl_flush_legacy_texobj_filters (CoglTexture *texture, + unsigned int min_filter, + unsigned int mag_filter) +{ + texture->vtable->gl_flush_legacy_texobj_filters (texture, + min_filter, mag_filter); +} + +void +_cogl_texture_gl_maybe_update_max_level (CoglTexture *texture, + int max_level) +{ + /* This isn't supported on GLES */ +#ifdef HAVE_COGL_GL + CoglContext *ctx = texture->context; + + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL) && + texture->max_level < max_level) + { + CoglContext *ctx = texture->context; + GLuint gl_handle; + GLenum gl_target; + + cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); + + texture->max_level = max_level; + + _cogl_bind_gl_texture_transient (gl_target, + gl_handle, + _cogl_texture_is_foreign (texture)); + + GE( ctx, glTexParameteri (gl_target, + GL_TEXTURE_MAX_LEVEL, texture->max_level)); + } +#endif /* HAVE_COGL_GL */ +} + +void +_cogl_texture_gl_generate_mipmaps (CoglTexture *texture) +{ + CoglContext *ctx = texture->context; + int n_levels = _cogl_texture_get_n_levels (texture); + GLuint gl_handle; + GLenum gl_target; + + _cogl_texture_gl_maybe_update_max_level (texture, n_levels - 1); + + cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); + + _cogl_bind_gl_texture_transient (gl_target, + gl_handle, + _cogl_texture_is_foreign (texture)); + GE( ctx, glGenerateMipmap (gl_target) ); +} + +GLenum +_cogl_texture_gl_get_format (CoglTexture *texture) +{ + return texture->vtable->get_gl_format (texture); +} diff --git a/cogl/cogl/driver/gl/cogl-util-gl-private.h b/cogl/cogl/driver/gl/cogl-util-gl-private.h new file mode 100644 index 000000000..dcc61c0c7 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-util-gl-private.h @@ -0,0 +1,93 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_UTIL_GL_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context.h" +#include "cogl-gl-header.h" +#include "cogl-texture.h" + +#ifdef COGL_GL_DEBUG + +const char * +_cogl_gl_error_to_string (GLenum error_code); + +#define GE(ctx, x) G_STMT_START { \ + GLenum __err; \ + (ctx)->x; \ + while ((__err = (ctx)->glGetError ()) != GL_NO_ERROR) \ + { \ + g_warning ("%s: GL error (%d): %s\n", \ + G_STRLOC, \ + __err, \ + _cogl_gl_error_to_string (__err)); \ + } } G_STMT_END + +#define GE_RET(ret, ctx, x) G_STMT_START { \ + GLenum __err; \ + ret = (ctx)->x; \ + while ((__err = (ctx)->glGetError ()) != GL_NO_ERROR) \ + { \ + g_warning ("%s: GL error (%d): %s\n", \ + G_STRLOC, \ + __err, \ + _cogl_gl_error_to_string (__err)); \ + } } G_STMT_END + +#else /* !COGL_GL_DEBUG */ + +#define GE(ctx, x) ((ctx)->x) +#define GE_RET(ret, ctx, x) (ret = ((ctx)->x)) + +#endif /* COGL_GL_DEBUG */ + +CoglBool +_cogl_gl_util_catch_out_of_memory (CoglContext *ctx, CoglError **error); + +void +_cogl_gl_util_get_texture_target_string (CoglTextureType texture_type, + const char **target_string_out, + const char **swizzle_out); + +/* Parses a GL version number stored in a string. @version_string must + * point to the beginning of the version number (ie, it can't point to + * the "OpenGL ES" part on GLES). The version number can be followed + * by the end of the string, a space or a full stop. Anything else + * will be treated as invalid. Returns TRUE and sets major_out and + * minor_out if it is succesfully parsed or FALSE otherwise. */ +CoglBool +_cogl_gl_util_parse_gl_version (const char *version_string, + int *major_out, + int *minor_out); + +#endif /* _COGL_UTIL_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-util-gl.c b/cogl/cogl/driver/gl/cogl-util-gl.c new file mode 100644 index 000000000..a50a8a305 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-util-gl.c @@ -0,0 +1,186 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012, 2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-types.h" +#include "cogl-context-private.h" +#include "cogl-error-private.h" +#include "cogl-util-gl-private.h" + +#ifdef COGL_GL_DEBUG +/* GL error to string conversion */ +static const struct { + GLuint error_code; + const char *error_string; +} gl_errors[] = { + { GL_NO_ERROR, "No error" }, + { GL_INVALID_ENUM, "Invalid enumeration value" }, + { GL_INVALID_VALUE, "Invalid value" }, + { GL_INVALID_OPERATION, "Invalid operation" }, +#ifdef HAVE_COGL_GL + { GL_STACK_OVERFLOW, "Stack overflow" }, + { GL_STACK_UNDERFLOW, "Stack underflow" }, +#endif + { GL_OUT_OF_MEMORY, "Out of memory" }, + +#ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT + { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "Invalid framebuffer operation" } +#endif +}; + +static const unsigned int n_gl_errors = G_N_ELEMENTS (gl_errors); + +const char * +_cogl_gl_error_to_string (GLenum error_code) +{ + int i; + + for (i = 0; i < n_gl_errors; i++) + { + if (gl_errors[i].error_code == error_code) + return gl_errors[i].error_string; + } + + return "Unknown GL error"; +} +#endif /* COGL_GL_DEBUG */ + +CoglBool +_cogl_gl_util_catch_out_of_memory (CoglContext *ctx, CoglError **error) +{ + GLenum gl_error; + CoglBool out_of_memory = FALSE; + + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + { + if (gl_error == GL_OUT_OF_MEMORY) + out_of_memory = TRUE; +#ifdef COGL_GL_DEBUG + else + { + g_warning ("%s: GL error (%d): %s\n", + G_STRLOC, + gl_error, + _cogl_gl_error_to_string (gl_error)); + } +#endif + } + + if (out_of_memory) + { + _cogl_set_error (error, COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY, + "Out of memory"); + return TRUE; + } + + return FALSE; +} + +void +_cogl_gl_util_get_texture_target_string (CoglTextureType texture_type, + const char **target_string_out, + const char **swizzle_out) +{ + const char *target_string, *tex_coord_swizzle; + + switch (texture_type) + { +#if 0 /* TODO */ + case COGL_TEXTURE_TYPE_1D: + target_string = "1D"; + tex_coord_swizzle = "s"; + break; +#endif + + case COGL_TEXTURE_TYPE_2D: + target_string = "2D"; + tex_coord_swizzle = "st"; + break; + + case COGL_TEXTURE_TYPE_3D: + target_string = "3D"; + tex_coord_swizzle = "stp"; + break; + + case COGL_TEXTURE_TYPE_RECTANGLE: + target_string = "2DRect"; + tex_coord_swizzle = "st"; + break; + + default: + target_string = "Unknown"; + tex_coord_swizzle = NULL; + g_assert_not_reached (); + } + + if (target_string_out) + *target_string_out = target_string; + if (swizzle_out) + *swizzle_out = tex_coord_swizzle; +} + +CoglBool +_cogl_gl_util_parse_gl_version (const char *version_string, + int *major_out, + int *minor_out) +{ + const char *major_end, *minor_end; + int major = 0, minor = 0; + + /* Extract the major number */ + for (major_end = version_string; *major_end >= '0' + && *major_end <= '9'; major_end++) + major = (major * 10) + *major_end - '0'; + /* If there were no digits or the major number isn't followed by a + dot then it is invalid */ + if (major_end == version_string || *major_end != '.') + return FALSE; + + /* Extract the minor number */ + for (minor_end = major_end + 1; *minor_end >= '0' + && *minor_end <= '9'; minor_end++) + minor = (minor * 10) + *minor_end - '0'; + /* If there were no digits or there is an unexpected character then + it is invalid */ + if (minor_end == major_end + 1 + || (*minor_end && *minor_end != ' ' && *minor_end != '.')) + return FALSE; + + *major_out = major; + *minor_out = minor; + + return TRUE; +} diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c new file mode 100644 index 000000000..716d1dd0b --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c @@ -0,0 +1,699 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-private.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-feature-private.h" +#include "cogl-renderer-private.h" +#include "cogl-error-private.h" +#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" +#include "cogl-buffer-gl-private.h" + +static CoglBool +_cogl_driver_pixel_format_from_gl_internal (CoglContext *context, + GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + /* It doesn't really matter we convert to exact same + format (some have no cogl match anyway) since format + is re-matched against cogl when getting or setting + texture image data. + */ + + switch (gl_int_format) + { + case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: + case GL_ALPHA12: case GL_ALPHA16: + /* Cogl only supports one single-component texture so if we have + * ended up with a red texture then it is probably being used as + * a component-alpha texture */ + case GL_RED: + + *out_format = COGL_PIXEL_FORMAT_A_8; + return TRUE; + + case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8: + case GL_LUMINANCE12: case GL_LUMINANCE16: + + *out_format = COGL_PIXEL_FORMAT_G_8; + return TRUE; + + case GL_RG: + *out_format = COGL_PIXEL_FORMAT_RG_88; + return TRUE; + + case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8: + case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2: + + *out_format = COGL_PIXEL_FORMAT_RGB_888; + return TRUE; + + case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: + case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: + + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; + return TRUE; + } + + return FALSE; +} + +static CoglPixelFormat +_cogl_driver_pixel_format_to_gl (CoglContext *context, + CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format; + GLenum glintformat = 0; + GLenum glformat = 0; + GLenum gltype = 0; + + required_format = format; + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + /* If the driver doesn't natively support alpha textures then we + * will use a red component texture with a swizzle to implement + * the texture */ + if (_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) == 0) + { + glintformat = GL_RED; + glformat = GL_RED; + } + else + { + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + } + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_RG_88: + if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG)) + { + glintformat = GL_RG; + glformat = GL_RG; + } + else + { + /* If red-green textures aren't supported then we'll use RGB + * as an internal format. Note this should only end up + * mattering for downloading the data because Cogl will + * refuse to allocate a texture with RG components if RG + * textures aren't supported */ + glintformat = GL_RGB; + glformat = GL_RGB; + required_format = COGL_PIXEL_FORMAT_RGB_888; + } + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_RGB_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_BGR; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + glintformat = GL_RGBA; + glformat = GL_BGRA; + gltype = GL_UNSIGNED_BYTE; + break; + + /* The following two types of channel ordering + * have no GL equivalent unless defined using + * system word byte ordering */ + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + glintformat = GL_RGBA; + glformat = GL_BGRA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + case COGL_PIXEL_FORMAT_ABGR_8888: + case COGL_PIXEL_FORMAT_ABGR_8888_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + case COGL_PIXEL_FORMAT_RGBA_1010102: + case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_INT_10_10_10_2; + break; + + case COGL_PIXEL_FORMAT_BGRA_1010102: + case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: + glintformat = GL_RGBA; + glformat = GL_BGRA; + gltype = GL_UNSIGNED_INT_10_10_10_2; + break; + + case COGL_PIXEL_FORMAT_ABGR_2101010: + case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + + case COGL_PIXEL_FORMAT_ARGB_2101010: + case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: + glintformat = GL_RGBA; + glformat = GL_BGRA; + gltype = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + case COGL_PIXEL_FORMAT_RGBA_4444_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + case COGL_PIXEL_FORMAT_RGBA_5551_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + case COGL_PIXEL_FORMAT_DEPTH_16: + glintformat = GL_DEPTH_COMPONENT16; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_UNSIGNED_SHORT; + break; + case COGL_PIXEL_FORMAT_DEPTH_32: + glintformat = GL_DEPTH_COMPONENT32; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_UNSIGNED_INT; + break; + + case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: + glintformat = GL_DEPTH_STENCIL; + glformat = GL_DEPTH_STENCIL; + gltype = GL_UNSIGNED_INT_24_8; + break; + + case COGL_PIXEL_FORMAT_ANY: + case COGL_PIXEL_FORMAT_YUV: + g_assert_not_reached (); + break; + } + + /* All of the pixel formats are handled above so if this hits then + we've been given an invalid pixel format */ + g_assert (glformat != 0); + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static CoglBool +_cogl_get_gl_version (CoglContext *ctx, + int *major_out, + int *minor_out) +{ + const char *version_string; + + /* Get the OpenGL version number */ + if ((version_string = _cogl_context_get_gl_version (ctx)) == NULL) + return FALSE; + + return _cogl_gl_util_parse_gl_version (version_string, major_out, minor_out); +} + +static CoglBool +check_gl_version (CoglContext *ctx, + char **gl_extensions, + CoglError **error) +{ + int major, minor; + + if (!_cogl_get_gl_version (ctx, &major, &minor)) + { + _cogl_set_error (error, + COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_UNKNOWN_VERSION, + "The OpenGL version could not be determined"); + return FALSE; + } + + /* GL 1.3 supports all of the required functionality in core */ + if (COGL_CHECK_GL_VERSION (major, minor, 1, 3)) + return TRUE; + + /* OpenGL 1.2 is only supported if we have the multitexturing + extension */ + if (!_cogl_check_extension ("GL_ARB_multitexture", gl_extensions)) + { + _cogl_set_error (error, + COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_INVALID_VERSION, + "The OpenGL driver is missing " + "the GL_ARB_multitexture extension"); + return FALSE; + } + + /* OpenGL 1.2 is required */ + if (!COGL_CHECK_GL_VERSION (major, minor, 1, 2)) + { + _cogl_set_error (error, + COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_INVALID_VERSION, + "The OpenGL version of your driver (%i.%i) " + "is not compatible with Cogl", + major, minor); + return FALSE; + } + + return TRUE; +} + +static CoglBool +_cogl_driver_update_features (CoglContext *ctx, + CoglError **error) +{ + CoglFeatureFlags flags = 0; + unsigned long private_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)] = { 0 }; + char **gl_extensions; + int gl_major = 0, gl_minor = 0; + int i; + + /* We have to special case getting the pointer to the glGetString* + functions because we need to use them to determine what functions + we can expect */ + ctx->glGetString = + (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, + "glGetString", + TRUE); + ctx->glGetStringi = + (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, + "glGetStringi", + TRUE); + ctx->glGetIntegerv = + (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, + "glGetIntegerv", + TRUE); + + gl_extensions = _cogl_context_get_gl_extensions (ctx); + + if (!check_gl_version (ctx, gl_extensions, error)) + return FALSE; + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) + { + char *all_extensions = g_strjoinv (" ", gl_extensions); + + COGL_NOTE (WINSYS, + "Checking features\n" + " GL_VENDOR: %s\n" + " GL_RENDERER: %s\n" + " GL_VERSION: %s\n" + " GL_EXTENSIONS: %s", + ctx->glGetString (GL_VENDOR), + ctx->glGetString (GL_RENDERER), + _cogl_context_get_gl_version (ctx), + all_extensions); + + g_free (all_extensions); + } + + _cogl_get_gl_version (ctx, &gl_major, &gl_minor); + + _cogl_gpu_info_init (ctx, &ctx->gpu); + + ctx->glsl_major = 1; + ctx->glsl_minor = 1; + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) + { + const char *glsl_version = + (char *)ctx->glGetString (GL_SHADING_LANGUAGE_VERSION); + _cogl_gl_util_parse_gl_version (glsl_version, + &ctx->glsl_major, + &ctx->glsl_minor); + } + + if (COGL_CHECK_GL_VERSION (ctx->glsl_major, ctx->glsl_minor, 1, 2)) + /* We want to use version 120 if it is available so that the + * gl_PointCoord can be used. */ + ctx->glsl_version_to_use = 120; + else + ctx->glsl_version_to_use = 110; + + flags = (COGL_FEATURE_TEXTURE_READ_PIXELS + | COGL_FEATURE_UNSIGNED_INT_INDICES + | COGL_FEATURE_DEPTH_RANGE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_DEPTH_RANGE, TRUE); + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 1, 4)) + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MIRRORED_REPEAT, TRUE); + + _cogl_feature_check_ext_functions (ctx, + gl_major, + gl_minor, + gl_extensions); + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0) || + _cogl_check_extension ("GL_ARB_texture_non_power_of_two", gl_extensions)) + { + flags |= COGL_FEATURE_TEXTURE_NPOT + | COGL_FEATURE_TEXTURE_NPOT_BASIC + | COGL_FEATURE_TEXTURE_NPOT_MIPMAP + | COGL_FEATURE_TEXTURE_NPOT_REPEAT; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT, TRUE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, TRUE); + } + + if (_cogl_check_extension ("GL_MESA_pack_invert", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, TRUE); + + if (ctx->glGenRenderbuffers) + { + flags |= COGL_FEATURE_OFFSCREEN; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS, + TRUE); + } + + if (ctx->glBlitFramebuffer) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, TRUE); + + if (ctx->glRenderbufferStorageMultisampleIMG) + { + flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE, TRUE); + } + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0) || + _cogl_check_extension ("GL_ARB_depth_texture", gl_extensions)) + { + flags |= COGL_FEATURE_DEPTH_TEXTURE; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_DEPTH_TEXTURE, TRUE); + } + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 1) || + _cogl_check_extension ("GL_EXT_pixel_buffer_object", gl_extensions)) + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_PBOS, TRUE); + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 1, 4) || + _cogl_check_extension ("GL_EXT_blend_color", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_BLEND_CONSTANT, TRUE); + + if (ctx->glGenPrograms) + { + flags |= COGL_FEATURE_SHADERS_ARBFP; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_ARBFP, TRUE); + } + + if (ctx->glCreateProgram) + { + flags |= COGL_FEATURE_SHADERS_GLSL; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, TRUE); + } + else + { + /* If all of the old GLSL extensions are available then we can fake + * the GL 2.0 GLSL support by diverting to the old function names */ + if (ctx->glCreateProgramObject && /* GL_ARB_shader_objects */ + ctx->glVertexAttribPointer && /* GL_ARB_vertex_shader */ + _cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions)) + { + ctx->glCreateShader = ctx->glCreateShaderObject; + ctx->glCreateProgram = ctx->glCreateProgramObject; + ctx->glDeleteShader = ctx->glDeleteObject; + ctx->glDeleteProgram = ctx->glDeleteObject; + ctx->glAttachShader = ctx->glAttachObject; + ctx->glUseProgram = ctx->glUseProgramObject; + ctx->glGetProgramInfoLog = ctx->glGetInfoLog; + ctx->glGetShaderInfoLog = ctx->glGetInfoLog; + ctx->glGetShaderiv = ctx->glGetObjectParameteriv; + ctx->glGetProgramiv = ctx->glGetObjectParameteriv; + ctx->glDetachShader = ctx->glDetachObject; + ctx->glGetAttachedShaders = ctx->glGetAttachedObjects; + /* FIXME: there doesn't seem to be an equivalent for glIsShader + * and glIsProgram. This doesn't matter for now because Cogl + * doesn't use these but if we add support for simulating a + * GLES2 context on top of regular GL then we'll need to do + * something here */ + + flags |= COGL_FEATURE_SHADERS_GLSL; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, TRUE); + } + } + + if ((COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0) || + _cogl_check_extension ("GL_ARB_point_sprite", gl_extensions)) && + + /* If GLSL is supported then we only enable point sprite support + * too if we have glsl >= 1.2 otherwise we don't have the + * gl_PointCoord builtin which we depend on in the glsl backend. + */ + (!COGL_FLAGS_GET (ctx->features, COGL_FEATURE_ID_GLSL) || + COGL_CHECK_GL_VERSION (ctx->glsl_major, ctx->glsl_minor, 1, 2))) + { + flags |= COGL_FEATURE_POINT_SPRITE; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_POINT_SPRITE, TRUE); + } + + if (ctx->glGenBuffers) + { + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_VBOS, TRUE); + flags |= (COGL_FEATURE_MAP_BUFFER_FOR_READ | + COGL_FEATURE_MAP_BUFFER_FOR_WRITE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, TRUE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); + } + + if (_cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions)) + { + flags |= COGL_FEATURE_TEXTURE_RECTANGLE; + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_RECTANGLE, TRUE); + } + + if (ctx->glTexImage3D) + { + flags |= COGL_FEATURE_TEXTURE_3D; + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_3D, TRUE); + } + + if (ctx->glEGLImageTargetTexture2D) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, TRUE); + + if (_cogl_check_extension ("GL_EXT_packed_depth_stencil", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL, TRUE); + + if (ctx->glGenSamplers) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS, TRUE); + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 3) || + _cogl_check_extension ("GL_ARB_texture_swizzle", gl_extensions) || + _cogl_check_extension ("GL_EXT_texture_swizzle", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE, TRUE); + + /* The per-vertex point size is only available via GLSL with the + * gl_PointSize builtin. This is only available in GL 2.0 (not the + * GLSL extensions) */ + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) + { + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE, TRUE); + } + + if (ctx->driver == COGL_DRIVER_GL) + { + int max_clip_planes = 0; + + /* Features which are not available in GL 3 */ + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_GL_FIXED, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEST, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_QUADS, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, TRUE); + + GE( ctx, glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, TRUE); + } + + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ANY_GL, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_FORMAT_CONVERSION, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_BLEND_CONSTANT, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL, TRUE); + + if (ctx->glFenceSync) + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE); + + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0) || + _cogl_check_extension ("GL_ARB_texture_rg", gl_extensions)) + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_TEXTURE_RG, + TRUE); + + /* Cache features */ + for (i = 0; i < G_N_ELEMENTS (private_features); i++) + ctx->private_features[i] |= private_features[i]; + ctx->feature_flags |= flags; + + g_strfreev (gl_extensions); + + if (!COGL_FLAGS_GET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && + !COGL_FLAGS_GET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) + { + _cogl_set_error (error, + COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, + "The GL_ARB_texture_swizzle extension is required " + "to use the GL3 driver"); + return FALSE; + } + + return TRUE; +} + +const CoglDriverVtable +_cogl_driver_gl = + { + _cogl_driver_pixel_format_from_gl_internal, + _cogl_driver_pixel_format_to_gl, + _cogl_driver_update_features, + _cogl_offscreen_gl_allocate, + _cogl_offscreen_gl_free, + _cogl_framebuffer_gl_flush_state, + _cogl_framebuffer_gl_clear, + _cogl_framebuffer_gl_query_bits, + _cogl_framebuffer_gl_finish, + _cogl_framebuffer_gl_discard_buffers, + _cogl_framebuffer_gl_draw_attributes, + _cogl_framebuffer_gl_draw_indexed_attributes, + _cogl_framebuffer_gl_read_pixels_into_bitmap, + _cogl_texture_2d_gl_free, + _cogl_texture_2d_gl_can_create, + _cogl_texture_2d_gl_init, + _cogl_texture_2d_gl_allocate, + _cogl_texture_2d_gl_copy_from_framebuffer, + _cogl_texture_2d_gl_get_gl_handle, + _cogl_texture_2d_gl_generate_mipmap, + _cogl_texture_2d_gl_copy_from_bitmap, + _cogl_texture_2d_gl_get_data, + _cogl_gl_flush_attributes_state, + _cogl_clip_stack_gl_flush, + _cogl_buffer_gl_create, + _cogl_buffer_gl_destroy, + _cogl_buffer_gl_map_range, + _cogl_buffer_gl_unmap, + _cogl_buffer_gl_set_data, + }; diff --git a/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp-private.h b/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp-private.h new file mode 100644 index 000000000..f054aada2 --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp-private.h @@ -0,0 +1,42 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_FRAGEND_ARBFP_PRIVATE_H +#define __COGL_PIPELINE_FRAGEND_ARBFP_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineFragend _cogl_pipeline_arbfp_fragend; + +#endif /* __COGL_PIPELINE_ARBFP_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp.c b/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp.c new file mode 100644 index 000000000..16b13be8e --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp.c @@ -0,0 +1,990 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-state-private.h" +#include "cogl-pipeline-layer-private.h" + +#ifdef COGL_PIPELINE_FRAGEND_ARBFP + +#include "cogl-context-private.h" +#include "cogl-object-private.h" + +#include "cogl-texture-private.h" +#include "cogl-blend-string.h" +#include "cogl-journal-private.h" +#include "cogl-color-private.h" +#include "cogl-profile.h" +#include "cogl-program-private.h" + +#include +#include +#include + +/* This might not be defined on GLES */ +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +const CoglPipelineFragend _cogl_pipeline_arbfp_fragend; + +typedef struct _UnitState +{ + int constant_id; /* The program.local[] index */ + unsigned int dirty_combine_constant:1; + unsigned int has_combine_constant:1; + + unsigned int sampled:1; +} UnitState; + +typedef struct +{ + int ref_count; + + CoglHandle user_program; + /* XXX: only valid during codegen */ + GString *source; + GLuint gl_program; + UnitState *unit_state; + int next_constant_id; + + /* Age of the program the last time the uniforms were flushed. This + is used to detect when we need to flush all of the uniforms */ + unsigned int user_program_age; + + /* We need to track the last pipeline that an ARBfp program was used + * with so know if we need to update any program.local parameters. */ + CoglPipeline *last_used_for_pipeline; + + CoglPipelineCacheEntry *cache_entry; +} CoglPipelineShaderState; + +static CoglUserDataKey shader_state_key; + +static CoglPipelineShaderState * +shader_state_new (int n_layers, + CoglPipelineCacheEntry *cache_entry) +{ + CoglPipelineShaderState *shader_state; + + shader_state = g_slice_new0 (CoglPipelineShaderState); + shader_state->ref_count = 1; + shader_state->unit_state = g_new0 (UnitState, n_layers); + shader_state->cache_entry = cache_entry; + + return shader_state; +} + +static CoglPipelineShaderState * +get_shader_state (CoglPipeline *pipeline) +{ + return cogl_object_get_user_data (COGL_OBJECT (pipeline), &shader_state_key); +} + +static void +destroy_shader_state (void *user_data, + void *instance) +{ + CoglPipelineShaderState *shader_state = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If the shader state was last used for this pipeline then clear it + so that if same address gets used again for a new pipeline then + we won't think it's the same pipeline and avoid updating the + constants */ + if (shader_state->last_used_for_pipeline == instance) + shader_state->last_used_for_pipeline = NULL; + + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != instance) + shader_state->cache_entry->usage_count--; + + if (--shader_state->ref_count == 0) + { + if (shader_state->gl_program) + { + GE (ctx, glDeletePrograms (1, &shader_state->gl_program)); + shader_state->gl_program = 0; + } + + g_free (shader_state->unit_state); + + g_slice_free (CoglPipelineShaderState, shader_state); + } +} + +static void +set_shader_state (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) +{ + if (shader_state) + { + shader_state->ref_count++; + + /* If we're not setting the state on the template pipeline then + * mark it as a usage of the pipeline cache entry */ + if (shader_state->cache_entry && + shader_state->cache_entry->pipeline != pipeline) + shader_state->cache_entry->usage_count++; + } + + _cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + shader_state, + destroy_shader_state); +} + +static void +dirty_shader_state (CoglPipeline *pipeline) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &shader_state_key, + NULL, + NULL); +} + +static void +_cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state; + CoglPipeline *authority; + CoglPipelineCacheEntry *cache_entry = NULL; + CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Now lookup our ARBfp backend private state */ + shader_state = get_shader_state (pipeline); + + /* If we have a valid shader_state then we are all set and don't + * need to generate a new program. */ + if (shader_state) + return; + + /* If we don't have an associated arbfp program yet then find the + * arbfp-authority (the oldest ancestor whose state will result in + * the same program being generated as for this pipeline). + * + * We always make sure to associate new programs with the + * arbfp-authority to maximize the chance that other pipelines can + * share it. + */ + authority = _cogl_pipeline_find_equivalent_parent + (pipeline, + _cogl_pipeline_get_state_for_fragment_codegen (ctx) & + ~COGL_PIPELINE_STATE_LAYERS, + _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx)); + shader_state = get_shader_state (authority); + if (shader_state) + { + /* If we are going to share our program state with an arbfp-authority + * then add a reference to the program state associated with that + * arbfp-authority... */ + set_shader_state (pipeline, shader_state); + return; + } + + /* If we haven't yet found an existing program then before we resort to + * generating a new arbfp program we see if we can find a suitable + * program in the pipeline_cache. */ + if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) + { + cache_entry = + _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache, + authority); + + shader_state = get_shader_state (cache_entry->pipeline); + + if (shader_state) + shader_state->ref_count++; + } + + /* If we still haven't got a shader state then we'll have to create + a new one */ + if (shader_state == NULL) + { + shader_state = shader_state_new (n_layers, cache_entry); + + shader_state->user_program = user_program; + if (user_program == COGL_INVALID_HANDLE) + { + /* We reuse a single grow-only GString for code-gen */ + g_string_set_size (ctx->codegen_source_buffer, 0); + shader_state->source = ctx->codegen_source_buffer; + g_string_append (shader_state->source, + "!!ARBfp1.0\n" + "TEMP output;\n" + "TEMP tmp0, tmp1, tmp2, tmp3, tmp4;\n" + "PARAM half = {.5, .5, .5, .5};\n" + "PARAM one = {1, 1, 1, 1};\n" + "PARAM two = {2, 2, 2, 2};\n" + "PARAM minus_one = {-1, -1, -1, -1};\n"); + } + } + + set_shader_state (pipeline, shader_state); + + shader_state->ref_count--; + + /* Since we have already resolved the arbfp-authority at this point + * we might as well also associate any program we find from the cache + * with the authority too... */ + if (authority != pipeline) + set_shader_state (authority, shader_state); + + /* If we found a template then we'll attach it to that too so that + next time a similar pipeline is used it can use the same state */ + if (cache_entry) + set_shader_state (cache_entry->pipeline, shader_state); +} + +static const char * +texture_type_to_arbfp_string (CoglTextureType texture_type) +{ + switch (texture_type) + { +#if 0 /* TODO */ + case COGL_TEXTURE_TYPE_1D: + return "1D"; +#endif + case COGL_TEXTURE_TYPE_2D: + return "2D"; + case COGL_TEXTURE_TYPE_3D: + return "3D"; + case COGL_TEXTURE_TYPE_RECTANGLE: + return "RECT"; + } + + g_warn_if_reached (); + + return "2D"; +} + +static void +setup_texture_source (CoglPipelineShaderState *shader_state, + int unit_index, + CoglTextureType texture_type) +{ + if (!shader_state->unit_state[unit_index].sampled) + { + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) + g_string_append_printf (shader_state->source, + "TEMP texel%d;\n" + "MOV texel%d, one;\n", + unit_index, + unit_index); + else + g_string_append_printf (shader_state->source, + "TEMP texel%d;\n" + "TEX texel%d,fragment.texcoord[%d]," + "texture[%d],%s;\n", + unit_index, + unit_index, + unit_index, + unit_index, + texture_type_to_arbfp_string (texture_type)); + shader_state->unit_state[unit_index].sampled = TRUE; + } +} + +typedef enum _CoglPipelineFragendARBfpArgType +{ + COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE, + COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_CONSTANT, + COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_TEXTURE +} CoglPipelineFragendARBfpArgType; + +typedef struct _CoglPipelineFragendARBfpArg +{ + const char *name; + + CoglPipelineFragendARBfpArgType type; + + /* for type = TEXTURE */ + int texture_unit; + CoglTextureType texture_type; + + /* for type = CONSTANT */ + int constant_id; + + const char *swizzle; + +} CoglPipelineFragendARBfpArg; + +static void +append_arg (GString *source, const CoglPipelineFragendARBfpArg *arg) +{ + switch (arg->type) + { + case COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_TEXTURE: + g_string_append_printf (source, "texel%d%s", + arg->texture_unit, arg->swizzle); + break; + case COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_CONSTANT: + g_string_append_printf (source, "program.local[%d]%s", + arg->constant_id, arg->swizzle); + break; + case COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE: + g_string_append_printf (source, "%s%s", + arg->name, arg->swizzle); + break; + } +} + +/* Note: we are trying to avoid duplicating strings during codegen + * which is why we have the slightly awkward + * CoglPipelineFragendARBfpArg mechanism. */ +static void +setup_arg (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglBlendStringChannelMask mask, + int arg_index, + CoglPipelineCombineSource src, + GLint op, + CoglPipelineFragendARBfpArg *arg) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + static const char *tmp_name[3] = { "tmp0", "tmp1", "tmp2" }; + + switch (src) + { + case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_TEXTURE; + arg->name = "texel%d"; + arg->texture_unit = _cogl_pipeline_layer_get_unit_index (layer); + setup_texture_source (shader_state, + arg->texture_unit, + _cogl_pipeline_layer_get_texture_type (layer)); + break; + case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + UnitState *unit_state = &shader_state->unit_state[unit_index]; + + unit_state->constant_id = shader_state->next_constant_id++; + unit_state->has_combine_constant = TRUE; + unit_state->dirty_combine_constant = TRUE; + + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_CONSTANT; + arg->name = "program.local[%d]"; + arg->constant_id = unit_state->constant_id; + break; + } + case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE; + arg->name = "fragment.color.primary"; + break; + case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE; + if (_cogl_pipeline_layer_get_unit_index (layer) == 0) + arg->name = "fragment.color.primary"; + else + arg->name = "output"; + break; + default: /* Sample the texture attached to a specific layer */ + { + int layer_num = src - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; + CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; + CoglPipelineLayer *other_layer = + _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); + + if (other_layer == NULL) + { + static CoglBool warning_seen = FALSE; + if (!warning_seen) + { + g_warning ("The application is trying to use a texture " + "combine with a layer number that does not exist"); + warning_seen = TRUE; + } + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE; + arg->name = "output"; + } + else + { + CoglTextureType texture_type; + + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_TEXTURE; + arg->name = "texture[%d]"; + arg->texture_unit = + _cogl_pipeline_layer_get_unit_index (other_layer); + texture_type = _cogl_pipeline_layer_get_texture_type (other_layer); + setup_texture_source (shader_state, + arg->texture_unit, + texture_type); + } + } + break; + } + + arg->swizzle = ""; + + switch (op) + { + case COGL_PIPELINE_COMBINE_OP_SRC_COLOR: + break; + case COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR: + g_string_append_printf (shader_state->source, + "SUB tmp%d, one, ", + arg_index); + append_arg (shader_state->source, arg); + g_string_append_printf (shader_state->source, ";\n"); + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE; + arg->name = tmp_name[arg_index]; + arg->swizzle = ""; + break; + case COGL_PIPELINE_COMBINE_OP_SRC_ALPHA: + /* avoid a swizzle if we know RGB are going to be masked + * in the end anyway */ + if (mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + arg->swizzle = ".a"; + break; + case COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA: + g_string_append_printf (shader_state->source, + "SUB tmp%d, one, ", + arg_index); + append_arg (shader_state->source, arg); + /* avoid a swizzle if we know RGB are going to be masked + * in the end anyway */ + if (mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + g_string_append_printf (shader_state->source, ".a;\n"); + else + g_string_append_printf (shader_state->source, ";\n"); + arg->type = COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_SIMPLE; + arg->name = tmp_name[arg_index]; + break; + default: + g_error ("Unknown texture combine operator %d", op); + break; + } +} + +static CoglBool +fragend_arbfp_args_equal (CoglPipelineFragendARBfpArg *arg0, + CoglPipelineFragendARBfpArg *arg1) +{ + if (arg0->type != arg1->type) + return FALSE; + + if (arg0->name != arg1->name && + strcmp (arg0->name, arg1->name) != 0) + return FALSE; + + if (arg0->type == COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_TEXTURE && + arg0->texture_unit != arg1->texture_unit) + return FALSE; + /* Note we don't have to check the target; a texture unit can only + * have one target enabled at a time. */ + + if (arg0->type == COGL_PIPELINE_FRAGEND_ARBFP_ARG_TYPE_CONSTANT && + arg0->constant_id != arg1->constant_id) + return FALSE; + + if (arg0->swizzle != arg1->swizzle && + strcmp (arg0->swizzle, arg1->swizzle) != 0) + return FALSE; + + return TRUE; +} + +static void +append_function (CoglPipeline *pipeline, + CoglBlendStringChannelMask mask, + GLint function, + CoglPipelineFragendARBfpArg *args, + int n_args) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + const char *mask_name; + + switch (mask) + { + case COGL_BLEND_STRING_CHANNEL_MASK_RGB: + mask_name = ".rgb"; + break; + case COGL_BLEND_STRING_CHANNEL_MASK_ALPHA: + mask_name = ".a"; + break; + case COGL_BLEND_STRING_CHANNEL_MASK_RGBA: + mask_name = ""; + break; + default: + g_error ("Unknown channel mask %d", mask); + mask_name = ""; + } + + switch (function) + { + case COGL_PIPELINE_COMBINE_FUNC_ADD: + g_string_append_printf (shader_state->source, + "ADD_SAT output%s, ", + mask_name); + break; + case COGL_PIPELINE_COMBINE_FUNC_MODULATE: + /* Note: no need to saturate since we can assume operands + * have values in the range [0,1] */ + g_string_append_printf (shader_state->source, "MUL output%s, ", + mask_name); + break; + case COGL_PIPELINE_COMBINE_FUNC_REPLACE: + /* Note: no need to saturate since we can assume operand + * has a value in the range [0,1] */ + g_string_append_printf (shader_state->source, "MOV output%s, ", + mask_name); + break; + case COGL_PIPELINE_COMBINE_FUNC_SUBTRACT: + g_string_append_printf (shader_state->source, + "SUB_SAT output%s, ", + mask_name); + break; + case COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED: + g_string_append_printf (shader_state->source, "ADD tmp3%s, ", + mask_name); + append_arg (shader_state->source, &args[0]); + g_string_append (shader_state->source, ", "); + append_arg (shader_state->source, &args[1]); + g_string_append (shader_state->source, ";\n"); + g_string_append_printf (shader_state->source, + "SUB_SAT output%s, tmp3, half", + mask_name); + n_args = 0; + break; + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB: + /* These functions are the same except that GL_DOT3_RGB never + * updates the alpha channel. + * + * NB: GL_DOT3_RGBA is a bit special because it effectively forces + * an RGBA mask and we end up ignoring any separate alpha channel + * function. + */ + case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA: + { + const char *tmp4 = "tmp4"; + + /* The maths for this was taken from Mesa; + * apparently: + * + * tmp3 = 2*src0 - 1 + * tmp4 = 2*src1 - 1 + * output = DP3 (tmp3, tmp4) + * + * is the same as: + * + * output = 4 * DP3 (src0 - 0.5, src1 - 0.5) + */ + + g_string_append (shader_state->source, "MAD tmp3, two, "); + append_arg (shader_state->source, &args[0]); + g_string_append (shader_state->source, ", minus_one;\n"); + + if (!fragend_arbfp_args_equal (&args[0], &args[1])) + { + g_string_append (shader_state->source, "MAD tmp4, two, "); + append_arg (shader_state->source, &args[1]); + g_string_append (shader_state->source, ", minus_one;\n"); + } + else + tmp4 = "tmp3"; + + g_string_append_printf (shader_state->source, + "DP3_SAT output%s, tmp3, %s", + mask_name, tmp4); + n_args = 0; + } + break; + case COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE: + /* Note: no need to saturate since we can assume operands + * have values in the range [0,1] */ + + /* NB: GL_INTERPOLATE = arg0*arg2 + arg1*(1-arg2) + * but LRP dst, a, b, c = b*a + c*(1-a) */ + g_string_append_printf (shader_state->source, "LRP output%s, ", + mask_name); + append_arg (shader_state->source, &args[2]); + g_string_append (shader_state->source, ", "); + append_arg (shader_state->source, &args[0]); + g_string_append (shader_state->source, ", "); + append_arg (shader_state->source, &args[1]); + n_args = 0; + break; + default: + g_error ("Unknown texture combine function %d", function); + g_string_append_printf (shader_state->source, "MUL_SAT output%s, ", + mask_name); + n_args = 2; + break; + } + + if (n_args > 0) + append_arg (shader_state->source, &args[0]); + if (n_args > 1) + { + g_string_append (shader_state->source, ", "); + append_arg (shader_state->source, &args[1]); + } + g_string_append (shader_state->source, ";\n"); +} + +static void +append_masked_combine (CoglPipeline *arbfp_authority, + CoglPipelineLayer *layer, + CoglBlendStringChannelMask mask, + CoglPipelineCombineFunc function, + CoglPipelineCombineSource *src, + CoglPipelineCombineOp *op) +{ + int i; + int n_args; + CoglPipelineFragendARBfpArg args[3]; + + n_args = _cogl_get_n_args_for_combine_func (function); + + for (i = 0; i < n_args; i++) + { + setup_arg (arbfp_authority, + layer, + mask, + i, + src[i], + op[i], + &args[i]); + } + + append_function (arbfp_authority, + mask, + function, + args, + n_args); +} + +static CoglBool +_cogl_pipeline_fragend_arbfp_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + CoglPipelineLayer *combine_authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_COMBINE); + CoglPipelineLayerBigState *big_state = combine_authority->big_state; + + /* Notes... + * + * We are ignoring the issue of texture indirection limits until + * someone complains (Ref Section 3.11.6 in the ARB_fragment_program + * spec) + * + * There always five TEMPs named tmp0, tmp1 and tmp2, tmp3 and tmp4 + * available and these constants: 'one' = {1, 1, 1, 1}, 'half' + * {.5, .5, .5, .5}, 'two' = {2, 2, 2, 2}, 'minus_one' = {-1, -1, + * -1, -1} + * + * tmp0-2 are intended for dealing with some of the texture combine + * operands (e.g. GL_ONE_MINUS_SRC_COLOR) tmp3/4 are for dealing + * with the GL_ADD_SIGNED texture combine and the GL_DOT3_RGB[A] + * functions. + * + * Each layer outputs to the TEMP called "output", and reads from + * output if it needs to refer to GL_PREVIOUS. (we detect if we are + * layer0 so we will read fragment.color for GL_PREVIOUS in that + * case) + * + * We aim to do all the channels together if the same function is + * used for RGB as for A. + * + * We aim to avoid string duplication / allocations during codegen. + * + * We are careful to only saturate when writing to output. + */ + + if (!shader_state->source) + return TRUE; + + if (!_cogl_pipeline_layer_needs_combine_separate (combine_authority)) + { + append_masked_combine (pipeline, + layer, + COGL_BLEND_STRING_CHANNEL_MASK_RGBA, + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src, + big_state->texture_combine_rgb_op); + } + else if (big_state->texture_combine_rgb_func == + COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA) + { + /* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function + * since if you use it, it overrides your ALPHA function... + */ + append_masked_combine (pipeline, + layer, + COGL_BLEND_STRING_CHANNEL_MASK_RGBA, + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src, + big_state->texture_combine_rgb_op); + } + else + { + append_masked_combine (pipeline, + layer, + COGL_BLEND_STRING_CHANNEL_MASK_RGB, + big_state->texture_combine_rgb_func, + big_state->texture_combine_rgb_src, + big_state->texture_combine_rgb_op); + append_masked_combine (pipeline, + layer, + COGL_BLEND_STRING_CHANNEL_MASK_ALPHA, + big_state->texture_combine_alpha_func, + big_state->texture_combine_alpha_src, + big_state->texture_combine_alpha_op); + } + + return TRUE; +} + +static CoglBool +_cogl_pipeline_fragend_arbfp_passthrough (CoglPipeline *pipeline) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + + if (!shader_state->source) + return TRUE; + + g_string_append (shader_state->source, + "MOV output, fragment.color.primary;\n"); + return TRUE; +} + +typedef struct _UpdateConstantsState +{ + int unit; + CoglBool update_all; + CoglPipelineShaderState *shader_state; +} UpdateConstantsState; + +static CoglBool +update_constants_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + UpdateConstantsState *state = user_data; + CoglPipelineShaderState *shader_state = state->shader_state; + UnitState *unit_state = &shader_state->unit_state[state->unit++]; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (unit_state->has_combine_constant && + (state->update_all || unit_state->dirty_combine_constant)) + { + float constant[4]; + _cogl_pipeline_get_layer_combine_constant (pipeline, + layer_index, + constant); + GE (ctx, glProgramLocalParameter4fv (GL_FRAGMENT_PROGRAM_ARB, + unit_state->constant_id, + constant)); + unit_state->dirty_combine_constant = FALSE; + } + return TRUE; +} + +static CoglBool +_cogl_pipeline_fragend_arbfp_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + CoglPipelineShaderState *shader_state = get_shader_state (pipeline); + GLuint gl_program; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (shader_state->source) + { + GLenum gl_error; + COGL_STATIC_COUNTER (fragend_arbfp_compile_counter, + "arbfp compile counter", + "Increments each time a new ARBfp " + "program is compiled", + 0 /* no application private data */); + + COGL_COUNTER_INC (_cogl_uprof_context, fragend_arbfp_compile_counter); + + g_string_append (shader_state->source, + "MOV result.color,output;\n"); + g_string_append (shader_state->source, "END\n"); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) + g_message ("pipeline program:\n%s", shader_state->source->str); + + GE (ctx, glGenPrograms (1, &shader_state->gl_program)); + + GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, + shader_state->gl_program)); + + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + ctx->glProgramString (GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + shader_state->source->len, + shader_state->source->str); + if (ctx->glGetError () != GL_NO_ERROR) + { + g_warning ("\n%s\n%s", + shader_state->source->str, + ctx->glGetString (GL_PROGRAM_ERROR_STRING_ARB)); + } + + shader_state->source = NULL; + } + + if (shader_state->user_program != COGL_INVALID_HANDLE) + { + /* An arbfp program should contain exactly one shader which we + can use directly */ + CoglProgram *program = shader_state->user_program; + CoglShader *shader = program->attached_shaders->data; + + gl_program = shader->gl_handle; + } + else + gl_program = shader_state->gl_program; + + GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, gl_program)); + _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_ARBFP); + + if (shader_state->user_program == COGL_INVALID_HANDLE) + { + UpdateConstantsState state; + state.unit = 0; + state.shader_state = shader_state; + /* If this arbfp program was last used with a different pipeline + * then we need to ensure we update all program.local params */ + state.update_all = + pipeline != shader_state->last_used_for_pipeline; + cogl_pipeline_foreach_layer (pipeline, + update_constants_cb, + &state); + } + else + { + CoglProgram *program = shader_state->user_program; + CoglBool program_changed; + + /* If the shader has changed since it was last flushed then we + need to update all uniforms */ + program_changed = program->age != shader_state->user_program_age; + + _cogl_program_flush_uniforms (program, gl_program, program_changed); + + shader_state->user_program_age = program->age; + } + + /* We need to track what pipeline used this arbfp program last since + * we will need to update program.local params when switching + * between different pipelines. */ + shader_state->last_used_for_pipeline = pipeline; + + return TRUE; +} + +static void +_cogl_pipeline_fragend_arbfp_pipeline_pre_change_notify ( + CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if ((change & _cogl_pipeline_get_state_for_fragment_codegen (ctx))) + dirty_shader_state (pipeline); +} + +/* NB: layers are considered immutable once they have any dependants + * so although multiple pipelines can end up depending on a single + * static layer, we can guarantee that if a layer is being *changed* + * then it can only have one pipeline depending on it. + * + * XXX: Don't forget this is *pre* change, we can't read the new value + * yet! + */ +static void +_cogl_pipeline_fragend_arbfp_layer_pre_change_notify ( + CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + CoglPipelineShaderState *shader_state = get_shader_state (owner); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!shader_state) + return; + + if ((change & _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx))) + { + dirty_shader_state (owner); + return; + } + + if (change & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) + { + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + shader_state->unit_state[unit_index].dirty_combine_constant = TRUE; + } + + /* TODO: we could be saving snippets of texture combine code along + * with each layer and then when a layer changes we would just free + * the snippet. */ + return; +} + +const CoglPipelineFragend _cogl_pipeline_arbfp_fragend = +{ + _cogl_pipeline_fragend_arbfp_start, + _cogl_pipeline_fragend_arbfp_add_layer, + _cogl_pipeline_fragend_arbfp_passthrough, + _cogl_pipeline_fragend_arbfp_end, + _cogl_pipeline_fragend_arbfp_pipeline_pre_change_notify, + NULL, + _cogl_pipeline_fragend_arbfp_layer_pre_change_notify +}; + +#endif /* COGL_PIPELINE_FRAGEND_ARBFP */ + diff --git a/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp-private.h b/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp-private.h new file mode 100644 index 000000000..ba0c713cd --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp-private.h @@ -0,0 +1,42 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_PROGEND_FIXED_ARBFP_PRIVATE_H +#define __COGL_PIPELINE_PROGEND_FIXED_ARBFP_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineProgend _cogl_pipeline_fixed_arbfp_progend; + +#endif /* __COGL_PIPELINE_PROGEND_FIXED_ARBFP_PRIVATE_H */ + diff --git a/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c b/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c new file mode 100644 index 000000000..956bffc84 --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c @@ -0,0 +1,121 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-state-private.h" + +#ifdef COGL_PIPELINE_PROGEND_FIXED_ARBFP + +#include "cogl-context.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-program-private.h" + +static CoglBool +_cogl_pipeline_progend_fixed_arbfp_start (CoglPipeline *pipeline) +{ + CoglHandle user_program; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED))) + return FALSE; + + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) + return FALSE; + + /* Vertex snippets are only supported in the GLSL fragend */ + if (_cogl_pipeline_has_vertex_snippets (pipeline)) + return FALSE; + + /* Validate that we can handle the fragment state using ARBfp + */ + + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_ARBFP)) + return FALSE; + + /* Fragment snippets are only supported in the GLSL fragend */ + if (_cogl_pipeline_has_fragment_snippets (pipeline)) + return FALSE; + + user_program = cogl_pipeline_get_user_program (pipeline); + if (user_program && + _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_ARBFP) + return FALSE; + + /* The ARBfp progend can't handle the per-vertex point size + * attribute */ + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + return FALSE; + + return TRUE; +} + +static void +_cogl_pipeline_progend_fixed_arbfp_pre_paint (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer) +{ + CoglContext *ctx = framebuffer->context; + + if (ctx->current_projection_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_projection_entry, + COGL_MATRIX_PROJECTION, + framebuffer, + FALSE /* enable flip */); + if (ctx->current_modelview_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_modelview_entry, + COGL_MATRIX_MODELVIEW, + framebuffer, + FALSE /* enable flip */); +} + +const CoglPipelineProgend _cogl_pipeline_fixed_arbfp_progend = + { + COGL_PIPELINE_VERTEND_FIXED, + COGL_PIPELINE_FRAGEND_ARBFP, + _cogl_pipeline_progend_fixed_arbfp_start, + NULL, /* end */ + NULL, /* pre_change_notify */ + NULL, /* layer_pre_change_notify */ + _cogl_pipeline_progend_fixed_arbfp_pre_paint + }; + +#endif /* COGL_PIPELINE_PROGEND_FIXED_ARBFP */ diff --git a/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c new file mode 100644 index 000000000..109af8133 --- /dev/null +++ b/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c @@ -0,0 +1,555 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-primitives.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" + +#include +#include +#include + +#ifndef GL_TEXTURE_SWIZZLE_RGBA +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + +static GLuint +_cogl_texture_driver_gen (CoglContext *ctx, + GLenum gl_target, + CoglPixelFormat internal_format) +{ + GLuint tex; + + GE (ctx, glGenTextures (1, &tex)); + + _cogl_bind_gl_texture_transient (gl_target, tex, FALSE); + + switch (gl_target) + { + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + /* In case automatic mipmap generation gets disabled for this + * texture but a minification filter depending on mipmap + * interpolation is selected then we initialize the max mipmap + * level to 0 so OpenGL will consider the texture storage to be + * "complete". + */ +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL)) + GE( ctx, glTexParameteri (gl_target, GL_TEXTURE_MAX_LEVEL, 0)); +#endif + + /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ + GE( ctx, glTexParameteri (gl_target, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR) ); + break; + + case GL_TEXTURE_RECTANGLE_ARB: + /* Texture rectangles already default to GL_LINEAR so nothing + needs to be done */ + break; + + default: + g_assert_not_reached(); + } + + /* If the driver doesn't support alpha textures directly then we'll + * fake them by setting the swizzle parameters */ + if (internal_format == COGL_PIXEL_FORMAT_A_8 && + !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && + _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) + { + static const GLint red_swizzle[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; + + GE( ctx, glTexParameteriv (gl_target, + GL_TEXTURE_SWIZZLE_RGBA, + red_swizzle) ); + } + + return tex; +} + +/* OpenGL - unlike GLES - can upload a sub region of pixel data from a larger + * source buffer */ +static void +prep_gl_for_pixels_upload_full (CoglContext *ctx, + int pixels_rowstride, + int image_height, + int pixels_src_x, + int pixels_src_y, + int pixels_bpp) +{ + GE( ctx, glPixelStorei (GL_UNPACK_ROW_LENGTH, + pixels_rowstride / pixels_bpp) ); + + GE( ctx, glPixelStorei (GL_UNPACK_SKIP_PIXELS, pixels_src_x) ); + GE( ctx, glPixelStorei (GL_UNPACK_SKIP_ROWS, pixels_src_y) ); + + if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) + GE( ctx, glPixelStorei (GL_UNPACK_IMAGE_HEIGHT, image_height) ); + + _cogl_texture_gl_prep_alignment_for_pixels_upload (ctx, pixels_rowstride); +} + +static void +_cogl_texture_driver_prep_gl_for_pixels_upload (CoglContext *ctx, + int pixels_rowstride, + int pixels_bpp) +{ + prep_gl_for_pixels_upload_full (ctx, pixels_rowstride, 0, 0, 0, pixels_bpp); +} + +/* OpenGL - unlike GLES - can download pixel data into a sub region of + * a larger destination buffer */ +static void +prep_gl_for_pixels_download_full (CoglContext *ctx, + int image_width, + int pixels_rowstride, + int image_height, + int pixels_src_x, + int pixels_src_y, + int pixels_bpp) +{ + GE( ctx, glPixelStorei (GL_PACK_ROW_LENGTH, pixels_rowstride / pixels_bpp) ); + + GE( ctx, glPixelStorei (GL_PACK_SKIP_PIXELS, pixels_src_x) ); + GE( ctx, glPixelStorei (GL_PACK_SKIP_ROWS, pixels_src_y) ); + + if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) + GE( ctx, glPixelStorei (GL_PACK_IMAGE_HEIGHT, image_height) ); + + _cogl_texture_gl_prep_alignment_for_pixels_download (ctx, + pixels_bpp, + image_width, + pixels_rowstride); +} + +static void +_cogl_texture_driver_prep_gl_for_pixels_download (CoglContext *ctx, + int image_width, + int pixels_rowstride, + int pixels_bpp) +{ + prep_gl_for_pixels_download_full (ctx, + pixels_rowstride, + image_width, + 0 /* image height */, + 0, 0, /* pixels_src_x/y */ + pixels_bpp); +} + +static CoglBool +_cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx, + CoglTexture *texture, + CoglBool is_foreign, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + int level, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + GLenum gl_target; + GLuint gl_handle; + uint8_t *data; + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + GLenum gl_error; + CoglBool status = TRUE; + CoglError *internal_error = NULL; + int level_width; + int level_height; + + cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); + + data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error); + + /* NB: _cogl_bitmap_gl_bind() may return NULL when successfull so we + * have to explicitly check the cogl error pointer to catch + * problems... */ + if (internal_error) + { + _cogl_propagate_error (error, internal_error); + return FALSE; + } + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (ctx, + cogl_bitmap_get_rowstride (source_bmp), + 0, + src_x, + src_y, + bpp); + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + _cogl_texture_get_level_size (texture, + level, + &level_width, + &level_height, + NULL); + + if (level_width == width && level_height == height) + { + /* GL gets upset if you use glTexSubImage2D to initialize the + * contents of a mipmap level so we make sure to use + * glTexImage2D if we are uploading a full mipmap level. + */ + ctx->glTexImage2D (gl_target, + level, + _cogl_texture_gl_get_format (texture), + width, + height, + 0, + source_gl_format, + source_gl_type, + data); + + } + else + { + /* GL gets upset if you use glTexSubImage2D to initialize the + * contents of a mipmap level so if this is the first time + * we've seen a request to upload to this level we call + * glTexImage2D first to assert that the storage for this + * level exists. + */ + if (texture->max_level < level) + { + ctx->glTexImage2D (gl_target, + level, + _cogl_texture_gl_get_format (texture), + level_width, + level_height, + 0, + source_gl_format, + source_gl_type, + NULL); + } + + ctx->glTexSubImage2D (gl_target, + level, + dst_x, dst_y, + width, height, + source_gl_format, + source_gl_type, + data); + } + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_bitmap_gl_unbind (source_bmp); + + return status; +} + +static CoglBool +_cogl_texture_driver_upload_to_gl (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + uint8_t *data; + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + GLenum gl_error; + CoglBool status = TRUE; + CoglError *internal_error = NULL; + + data = _cogl_bitmap_gl_bind (source_bmp, + COGL_BUFFER_ACCESS_READ, + 0, /* hints */ + &internal_error); + + /* NB: _cogl_bitmap_gl_bind() may return NULL when successful so we + * have to explicitly check the cogl error pointer to catch + * problems... */ + if (internal_error) + { + _cogl_propagate_error (error, internal_error); + return FALSE; + } + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (ctx, + cogl_bitmap_get_rowstride (source_bmp), + 0, 0, 0, bpp); + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage2D (gl_target, 0, + internal_gl_format, + cogl_bitmap_get_width (source_bmp), + cogl_bitmap_get_height (source_bmp), + 0, + source_gl_format, + source_gl_type, + data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_bitmap_gl_unbind (source_bmp); + + return status; +} + +static CoglBool +_cogl_texture_driver_upload_to_gl_3d (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + GLint height, + GLint depth, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + uint8_t *data; + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + GLenum gl_error; + CoglBool status = TRUE; + + data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, error); + if (!data) + return FALSE; + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (ctx, + cogl_bitmap_get_rowstride (source_bmp), + (cogl_bitmap_get_height (source_bmp) / + depth), + 0, 0, bpp); + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage3D (gl_target, + 0, /* level */ + internal_gl_format, + cogl_bitmap_get_width (source_bmp), + height, + depth, + 0, + source_gl_format, + source_gl_type, + data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_bitmap_gl_unbind (source_bmp); + + return status; +} + +static CoglBool +_cogl_texture_driver_gl_get_tex_image (CoglContext *ctx, + GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + uint8_t *dest) +{ + GE (ctx, glGetTexImage (gl_target, + 0, /* level */ + dest_gl_format, + dest_gl_type, + (GLvoid *)dest)); + return TRUE; +} + +static CoglBool +_cogl_texture_driver_size_supported_3d (CoglContext *ctx, + GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height, + int depth) +{ + GLenum proxy_target; + GLint new_width = 0; + + if (gl_target == GL_TEXTURE_3D) + proxy_target = GL_PROXY_TEXTURE_3D; + else + /* Unknown target, assume it's not supported */ + return FALSE; + + /* Proxy texture allows for a quick check for supported size */ + GE( ctx, glTexImage3D (proxy_target, 0, GL_RGBA, + width, height, depth, 0 /* border */, + gl_format, gl_type, NULL) ); + + GE( ctx, glGetTexLevelParameteriv (proxy_target, 0, + GL_TEXTURE_WIDTH, &new_width) ); + + return new_width != 0; +} + +static CoglBool +_cogl_texture_driver_size_supported (CoglContext *ctx, + GLenum gl_target, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + GLenum proxy_target; + GLint new_width = 0; + + if (gl_target == GL_TEXTURE_2D) + proxy_target = GL_PROXY_TEXTURE_2D; +#if HAVE_COGL_GL + else if (gl_target == GL_TEXTURE_RECTANGLE_ARB) + proxy_target = GL_PROXY_TEXTURE_RECTANGLE_ARB; +#endif + else + /* Unknown target, assume it's not supported */ + return FALSE; + + /* Proxy texture allows for a quick check for supported size */ + GE( ctx, glTexImage2D (proxy_target, 0, gl_intformat, + width, height, 0 /* border */, + gl_format, gl_type, NULL) ); + + GE( ctx, glGetTexLevelParameteriv (proxy_target, 0, + GL_TEXTURE_WIDTH, &new_width) ); + + return new_width != 0; +} + +static void +_cogl_texture_driver_try_setting_gl_border_color + (CoglContext *ctx, + GLuint gl_target, + const GLfloat *transparent_color) +{ + /* Use a transparent border color so that we can leave the + color buffer alone when using texture co-ordinates + outside of the texture */ + GE( ctx, glTexParameterfv (gl_target, GL_TEXTURE_BORDER_COLOR, + transparent_color) ); +} + +static CoglBool +_cogl_texture_driver_allows_foreign_gl_target (CoglContext *ctx, + GLenum gl_target) +{ + /* GL_ARB_texture_rectangle textures are supported if they are + created from foreign because some chipsets have trouble with + GL_ARB_texture_non_power_of_two. There is no Cogl call to create + them directly to emphasize the fact that they don't work fully + (for example, no mipmapping and complicated shader support) */ + + /* Allow 2-dimensional or rectangle textures only */ + if (gl_target != GL_TEXTURE_2D && gl_target != GL_TEXTURE_RECTANGLE_ARB) + return FALSE; + + return TRUE; +} + +static CoglPixelFormat +_cogl_texture_driver_find_best_gl_get_data_format + (CoglContext *context, + CoglPixelFormat format, + GLenum *closest_gl_format, + GLenum *closest_gl_type) +{ + return context->driver_vtable->pixel_format_to_gl (context, + format, + NULL, /* don't need */ + closest_gl_format, + closest_gl_type); +} + +const CoglTextureDriver +_cogl_texture_driver_gl = + { + _cogl_texture_driver_gen, + _cogl_texture_driver_prep_gl_for_pixels_upload, + _cogl_texture_driver_upload_subregion_to_gl, + _cogl_texture_driver_upload_to_gl, + _cogl_texture_driver_upload_to_gl_3d, + _cogl_texture_driver_prep_gl_for_pixels_download, + _cogl_texture_driver_gl_get_tex_image, + _cogl_texture_driver_size_supported, + _cogl_texture_driver_size_supported_3d, + _cogl_texture_driver_try_setting_gl_border_color, + _cogl_texture_driver_allows_foreign_gl_target, + _cogl_texture_driver_find_best_gl_get_data_format + }; diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c new file mode 100644 index 000000000..e94449f4a --- /dev/null +++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c @@ -0,0 +1,487 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-context-private.h" +#include "cogl-util-gl-private.h" +#include "cogl-feature-private.h" +#include "cogl-renderer-private.h" +#include "cogl-private.h" +#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" +#include "cogl-buffer-gl-private.h" + +#ifndef GL_UNSIGNED_INT_24_8 +#define GL_UNSIGNED_INT_24_8 0x84FA +#endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif +#ifndef GL_RG +#define GL_RG 0x8227 +#endif +#ifndef GL_RG8 +#define GL_RG8 0x822B +#endif + +static CoglBool +_cogl_driver_pixel_format_from_gl_internal (CoglContext *context, + GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + return TRUE; +} + +static CoglPixelFormat +_cogl_driver_pixel_format_to_gl (CoglContext *context, + CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format; + GLenum glintformat; + GLenum glformat = 0; + GLenum gltype; + + required_format = format; + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_RG_88: + if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG)) + { + glintformat = GL_RG8; + glformat = GL_RG; + } + else + { + /* If red-green textures aren't supported then we'll use RGB + * as an internal format. Note this should only end up + * mattering for downloading the data because Cogl will + * refuse to allocate a texture with RG components if RG + * textures aren't supported */ + glintformat = GL_RGB; + glformat = GL_RGB; + required_format = COGL_PIXEL_FORMAT_RGB_888; + } + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + /* There is an extension to support this format */ + if (_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888)) + { + /* For some reason the extension says you have to specify + BGRA for the internal format too */ + glintformat = GL_BGRA_EXT; + glformat = GL_BGRA_EXT; + gltype = GL_UNSIGNED_BYTE; + required_format = format; + break; + } + /* flow through */ + + /* Just one 24-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGB_888: + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGB_888; + break; + + /* Just one 32-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + case COGL_PIXEL_FORMAT_ABGR_8888: + case COGL_PIXEL_FORMAT_ABGR_8888_PRE: + case COGL_PIXEL_FORMAT_RGBA_1010102: + case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: + case COGL_PIXEL_FORMAT_BGRA_1010102: + case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: + case COGL_PIXEL_FORMAT_ABGR_2101010: + case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: + case COGL_PIXEL_FORMAT_ARGB_2101010: + case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGBA_8888; + required_format |= (format & COGL_PREMULT_BIT); + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + case COGL_PIXEL_FORMAT_RGBA_4444_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + case COGL_PIXEL_FORMAT_RGBA_5551_PRE: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + case COGL_PIXEL_FORMAT_DEPTH_16: + glintformat = GL_DEPTH_COMPONENT; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_UNSIGNED_SHORT; + break; + case COGL_PIXEL_FORMAT_DEPTH_32: + glintformat = GL_DEPTH_COMPONENT; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_UNSIGNED_INT; + break; + + case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: + glintformat = GL_DEPTH_STENCIL; + glformat = GL_DEPTH_STENCIL; + gltype = GL_UNSIGNED_INT_24_8; + break; + + case COGL_PIXEL_FORMAT_ANY: + case COGL_PIXEL_FORMAT_YUV: + g_assert_not_reached (); + break; + } + + /* All of the pixel formats are handled above so if this hits then + we've been given an invalid pixel format */ + g_assert (glformat != 0); + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static CoglBool +_cogl_get_gl_version (CoglContext *ctx, + int *major_out, + int *minor_out) +{ + const char *version_string; + + /* Get the OpenGL version number */ + if ((version_string = _cogl_context_get_gl_version (ctx)) == NULL) + return FALSE; + + if (!g_str_has_prefix (version_string, "OpenGL ES ")) + return FALSE; + + return _cogl_gl_util_parse_gl_version (version_string + 10, + major_out, + minor_out); +} + +static CoglBool +_cogl_driver_update_features (CoglContext *context, + CoglError **error) +{ + unsigned long private_features + [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)] = { 0 }; + CoglFeatureFlags flags = 0; + char **gl_extensions; + int gl_major, gl_minor; + int i; + + /* We have to special case getting the pointer to the glGetString + function because we need to use it to determine what functions we + can expect */ + context->glGetString = + (void *) _cogl_renderer_get_proc_address (context->display->renderer, + "glGetString", + TRUE); + + gl_extensions = _cogl_context_get_gl_extensions (context); + + if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) + { + char *all_extensions = g_strjoinv (" ", gl_extensions); + + COGL_NOTE (WINSYS, + "Checking features\n" + " GL_VENDOR: %s\n" + " GL_RENDERER: %s\n" + " GL_VERSION: %s\n" + " GL_EXTENSIONS: %s", + context->glGetString (GL_VENDOR), + context->glGetString (GL_RENDERER), + _cogl_context_get_gl_version (context), + all_extensions); + + g_free (all_extensions); + } + + context->glsl_major = 1; + context->glsl_minor = 0; + context->glsl_version_to_use = 100; + + _cogl_gpu_info_init (context, &context->gpu); + + if (!_cogl_get_gl_version (context, &gl_major, &gl_minor)) + { + gl_major = 1; + gl_minor = 1; + } + + _cogl_feature_check_ext_functions (context, + gl_major, + gl_minor, + gl_extensions); + +#ifdef HAVE_COGL_GLES + if (context->driver == COGL_DRIVER_GLES1) + { + int max_clip_planes; + GE( context, glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, TRUE); + } +#endif + + if (context->driver == COGL_DRIVER_GLES2) + { + flags |= COGL_FEATURE_SHADERS_GLSL | COGL_FEATURE_OFFSCREEN; + /* Note GLES 2 core doesn't support mipmaps for npot textures or + * repeat modes other than CLAMP_TO_EDGE. */ + flags |= COGL_FEATURE_TEXTURE_NPOT_BASIC; + flags |= COGL_FEATURE_DEPTH_RANGE; + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_GLSL, TRUE); + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_DEPTH_RANGE, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_MIRRORED_REPEAT, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, TRUE); + + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_BLEND_CONSTANT, TRUE); + } + else if (context->driver == COGL_DRIVER_GLES1) + { + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_GL_FIXED, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEST, TRUE); + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM, TRUE); + } + + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_VBOS, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ANY_GL, TRUE); + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, TRUE); + + /* Both GLES 1.1 and GLES 2.0 support point sprites in core */ + flags |= COGL_FEATURE_POINT_SPRITE; + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_POINT_SPRITE, TRUE); + + if (context->glGenRenderbuffers) + { + flags |= COGL_FEATURE_OFFSCREEN; + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); + } + + if (context->glBlitFramebuffer) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, TRUE); + + if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) + { + flags |= COGL_FEATURE_UNSIGNED_INT_INDICES; + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); + } + + if (_cogl_check_extension ("GL_OES_depth_texture", gl_extensions)) + { + flags |= COGL_FEATURE_DEPTH_TEXTURE; + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_DEPTH_TEXTURE, TRUE); + } + + if (_cogl_check_extension ("GL_OES_texture_npot", gl_extensions)) + { + flags |= (COGL_FEATURE_TEXTURE_NPOT | + COGL_FEATURE_TEXTURE_NPOT_BASIC | + COGL_FEATURE_TEXTURE_NPOT_MIPMAP | + COGL_FEATURE_TEXTURE_NPOT_REPEAT); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, TRUE); + } + else if (_cogl_check_extension ("GL_IMG_texture_npot", gl_extensions)) + { + flags |= (COGL_FEATURE_TEXTURE_NPOT_BASIC | + COGL_FEATURE_TEXTURE_NPOT_MIPMAP); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); + } + + if (context->glTexImage3D) + { + flags |= COGL_FEATURE_TEXTURE_3D; + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_3D, TRUE); + } + + if (context->glMapBuffer) + { + /* The GL_OES_mapbuffer extension doesn't support mapping for + read */ + flags |= COGL_FEATURE_MAP_BUFFER_FOR_WRITE; + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); + } + + if (context->glMapBufferRange) + { + /* MapBufferRange in ES3+ does support mapping for read */ + flags |= (COGL_FEATURE_MAP_BUFFER_FOR_WRITE | + COGL_FEATURE_MAP_BUFFER_FOR_READ); + COGL_FLAGS_SET(context->features, + COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); + COGL_FLAGS_SET(context->features, + COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, TRUE); + } + + if (context->glEGLImageTargetTexture2D) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, TRUE); + + if (_cogl_check_extension ("GL_OES_packed_depth_stencil", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL, TRUE); + + if (_cogl_check_extension ("GL_EXT_texture_format_BGRA8888", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888, TRUE); + + if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions)) + COGL_FLAGS_SET (private_features, + COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE, TRUE); + + /* A nameless vendor implemented the extension, but got the case wrong + * per the spec. */ + if (_cogl_check_extension ("GL_OES_EGL_sync", gl_extensions) || + _cogl_check_extension ("GL_OES_egl_sync", gl_extensions)) + COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_OES_EGL_SYNC, TRUE); + + if (_cogl_check_extension ("GL_EXT_texture_rg", gl_extensions)) + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_TEXTURE_RG, + TRUE); + + /* Cache features */ + for (i = 0; i < G_N_ELEMENTS (private_features); i++) + context->private_features[i] |= private_features[i]; + context->feature_flags |= flags; + + g_strfreev (gl_extensions); + + return TRUE; +} + +const CoglDriverVtable +_cogl_driver_gles = + { + _cogl_driver_pixel_format_from_gl_internal, + _cogl_driver_pixel_format_to_gl, + _cogl_driver_update_features, + _cogl_offscreen_gl_allocate, + _cogl_offscreen_gl_free, + _cogl_framebuffer_gl_flush_state, + _cogl_framebuffer_gl_clear, + _cogl_framebuffer_gl_query_bits, + _cogl_framebuffer_gl_finish, + _cogl_framebuffer_gl_discard_buffers, + _cogl_framebuffer_gl_draw_attributes, + _cogl_framebuffer_gl_draw_indexed_attributes, + _cogl_framebuffer_gl_read_pixels_into_bitmap, + _cogl_texture_2d_gl_free, + _cogl_texture_2d_gl_can_create, + _cogl_texture_2d_gl_init, + _cogl_texture_2d_gl_allocate, + _cogl_texture_2d_gl_copy_from_framebuffer, + _cogl_texture_2d_gl_get_gl_handle, + _cogl_texture_2d_gl_generate_mipmap, + _cogl_texture_2d_gl_copy_from_bitmap, + NULL, /* texture_2d_get_data */ + _cogl_gl_flush_attributes_state, + _cogl_clip_stack_gl_flush, + _cogl_buffer_gl_create, + _cogl_buffer_gl_destroy, + _cogl_buffer_gl_map_range, + _cogl_buffer_gl_unmap, + _cogl_buffer_gl_set_data, + }; diff --git a/cogl/cogl/driver/gl/gles/cogl-texture-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-texture-driver-gles.c new file mode 100644 index 000000000..f87f1e907 --- /dev/null +++ b/cogl/cogl/driver/gl/gles/cogl-texture-driver-gles.c @@ -0,0 +1,652 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-private.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-pipeline.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-context-private.h" +#include "cogl-object-private.h" +#include "cogl-primitives.h" +#include "cogl-util-gl-private.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" + +#include +#include +#include + +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif +#ifndef GL_MAX_3D_TEXTURE_SIZE_OES +#define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073 +#endif + +/* This extension isn't available for GLES 1.1 so these won't be + defined */ +#ifndef GL_UNPACK_ROW_LENGTH +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#endif +#ifndef GL_UNPACK_SKIP_ROWS +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#endif +#ifndef GL_UNPACK_SKIP_PIXELS +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#endif + +static GLuint +_cogl_texture_driver_gen (CoglContext *ctx, + GLenum gl_target, + CoglPixelFormat internal_format) +{ + GLuint tex; + + GE (ctx, glGenTextures (1, &tex)); + + _cogl_bind_gl_texture_transient (gl_target, tex, FALSE); + + switch (gl_target) + { + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ + GE( ctx, glTexParameteri (gl_target, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR) ); + break; + + default: + g_assert_not_reached(); + } + + return tex; +} + +static void +prep_gl_for_pixels_upload_full (CoglContext *ctx, + int pixels_rowstride, + int pixels_src_x, + int pixels_src_y, + int pixels_bpp) +{ + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE)) + { + GE( ctx, glPixelStorei (GL_UNPACK_ROW_LENGTH, + pixels_rowstride / pixels_bpp) ); + + GE( ctx, glPixelStorei (GL_UNPACK_SKIP_PIXELS, pixels_src_x) ); + GE( ctx, glPixelStorei (GL_UNPACK_SKIP_ROWS, pixels_src_y) ); + } + else + { + g_assert (pixels_src_x == 0); + g_assert (pixels_src_y == 0); + } + + _cogl_texture_gl_prep_alignment_for_pixels_upload (ctx, pixels_rowstride); +} + +static void +_cogl_texture_driver_prep_gl_for_pixels_upload (CoglContext *ctx, + int pixels_rowstride, + int pixels_bpp) +{ + prep_gl_for_pixels_upload_full (ctx, + pixels_rowstride, + 0, 0, /* src_x/y */ + pixels_bpp); +} + +static void +_cogl_texture_driver_prep_gl_for_pixels_download (CoglContext *ctx, + int pixels_rowstride, + int image_width, + int pixels_bpp) +{ + _cogl_texture_gl_prep_alignment_for_pixels_download (ctx, + pixels_bpp, + image_width, + pixels_rowstride); +} + +static CoglBitmap * +prepare_bitmap_alignment_for_upload (CoglContext *ctx, + CoglBitmap *src_bmp, + CoglError **error) +{ + CoglPixelFormat format = cogl_bitmap_get_format (src_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + int src_rowstride = cogl_bitmap_get_rowstride (src_bmp); + int width = cogl_bitmap_get_width (src_bmp); + int alignment = 1; + + if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE) || + src_rowstride == 0) + return cogl_object_ref (src_bmp); + + /* Work out the alignment of the source rowstride */ + alignment = 1 << (_cogl_util_ffs (src_rowstride) - 1); + alignment = MIN (alignment, 8); + + /* If the aligned data equals the rowstride then we can upload from + the bitmap directly using GL_UNPACK_ALIGNMENT */ + if (((width * bpp + alignment - 1) & ~(alignment - 1)) == src_rowstride) + return cogl_object_ref (src_bmp); + /* Otherwise we need to copy the bitmap to pack the alignment + because GLES has no GL_ROW_LENGTH */ + else + return _cogl_bitmap_copy (src_bmp, error); +} + +static CoglBool +_cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx, + CoglTexture *texture, + CoglBool is_foreign, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height, + int level, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + GLenum gl_target; + GLuint gl_handle; + uint8_t *data; + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + CoglBitmap *slice_bmp; + int rowstride; + GLenum gl_error; + CoglBool status = TRUE; + CoglError *internal_error = NULL; + int level_width; + int level_height; + + cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); + + /* If we have the GL_EXT_unpack_subimage extension then we can + upload from subregions directly. Otherwise we may need to copy + the bitmap */ + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE) && + (src_x != 0 || src_y != 0 || + width != cogl_bitmap_get_width (source_bmp) || + height != cogl_bitmap_get_height (source_bmp))) + { + slice_bmp = + _cogl_bitmap_new_with_malloc_buffer (ctx, + width, height, + source_format, + error); + if (!slice_bmp) + return FALSE; + + if (!_cogl_bitmap_copy_subregion (source_bmp, + slice_bmp, + src_x, src_y, + 0, 0, /* dst_x/y */ + width, height, + error)) + { + cogl_object_unref (slice_bmp); + return FALSE; + } + + src_x = src_y = 0; + } + else + { + slice_bmp = prepare_bitmap_alignment_for_upload (ctx, source_bmp, error); + if (!slice_bmp) + return FALSE; + } + + rowstride = cogl_bitmap_get_rowstride (slice_bmp); + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (ctx, rowstride, src_x, src_y, bpp); + + data = _cogl_bitmap_gl_bind (slice_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error); + + /* NB: _cogl_bitmap_gl_bind() may return NULL when successfull so we + * have to explicitly check the cogl error pointer to catch + * problems... */ + if (internal_error) + { + _cogl_propagate_error (error, internal_error); + cogl_object_unref (slice_bmp); + return FALSE; + } + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + _cogl_texture_get_level_size (texture, + level, + &level_width, + &level_height, + NULL); + + if (level_width == width && level_height == height) + { + /* GL gets upset if you use glTexSubImage2D to define the + * contents of a mipmap level so we make sure to use + * glTexImage2D if we are uploading a full mipmap level. + */ + ctx->glTexImage2D (gl_target, + level, + _cogl_texture_gl_get_format (texture), + width, + height, + 0, + source_gl_format, + source_gl_type, + data); + } + else + { + /* GL gets upset if you use glTexSubImage2D to initialize the + * contents of a mipmap level so if this is the first time + * we've seen a request to upload to this level we call + * glTexImage2D first to assert that the storage for this + * level exists. + */ + if (texture->max_level < level) + { + ctx->glTexImage2D (gl_target, + level, + _cogl_texture_gl_get_format (texture), + level_width, + level_height, + 0, + source_gl_format, + source_gl_type, + NULL); + } + + ctx->glTexSubImage2D (gl_target, + level, + dst_x, dst_y, + width, height, + source_gl_format, + source_gl_type, + data); + } + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_bitmap_gl_unbind (slice_bmp); + + cogl_object_unref (slice_bmp); + + return status; +} + +static CoglBool +_cogl_texture_driver_upload_to_gl (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + int rowstride; + int bmp_width = cogl_bitmap_get_width (source_bmp); + int bmp_height = cogl_bitmap_get_height (source_bmp); + CoglBitmap *bmp; + uint8_t *data; + GLenum gl_error; + CoglError *internal_error = NULL; + CoglBool status = TRUE; + + bmp = prepare_bitmap_alignment_for_upload (ctx, source_bmp, error); + if (!bmp) + return FALSE; + + rowstride = cogl_bitmap_get_rowstride (bmp); + + /* Setup gl alignment to match rowstride and top-left corner */ + _cogl_texture_driver_prep_gl_for_pixels_upload (ctx, rowstride, bpp); + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + data = _cogl_bitmap_gl_bind (bmp, + COGL_BUFFER_ACCESS_READ, + 0, /* hints */ + &internal_error); + + /* NB: _cogl_bitmap_gl_bind() may return NULL when successful so we + * have to explicitly check the cogl error pointer to catch + * problems... */ + if (internal_error) + { + cogl_object_unref (bmp); + _cogl_propagate_error (error, internal_error); + return FALSE; + } + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage2D (gl_target, 0, + internal_gl_format, + bmp_width, bmp_height, + 0, + source_gl_format, + source_gl_type, + data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + status = FALSE; + + _cogl_bitmap_gl_unbind (bmp); + + cogl_object_unref (bmp); + + return status; +} + +static CoglBool +_cogl_texture_driver_upload_to_gl_3d (CoglContext *ctx, + GLenum gl_target, + GLuint gl_handle, + CoglBool is_foreign, + GLint height, + GLint depth, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type, + CoglError **error) +{ + CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); + int rowstride = cogl_bitmap_get_rowstride (source_bmp); + int bmp_width = cogl_bitmap_get_width (source_bmp); + int bmp_height = cogl_bitmap_get_height (source_bmp); + uint8_t *data; + GLenum gl_error; + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); + + /* If the rowstride or image height can't be specified with just + GL_ALIGNMENT alone then we need to copy the bitmap because there + is no GL_ROW_LENGTH */ + if (rowstride / bpp != bmp_width || + height != bmp_height / depth) + { + CoglBitmap *bmp; + int image_height = bmp_height / depth; + CoglPixelFormat source_bmp_format = cogl_bitmap_get_format (source_bmp); + int i; + + _cogl_texture_driver_prep_gl_for_pixels_upload (ctx, bmp_width * bpp, bpp); + + /* Initialize the texture with empty data and then upload each + image with a sub-region update */ + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage3D (gl_target, + 0, /* level */ + internal_gl_format, + bmp_width, + height, + depth, + 0, + source_gl_format, + source_gl_type, + NULL); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + return FALSE; + + bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, + bmp_width, + height, + source_bmp_format, + error); + if (!bmp) + return FALSE; + + for (i = 0; i < depth; i++) + { + if (!_cogl_bitmap_copy_subregion (source_bmp, + bmp, + 0, image_height * i, + 0, 0, + bmp_width, + height, + error)) + { + cogl_object_unref (bmp); + return FALSE; + } + + data = _cogl_bitmap_gl_bind (bmp, + COGL_BUFFER_ACCESS_READ, 0, error); + if (!data) + { + cogl_object_unref (bmp); + return FALSE; + } + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexSubImage3D (gl_target, + 0, /* level */ + 0, /* xoffset */ + 0, /* yoffset */ + i, /* zoffset */ + bmp_width, /* width */ + height, /* height */ + 1, /* depth */ + source_gl_format, + source_gl_type, + data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + cogl_object_unref (bmp); + _cogl_bitmap_gl_unbind (bmp); + return FALSE; + } + + _cogl_bitmap_gl_unbind (bmp); + } + + cogl_object_unref (bmp); + } + else + { + data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, error); + if (!data) + return FALSE; + + _cogl_texture_driver_prep_gl_for_pixels_upload (ctx, rowstride, bpp); + + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage3D (gl_target, + 0, /* level */ + internal_gl_format, + bmp_width, + height, + depth, + 0, + source_gl_format, + source_gl_type, + data); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + _cogl_bitmap_gl_unbind (source_bmp); + return FALSE; + } + + _cogl_bitmap_gl_unbind (source_bmp); + } + + return TRUE; +} + +/* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead + * fallback to a generic render + readpixels approach to downloading + * texture data. (See _cogl_texture_draw_and_read() ) */ +static CoglBool +_cogl_texture_driver_gl_get_tex_image (CoglContext *ctx, + GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + uint8_t *dest) +{ + return FALSE; +} + +static CoglBool +_cogl_texture_driver_size_supported_3d (CoglContext *ctx, + GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height, + int depth) +{ + GLint max_size; + + /* GLES doesn't support a proxy texture target so let's at least + check whether the size is greater than + GL_MAX_3D_TEXTURE_SIZE_OES */ + GE( ctx, glGetIntegerv (GL_MAX_3D_TEXTURE_SIZE_OES, &max_size) ); + + return width <= max_size && height <= max_size && depth <= max_size; +} + +static CoglBool +_cogl_texture_driver_size_supported (CoglContext *ctx, + GLenum gl_target, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + GLint max_size; + + /* GLES doesn't support a proxy texture target so let's at least + check whether the size is greater than GL_MAX_TEXTURE_SIZE */ + GE( ctx, glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size) ); + + return width <= max_size && height <= max_size; +} + +static void +_cogl_texture_driver_try_setting_gl_border_color + (CoglContext *ctx, + GLuint gl_target, + const GLfloat *transparent_color) +{ + /* FAIL! */ +} + +static CoglBool +_cogl_texture_driver_allows_foreign_gl_target (CoglContext *ctx, + GLenum gl_target) +{ + /* Allow 2-dimensional textures only */ + if (gl_target != GL_TEXTURE_2D) + return FALSE; + return TRUE; +} + +static CoglPixelFormat +_cogl_texture_driver_find_best_gl_get_data_format + (CoglContext *context, + CoglPixelFormat format, + GLenum *closest_gl_format, + GLenum *closest_gl_type) +{ + /* Find closest format that's supported by GL + (Can't use _cogl_pixel_format_to_gl since available formats + when reading pixels on GLES are severely limited) */ + *closest_gl_format = GL_RGBA; + *closest_gl_type = GL_UNSIGNED_BYTE; + return COGL_PIXEL_FORMAT_RGBA_8888; +} + +const CoglTextureDriver +_cogl_texture_driver_gles = + { + _cogl_texture_driver_gen, + _cogl_texture_driver_prep_gl_for_pixels_upload, + _cogl_texture_driver_upload_subregion_to_gl, + _cogl_texture_driver_upload_to_gl, + _cogl_texture_driver_upload_to_gl_3d, + _cogl_texture_driver_prep_gl_for_pixels_download, + _cogl_texture_driver_gl_get_tex_image, + _cogl_texture_driver_size_supported, + _cogl_texture_driver_size_supported_3d, + _cogl_texture_driver_try_setting_gl_border_color, + _cogl_texture_driver_allows_foreign_gl_target, + _cogl_texture_driver_find_best_gl_get_data_format + }; diff --git a/cogl/cogl/driver/nop/cogl-attribute-nop-private.h b/cogl/cogl/driver/nop/cogl-attribute-nop-private.h new file mode 100644 index 000000000..9c8547dad --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-attribute-nop-private.h @@ -0,0 +1,48 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_ATTRIBUTE_NOP_PRIVATE_H_ +#define _COGL_ATTRIBUTE_NOP_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context-private.h" + +void +_cogl_nop_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglFlushLayerState *layers_state, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes); + +#endif /* _COGL_ATTRIBUTE_NOP_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/nop/cogl-attribute-nop.c b/cogl/cogl/driver/nop/cogl-attribute-nop.c new file mode 100644 index 000000000..5441d5bd4 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-attribute-nop.c @@ -0,0 +1,49 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-types.h" +#include "cogl-framebuffer.h" +#include "cogl-attribute.h" +#include "cogl-attribute-private.h" +#include "cogl-attribute-nop-private.h" + +void +_cogl_nop_flush_attributes_state (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglFlushLayerState *layers_state, + CoglDrawFlags flags, + CoglAttribute **attributes, + int n_attributes) +{ +} diff --git a/cogl/cogl/driver/nop/cogl-clip-stack-nop-private.h b/cogl/cogl/driver/nop/cogl-clip-stack-nop-private.h new file mode 100644 index 000000000..4d8513b06 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-clip-stack-nop-private.h @@ -0,0 +1,44 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_CLIP_STACK_NOP_PRIVATE_H_ +#define _COGL_CLIP_STACK_NOP_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context-private.h" + +void +_cogl_clip_stack_nop_flush (CoglClipStack *stack, + CoglFramebuffer *framebuffer); + +#endif /* _COGL_CLIP_STACK_NOP_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/nop/cogl-clip-stack-nop.c b/cogl/cogl/driver/nop/cogl-clip-stack-nop.c new file mode 100644 index 000000000..314e7e764 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-clip-stack-nop.c @@ -0,0 +1,43 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-clip-stack.h" +#include "cogl-clip-stack-nop-private.h" +#include "cogl-framebuffer-private.h" + +void +_cogl_clip_stack_nop_flush (CoglClipStack *stack, + CoglFramebuffer *framebuffer) +{ +} diff --git a/cogl/cogl/driver/nop/cogl-driver-nop.c b/cogl/cogl/driver/nop/cogl-driver-nop.c new file mode 100644 index 000000000..53f597564 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-driver-nop.c @@ -0,0 +1,86 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-private.h" +#include "cogl-context-private.h" +#include "cogl-feature-private.h" +#include "cogl-renderer-private.h" +#include "cogl-error-private.h" +#include "cogl-framebuffer-nop-private.h" +#include "cogl-texture-2d-nop-private.h" +#include "cogl-attribute-nop-private.h" +#include "cogl-clip-stack-nop-private.h" + +static CoglBool +_cogl_driver_update_features (CoglContext *ctx, + CoglError **error) +{ + /* _cogl_gpu_info_init (ctx, &ctx->gpu); */ + + memset (ctx->private_features, 0, sizeof (ctx->private_features)); + ctx->feature_flags = 0; + + return TRUE; +} + +const CoglDriverVtable +_cogl_driver_nop = + { + NULL, /* pixel_format_from_gl_internal */ + NULL, /* pixel_format_to_gl */ + _cogl_driver_update_features, + _cogl_offscreen_nop_allocate, + _cogl_offscreen_nop_free, + _cogl_framebuffer_nop_flush_state, + _cogl_framebuffer_nop_clear, + _cogl_framebuffer_nop_query_bits, + _cogl_framebuffer_nop_finish, + _cogl_framebuffer_nop_discard_buffers, + _cogl_framebuffer_nop_draw_attributes, + _cogl_framebuffer_nop_draw_indexed_attributes, + _cogl_framebuffer_nop_read_pixels_into_bitmap, + _cogl_texture_2d_nop_free, + _cogl_texture_2d_nop_can_create, + _cogl_texture_2d_nop_init, + _cogl_texture_2d_nop_allocate, + _cogl_texture_2d_nop_copy_from_framebuffer, + _cogl_texture_2d_nop_get_gl_handle, + _cogl_texture_2d_nop_generate_mipmap, + _cogl_texture_2d_nop_copy_from_bitmap, + NULL, /* texture_2d_get_data */ + _cogl_nop_flush_attributes_state, + _cogl_clip_stack_nop_flush, + }; diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h new file mode 100644 index 000000000..1e9630feb --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h @@ -0,0 +1,100 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ +#define _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context-private.h" + +CoglBool +_cogl_offscreen_nop_allocate (CoglOffscreen *offscreen, + CoglError **error); + +void +_cogl_offscreen_nop_free (CoglOffscreen *offscreen); + +void +_cogl_framebuffer_nop_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state); + +void +_cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha); + +void +_cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits); + +void +_cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer); + +void +_cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers); + +void +_cogl_framebuffer_nop_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +void +_cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags); + +CoglBool +_cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error); + +#endif /* _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c new file mode 100644 index 000000000..819078424 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c @@ -0,0 +1,121 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-framebuffer-nop-private.h" + +#include +#include + +void +_cogl_framebuffer_nop_flush_state (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer, + CoglFramebufferState state) +{ +} + +CoglBool +_cogl_offscreen_nop_allocate (CoglOffscreen *offscreen, + CoglError **error) +{ + return TRUE; +} + +void +_cogl_offscreen_nop_free (CoglOffscreen *offscreen) +{ +} + +void +_cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, + unsigned long buffers, + float red, + float green, + float blue, + float alpha) +{ +} + +void +_cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits) +{ + memset (bits, 0, sizeof (CoglFramebufferBits)); +} + +void +_cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer) +{ +} + +void +_cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers) +{ +} + +void +_cogl_framebuffer_nop_draw_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ +} + +void +_cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglVerticesMode mode, + int first_vertex, + int n_vertices, + CoglIndices *indices, + CoglAttribute **attributes, + int n_attributes, + CoglDrawFlags flags) +{ +} + +CoglBool +_cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, + int x, + int y, + CoglReadPixelsFlags source, + CoglBitmap *bitmap, + CoglError **error) +{ + return TRUE; +} diff --git a/cogl/cogl/driver/nop/cogl-texture-2d-nop-private.h b/cogl/cogl/driver/nop/cogl-texture-2d-nop-private.h new file mode 100644 index 000000000..51de1e3c1 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-texture-2d-nop-private.h @@ -0,0 +1,103 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef _COGL_TEXTURE_2D_NOP_PRIVATE_H_ +#define _COGL_TEXTURE_2D_NOP_PRIVATE_H_ + +#include "cogl-types.h" +#include "cogl-context-private.h" +#include "cogl-texture.h" + +void +_cogl_texture_2d_nop_free (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_nop_can_create (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format); + +void +_cogl_texture_2d_nop_init (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_nop_allocate (CoglTexture *tex, + CoglError **error); + +void +_cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter); + +void +_cogl_texture_2d_nop_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p); + +void +_cogl_texture_2d_nop_copy_from_framebuffer (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level); + +unsigned int +_cogl_texture_2d_nop_get_gl_handle (CoglTexture2D *tex_2d); + +void +_cogl_texture_2d_nop_generate_mipmap (CoglTexture2D *tex_2d); + +CoglBool +_cogl_texture_2d_nop_copy_from_bitmap (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bitmap, + int dst_x, + int dst_y, + int level, + CoglError **error); + +void +_cogl_texture_2d_nop_get_data (CoglTexture2D *tex_2d, + CoglPixelFormat format, + size_t rowstride, + uint8_t *data); + +#endif /* _COGL_TEXTURE_2D_NOP_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/nop/cogl-texture-2d-nop.c b/cogl/cogl/driver/nop/cogl-texture-2d-nop.c new file mode 100644 index 000000000..b52149831 --- /dev/null +++ b/cogl/cogl/driver/nop/cogl-texture-2d-nop.c @@ -0,0 +1,132 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009,2010,2011,2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-private.h" +#include "cogl-texture-2d-nop-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-error-private.h" + +void +_cogl_texture_2d_nop_free (CoglTexture2D *tex_2d) +{ +} + +CoglBool +_cogl_texture_2d_nop_can_create (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format) +{ + return TRUE; +} + +void +_cogl_texture_2d_nop_init (CoglTexture2D *tex_2d) +{ +} + +CoglBool +_cogl_texture_2d_nop_allocate (CoglTexture *tex, + CoglError **error) +{ + return TRUE; +} + +void +_cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ +} + +void +_cogl_texture_2d_nop_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ +} + +void +_cogl_texture_2d_nop_copy_from_framebuffer (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglFramebuffer *src_fb, + int dst_x, + int dst_y, + int level) +{ +} + +unsigned int +_cogl_texture_2d_nop_get_gl_handle (CoglTexture2D *tex_2d) +{ + return 0; +} + +void +_cogl_texture_2d_nop_generate_mipmap (CoglTexture2D *tex_2d) +{ +} + +CoglBool +_cogl_texture_2d_nop_copy_from_bitmap (CoglTexture2D *tex_2d, + int src_x, + int src_y, + int width, + int height, + CoglBitmap *bitmap, + int dst_x, + int dst_y, + int level, + CoglError **error) +{ + return TRUE; +} + +void +_cogl_texture_2d_nop_get_data (CoglTexture2D *tex_2d, + CoglPixelFormat format, + size_t rowstride, + uint8_t *data) +{ +} diff --git a/cogl/cogl/gl-prototypes/cogl-all-functions.h b/cogl/cogl/gl-prototypes/cogl-all-functions.h new file mode 100644 index 000000000..7ac9022b7 --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-all-functions.h @@ -0,0 +1,328 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +/* The functions in this file are part of the core GL,GLES1 and GLES2 apis */ +#include "cogl-core-functions.h" + +/* The functions in this file are core to GLES1 only but may also be + * extensions available for GLES2 and GL */ +#include "cogl-in-gles1-core-functions.h" + +/* The functions in this file are core to GLES2 only but + * may be extensions for GLES1 and GL */ +#include "cogl-in-gles2-core-functions.h" + +/* The functions in this file are core to GLES1 and GLES2 but not core + * to GL but they may be extensions available for GL */ +#include "cogl-in-gles-core-functions.h" + +/* These are fixed-function APIs core to GL and GLES1 */ +#include "cogl-fixed-functions.h" + +/* These are GLSL shader APIs core to GL 2.0 and GLES2 */ +#include "cogl-glsl-functions.h" + +/* These are the core GL functions which are only available in big + GL */ +COGL_EXT_BEGIN (only_in_big_gl, + 0, 0, + 0, /* not in GLES */ + "\0", + "\0") +COGL_EXT_FUNCTION (void, glGetTexLevelParameteriv, + (GLenum target, GLint level, + GLenum pname, GLint *params)) +COGL_EXT_FUNCTION (void, glGetTexImage, + (GLenum target, GLint level, + GLenum format, GLenum type, + GLvoid *pixels)) +COGL_EXT_FUNCTION (void, glClipPlane, + (GLenum plane, const double *equation)) +COGL_EXT_FUNCTION (void, glDepthRange, + (double near_val, double far_val)) +COGL_EXT_FUNCTION (void, glDrawBuffer, + (GLenum mode)) +COGL_EXT_END () + + +/* GLES doesn't support mapping buffers in core so this has to be a + separate check */ +COGL_EXT_BEGIN (map_vbos, 1, 5, + 0, /* not in GLES core */ + "ARB\0OES\0", + "vertex_buffer_object\0mapbuffer\0") +COGL_EXT_FUNCTION (void *, glMapBuffer, + (GLenum target, + GLenum access)) +COGL_EXT_FUNCTION (GLboolean, glUnmapBuffer, + (GLenum target)) +COGL_EXT_END () + +COGL_EXT_BEGIN (texture_3d, 1, 2, + 0, /* not in either GLES */ + "OES\0", + "texture_3D\0") +COGL_EXT_FUNCTION (void, glTexImage3D, + (GLenum target, GLint level, + GLint internalFormat, + GLsizei width, GLsizei height, + GLsizei depth, GLint border, + GLenum format, GLenum type, + const GLvoid *pixels)) +COGL_EXT_FUNCTION (void, glTexSubImage3D, + (GLenum target, GLint level, + GLint xoffset, GLint yoffset, + GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth, + GLenum format, + GLenum type, const GLvoid *pixels)) +COGL_EXT_END () + + + +COGL_EXT_BEGIN (offscreen_blit, 3, 0, + 0, /* not in either GLES */ + "EXT\0ANGLE\0", + "framebuffer_blit\0") +COGL_EXT_FUNCTION (void, glBlitFramebuffer, + (GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter)) +COGL_EXT_END () + +/* ARB_fragment_program */ +COGL_EXT_BEGIN (arbfp, 255, 255, + 0, /* not in either GLES */ + "ARB\0", + "fragment_program\0") +COGL_EXT_FUNCTION (void, glGenPrograms, + (GLsizei n, + GLuint *programs)) +COGL_EXT_FUNCTION (void, glDeletePrograms, + (GLsizei n, + GLuint *programs)) +COGL_EXT_FUNCTION (void, glBindProgram, + (GLenum target, + GLuint program)) +COGL_EXT_FUNCTION (void, glProgramString, + (GLenum target, + GLenum format, + GLsizei len, + const void *program)) +COGL_EXT_FUNCTION (void, glProgramLocalParameter4fv, + (GLenum target, + GLuint index, + GLfloat *params)) +COGL_EXT_END () + +COGL_EXT_BEGIN (EGL_image, 255, 255, + 0, /* not in either GLES */ + "OES\0", + "EGL_image\0") +COGL_EXT_FUNCTION (void, glEGLImageTargetTexture2D, + (GLenum target, + GLeglImageOES image)) +COGL_EXT_FUNCTION (void, glEGLImageTargetRenderbufferStorage, + (GLenum target, + GLeglImageOES image)) +COGL_EXT_END () + +COGL_EXT_BEGIN (framebuffer_discard, 255, 255, + 0, /* not in either GLES */ + "EXT\0", + "framebuffer_discard\0") +COGL_EXT_FUNCTION (void, glDiscardFramebuffer, + (GLenum target, + GLsizei numAttachments, + const GLenum *attachments)) +COGL_EXT_END () + +COGL_EXT_BEGIN (IMG_multisampled_render_to_texture, 255, 255, + 0, /* not in either GLES */ + "\0", + "IMG_multisampled_render_to_texture\0") +COGL_EXT_FUNCTION (void, glRenderbufferStorageMultisampleIMG, + (GLenum target, + GLsizei samples, + GLenum internal_format, + GLsizei width, + GLsizei height)) +COGL_EXT_FUNCTION (void, glFramebufferTexture2DMultisampleIMG, + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples)) +COGL_EXT_END () + +COGL_EXT_BEGIN (ARB_sampler_objects, 3, 3, + 0, /* not in either GLES */ + "ARB:\0", + "sampler_objects\0") +COGL_EXT_FUNCTION (void, glGenSamplers, + (GLsizei count, + GLuint *samplers)) +COGL_EXT_FUNCTION (void, glDeleteSamplers, + (GLsizei count, + const GLuint *samplers)) +COGL_EXT_FUNCTION (void, glBindSampler, + (GLuint unit, + GLuint sampler)) +COGL_EXT_FUNCTION (void, glSamplerParameteri, + (GLuint sampler, + GLenum pname, + GLint param)) +COGL_EXT_END () + +/* These only list functions that come from the old GLSL extensions. + * Functions that are common to the extensions and GLSL 2.0 should + * instead be listed in cogl-glsl-functions.h */ +COGL_EXT_BEGIN (shader_objects, 255, 255, + 0, /* not in either GLES */ + "ARB\0", + "shader_objects\0") +COGL_EXT_FUNCTION (GLuint, glCreateProgramObject, + (void)) +COGL_EXT_FUNCTION (GLuint, glCreateShaderObject, + (GLenum shaderType)) +COGL_EXT_FUNCTION (void, glDeleteObject, + (GLuint obj)) +COGL_EXT_FUNCTION (void, glAttachObject, + (GLuint container, GLuint obj)) +COGL_EXT_FUNCTION (void, glUseProgramObject, + (GLuint programObj)) +COGL_EXT_FUNCTION (void, glGetInfoLog, + (GLuint obj, + GLsizei maxLength, + GLsizei *length, + char *infoLog)) +COGL_EXT_FUNCTION (void, glGetObjectParameteriv, + (GLuint obj, + GLenum pname, + GLint *params)) +COGL_EXT_FUNCTION (void, glDetachObject, + (GLuint container, GLuint obj)) +COGL_EXT_FUNCTION (void, glGetAttachedObjects, + (GLuint program, + GLsizei maxcount, + GLsizei* count, + GLuint* shaders)) +COGL_EXT_END () + +COGL_EXT_BEGIN (only_gl3, 3, 0, + 0, /* not in either GLES */ + "\0", + "\0") +COGL_EXT_FUNCTION (const GLubyte *, glGetStringi, + (GLenum name, GLuint index)) +COGL_EXT_END () + +COGL_EXT_BEGIN (vertex_array_object, 3, 0, + 0, /* not in either GLES */ + "ARB\0OES\0", + "vertex_array_object\0") +COGL_EXT_FUNCTION (void, glBindVertexArray, + (GLuint array)) +COGL_EXT_FUNCTION (void, glDeleteVertexArrays, + (GLsizei n, + const GLuint *arrays)) +COGL_EXT_FUNCTION (void, glGenVertexArrays, + (GLsizei n, + GLuint *arrays)) +COGL_EXT_END () + +COGL_EXT_BEGIN (map_region, 3, 0, + COGL_EXT_IN_GLES3, + "ARB:\0", + "map_buffer_range\0") +COGL_EXT_FUNCTION (GLvoid *, glMapBufferRange, + (GLenum target, + GLintptr offset, + GLsizeiptr length, + GLbitfield access)) +COGL_EXT_END () + +#ifdef GL_ARB_sync +COGL_EXT_BEGIN (sync, 3, 2, + 0, /* not in either GLES */ + "ARB:\0", + "sync\0") +COGL_EXT_FUNCTION (GLsync, glFenceSync, + (GLenum condition, GLbitfield flags)) +COGL_EXT_FUNCTION (GLenum, glClientWaitSync, + (GLsync sync, GLbitfield flags, GLuint64 timeout)) +COGL_EXT_FUNCTION (void, glDeleteSync, + (GLsync sync)) +COGL_EXT_END () +#endif + +COGL_EXT_BEGIN (draw_buffers, 2, 0, + COGL_EXT_IN_GLES3, + "ARB\0EXT\0", + "draw_buffers\0") +COGL_EXT_FUNCTION (void, glDrawBuffers, + (GLsizei n, const GLenum *bufs)) +COGL_EXT_END () diff --git a/cogl/cogl/gl-prototypes/cogl-core-functions.h b/cogl/cogl/gl-prototypes/cogl-core-functions.h new file mode 100644 index 000000000..f37041b7b --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-core-functions.h @@ -0,0 +1,198 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +/* These are the core GL functions which we assume will always be + available */ +COGL_EXT_BEGIN (core, + 0, 0, + COGL_EXT_IN_GLES | COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glBindTexture, + (GLenum target, GLuint texture)) +COGL_EXT_FUNCTION (void, glBlendFunc, + (GLenum sfactor, GLenum dfactor)) +COGL_EXT_FUNCTION (void, glClear, + (GLbitfield mask)) +COGL_EXT_FUNCTION (void, glClearColor, + (GLclampf red, + GLclampf green, + GLclampf blue, + GLclampf alpha)) +COGL_EXT_FUNCTION (void, glClearStencil, + (GLint s)) +COGL_EXT_FUNCTION (void, glColorMask, + (GLboolean red, + GLboolean green, + GLboolean blue, + GLboolean alpha)) +COGL_EXT_FUNCTION (void, glCopyTexSubImage2D, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height)) +COGL_EXT_FUNCTION (void, glDeleteTextures, + (GLsizei n, const GLuint* textures)) +COGL_EXT_FUNCTION (void, glDepthFunc, + (GLenum func)) +COGL_EXT_FUNCTION (void, glDepthMask, + (GLboolean flag)) +COGL_EXT_FUNCTION (void, glDisable, + (GLenum cap)) +COGL_EXT_FUNCTION (void, glDrawArrays, + (GLenum mode, GLint first, GLsizei count)) +COGL_EXT_FUNCTION (void, glDrawElements, + (GLenum mode, + GLsizei count, + GLenum type, + const GLvoid* indices)) +COGL_EXT_FUNCTION (void, glEnable, + (GLenum cap)) +COGL_EXT_FUNCTION (void, glFinish, + (void)) +COGL_EXT_FUNCTION (void, glFlush, + (void)) +COGL_EXT_FUNCTION (void, glFrontFace, + (GLenum mode)) +COGL_EXT_FUNCTION (void, glCullFace, + (GLenum mode)) +COGL_EXT_FUNCTION (void, glGenTextures, + (GLsizei n, GLuint* textures)) +COGL_EXT_FUNCTION (GLenum, glGetError, + (void)) +COGL_EXT_FUNCTION (void, glGetIntegerv, + (GLenum pname, GLint* params)) +COGL_EXT_FUNCTION (void, glGetBooleanv, + (GLenum pname, GLboolean* params)) +COGL_EXT_FUNCTION (void, glGetFloatv, + (GLenum pname, GLfloat* params)) +COGL_EXT_FUNCTION (const GLubyte*, glGetString, + (GLenum name)) +COGL_EXT_FUNCTION (void, glHint, + (GLenum target, GLenum mode)) +COGL_EXT_FUNCTION (GLboolean, glIsTexture, + (GLuint texture)) +COGL_EXT_FUNCTION (void, glPixelStorei, + (GLenum pname, GLint param)) +COGL_EXT_FUNCTION (void, glReadPixels, + (GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid* pixels)) +COGL_EXT_FUNCTION (void, glScissor, + (GLint x, GLint y, GLsizei width, GLsizei height)) +COGL_EXT_FUNCTION (void, glStencilFunc, + (GLenum func, GLint ref, GLuint mask)) +COGL_EXT_FUNCTION (void, glStencilMask, + (GLuint mask)) +COGL_EXT_FUNCTION (void, glStencilOp, + (GLenum fail, GLenum zfail, GLenum zpass)) +COGL_EXT_FUNCTION (void, glTexImage2D, + (GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const GLvoid* pixels)) +COGL_EXT_FUNCTION (void, glTexParameterf, + (GLenum target, GLenum pname, GLfloat param)) +COGL_EXT_FUNCTION (void, glTexParameterfv, + (GLenum target, GLenum pname, const GLfloat* params)) +COGL_EXT_FUNCTION (void, glTexParameteri, + (GLenum target, GLenum pname, GLint param)) +COGL_EXT_FUNCTION (void, glTexParameteriv, + (GLenum target, GLenum pname, const GLint* params)) +COGL_EXT_FUNCTION (void, glGetTexParameterfv, + (GLenum target, GLenum pname, GLfloat* params)) +COGL_EXT_FUNCTION (void, glGetTexParameteriv, + (GLenum target, GLenum pname, GLint* params)) +COGL_EXT_FUNCTION (void, glTexSubImage2D, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const GLvoid* pixels)) +COGL_EXT_FUNCTION (void, glCopyTexImage2D, + (GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border)) +COGL_EXT_FUNCTION (void, glViewport, + (GLint x, GLint y, GLsizei width, GLsizei height)) +COGL_EXT_FUNCTION (GLboolean, glIsEnabled, (GLenum cap)) +COGL_EXT_FUNCTION (void, glLineWidth, (GLfloat width)) +COGL_EXT_FUNCTION (void, glPolygonOffset, (GLfloat factor, GLfloat units)) +COGL_EXT_END () diff --git a/cogl/cogl/gl-prototypes/cogl-fixed-functions.h b/cogl/cogl/gl-prototypes/cogl-fixed-functions.h new file mode 100644 index 000000000..ce7b4e0de --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-fixed-functions.h @@ -0,0 +1,119 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +/* These are the core GL functions which are available when the API + supports fixed-function (ie, GL and GLES1.1) */ +COGL_EXT_BEGIN (fixed_function_core, + 0, 0, + COGL_EXT_IN_GLES, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glAlphaFunc, + (GLenum func, GLclampf ref)) +COGL_EXT_FUNCTION (void, glFogf, + (GLenum pname, GLfloat param)) +COGL_EXT_FUNCTION (void, glFogfv, + (GLenum pname, const GLfloat *params)) +COGL_EXT_FUNCTION (void, glLoadMatrixf, + (const GLfloat *m)) +COGL_EXT_FUNCTION (void, glMaterialfv, + (GLenum face, GLenum pname, const GLfloat *params)) +COGL_EXT_FUNCTION (void, glPointSize, + (GLfloat size)) +COGL_EXT_FUNCTION (void, glTexEnvfv, + (GLenum target, GLenum pname, const GLfloat *params)) +COGL_EXT_FUNCTION (void, glColor4ub, + (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)) +COGL_EXT_FUNCTION (void, glColor4f, + (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) +COGL_EXT_FUNCTION (void, glColorPointer, + (GLint size, + GLenum type, + GLsizei stride, + const GLvoid *pointer)) +COGL_EXT_FUNCTION (void, glDisableClientState, + (GLenum array)) +COGL_EXT_FUNCTION (void, glEnableClientState, + (GLenum array)) +COGL_EXT_FUNCTION (void, glLoadIdentity, + (void)) +COGL_EXT_FUNCTION (void, glMatrixMode, + (GLenum mode)) +COGL_EXT_FUNCTION (void, glNormal3f, + (GLfloat x, GLfloat y, GLfloat z)) +COGL_EXT_FUNCTION (void, glNormalPointer, + (GLenum type, GLsizei stride, const GLvoid *pointer)) +COGL_EXT_FUNCTION (void, glMultiTexCoord4f, + (GLfloat s, GLfloat t, GLfloat r, GLfloat q)) +COGL_EXT_FUNCTION (void, glTexCoordPointer, + (GLint size, + GLenum type, + GLsizei stride, + const GLvoid *pointer)) +COGL_EXT_FUNCTION (void, glTexEnvi, + (GLenum target, + GLenum pname, + GLint param)) +COGL_EXT_FUNCTION (void, glVertex4f, + (GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +COGL_EXT_FUNCTION (void, glVertexPointer, + (GLint size, + GLenum type, + GLsizei stride, + const GLvoid *pointer)) +COGL_EXT_END () diff --git a/cogl/cogl/gl-prototypes/cogl-gles1-functions.h b/cogl/cogl/gl-prototypes/cogl-gles1-functions.h new file mode 100644 index 000000000..774b9b33b --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-gles1-functions.h @@ -0,0 +1,43 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* The functions in this file are part of the core GL,GLES1 and GLES2 apis */ +#include "cogl-core-functions.h" + +/* The functions in this file are core to GLES1 and GLES2 but not core + * to GL but they may be extensions available for GL */ +#include "cogl-in-gles-core-functions.h" + +/* The functions in this file are core to GLES1 only but + * may be extensions for GLES2 and GL */ +#include "cogl-in-gles1-core-functions.h" + +/* These are fixed-function APIs core to GL and GLES1 */ +#include "cogl-fixed-functions.h" diff --git a/cogl/cogl/gl-prototypes/cogl-gles2-functions.h b/cogl/cogl/gl-prototypes/cogl-gles2-functions.h new file mode 100644 index 000000000..aeb57a487 --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-gles2-functions.h @@ -0,0 +1,43 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* The functions in this file are part of the core GL,GLES1 and GLES2 apis */ +#include "cogl-core-functions.h" + +/* The functions in this file are core to GLES1 and GLES2 but not core + * to GL but they may be extensions available for GL */ +#include "cogl-in-gles-core-functions.h" + +/* The functions in this file are core to GLES2 only but + * may be extensions for GLES1 and GL */ +#include "cogl-in-gles2-core-functions.h" + +/* These are APIs for using GLSL used by GL and GLES2 */ +#include "cogl-glsl-functions.h" diff --git a/cogl/cogl/gl-prototypes/cogl-glsl-functions.h b/cogl/cogl/gl-prototypes/cogl-glsl-functions.h new file mode 100644 index 000000000..980f8adf0 --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-glsl-functions.h @@ -0,0 +1,286 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +/* This lists functions that are unique to GL 2.0 or GLES 2.0 and are + * not in the old GLSL extensions */ +COGL_EXT_BEGIN (shaders_glsl_2_only, 2, 0, + COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (GLuint, glCreateProgram, + (void)) +COGL_EXT_FUNCTION (GLuint, glCreateShader, + (GLenum shaderType)) +COGL_EXT_FUNCTION (void, glDeleteShader, + (GLuint shader)) +COGL_EXT_FUNCTION (void, glAttachShader, + (GLuint program, + GLuint shader)) +COGL_EXT_FUNCTION (void, glUseProgram, + (GLuint program)) +COGL_EXT_FUNCTION (void, glDeleteProgram, + (GLuint program)) +COGL_EXT_FUNCTION (void, glGetShaderInfoLog, + (GLuint shader, + GLsizei maxLength, + GLsizei *length, + char *infoLog)) +COGL_EXT_FUNCTION (void, glGetProgramInfoLog, + (GLuint program, + GLsizei bufSize, + GLsizei *length, + char *infoLog)) +COGL_EXT_FUNCTION (void, glGetShaderiv, + (GLuint shader, + GLenum pname, + GLint *params)) +COGL_EXT_FUNCTION (void, glGetProgramiv, + (GLuint program, + GLenum pname, + GLint *params)) +COGL_EXT_FUNCTION (void, glDetachShader, + (GLuint program, GLuint shader)) +COGL_EXT_FUNCTION (void, glGetAttachedShaders, + (GLuint program, + GLsizei maxcount, + GLsizei* count, + GLuint* shaders)) +COGL_EXT_FUNCTION (GLboolean, glIsShader, + (GLuint shader)) +COGL_EXT_FUNCTION (GLboolean, glIsProgram, + (GLuint program)) +COGL_EXT_END () + +/* These functions are provided by GL_ARB_shader_objects or are in GL + * 2.0 core */ +COGL_EXT_BEGIN (shader_objects_or_gl2, 2, 0, + COGL_EXT_IN_GLES2, + "ARB\0", + "shader_objects\0") +COGL_EXT_FUNCTION (void, glShaderSource, + (GLuint shader, + GLsizei count, + const char * const *string, + const GLint *length)) +COGL_EXT_FUNCTION (void, glCompileShader, + (GLuint shader)) +COGL_EXT_FUNCTION (void, glLinkProgram, + (GLuint program)) +COGL_EXT_FUNCTION (GLint, glGetUniformLocation, + (GLuint program, + const char *name)) +COGL_EXT_FUNCTION (void, glUniform1f, + (GLint location, + GLfloat v0)) +COGL_EXT_FUNCTION (void, glUniform2f, + (GLint location, + GLfloat v0, + GLfloat v1)) +COGL_EXT_FUNCTION (void, glUniform3f, + (GLint location, + GLfloat v0, + GLfloat v1, + GLfloat v2)) +COGL_EXT_FUNCTION (void, glUniform4f, + (GLint location, + GLfloat v0, + GLfloat v1, + GLfloat v2, + GLfloat v3)) +COGL_EXT_FUNCTION (void, glUniform1fv, + (GLint location, + GLsizei count, + const GLfloat * value)) +COGL_EXT_FUNCTION (void, glUniform2fv, + (GLint location, + GLsizei count, + const GLfloat * value)) +COGL_EXT_FUNCTION (void, glUniform3fv, + (GLint location, + GLsizei count, + const GLfloat * value)) +COGL_EXT_FUNCTION (void, glUniform4fv, + (GLint location, + GLsizei count, + const GLfloat * value)) +COGL_EXT_FUNCTION (void, glUniform1i, + (GLint location, + GLint v0)) +COGL_EXT_FUNCTION (void, glUniform2i, + (GLint location, + GLint v0, + GLint v1)) +COGL_EXT_FUNCTION (void, glUniform3i, + (GLint location, + GLint v0, + GLint v1, + GLint v2)) +COGL_EXT_FUNCTION (void, glUniform4i, + (GLint location, + GLint v0, + GLint v1, + GLint v2, + GLint v3)) +COGL_EXT_FUNCTION (void, glUniform1iv, + (GLint location, + GLsizei count, + const GLint * value)) +COGL_EXT_FUNCTION (void, glUniform2iv, + (GLint location, + GLsizei count, + const GLint * value)) +COGL_EXT_FUNCTION (void, glUniform3iv, + (GLint location, + GLsizei count, + const GLint * value)) +COGL_EXT_FUNCTION (void, glUniform4iv, + (GLint location, + GLsizei count, + const GLint * value)) +COGL_EXT_FUNCTION (void, glUniformMatrix2fv, + (GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value)) +COGL_EXT_FUNCTION (void, glUniformMatrix3fv, + (GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value)) +COGL_EXT_FUNCTION (void, glUniformMatrix4fv, + (GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value)) + +COGL_EXT_FUNCTION (void, glGetUniformfv, + (GLuint program, + GLint location, + GLfloat *params)) +COGL_EXT_FUNCTION (void, glGetUniformiv, + (GLuint program, + GLint location, + GLint *params)) +COGL_EXT_FUNCTION (void, glGetActiveUniform, + (GLuint program, + GLuint index, + GLsizei bufsize, + GLsizei* length, + GLint* size, + GLenum* type, + GLchar* name)) +COGL_EXT_FUNCTION (void, glGetShaderSource, + (GLuint shader, + GLsizei bufsize, + GLsizei* length, + GLchar* source)) +COGL_EXT_FUNCTION (void, glValidateProgram, (GLuint program)) +COGL_EXT_END () + +/* These functions are provided by GL_ARB_vertex_shader or are in GL + * 2.0 core */ +COGL_EXT_BEGIN (vertex_shaders, 2, 0, + COGL_EXT_IN_GLES2, + "ARB\0", + "vertex_shader\0") +COGL_EXT_FUNCTION (void, glVertexAttribPointer, + (GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer)) +COGL_EXT_FUNCTION (void, glEnableVertexAttribArray, + (GLuint index)) +COGL_EXT_FUNCTION (void, glDisableVertexAttribArray, + (GLuint index)) +COGL_EXT_FUNCTION (void, glVertexAttrib1f, (GLuint indx, GLfloat x)) +COGL_EXT_FUNCTION (void, glVertexAttrib1fv, + (GLuint indx, const GLfloat* values)) +COGL_EXT_FUNCTION (void, glVertexAttrib2f, (GLuint indx, GLfloat x, GLfloat y)) +COGL_EXT_FUNCTION (void, glVertexAttrib2fv, + (GLuint indx, const GLfloat* values)) +COGL_EXT_FUNCTION (void, glVertexAttrib3f, + (GLuint indx, GLfloat x, GLfloat y, GLfloat z)) +COGL_EXT_FUNCTION (void, glVertexAttrib3fv, + (GLuint indx, const GLfloat* values)) +COGL_EXT_FUNCTION (void, glVertexAttrib4f, + (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +COGL_EXT_FUNCTION (void, glVertexAttrib4fv, + (GLuint indx, const GLfloat* values)) +COGL_EXT_FUNCTION (void, glGetVertexAttribfv, + (GLuint index, GLenum pname, GLfloat* params)) +COGL_EXT_FUNCTION (void, glGetVertexAttribiv, + (GLuint index, GLenum pname, GLint* params)) +COGL_EXT_FUNCTION (void, glGetVertexAttribPointerv, + (GLuint index, GLenum pname, GLvoid** pointer)) +COGL_EXT_FUNCTION (GLint, glGetAttribLocation, + (GLuint program, const char *name)) +COGL_EXT_FUNCTION (void, glBindAttribLocation, + (GLuint program, + GLuint index, + const GLchar* name)) +COGL_EXT_FUNCTION (void, glGetActiveAttrib, + (GLuint program, + GLuint index, + GLsizei bufsize, + GLsizei* length, + GLint* size, + GLenum* type, + GLchar* name)) +COGL_EXT_END () diff --git a/cogl/cogl/gl-prototypes/cogl-in-gles-core-functions.h b/cogl/cogl/gl-prototypes/cogl-in-gles-core-functions.h new file mode 100644 index 000000000..5679e7221 --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-in-gles-core-functions.h @@ -0,0 +1,148 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +COGL_EXT_BEGIN (only_in_both_gles, + 4, 1, + COGL_EXT_IN_GLES | + COGL_EXT_IN_GLES2, + "ARB\0", + "ES2_compatibility\0") +COGL_EXT_FUNCTION (void, glDepthRangef, + (GLfloat near_val, GLfloat far_val)) +COGL_EXT_FUNCTION (void, glClearDepthf, + (GLclampf depth)) +COGL_EXT_END () + +COGL_EXT_BEGIN (only_in_both_gles_and_gl_1_3, + 1, 3, + COGL_EXT_IN_GLES | + COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glCompressedTexImage2D, + (GLenum target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLsizei imageSize, + const GLvoid* data)) +COGL_EXT_FUNCTION (void, glCompressedTexSubImage2D, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLsizei imageSize, + const GLvoid* data)) +COGL_EXT_FUNCTION (void, glSampleCoverage, + (GLclampf value, GLboolean invert)) +COGL_EXT_END () + +COGL_EXT_BEGIN (only_in_both_gles_and_gl_1_5, + 1, 5, + COGL_EXT_IN_GLES | + COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glGetBufferParameteriv, + (GLenum target, GLenum pname, GLint* params)) +COGL_EXT_END () + +COGL_EXT_BEGIN (vbos, 1, 5, + COGL_EXT_IN_GLES | + COGL_EXT_IN_GLES2, + "ARB\0", + "vertex_buffer_object\0") +COGL_EXT_FUNCTION (void, glGenBuffers, + (GLsizei n, + GLuint *buffers)) +COGL_EXT_FUNCTION (void, glBindBuffer, + (GLenum target, + GLuint buffer)) +COGL_EXT_FUNCTION (void, glBufferData, + (GLenum target, + GLsizeiptr size, + const GLvoid *data, + GLenum usage)) +COGL_EXT_FUNCTION (void, glBufferSubData, + (GLenum target, + GLintptr offset, + GLsizeiptr size, + const GLvoid *data)) +COGL_EXT_FUNCTION (void, glDeleteBuffers, + (GLsizei n, + const GLuint *buffers)) +COGL_EXT_FUNCTION (GLboolean, glIsBuffer, + (GLuint buffer)) +COGL_EXT_END () + +/* Available in GL 1.3, the multitexture extension or GLES. These are + required */ +COGL_EXT_BEGIN (multitexture_part0, 1, 3, + COGL_EXT_IN_GLES | + COGL_EXT_IN_GLES2, + "ARB\0", + "multitexture\0") +COGL_EXT_FUNCTION (void, glActiveTexture, + (GLenum texture)) +COGL_EXT_END () + diff --git a/cogl/cogl/gl-prototypes/cogl-in-gles1-core-functions.h b/cogl/cogl/gl-prototypes/cogl-in-gles1-core-functions.h new file mode 100644 index 000000000..20dc1a81f --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-in-gles1-core-functions.h @@ -0,0 +1,78 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +/* These functions are only available in GLES and are used as + replacements for some GL equivalents that only accept double + arguments */ +COGL_EXT_BEGIN (only_in_gles1, + 255, 255, + COGL_EXT_IN_GLES, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glClipPlanef, + (GLenum plane, const GLfloat *equation)) +COGL_EXT_END () + +COGL_EXT_BEGIN (multitexture_part1, 1, 3, + COGL_EXT_IN_GLES, + "ARB\0", + "multitexture\0") +COGL_EXT_FUNCTION (void, glClientActiveTexture, + (GLenum texture)) +COGL_EXT_END () + diff --git a/cogl/cogl/gl-prototypes/cogl-in-gles2-core-functions.h b/cogl/cogl/gl-prototypes/cogl-in-gles2-core-functions.h new file mode 100644 index 000000000..1e2f79ea4 --- /dev/null +++ b/cogl/cogl/gl-prototypes/cogl-in-gles2-core-functions.h @@ -0,0 +1,186 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This is included multiple times with different definitions for + * these macros. The macros are given the following arguments: + * + * COGL_EXT_BEGIN: + * + * @name: a unique symbol name for this feature + * + * @min_gl_major: the major part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * @min_gl_minor: the minor part of the minimum GL version where these + * functions are available in core, or 255 if it isn't available in + * any version. + * + * @gles_availability: flags to specify which versions of GLES the + * functions are available in. Should be a combination of + * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. + * + * @extension_suffixes: A zero-separated list of suffixes in a + * string. These are appended to the extension name to get a complete + * extension name to try. The suffix is also appended to all of the + * function names. The suffix can optionally include a ':' to specify + * an alternate suffix for the function names. + * + * @extension_names: A list of extension names to try. If any of these + * extensions match then it will be used. + */ + +COGL_EXT_BEGIN (offscreen, + 3, 0, + COGL_EXT_IN_GLES2, + /* for some reason the ARB version of this + extension doesn't have an ARB suffix for the + functions */ + "ARB:\0EXT\0OES\0", + "framebuffer_object\0") +COGL_EXT_FUNCTION (void, glGenRenderbuffers, + (GLsizei n, + GLuint *renderbuffers)) +COGL_EXT_FUNCTION (void, glDeleteRenderbuffers, + (GLsizei n, + const GLuint *renderbuffers)) +COGL_EXT_FUNCTION (void, glBindRenderbuffer, + (GLenum target, + GLuint renderbuffer)) +COGL_EXT_FUNCTION (void, glRenderbufferStorage, + (GLenum target, + GLenum internalformat, + GLsizei width, + GLsizei height)) +COGL_EXT_FUNCTION (void, glGenFramebuffers, + (GLsizei n, + GLuint *framebuffers)) +COGL_EXT_FUNCTION (void, glBindFramebuffer, + (GLenum target, + GLuint framebuffer)) +COGL_EXT_FUNCTION (void, glFramebufferTexture2D, + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level)) +COGL_EXT_FUNCTION (void, glFramebufferRenderbuffer, + (GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer)) +COGL_EXT_FUNCTION (GLboolean, glIsRenderbuffer, + (GLuint renderbuffer)) +COGL_EXT_FUNCTION (GLenum, glCheckFramebufferStatus, + (GLenum target)) +COGL_EXT_FUNCTION (void, glDeleteFramebuffers, + (GLsizei n, + const GLuint *framebuffers)) +COGL_EXT_FUNCTION (void, glGenerateMipmap, + (GLenum target)) +COGL_EXT_FUNCTION (void, glGetFramebufferAttachmentParameteriv, + (GLenum target, + GLenum attachment, + GLenum pname, + GLint *params)) +COGL_EXT_FUNCTION (void, glGetRenderbufferParameteriv, + (GLenum target, + GLenum pname, + GLint *params)) +COGL_EXT_FUNCTION (GLboolean, glIsFramebuffer, + (GLuint framebuffer)) +COGL_EXT_END () + +COGL_EXT_BEGIN (blending, 1, 2, + COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glBlendEquation, + (GLenum mode)) +COGL_EXT_FUNCTION (void, glBlendColor, + (GLclampf red, + GLclampf green, + GLclampf blue, + GLclampf alpha)) +COGL_EXT_END () + +/* Optional, declared in 1.4 or GLES 1.2 */ +COGL_EXT_BEGIN (blend_func_separate, 1, 4, + COGL_EXT_IN_GLES2, + "EXT\0", + "blend_func_separate\0") +COGL_EXT_FUNCTION (void, glBlendFuncSeparate, + (GLenum srcRGB, + GLenum dstRGB, + GLenum srcAlpha, + GLenum dstAlpha)) +COGL_EXT_END () + +/* Optional, declared in 2.0 */ +COGL_EXT_BEGIN (blend_equation_separate, 2, 0, + COGL_EXT_IN_GLES2, + "EXT\0", + "blend_equation_separate\0") +COGL_EXT_FUNCTION (void, glBlendEquationSeparate, + (GLenum modeRGB, + GLenum modeAlpha)) +COGL_EXT_END () + +COGL_EXT_BEGIN (gles2_only_api, + 4, 1, + COGL_EXT_IN_GLES2, + "ARB:\0", + "ES2_compatibility\0") +COGL_EXT_FUNCTION (void, glReleaseShaderCompiler, (void)) +COGL_EXT_FUNCTION (void, glGetShaderPrecisionFormat, + (GLenum shadertype, + GLenum precisiontype, + GLint* range, + GLint* precision)) +COGL_EXT_FUNCTION (void, glShaderBinary, + (GLsizei n, + const GLuint* shaders, + GLenum binaryformat, + const GLvoid* binary, + GLsizei length)) +COGL_EXT_END () + +/* GL and GLES 2.0 apis */ +COGL_EXT_BEGIN (two_point_zero_api, + 2, 0, + COGL_EXT_IN_GLES2, + "\0", + "\0") +COGL_EXT_FUNCTION (void, glStencilFuncSeparate, + (GLenum face, GLenum func, GLint ref, GLuint mask)) +COGL_EXT_FUNCTION (void, glStencilMaskSeparate, + (GLenum face, GLuint mask)) +COGL_EXT_FUNCTION (void, glStencilOpSeparate, + (GLenum face, GLenum fail, GLenum zfail, GLenum zpass)) +COGL_EXT_END () diff --git a/cogl/cogl/mutter-cogl-1.0.pc.in b/cogl/cogl/mutter-cogl-1.0.pc.in new file mode 100644 index 000000000..92e3a4312 --- /dev/null +++ b/cogl/cogl/mutter-cogl-1.0.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/mutter +includedir=@includedir@/mutter +apiversion=1.0 +requires=@COGL_PKG_REQUIRES@ + +Name: Cogl +Description: An object oriented GL/GLES Abstraction/Utility Layer +Version: @COGL_1_VERSION@ +Libs: -L${libdir} -lmutter-cogl +Cflags: -I${includedir}/cogl +Requires: ${requires} diff --git a/cogl/cogl/winsys/cogl-texture-pixmap-x11-private.h b/cogl/cogl/winsys/cogl-texture-pixmap-x11-private.h new file mode 100644 index 000000000..5da998f89 --- /dev/null +++ b/cogl/cogl/winsys/cogl-texture-pixmap-x11-private.h @@ -0,0 +1,103 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H +#define __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H + +#include +#include +#include + +#include + +#ifdef COGL_HAS_GLX_SUPPORT +#include +#endif + +#include "cogl-object-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-pixmap-x11.h" + +typedef struct _CoglDamageRectangle CoglDamageRectangle; + +struct _CoglDamageRectangle +{ + unsigned int x1; + unsigned int y1; + unsigned int x2; + unsigned int y2; +}; + +/* For stereo, there are a pair of textures, but we want to share most + * other state (the GLXPixmap, visual, etc.) The way we do this is that + * the left-eye texture has all the state (there is in fact, no internal + * difference between the a MONO and a LEFT texture ), and the + * right-eye texture simply points to the left eye texture, with all + * other fields ignored. + */ +typedef enum +{ + COGL_TEXTURE_PIXMAP_MONO, + COGL_TEXTURE_PIXMAP_LEFT, + COGL_TEXTURE_PIXMAP_RIGHT +} CoglTexturePixmapStereoMode; + +struct _CoglTexturePixmapX11 +{ + CoglTexture _parent; + + CoglTexturePixmapStereoMode stereo_mode; + CoglTexturePixmapX11 *left; /* Set only if stereo_mode=RIGHT */ + + Pixmap pixmap; + CoglTexture *tex; + + unsigned int depth; + Visual *visual; + + XImage *image; + + XShmSegmentInfo shm_info; + + Damage damage; + CoglTexturePixmapX11ReportLevel damage_report_level; + CoglBool damage_owned; + CoglDamageRectangle damage_rect; + + void *winsys; + + /* During the pre_paint method, this will be set to TRUE if we + should use the winsys texture, otherwise we will use the regular + texture */ + CoglBool use_winsys_texture; +}; + + +#endif /* __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/cogl/winsys/cogl-texture-pixmap-x11.c new file mode 100644 index 000000000..398c357d3 --- /dev/null +++ b/cogl/cogl/winsys/cogl-texture-pixmap-x11.c @@ -0,0 +1,1184 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * + * Authors: + * Neil Roberts + * Johan Bilien + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-debug.h" +#include "cogl-util.h" +#include "cogl-texture-pixmap-x11.h" +#include "cogl-texture-pixmap-x11-private.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-driver.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-2d-sliced.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-context-private.h" +#include "cogl-display-private.h" +#include "cogl-renderer-private.h" +#include "cogl-object-private.h" +#include "cogl-winsys-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-xlib.h" +#include "cogl-error-private.h" +#include "cogl-texture-gl-private.h" +#include "cogl-private.h" +#include "cogl-gtype-private.h" + +#include +#include + +#include +#include +#include + +#include +#include + +static void _cogl_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap); + +COGL_TEXTURE_DEFINE (TexturePixmapX11, texture_pixmap_x11); +COGL_GTYPE_DEFINE_CLASS (TexturePixmapX11, texture_pixmap_x11); + +static const CoglTextureVtable cogl_texture_pixmap_x11_vtable; + +uint32_t +cogl_texture_pixmap_x11_error_quark (void) +{ + return g_quark_from_static_string ("cogl-texture-pixmap-error-quark"); +} + +static void +cogl_damage_rectangle_union (CoglDamageRectangle *damage_rect, + int x, + int y, + int width, + int height) +{ + /* If the damage region is empty then we'll just copy the new + rectangle directly */ + if (damage_rect->x1 == damage_rect->x2 || + damage_rect->y1 == damage_rect->y2) + { + damage_rect->x1 = x; + damage_rect->y1 = y; + damage_rect->x2 = x + width; + damage_rect->y2 = y + height; + } + else + { + if (damage_rect->x1 > x) + damage_rect->x1 = x; + if (damage_rect->y1 > y) + damage_rect->y1 = y; + if (damage_rect->x2 < x + width) + damage_rect->x2 = x + width; + if (damage_rect->y2 < y + height) + damage_rect->y2 = y + height; + } +} + +static CoglBool +cogl_damage_rectangle_is_whole (const CoglDamageRectangle *damage_rect, + unsigned int width, + unsigned int height) +{ + return (damage_rect->x1 == 0 && damage_rect->y1 == 0 + && damage_rect->x2 == width && damage_rect->y2 == height); +} + +static const CoglWinsysVtable * +_cogl_texture_pixmap_x11_get_winsys (CoglTexturePixmapX11 *tex_pixmap) +{ + /* FIXME: A CoglContext should be reachable from a CoglTexture + * pointer */ + _COGL_GET_CONTEXT (ctx, NULL); + + return ctx->display->renderer->winsys_vtable; +} + +static void +process_damage_event (CoglTexturePixmapX11 *tex_pixmap, + XDamageNotifyEvent *damage_event) +{ + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + Display *display; + enum { DO_NOTHING, NEEDS_SUBTRACT, NEED_BOUNDING_BOX } handle_mode; + const CoglWinsysVtable *winsys; + + _COGL_GET_CONTEXT (ctxt, NO_RETVAL); + + display = cogl_xlib_renderer_get_display (ctxt->display->renderer); + + COGL_NOTE (TEXTURE_PIXMAP, "Damage event received for %p", tex_pixmap); + + switch (tex_pixmap->damage_report_level) + { + case COGL_TEXTURE_PIXMAP_X11_DAMAGE_RAW_RECTANGLES: + /* For raw rectangles we don't need do look at the damage region + at all because the damage area is directly given in the event + struct and the reporting of events is not affected by + clearing the damage region */ + handle_mode = DO_NOTHING; + break; + + case COGL_TEXTURE_PIXMAP_X11_DAMAGE_DELTA_RECTANGLES: + case COGL_TEXTURE_PIXMAP_X11_DAMAGE_NON_EMPTY: + /* For delta rectangles and non empty we'll query the damage + region for the bounding box */ + handle_mode = NEED_BOUNDING_BOX; + break; + + case COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX: + /* For bounding box we need to clear the damage region but we + don't actually care what it was because the damage event + itself contains the bounding box of the region */ + handle_mode = NEEDS_SUBTRACT; + break; + + default: + g_assert_not_reached (); + } + + /* If the damage already covers the whole rectangle then we don't + need to request the bounding box of the region because we're + going to update the whole texture anyway. */ + if (cogl_damage_rectangle_is_whole (&tex_pixmap->damage_rect, + tex->width, + tex->height)) + { + if (handle_mode != DO_NOTHING) + XDamageSubtract (display, tex_pixmap->damage, None, None); + } + else if (handle_mode == NEED_BOUNDING_BOX) + { + XserverRegion parts; + int r_count; + XRectangle r_bounds; + XRectangle *r_damage; + + /* We need to extract the damage region so we can get the + bounding box */ + + parts = XFixesCreateRegion (display, 0, 0); + XDamageSubtract (display, tex_pixmap->damage, None, parts); + r_damage = XFixesFetchRegionAndBounds (display, + parts, + &r_count, + &r_bounds); + cogl_damage_rectangle_union (&tex_pixmap->damage_rect, + r_bounds.x, + r_bounds.y, + r_bounds.width, + r_bounds.height); + if (r_damage) + XFree (r_damage); + + XFixesDestroyRegion (display, parts); + } + else + { + if (handle_mode == NEEDS_SUBTRACT) + /* We still need to subtract from the damage region but we + don't care what the region actually was */ + XDamageSubtract (display, tex_pixmap->damage, None, None); + + cogl_damage_rectangle_union (&tex_pixmap->damage_rect, + damage_event->area.x, + damage_event->area.y, + damage_event->area.width, + damage_event->area.height); + } + + if (tex_pixmap->winsys) + { + /* If we're using the texture from pixmap extension then there's no + point in getting the region and we can just mark that the texture + needs updating */ + winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + winsys->texture_pixmap_x11_damage_notify (tex_pixmap); + } +} + +static CoglFilterReturn +_cogl_texture_pixmap_x11_filter (XEvent *event, void *data) +{ + CoglTexturePixmapX11 *tex_pixmap = data; + int damage_base; + + _COGL_GET_CONTEXT (ctxt, COGL_FILTER_CONTINUE); + + damage_base = _cogl_xlib_get_damage_base (); + if (event->type == damage_base + XDamageNotify) + { + XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) event; + + if (damage_event->damage == tex_pixmap->damage) + process_damage_event (tex_pixmap, damage_event); + } + + return COGL_FILTER_CONTINUE; +} + +static void +set_damage_object_internal (CoglContext *ctx, + CoglTexturePixmapX11 *tex_pixmap, + Damage damage, + CoglTexturePixmapX11ReportLevel report_level) +{ + Display *display = cogl_xlib_renderer_get_display (ctx->display->renderer); + + if (tex_pixmap->damage) + { + cogl_xlib_renderer_remove_filter (ctx->display->renderer, + _cogl_texture_pixmap_x11_filter, + tex_pixmap); + + if (tex_pixmap->damage_owned) + { + XDamageDestroy (display, tex_pixmap->damage); + tex_pixmap->damage_owned = FALSE; + } + } + + tex_pixmap->damage = damage; + tex_pixmap->damage_report_level = report_level; + + if (damage) + cogl_xlib_renderer_add_filter (ctx->display->renderer, + _cogl_texture_pixmap_x11_filter, + tex_pixmap); +} + +static CoglTexturePixmapX11 * +_cogl_texture_pixmap_x11_new (CoglContext *ctxt, + uint32_t pixmap, + CoglBool automatic_updates, + CoglTexturePixmapStereoMode stereo_mode, + CoglError **error) +{ + CoglTexturePixmapX11 *tex_pixmap = g_new (CoglTexturePixmapX11, 1); + Display *display = cogl_xlib_renderer_get_display (ctxt->display->renderer); + Window pixmap_root_window; + int pixmap_x, pixmap_y; + unsigned int pixmap_width, pixmap_height; + unsigned int pixmap_border_width; + CoglPixelFormat internal_format; + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + XWindowAttributes window_attributes; + int damage_base; + const CoglWinsysVtable *winsys; + + if (!XGetGeometry (display, pixmap, &pixmap_root_window, + &pixmap_x, &pixmap_y, + &pixmap_width, &pixmap_height, + &pixmap_border_width, &tex_pixmap->depth)) + { + g_free (tex_pixmap); + _cogl_set_error (error, + COGL_TEXTURE_PIXMAP_X11_ERROR, + COGL_TEXTURE_PIXMAP_X11_ERROR_X11, + "Unable to query pixmap size"); + return NULL; + } + + /* Note: the detailed pixel layout doesn't matter here, we are just + * interested in RGB vs RGBA... */ + internal_format = (tex_pixmap->depth >= 32 + ? COGL_PIXEL_FORMAT_RGBA_8888_PRE + : COGL_PIXEL_FORMAT_RGB_888); + + _cogl_texture_init (tex, ctxt, pixmap_width, pixmap_height, + internal_format, + NULL, /* no loader */ + &cogl_texture_pixmap_x11_vtable); + + tex_pixmap->pixmap = pixmap; + tex_pixmap->stereo_mode = stereo_mode; + tex_pixmap->left = NULL; + tex_pixmap->image = NULL; + tex_pixmap->shm_info.shmid = -1; + tex_pixmap->tex = NULL; + tex_pixmap->damage_owned = FALSE; + tex_pixmap->damage = 0; + + /* We need a visual to use for shared memory images so we'll query + it from the pixmap's root window */ + if (!XGetWindowAttributes (display, pixmap_root_window, &window_attributes)) + { + g_free (tex_pixmap); + _cogl_set_error (error, + COGL_TEXTURE_PIXMAP_X11_ERROR, + COGL_TEXTURE_PIXMAP_X11_ERROR_X11, + "Unable to query root window attributes"); + return NULL; + } + + tex_pixmap->visual = window_attributes.visual; + + /* If automatic updates are requested and the Xlib connection + supports damage events then we'll register a damage object on the + pixmap */ + damage_base = _cogl_xlib_get_damage_base (); + if (automatic_updates && damage_base >= 0) + { + Damage damage = XDamageCreate (display, + pixmap, + XDamageReportBoundingBox); + set_damage_object_internal (ctxt, + tex_pixmap, + damage, + COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX); + tex_pixmap->damage_owned = TRUE; + } + + /* Assume the entire pixmap is damaged to begin with */ + tex_pixmap->damage_rect.x1 = 0; + tex_pixmap->damage_rect.x2 = pixmap_width; + tex_pixmap->damage_rect.y1 = 0; + tex_pixmap->damage_rect.y2 = pixmap_height; + + winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + if (winsys->texture_pixmap_x11_create) + { + tex_pixmap->use_winsys_texture = + winsys->texture_pixmap_x11_create (tex_pixmap); + } + else + tex_pixmap->use_winsys_texture = FALSE; + + if (!tex_pixmap->use_winsys_texture) + tex_pixmap->winsys = NULL; + + _cogl_texture_set_allocated (tex, internal_format, + pixmap_width, pixmap_height); + + return _cogl_texture_pixmap_x11_object_new (tex_pixmap); +} + +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new (CoglContext *ctxt, + uint32_t pixmap, + CoglBool automatic_updates, + CoglError **error) + +{ + return _cogl_texture_pixmap_x11_new (ctxt, pixmap, + automatic_updates, COGL_TEXTURE_PIXMAP_MONO, + error); +} + +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new_left (CoglContext *ctxt, + uint32_t pixmap, + CoglBool automatic_updates, + CoglError **error) +{ + return _cogl_texture_pixmap_x11_new (ctxt, pixmap, + automatic_updates, COGL_TEXTURE_PIXMAP_LEFT, + error); +} + +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new_right (CoglTexturePixmapX11 *tfp_left) +{ + CoglTexture *texture_left = COGL_TEXTURE (tfp_left); + CoglTexturePixmapX11 *tfp_right; + CoglPixelFormat internal_format; + + g_return_val_if_fail (tfp_left->stereo_mode == COGL_TEXTURE_PIXMAP_LEFT, NULL); + + tfp_right = g_new0 (CoglTexturePixmapX11, 1); + tfp_right->stereo_mode = COGL_TEXTURE_PIXMAP_RIGHT; + tfp_right->left = cogl_object_ref (tfp_left); + + internal_format = (tfp_left->depth >= 32 + ? COGL_PIXEL_FORMAT_RGBA_8888_PRE + : COGL_PIXEL_FORMAT_RGB_888); + _cogl_texture_init (COGL_TEXTURE (tfp_right), + texture_left->context, + texture_left->width, + texture_left->height, + internal_format, + NULL, /* no loader */ + &cogl_texture_pixmap_x11_vtable); + + _cogl_texture_set_allocated (COGL_TEXTURE (tfp_right), internal_format, + texture_left->width, texture_left->height); + + return _cogl_texture_pixmap_x11_object_new (tfp_right); +} + +static CoglBool +_cogl_texture_pixmap_x11_allocate (CoglTexture *tex, + CoglError **error) +{ + return TRUE; +} + +/* Tries to allocate enough shared mem to handle a full size + * update size of the X Pixmap. */ +static void +try_alloc_shm (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + XImage *dummy_image; + Display *display; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + display = cogl_xlib_renderer_get_display (ctx->display->renderer); + + if (!XShmQueryExtension (display)) + return; + + /* We are creating a dummy_image so we can have Xlib calculate + * image->bytes_per_line - including any magic padding it may + * want - for the largest possible ximage we might need to use + * when handling updates to the texture. + * + * Note: we pass a NULL shminfo here, but that has no bearing + * on the setup of the XImage, except that ximage->obdata will + * == NULL. + */ + dummy_image = + XShmCreateImage (display, + tex_pixmap->visual, + tex_pixmap->depth, + ZPixmap, + NULL, + NULL, /* shminfo, */ + tex->width, + tex->height); + if (!dummy_image) + goto failed_image_create; + + tex_pixmap->shm_info.shmid = shmget (IPC_PRIVATE, + dummy_image->bytes_per_line + * dummy_image->height, + IPC_CREAT | 0777); + if (tex_pixmap->shm_info.shmid == -1) + goto failed_shmget; + + tex_pixmap->shm_info.shmaddr = shmat (tex_pixmap->shm_info.shmid, 0, 0); + if (tex_pixmap->shm_info.shmaddr == (void *) -1) + goto failed_shmat; + + tex_pixmap->shm_info.readOnly = False; + + if (XShmAttach (display, &tex_pixmap->shm_info) == 0) + goto failed_xshmattach; + + XDestroyImage (dummy_image); + + return; + + failed_xshmattach: + g_warning ("XShmAttach failed"); + shmdt (tex_pixmap->shm_info.shmaddr); + + failed_shmat: + g_warning ("shmat failed"); + shmctl (tex_pixmap->shm_info.shmid, IPC_RMID, 0); + + failed_shmget: + g_warning ("shmget failed"); + XDestroyImage (dummy_image); + + failed_image_create: + tex_pixmap->shm_info.shmid = -1; +} + +void +cogl_texture_pixmap_x11_update_area (CoglTexturePixmapX11 *tex_pixmap, + int x, + int y, + int width, + int height) +{ + /* We'll queue the update for both the GLX texture and the regular + texture because we can't determine which will be needed until we + actually render something */ + + if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + tex_pixmap = tex_pixmap->left; + + if (tex_pixmap->winsys) + { + const CoglWinsysVtable *winsys; + winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + winsys->texture_pixmap_x11_damage_notify (tex_pixmap); + } + + cogl_damage_rectangle_union (&tex_pixmap->damage_rect, + x, y, width, height); +} + +CoglBool +cogl_texture_pixmap_x11_is_using_tfp_extension (CoglTexturePixmapX11 *tex_pixmap) +{ + if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + tex_pixmap = tex_pixmap->left; + + return !!tex_pixmap->winsys; +} + +void +cogl_texture_pixmap_x11_set_damage_object (CoglTexturePixmapX11 *tex_pixmap, + uint32_t damage, + CoglTexturePixmapX11ReportLevel + report_level) +{ + int damage_base; + + _COGL_GET_CONTEXT (ctxt, NO_RETVAL); + + g_return_if_fail (tex_pixmap->stereo_mode != COGL_TEXTURE_PIXMAP_RIGHT); + + damage_base = _cogl_xlib_get_damage_base (); + if (damage_base >= 0) + set_damage_object_internal (ctxt, tex_pixmap, damage, report_level); +} + +static CoglTexture * +create_fallback_texture (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + CoglError *skip_error = NULL; + + if ((_cogl_util_is_pot (width) && _cogl_util_is_pot (height)) || + (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + /* First try creating a fast-path non-sliced texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, + width, height)); + + _cogl_texture_set_internal_format (tex, internal_format); + + /* TODO: instead of allocating storage here it would be better + * if we had some api that let us just check that the size is + * supported by the hardware so storage could be allocated + * lazily when uploading data. */ + if (!cogl_texture_allocate (tex, &skip_error)) + { + cogl_error_free (skip_error); + cogl_object_unref (tex); + tex = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_with_size (ctx, + width, + height, + COGL_TEXTURE_MAX_WASTE); + tex = COGL_TEXTURE (tex_2ds); + + _cogl_texture_set_internal_format (tex, internal_format); + } + + return tex; +} + +static void +_cogl_texture_pixmap_x11_update_image_texture (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + Display *display; + Visual *visual; + CoglPixelFormat image_format; + XImage *image; + int src_x, src_y; + int x, y, width, height; + int bpp; + int offset; + CoglError *ignore = NULL; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + display = cogl_xlib_renderer_get_display (ctx->display->renderer); + visual = tex_pixmap->visual; + + /* If the damage region is empty then there's nothing to do */ + if (tex_pixmap->damage_rect.x2 == tex_pixmap->damage_rect.x1) + return; + + x = tex_pixmap->damage_rect.x1; + y = tex_pixmap->damage_rect.y1; + width = tex_pixmap->damage_rect.x2 - x; + height = tex_pixmap->damage_rect.y2 - y; + + /* We lazily create the texture the first time it is needed in case + this texture can be entirely handled using the GLX texture + instead */ + if (tex_pixmap->tex == NULL) + { + CoglPixelFormat texture_format; + + texture_format = (tex_pixmap->depth >= 32 + ? COGL_PIXEL_FORMAT_RGBA_8888_PRE + : COGL_PIXEL_FORMAT_RGB_888); + + tex_pixmap->tex = create_fallback_texture (ctx, + tex->width, + tex->height, + texture_format); + } + + if (tex_pixmap->image == NULL) + { + /* If we also haven't got a shm segment then this must be the + first time we've tried to update, so lets try allocating shm + first */ + if (tex_pixmap->shm_info.shmid == -1) + try_alloc_shm (tex_pixmap); + + if (tex_pixmap->shm_info.shmid == -1) + { + COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XGetImage", tex_pixmap); + + /* We'll fallback to using a regular XImage. We'll download + the entire area instead of a sub region because presumably + if this is the first update then the entire pixmap is + needed anyway and it saves trying to manually allocate an + XImage at the right size */ + tex_pixmap->image = XGetImage (display, + tex_pixmap->pixmap, + 0, 0, + tex->width, tex->height, + AllPlanes, ZPixmap); + image = tex_pixmap->image; + src_x = x; + src_y = y; + } + else + { + COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XShmGetImage", + tex_pixmap); + + /* Create a temporary image using the beginning of the + shared memory segment and the right size for the region + we want to update. We need to reallocate the XImage every + time because there is no XShmGetSubImage. */ + image = XShmCreateImage (display, + tex_pixmap->visual, + tex_pixmap->depth, + ZPixmap, + NULL, + &tex_pixmap->shm_info, + width, + height); + image->data = tex_pixmap->shm_info.shmaddr; + src_x = 0; + src_y = 0; + + XShmGetImage (display, tex_pixmap->pixmap, image, x, y, AllPlanes); + } + } + else + { + COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XGetSubImage", tex_pixmap); + + image = tex_pixmap->image; + src_x = x; + src_y = y; + + XGetSubImage (display, + tex_pixmap->pixmap, + x, y, width, height, + AllPlanes, ZPixmap, + image, + x, y); + } + + image_format = + _cogl_util_pixel_format_from_masks (visual->red_mask, + visual->green_mask, + visual->blue_mask, + image->depth, + image->bits_per_pixel, + image->byte_order == LSBFirst); + + bpp = _cogl_pixel_format_get_bytes_per_pixel (image_format); + offset = image->bytes_per_line * src_y + bpp * src_x; + + _cogl_texture_set_region (tex_pixmap->tex, + width, + height, + image_format, + image->bytes_per_line, + ((const uint8_t *) image->data) + offset, + x, y, + 0, /* level */ + &ignore); + + /* If we have a shared memory segment then the XImage would be a + temporary one with no data allocated so we can just XFree it */ + if (tex_pixmap->shm_info.shmid != -1) + XFree (image); + + memset (&tex_pixmap->damage_rect, 0, sizeof (CoglDamageRectangle)); +} + +static void +_cogl_texture_pixmap_x11_set_use_winsys_texture (CoglTexturePixmapX11 *tex_pixmap, + CoglBool new_value) +{ + if (tex_pixmap->use_winsys_texture != new_value) + { + /* Notify cogl-pipeline.c that the texture's underlying GL texture + * storage is changing so it knows it may need to bind a new texture + * if the CoglTexture is reused with the same texture unit. */ + _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (tex_pixmap)); + + tex_pixmap->use_winsys_texture = new_value; + } +} + +static void +_cogl_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, + CoglBool needs_mipmap) +{ + CoglTexturePixmapStereoMode stereo_mode = tex_pixmap->stereo_mode; + if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + tex_pixmap = tex_pixmap->left; + + if (tex_pixmap->winsys) + { + const CoglWinsysVtable *winsys = + _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + + if (winsys->texture_pixmap_x11_update (tex_pixmap, stereo_mode, needs_mipmap)) + { + _cogl_texture_pixmap_x11_set_use_winsys_texture (tex_pixmap, TRUE); + return; + } + } + + /* If it didn't work then fallback to using XGetImage. This may be + temporary */ + _cogl_texture_pixmap_x11_set_use_winsys_texture (tex_pixmap, FALSE); + + _cogl_texture_pixmap_x11_update_image_texture (tex_pixmap); +} + +static CoglTexture * +_cogl_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexturePixmapX11 *original_pixmap = tex_pixmap; + CoglTexture *tex; + int i; + CoglTexturePixmapStereoMode stereo_mode = tex_pixmap->stereo_mode; + + if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + tex_pixmap = tex_pixmap->left; + + /* We try getting the texture twice, once without flushing the + updates and once with. If pre_paint has been called already then + we should have a good idea of which texture to use so we don't + want to mess with that by ensuring the updates. However, if we + couldn't find a texture then we'll just make a best guess by + flushing without expecting mipmap support and try again. This + would happen for example if an application calls + get_gl_texture before the first paint */ + + for (i = 0; i < 2; i++) + { + if (tex_pixmap->use_winsys_texture) + { + const CoglWinsysVtable *winsys = + _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + tex = winsys->texture_pixmap_x11_get_texture (tex_pixmap, stereo_mode); + } + else + tex = tex_pixmap->tex; + + if (tex) + return tex; + + _cogl_texture_pixmap_x11_update (original_pixmap, FALSE); + } + + g_assert_not_reached (); + + return NULL; +} + +static CoglBool +_cogl_texture_pixmap_x11_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + int dst_width, + int dst_height, + int level, + CoglBitmap *bmp, + CoglError **error) +{ + /* This doesn't make much sense for texture from pixmap so it's not + supported */ + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Explicitly setting a region of a TFP texture unsupported"); + return FALSE; +} + +static CoglBool +_cogl_texture_pixmap_x11_get_data (CoglTexture *tex, + CoglPixelFormat format, + int rowstride, + uint8_t *data) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + return cogl_texture_get_data (child_tex, format, rowstride, data); +} + +typedef struct _NormalizeCoordsWrapperData +{ + int width; + int height; + CoglMetaTextureCallback callback; + void *user_data; +} NormalizeCoordsWrapperData; + +static void +normalize_coords_wrapper_cb (CoglTexture *child_texture, + const float *child_texture_coords, + const float *meta_coords, + void *user_data) +{ + NormalizeCoordsWrapperData *data = user_data; + float normalized_coords[4]; + + normalized_coords[0] = meta_coords[0] / data->width; + normalized_coords[1] = meta_coords[1] / data->height; + normalized_coords[2] = meta_coords[2] / data->width; + normalized_coords[3] = meta_coords[3] / data->height; + + data->callback (child_texture, + child_texture_coords, normalized_coords, + data->user_data); +} + +static void +_cogl_texture_pixmap_x11_foreach_sub_texture_in_region + (CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglMetaTextureCallback callback, + void *user_data) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + + /* tfp textures may be implemented in terms of a + * CoglTextureRectangle texture which uses un-normalized texture + * coordinates but we want to consistently deal with normalized + * texture coordinates with CoglTexturePixmapX11... */ + if (cogl_is_texture_rectangle (child_tex)) + { + NormalizeCoordsWrapperData data; + int width = tex->width; + int height = tex->height; + + virtual_tx_1 *= width; + virtual_ty_1 *= height; + virtual_tx_2 *= width; + virtual_ty_2 *= height; + + data.width = width; + data.height = height; + data.callback = callback; + data.user_data = user_data; + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (child_tex), + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + normalize_coords_wrapper_cb, + &data); + } + else + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (child_tex), + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + callback, + user_data); +} + +static int +_cogl_texture_pixmap_x11_get_max_waste (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + return cogl_texture_get_max_waste (child_tex); +} + +static CoglBool +_cogl_texture_pixmap_x11_is_sliced (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + return cogl_texture_is_sliced (child_tex); +} + +static CoglBool +_cogl_texture_pixmap_x11_can_hardware_repeat (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + return _cogl_texture_can_hardware_repeat (child_tex); +} + +static void +_cogl_texture_pixmap_x11_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + _cogl_texture_transform_coords_to_gl (child_tex, s, t); +} + +static CoglTransformResult +_cogl_texture_pixmap_x11_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + return _cogl_texture_transform_quad_coords_to_gl (child_tex, coords); +} + +static CoglBool +_cogl_texture_pixmap_x11_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + return cogl_texture_get_gl_texture (child_tex, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_texture_pixmap_x11_gl_flush_legacy_texobj_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + _cogl_texture_gl_flush_legacy_texobj_filters (child_tex, + min_filter, mag_filter); +} + +static void +_cogl_texture_pixmap_x11_pre_paint (CoglTexture *tex, + CoglTexturePrePaintFlags flags) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex; + + _cogl_texture_pixmap_x11_update (tex_pixmap, + !!(flags & COGL_TEXTURE_NEEDS_MIPMAP)); + + child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + _cogl_texture_pre_paint (child_tex, flags); +} + +static void +_cogl_texture_pixmap_x11_ensure_non_quad_rendering (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + _cogl_texture_ensure_non_quad_rendering (child_tex); +} + +static void +_cogl_texture_pixmap_x11_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, + GLenum wrap_mode_s, + GLenum wrap_mode_t, + GLenum wrap_mode_p) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + _cogl_texture_gl_flush_legacy_texobj_wrap_modes (child_tex, + wrap_mode_s, + wrap_mode_t, + wrap_mode_p); +} + +static CoglPixelFormat +_cogl_texture_pixmap_x11_get_format (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + return _cogl_texture_get_format (child_tex); +} + +static GLenum +_cogl_texture_pixmap_x11_get_gl_format (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + return _cogl_texture_gl_get_format (child_tex); +} + +static CoglTextureType +_cogl_texture_pixmap_x11_get_type (CoglTexture *tex) +{ + CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); + CoglTexture *child_tex; + + child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); + + /* Forward on to the child texture */ + return _cogl_texture_get_type (child_tex); +} + +static void +_cogl_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) +{ + Display *display; + + _COGL_GET_CONTEXT (ctxt, NO_RETVAL); + + if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + { + cogl_object_unref (tex_pixmap->left); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_pixmap)); + + return; + } + + display = cogl_xlib_renderer_get_display (ctxt->display->renderer); + + set_damage_object_internal (ctxt, tex_pixmap, 0, 0); + + if (tex_pixmap->image) + XDestroyImage (tex_pixmap->image); + + if (tex_pixmap->shm_info.shmid != -1) + { + XShmDetach (display, &tex_pixmap->shm_info); + shmdt (tex_pixmap->shm_info.shmaddr); + shmctl (tex_pixmap->shm_info.shmid, IPC_RMID, 0); + } + + if (tex_pixmap->tex) + cogl_object_unref (tex_pixmap->tex); + + if (tex_pixmap->winsys) + { + const CoglWinsysVtable *winsys = + _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); + winsys->texture_pixmap_x11_free (tex_pixmap); + } + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_pixmap)); +} + +static const CoglTextureVtable +cogl_texture_pixmap_x11_vtable = + { + FALSE, /* not primitive */ + _cogl_texture_pixmap_x11_allocate, + _cogl_texture_pixmap_x11_set_region, + _cogl_texture_pixmap_x11_get_data, + _cogl_texture_pixmap_x11_foreach_sub_texture_in_region, + _cogl_texture_pixmap_x11_get_max_waste, + _cogl_texture_pixmap_x11_is_sliced, + _cogl_texture_pixmap_x11_can_hardware_repeat, + _cogl_texture_pixmap_x11_transform_coords_to_gl, + _cogl_texture_pixmap_x11_transform_quad_coords_to_gl, + _cogl_texture_pixmap_x11_get_gl_texture, + _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_filters, + _cogl_texture_pixmap_x11_pre_paint, + _cogl_texture_pixmap_x11_ensure_non_quad_rendering, + _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_wrap_modes, + _cogl_texture_pixmap_x11_get_format, + _cogl_texture_pixmap_x11_get_gl_format, + _cogl_texture_pixmap_x11_get_type, + NULL, /* is_foreign */ + NULL /* set_auto_mipmap */ + }; diff --git a/cogl/cogl/winsys/cogl-texture-pixmap-x11.h b/cogl/cogl/winsys/cogl-texture-pixmap-x11.h new file mode 100644 index 000000000..6340bb54e --- /dev/null +++ b/cogl/cogl/winsys/cogl-texture-pixmap-x11.h @@ -0,0 +1,294 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_TEXTURE_PIXMAP_X11_H +#define __COGL_TEXTURE_PIXMAP_X11_H + +/* NB: this is a top-level header that can be included directly but we + * want to be careful not to define __COGL_H_INSIDE__ when this is + * included internally while building Cogl itself since + * __COGL_H_INSIDE__ is used in headers to guard public vs private api + * definitions + */ +#ifndef COGL_COMPILATION + +/* Note: When building Cogl .gir we explicitly define + * __COGL_H_INSIDE__ */ +#ifndef __COGL_H_INSIDE__ +#define __COGL_H_INSIDE__ +#define __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* COGL_COMPILATION */ + +#include + +#ifdef COGL_HAS_GTYPE_SUPPORT +#include +#endif + +COGL_BEGIN_DECLS + +#ifdef COGL_ENABLE_EXPERIMENTAL_API + +/** + * SECTION:cogl-texture-pixmap-x11 + * @short_description: Functions for creating and manipulating 2D meta + * textures derived from X11 pixmaps. + * + * These functions allow high-level meta textures (See the + * #CoglMetaTexture interface) that derive their contents from an X11 + * pixmap. + */ + +typedef struct _CoglTexturePixmapX11 CoglTexturePixmapX11; + +#define COGL_TEXTURE_PIXMAP_X11(X) ((CoglTexturePixmapX11 *)X) + +#ifdef COGL_HAS_GTYPE_SUPPORT +/** + * cogl_texture_pixmap_x11_get_gtype: + * + * Returns: a #GType that can be used with the GLib type system. + */ +GType cogl_texture_pixmap_x11_get_gtype (void); +#endif + +typedef enum +{ + COGL_TEXTURE_PIXMAP_X11_DAMAGE_RAW_RECTANGLES, + COGL_TEXTURE_PIXMAP_X11_DAMAGE_DELTA_RECTANGLES, + COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX, + COGL_TEXTURE_PIXMAP_X11_DAMAGE_NON_EMPTY +} CoglTexturePixmapX11ReportLevel; + +/** + * COGL_TEXTURE_PIXMAP_X11_ERROR: + * + * #CoglError domain for texture-pixmap-x11 errors. + * + * Since: 1.10 + */ +#define COGL_TEXTURE_PIXMAP_X11_ERROR (cogl_texture_pixmap_x11_error_quark ()) + +/** + * CoglTexturePixmapX11Error: + * @COGL_TEXTURE_PIXMAP_X11_ERROR_X11: An X11 protocol error + * + * Error codes that can be thrown when performing texture-pixmap-x11 + * operations. + * + * Since: 1.10 + */ +typedef enum { + COGL_TEXTURE_PIXMAP_X11_ERROR_X11, +} CoglTexturePixmapX11Error; + +uint32_t cogl_texture_pixmap_x11_error_quark (void); + +/** + * cogl_texture_pixmap_x11_new: + * @context: A #CoglContext + * @pixmap: A X11 pixmap ID + * @automatic_updates: Whether to automatically copy the contents of + * the pixmap to the texture. + * @error: A #CoglError for exceptions + * + * Creates a texture that contains the contents of @pixmap. If + * @automatic_updates is %TRUE then Cogl will attempt to listen for + * damage events on the pixmap and automatically update the texture + * when it changes. + * + * Return value: a new #CoglTexturePixmapX11 instance + * + * Since: 1.10 + * Stability: Unstable + */ +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new (CoglContext *context, + uint32_t pixmap, + CoglBool automatic_updates, + CoglError **error); + +/** + * cogl_texture_pixmap_x11_new_left: + * @context: A #CoglContext + * @pixmap: A X11 pixmap ID + * @automatic_updates: Whether to automatically copy the contents of + * the pixmap to the texture. + * @error: A #CoglError for exceptions + * + * Creates one of a pair of textures to contain the contents of @pixmap, + * which has stereo content. (Different images for the right and left eyes.) + * The left image is drawn using this texture; the right image is drawn + * using a texture created by calling + * cogl_texture_pixmap_x11_new_right() and passing in this texture as an + * argument. + * + * In general, you should not use this function unless you have + * queried the %GLX_STEREO_TREE_EXT attribute of the corresponding + * window using glXQueryDrawable() and determined that the window is + * stereo. Note that this attribute can change over time and + * notification is also provided through events defined in the + * EXT_stereo_tree GLX extension. As long as the system has support for + * stereo content, drawing using the left and right pixmaps will not + * produce an error even if the window doesn't have stereo + * content any more, but drawing with the right pixmap will produce + * undefined output, so you need to listen for these events and + * re-render to avoid race conditions. (Recreating a non-stereo + * pixmap is not necessary, but may save resources.) + * + * Return value: a new #CoglTexturePixmapX11 instance + * + * Since: 1.20 + * Stability: Unstable + */ +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new_left (CoglContext *context, + uint32_t pixmap, + CoglBool automatic_updates, + CoglError **error); + +/** + * cogl_texture_pixmap_x11_new_right: + * @left_texture: A #CoglTexturePixmapX11 instance created with + * cogl_texture_pixmap_x11_new_left(). + * + * Creates a texture object that corresponds to the right-eye image + * of a pixmap with stereo content. @left_texture must have been + * created using cogl_texture_pixmap_x11_new_left(). + * + * Return value: a new #CoglTexturePixmapX11 instance + * + * Since: 1.20 + * Stability: Unstable + */ +CoglTexturePixmapX11 * +cogl_texture_pixmap_x11_new_right (CoglTexturePixmapX11 *left_texture); + +/** + * cogl_texture_pixmap_x11_update_area: + * @texture: A #CoglTexturePixmapX11 instance + * @x: x coordinate of the area to update + * @y: y coordinate of the area to update + * @width: width of the area to update + * @height: height of the area to update + * + * Forces an update of the given @texture so that it is refreshed with + * the contents of the pixmap that was given to + * cogl_texture_pixmap_x11_new(). + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_texture_pixmap_x11_update_area (CoglTexturePixmapX11 *texture, + int x, + int y, + int width, + int height); + +/** + * cogl_texture_pixmap_x11_is_using_tfp_extension: + * @texture: A #CoglTexturePixmapX11 instance + * + * Checks whether the given @texture is using the + * GLX_EXT_texture_from_pixmap or similar extension to copy the + * contents of the pixmap to the texture. This extension is usually + * implemented as zero-copy operation so it implies the updates are + * working efficiently. + * + * Return value: %TRUE if the texture is using an efficient extension + * and %FALSE otherwise + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_texture_pixmap_x11_is_using_tfp_extension (CoglTexturePixmapX11 *texture); + +/** + * cogl_texture_pixmap_x11_set_damage_object: + * @texture: A #CoglTexturePixmapX11 instance + * @damage: A X11 Damage object or 0 + * @report_level: The report level which describes how to interpret + * the damage events. This should match the level that the damage + * object was created with. + * + * Sets the damage object that will be used to track automatic updates + * to the @texture. Damage tracking can be disabled by passing 0 for + * @damage. Otherwise this damage will replace the one used if %TRUE + * was passed for automatic_updates to cogl_texture_pixmap_x11_new(). + * + * Note that Cogl will subtract from the damage region as it processes + * damage events. + * + * Since: 1.4 + * Stability: Unstable + */ +void +cogl_texture_pixmap_x11_set_damage_object (CoglTexturePixmapX11 *texture, + uint32_t damage, + CoglTexturePixmapX11ReportLevel + report_level); + +/** + * cogl_is_texture_pixmap_x11: + * @object: A pointer to a #CoglObject + * + * Checks whether @object points to a #CoglTexturePixmapX11 instance. + * + * Return value: %TRUE if the object is a #CoglTexturePixmapX11, and + * %FALSE otherwise + * + * Since: 1.4 + * Stability: Unstable + */ +CoglBool +cogl_is_texture_pixmap_x11 (void *object); + +#endif /* COGL_ENABLE_EXPERIMENTAL_API */ + +COGL_END_DECLS + +/* The gobject introspection scanner seems to parse public headers in + * isolation which means we need to be extra careful about how we + * define and undefine __COGL_H_INSIDE__ used to detect when internal + * headers are incorrectly included by developers. In the gobject + * introspection case we have to manually define __COGL_H_INSIDE__ as + * a commandline argument for the scanner which means we must be + * careful not to undefine it in a header... + */ +#ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#undef __COGL_H_INSIDE__ +#undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ +#endif + +#endif /* __COGL_TEXTURE_PIXMAP_X11_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h b/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h new file mode 100644 index 000000000..17a99f269 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h @@ -0,0 +1,149 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This can be included multiple times with different definitions for + * the COGL_WINSYS_FEATURE_* functions. + */ + +/* Macro prototypes: + * COGL_WINSYS_FEATURE_BEGIN (name, namespaces, extension_names, + * implied_private_egl_feature_flags) + * COGL_WINSYS_FEATURE_FUNCTION (return_type, function_name, + * (arguments)) + * ... + * COGL_WINSYS_FEATURE_END () + * + * Note: You can list multiple namespace and extension names if the + * corresponding _FEATURE_FUNCTIONS have the same semantics accross + * the different extension variants. + * + * XXX: NB: Don't add a trailing semicolon when using these macros + */ + +COGL_WINSYS_FEATURE_BEGIN (swap_region, + "NOK\0", + "swap_region\0", + COGL_EGL_WINSYS_FEATURE_SWAP_REGION) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersRegion, + (EGLDisplay dpy, + EGLSurface surface, + EGLint numRects, + const EGLint *rects)) +COGL_WINSYS_FEATURE_END () +/* XXX: These macros can't handle falling back to looking for + * EGL_KHR_image if EGL_KHR_image_base and EGL_KHR_image_pixmap aren't + * found... */ +#ifdef EGL_KHR_image_base +COGL_WINSYS_FEATURE_BEGIN (image_base, + "KHR\0", + "image_base\0", + 0) +COGL_WINSYS_FEATURE_FUNCTION (EGLImageKHR, eglCreateImage, + (EGLDisplay dpy, + EGLContext ctx, + EGLenum target, + EGLClientBuffer buffer, + const EGLint *attrib_list)) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroyImage, + (EGLDisplay dpy, + EGLImageKHR image)) +COGL_WINSYS_FEATURE_END () +#endif +COGL_WINSYS_FEATURE_BEGIN (image_pixmap, + "KHR\0", + "image_pixmap\0", + COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP) +COGL_WINSYS_FEATURE_END () +#ifdef EGL_WL_bind_wayland_display +COGL_WINSYS_FEATURE_BEGIN (bind_wayland_display, + "WL\0", + "bind_wayland_display\0", + COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER) +COGL_WINSYS_FEATURE_FUNCTION (EGLImageKHR, eglBindWaylandDisplay, + (EGLDisplay dpy, + struct wl_display *wayland_display)) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglUnbindWaylandDisplay, + (EGLDisplay dpy, + struct wl_display *wayland_display)) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglQueryWaylandBuffer, + (EGLDisplay dpy, + struct wl_resource *buffer, + EGLint attribute, EGLint *value)) +COGL_WINSYS_FEATURE_END () +#endif /* EGL_WL_bind_wayland_display */ + +COGL_WINSYS_FEATURE_BEGIN (create_context, + "KHR\0", + "create_context\0", + COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (buffer_age, + "EXT\0", + "buffer_age\0", + COGL_EGL_WINSYS_FEATURE_BUFFER_AGE) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (swap_buffers_with_damage, + "EXT\0", + "swap_buffers_with_damage\0", + 0) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersWithDamage, + (EGLDisplay dpy, + EGLSurface surface, + const EGLint *rects, + EGLint n_rects)) +COGL_WINSYS_FEATURE_END () + +#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) +COGL_WINSYS_FEATURE_BEGIN (fence_sync, + "KHR\0", + "fence_sync\0", + COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) +COGL_WINSYS_FEATURE_FUNCTION (EGLSyncKHR, eglCreateSync, + (EGLDisplay dpy, + EGLenum type, + const EGLint *attrib_list)) +COGL_WINSYS_FEATURE_FUNCTION (EGLint, eglClientWaitSync, + (EGLDisplay dpy, + EGLSyncKHR sync, + EGLint flags, + EGLTimeKHR timeout)) +COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroySync, + (EGLDisplay dpy, + EGLSyncKHR sync)) +COGL_WINSYS_FEATURE_END () +#endif + +COGL_WINSYS_FEATURE_BEGIN (surfaceless_context, + "KHR\0", + "surfaceless_context\0", + COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) +COGL_WINSYS_FEATURE_END () diff --git a/cogl/cogl/winsys/cogl-winsys-egl-kms-private.h b/cogl/cogl/winsys/cogl-winsys-egl-kms-private.h new file mode 100644 index 000000000..dea05db47 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-kms-private.h @@ -0,0 +1,41 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_WINSYS_EGL_KMS_PRIVATE_H +#define __COGL_WINSYS_EGL_KMS_PRIVATE_H + +#include "cogl-winsys-private.h" + +const CoglWinsysVtable * +_cogl_winsys_egl_kms_get_vtable (void); + +#endif /* __COGL_WINSYS_EGL_KMS_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/cogl/winsys/cogl-winsys-egl-kms.c new file mode 100644 index 000000000..4da1f1410 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-kms.c @@ -0,0 +1,1335 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Rob Bradford + * Kristian Høgsberg (from eglkms.c) + * Benjamin Franzke (from eglkms.c) + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cogl-winsys-egl-kms-private.h" +#include "cogl-winsys-egl-private.h" +#include "cogl-renderer-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-kms-renderer.h" +#include "cogl-kms-display.h" +#include "cogl-version.h" +#include "cogl-error-private.h" +#include "cogl-poll-private.h" + +static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable; + +static const CoglWinsysVtable *parent_vtable; + +typedef struct _CoglRendererKMS +{ + int fd; + int opened_fd; + struct gbm_device *gbm; + CoglClosure *swap_notify_idle; + CoglBool page_flips_not_supported; +} CoglRendererKMS; + +typedef struct _CoglOutputKMS +{ + drmModeConnector *connector; + drmModeEncoder *encoder; + drmModeCrtc *saved_crtc; + drmModeModeInfo *modes; + int n_modes; + drmModeModeInfo mode; +} CoglOutputKMS; + +typedef struct _CoglDisplayKMS +{ + GList *outputs; + GList *crtcs; + + int width, height; + CoglBool pending_set_crtc; + struct gbm_surface *dummy_gbm_surface; + + CoglOnscreen *onscreen; +} CoglDisplayKMS; + +typedef struct _CoglFlipKMS +{ + CoglOnscreen *onscreen; + int pending; +} CoglFlipKMS; + +typedef struct _CoglOnscreenKMS +{ + struct gbm_surface *surface; + uint32_t current_fb_id; + uint32_t next_fb_id; + struct gbm_bo *current_bo; + struct gbm_bo *next_bo; + CoglBool pending_swap_notify; + + EGLSurface *pending_egl_surface; + struct gbm_surface *pending_surface; +} CoglOnscreenKMS; + +static const char device_name[] = "/dev/dri/card0"; + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + if (egl_renderer->edpy != EGL_NO_DISPLAY) + eglTerminate (egl_renderer->edpy); + + if (kms_renderer->gbm != NULL) + gbm_device_destroy (kms_renderer->gbm); + + if (kms_renderer->opened_fd >= 0) + close (kms_renderer->opened_fd); + + g_slice_free (CoglRendererKMS, kms_renderer); + g_slice_free (CoglRendererEGL, egl_renderer); +} + +static void +flush_pending_swap_notify_cb (void *data, + void *user_data) +{ + CoglFramebuffer *framebuffer = data; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + + if (kms_onscreen->pending_swap_notify) + { + CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos); + + _cogl_onscreen_notify_frame_sync (onscreen, info); + _cogl_onscreen_notify_complete (onscreen, info); + kms_onscreen->pending_swap_notify = FALSE; + + cogl_object_unref (info); + } + } +} + +static void +flush_pending_swap_notify_idle (void *user_data) +{ + CoglContext *context = user_data; + CoglRendererEGL *egl_renderer = context->display->renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + /* This needs to be disconnected before invoking the callbacks in + * case the callbacks cause it to be queued again */ + _cogl_closure_disconnect (kms_renderer->swap_notify_idle); + kms_renderer->swap_notify_idle = NULL; + + g_list_foreach (context->framebuffers, + flush_pending_swap_notify_cb, + NULL); +} + +static void +free_current_bo (CoglOnscreen *onscreen) +{ + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + if (kms_onscreen->current_fb_id) + { + drmModeRmFB (kms_renderer->fd, + kms_onscreen->current_fb_id); + kms_onscreen->current_fb_id = 0; + } + if (kms_onscreen->current_bo) + { + gbm_surface_release_buffer (kms_onscreen->surface, + kms_onscreen->current_bo); + kms_onscreen->current_bo = NULL; + } +} + +static void +queue_swap_notify_for_onscreen (CoglOnscreen *onscreen) +{ + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + /* We only want to notify that the swap is complete when the + * application calls cogl_context_dispatch so instead of + * immediately notifying we queue an idle callback */ + if (!kms_renderer->swap_notify_idle) + { + kms_renderer->swap_notify_idle = + _cogl_poll_renderer_add_idle (renderer, + flush_pending_swap_notify_idle, + context, + NULL); + } + + kms_onscreen->pending_swap_notify = TRUE; +} + +static void +process_flip (CoglFlipKMS *flip) +{ + /* We're only ready to dispatch a swap notification once all outputs + * have flipped... */ + flip->pending--; + if (flip->pending == 0) + { + CoglOnscreen *onscreen = flip->onscreen; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + + queue_swap_notify_for_onscreen (onscreen); + + free_current_bo (onscreen); + + kms_onscreen->current_fb_id = kms_onscreen->next_fb_id; + kms_onscreen->next_fb_id = 0; + + kms_onscreen->current_bo = kms_onscreen->next_bo; + kms_onscreen->next_bo = NULL; + + cogl_object_unref (flip->onscreen); + + g_slice_free (CoglFlipKMS, flip); + } +} + +static void +page_flip_handler (int fd, + unsigned int frame, + unsigned int sec, + unsigned int usec, + void *data) +{ + CoglFlipKMS *flip = data; + + process_flip (flip); +} + +static void +handle_drm_event (CoglRendererKMS *kms_renderer) +{ + drmEventContext evctx; + + if (kms_renderer->page_flips_not_supported) + return; + + memset (&evctx, 0, sizeof evctx); + evctx.version = DRM_EVENT_CONTEXT_VERSION; + evctx.page_flip_handler = page_flip_handler; + drmHandleEvent (kms_renderer->fd, &evctx); +} + +static void +dispatch_kms_events (void *user_data, int revents) +{ + CoglRenderer *renderer = user_data; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + if (!revents) + return; + + handle_drm_event (kms_renderer); +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + CoglError **error) +{ + CoglRendererEGL *egl_renderer; + CoglRendererKMS *kms_renderer; + + renderer->winsys = g_slice_new0 (CoglRendererEGL); + egl_renderer = renderer->winsys; + + egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable; + egl_renderer->platform = g_slice_new0 (CoglRendererKMS); + kms_renderer = egl_renderer->platform; + + kms_renderer->fd = -1; + kms_renderer->opened_fd = -1; + + egl_renderer->edpy = EGL_NO_DISPLAY; + + if (renderer->kms_fd >= 0) + { + kms_renderer->fd = renderer->kms_fd; + } + else + { + kms_renderer->opened_fd = open (device_name, O_RDWR); + kms_renderer->fd = kms_renderer->opened_fd; + if (kms_renderer->fd < 0) + { + /* Probably permissions error */ + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Couldn't open %s", device_name); + return FALSE; + } + } + + kms_renderer->gbm = gbm_create_device (kms_renderer->fd); + if (kms_renderer->gbm == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Couldn't create gbm device"); + goto fail; + } + + egl_renderer->edpy = eglGetDisplay ((EGLNativeDisplayType)kms_renderer->gbm); + if (egl_renderer->edpy == EGL_NO_DISPLAY) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Couldn't get eglDisplay"); + goto fail; + } + + if (!_cogl_winsys_egl_renderer_connect_common (renderer, error)) + goto fail; + + _cogl_poll_renderer_add_fd (renderer, + kms_renderer->fd, + COGL_POLL_FD_EVENT_IN, + NULL, /* no prepare callback */ + dispatch_kms_events, + renderer); + + return TRUE; + +fail: + _cogl_winsys_renderer_disconnect (renderer); + + return FALSE; +} + +static CoglBool +is_connector_excluded (int id, + int *excluded_connectors, + int n_excluded_connectors) +{ + int i; + for (i = 0; i < n_excluded_connectors; i++) + if (excluded_connectors[i] == id) + return TRUE; + return FALSE; +} + +static drmModeConnector * +find_connector (int fd, + drmModeRes *resources, + int *excluded_connectors, + int n_excluded_connectors) +{ + int i; + + for (i = 0; i < resources->count_connectors; i++) + { + drmModeConnector *connector = + drmModeGetConnector (fd, resources->connectors[i]); + + if (connector && + connector->connection == DRM_MODE_CONNECTED && + connector->count_modes > 0 && + !is_connector_excluded (connector->connector_id, + excluded_connectors, + n_excluded_connectors)) + return connector; + drmModeFreeConnector(connector); + } + return NULL; +} + +static CoglBool +find_mirror_modes (drmModeModeInfo *modes0, + int n_modes0, + drmModeModeInfo *modes1, + int n_modes1, + drmModeModeInfo *mode1_out, + drmModeModeInfo *mode0_out) +{ + int i; + for (i = 0; i < n_modes0; i++) + { + int j; + drmModeModeInfo *mode0 = &modes0[i]; + for (j = 0; j < n_modes1; j++) + { + drmModeModeInfo *mode1 = &modes1[j]; + if (mode1->hdisplay == mode0->hdisplay && + mode1->vdisplay == mode0->vdisplay) + { + *mode0_out = *mode0; + *mode1_out = *mode1; + return TRUE; + } + } + } + return FALSE; +} + +static drmModeModeInfo builtin_1024x768 = +{ + 63500, /* clock */ + 1024, 1072, 1176, 1328, 0, + 768, 771, 775, 798, 0, + 59920, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, + 0, + "1024x768" +}; + +static CoglBool +is_panel (int type) +{ + return (type == DRM_MODE_CONNECTOR_LVDS || + type == DRM_MODE_CONNECTOR_eDP); +} + +static CoglOutputKMS * +find_output (int _index, + int fd, + drmModeRes *resources, + int *excluded_connectors, + int n_excluded_connectors, + CoglError **error) +{ + char *connector_env_name = g_strdup_printf ("COGL_KMS_CONNECTOR%d", _index); + char *mode_env_name; + drmModeConnector *connector; + drmModeEncoder *encoder; + CoglOutputKMS *output; + drmModeModeInfo *modes; + int n_modes; + + if (getenv (connector_env_name)) + { + unsigned long id = strtoul (getenv (connector_env_name), NULL, 10); + connector = drmModeGetConnector (fd, id); + } + else + connector = NULL; + g_free (connector_env_name); + + if (connector == NULL) + connector = find_connector (fd, resources, + excluded_connectors, n_excluded_connectors); + if (connector == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "No currently active connector found"); + return NULL; + } + + /* XXX: At this point it seems connector->encoder_id may be an invalid id of 0 + * even though the connector is marked as connected. Referencing ->encoders[0] + * seems more reliable. */ + encoder = drmModeGetEncoder (fd, connector->encoders[0]); + + output = g_slice_new0 (CoglOutputKMS); + output->connector = connector; + output->encoder = encoder; + output->saved_crtc = drmModeGetCrtc (fd, encoder->crtc_id); + + if (is_panel (connector->connector_type)) + { + n_modes = connector->count_modes + 1; + modes = g_new (drmModeModeInfo, n_modes); + memcpy (modes, connector->modes, + sizeof (drmModeModeInfo) * connector->count_modes); + /* TODO: parse EDID */ + modes[n_modes - 1] = builtin_1024x768; + } + else + { + n_modes = connector->count_modes; + modes = g_new (drmModeModeInfo, n_modes); + memcpy (modes, connector->modes, + sizeof (drmModeModeInfo) * n_modes); + } + + mode_env_name = g_strdup_printf ("COGL_KMS_CONNECTOR%d_MODE", _index); + if (getenv (mode_env_name)) + { + const char *name = getenv (mode_env_name); + int i; + CoglBool found = FALSE; + drmModeModeInfo mode; + + for (i = 0; i < n_modes; i++) + { + if (strcmp (modes[i].name, name) == 0) + { + found = TRUE; + break; + } + } + if (!found) + { + g_free (mode_env_name); + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "COGL_KMS_CONNECTOR%d_MODE of %s could not be found", + _index, name); + return NULL; + } + n_modes = 1; + mode = modes[i]; + g_free (modes); + modes = g_new (drmModeModeInfo, 1); + modes[0] = mode; + } + g_free (mode_env_name); + + output->modes = modes; + output->n_modes = n_modes; + + return output; +} + +static void +setup_crtc_modes (CoglDisplay *display, int fb_id) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRendererEGL *egl_renderer = display->renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + GList *l; + + for (l = kms_display->crtcs; l; l = l->next) + { + CoglKmsCrtc *crtc = l->data; + + int ret = drmModeSetCrtc (kms_renderer->fd, + crtc->id, + fb_id, crtc->x, crtc->y, + crtc->connectors, crtc->count, + crtc->count ? &crtc->mode : NULL); + if (ret) + g_warning ("Failed to set crtc mode %s: %m", crtc->mode.name); + } +} + +static void +flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRendererEGL *egl_renderer = display->renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + GList *l; + gboolean needs_flip = FALSE; + + for (l = kms_display->crtcs; l; l = l->next) + { + CoglKmsCrtc *crtc = l->data; + int ret = 0; + + if (crtc->count == 0 || crtc->ignore) + continue; + + needs_flip = TRUE; + + if (!kms_renderer->page_flips_not_supported) + { + ret = drmModePageFlip (kms_renderer->fd, + crtc->id, fb_id, + DRM_MODE_PAGE_FLIP_EVENT, flip); + if (ret != 0 && ret != -EACCES) + { + g_warning ("Failed to flip: %m"); + kms_renderer->page_flips_not_supported = TRUE; + break; + } + } + + if (ret == 0) + flip->pending++; + } + + if (kms_renderer->page_flips_not_supported && needs_flip) + flip->pending = 1; +} + +static void +crtc_free (CoglKmsCrtc *crtc) +{ + g_free (crtc->connectors); + g_slice_free (CoglKmsCrtc, crtc); +} + +static CoglKmsCrtc * +crtc_copy (CoglKmsCrtc *from) +{ + CoglKmsCrtc *new; + + new = g_slice_new (CoglKmsCrtc); + + *new = *from; + new->connectors = g_memdup (from->connectors, from->count * sizeof(uint32_t)); + + return new; +} + +static CoglBool +_cogl_winsys_egl_display_setup (CoglDisplay *display, + CoglError **error) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display; + CoglRendererEGL *egl_renderer = display->renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + drmModeRes *resources; + CoglOutputKMS *output0, *output1; + CoglBool mirror; + CoglKmsCrtc *crtc0, *crtc1; + + kms_display = g_slice_new0 (CoglDisplayKMS); + egl_display->platform = kms_display; + + resources = drmModeGetResources (kms_renderer->fd); + if (!resources) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "drmModeGetResources failed"); + return FALSE; + } + + /* Force a full modeset / drmModeSetCrtc on + * the first swap buffers call. + */ + kms_display->pending_set_crtc = TRUE; + + if (kms_renderer->opened_fd < 0) + return TRUE; + + output0 = find_output (0, + kms_renderer->fd, + resources, + NULL, + 0, /* n excluded connectors */ + error); + if (!output0) + return FALSE; + + kms_display->outputs = g_list_append (kms_display->outputs, output0); + + if (getenv ("COGL_KMS_MIRROR")) + mirror = TRUE; + else + mirror = FALSE; + + if (mirror) + { + int exclude_connector = output0->connector->connector_id; + output1 = find_output (1, + kms_renderer->fd, + resources, + &exclude_connector, + 1, /* n excluded connectors */ + error); + if (!output1) + return FALSE; + + kms_display->outputs = g_list_append (kms_display->outputs, output1); + + if (!find_mirror_modes (output0->modes, output0->n_modes, + output1->modes, output1->n_modes, + &output0->mode, + &output1->mode)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Failed to find matching modes for mirroring"); + return FALSE; + } + } + else + { + output0->mode = output0->modes[0]; + output1 = NULL; + } + + crtc0 = g_slice_new (CoglKmsCrtc); + crtc0->id = output0->encoder->crtc_id; + crtc0->x = 0; + crtc0->y = 0; + crtc0->mode = output0->mode; + crtc0->connectors = g_new (uint32_t, 1); + crtc0->connectors[0] = output0->connector->connector_id; + crtc0->count = 1; + kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc0); + + if (output1) + { + crtc1 = g_slice_new (CoglKmsCrtc); + crtc1->id = output1->encoder->crtc_id; + crtc1->x = 0; + crtc1->y = 0; + crtc1->mode = output1->mode; + crtc1->connectors = g_new (uint32_t, 1); + crtc1->connectors[0] = output1->connector->connector_id; + crtc1->count = 1; + kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc1); + } + + kms_display->width = output0->mode.hdisplay; + kms_display->height = output0->mode.vdisplay; + + return TRUE; +} + +static void +output_free (int fd, CoglOutputKMS *output) +{ + if (output->modes) + g_free (output->modes); + + if (output->encoder) + drmModeFreeEncoder (output->encoder); + + if (output->connector) + { + if (output->saved_crtc) + { + int ret = drmModeSetCrtc (fd, + output->saved_crtc->crtc_id, + output->saved_crtc->buffer_id, + output->saved_crtc->x, + output->saved_crtc->y, + &output->connector->connector_id, 1, + &output->saved_crtc->mode); + if (ret) + g_warning (G_STRLOC ": Error restoring saved CRTC"); + } + drmModeFreeConnector (output->connector); + } + + g_slice_free (CoglOutputKMS, output); +} + +static void +_cogl_winsys_egl_display_destroy (CoglDisplay *display) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + GList *l; + + for (l = kms_display->outputs; l; l = l->next) + output_free (kms_renderer->fd, l->data); + g_list_free (kms_display->outputs); + kms_display->outputs = NULL; + + g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free); + + g_slice_free (CoglDisplayKMS, egl_display->platform); +} + +static CoglBool +_cogl_winsys_egl_context_created (CoglDisplay *display, + CoglError **error) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + + if ((egl_renderer->private_features & + COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0) + { + kms_display->dummy_gbm_surface = + gbm_surface_create (kms_renderer->gbm, + 16, 16, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_RENDERING); + if (!kms_display->dummy_gbm_surface) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Failed to create dummy GBM surface"); + return FALSE; + } + + egl_display->dummy_surface = + eglCreateWindowSurface (egl_renderer->edpy, + egl_display->egl_config, + (EGLNativeWindowType) + kms_display->dummy_gbm_surface, + NULL); + if (egl_display->dummy_surface == EGL_NO_SURFACE) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Failed to create dummy EGL surface"); + return FALSE; + } + } + + if (!_cogl_winsys_egl_make_current (display, + egl_display->dummy_surface, + egl_display->dummy_surface, + egl_display->egl_context)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Failed to make context current"); + return FALSE; + } + + return TRUE; +} + +static void +_cogl_winsys_egl_cleanup_context (CoglDisplay *display) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (egl_display->dummy_surface != EGL_NO_SURFACE) + { + eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface); + egl_display->dummy_surface = EGL_NO_SURFACE; + } + + if (kms_display->dummy_gbm_surface != NULL) + { + gbm_surface_destroy (kms_display->dummy_gbm_surface); + kms_display->dummy_gbm_surface = NULL; + } +} + +static void +_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglDisplayEGL *egl_display = context->display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + uint32_t handle, stride; + CoglFlipKMS *flip; + + /* If we already have a pending swap then block until it completes */ + while (kms_onscreen->next_fb_id != 0) + handle_drm_event (kms_renderer); + + if (kms_onscreen->pending_egl_surface) + { + eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface); + egl_onscreen->egl_surface = kms_onscreen->pending_egl_surface; + kms_onscreen->pending_egl_surface = NULL; + + _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (kms_display->onscreen), + kms_display->width, kms_display->height); + context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND; + } + parent_vtable->onscreen_swap_buffers_with_damage (onscreen, + rectangles, + n_rectangles); + + if (kms_onscreen->pending_surface) + { + free_current_bo (onscreen); + if (kms_onscreen->surface) + gbm_surface_destroy (kms_onscreen->surface); + kms_onscreen->surface = kms_onscreen->pending_surface; + kms_onscreen->pending_surface = NULL; + } + /* Now we need to set the CRTC to whatever is the front buffer */ + kms_onscreen->next_bo = gbm_surface_lock_front_buffer (kms_onscreen->surface); + +#if (COGL_VERSION_ENCODE (COGL_GBM_MAJOR, COGL_GBM_MINOR, COGL_GBM_MICRO) >= \ + COGL_VERSION_ENCODE (8, 1, 0)) + stride = gbm_bo_get_stride (kms_onscreen->next_bo); +#else + stride = gbm_bo_get_pitch (kms_onscreen->next_bo); +#endif + handle = gbm_bo_get_handle (kms_onscreen->next_bo).u32; + + if (drmModeAddFB (kms_renderer->fd, + kms_display->width, + kms_display->height, + 24, /* depth */ + 32, /* bpp */ + stride, + handle, + &kms_onscreen->next_fb_id)) + { + g_warning ("Failed to create new back buffer handle: %m"); + gbm_surface_release_buffer (kms_onscreen->surface, + kms_onscreen->next_bo); + kms_onscreen->next_bo = NULL; + kms_onscreen->next_fb_id = 0; + return; + } + + /* If this is the first framebuffer to be presented then we now setup the + * crtc modes, else we flip from the previous buffer */ + if (kms_display->pending_set_crtc) + { + setup_crtc_modes (context->display, kms_onscreen->next_fb_id); + kms_display->pending_set_crtc = FALSE; + } + + flip = g_slice_new0 (CoglFlipKMS); + flip->onscreen = onscreen; + + flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id); + + if (flip->pending == 0) + { + drmModeRmFB (kms_renderer->fd, kms_onscreen->next_fb_id); + gbm_surface_release_buffer (kms_onscreen->surface, + kms_onscreen->next_bo); + kms_onscreen->next_bo = NULL; + kms_onscreen->next_fb_id = 0; + g_slice_free (CoglFlipKMS, flip); + flip = NULL; + + queue_swap_notify_for_onscreen (onscreen); + } + else + { + /* Ensure the onscreen remains valid while it has any pending flips... */ + cogl_object_ref (flip->onscreen); + + /* Process flip right away if we can't wait for vblank */ + if (kms_renderer->page_flips_not_supported) + { + setup_crtc_modes (context->display, kms_onscreen->next_fb_id); + process_flip (flip); + } + } +} + +static CoglBool +_cogl_winsys_egl_context_init (CoglContext *context, + CoglError **error) +{ + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE); + /* TODO: remove this deprecated feature */ + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, + TRUE); + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, + TRUE); + + return TRUE; +} + +static CoglBool +_cogl_winsys_onscreen_init (CoglOnscreen *onscreen, + CoglError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + CoglOnscreenEGL *egl_onscreen; + CoglOnscreenKMS *kms_onscreen; + + _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE); + + if (kms_display->onscreen) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Cannot have multiple onscreens in the KMS platform"); + return FALSE; + } + + kms_display->onscreen = onscreen; + + onscreen->winsys = g_slice_new0 (CoglOnscreenEGL); + egl_onscreen = onscreen->winsys; + + kms_onscreen = g_slice_new0 (CoglOnscreenKMS); + egl_onscreen->platform = kms_onscreen; + + /* If a kms_fd is set then the display width and height + * won't be available until cogl_kms_display_set_layout + * is called. In that case, defer creating the surface + * until then. + */ + if (kms_display->width == 0 || + kms_display->height == 0) + return TRUE; + + kms_onscreen->surface = + gbm_surface_create (kms_renderer->gbm, + kms_display->width, + kms_display->height, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | + GBM_BO_USE_RENDERING); + + if (!kms_onscreen->surface) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Failed to allocate surface"); + return FALSE; + } + + egl_onscreen->egl_surface = + eglCreateWindowSurface (egl_renderer->edpy, + egl_display->egl_config, + (EGLNativeWindowType) kms_onscreen->surface, + NULL); + if (egl_onscreen->egl_surface == EGL_NO_SURFACE) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Failed to allocate surface"); + return FALSE; + } + + _cogl_framebuffer_winsys_update_size (framebuffer, + kms_display->width, + kms_display->height); + + return TRUE; +} + +static void +_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen; + + /* If we never successfully allocated then there's nothing to do */ + if (egl_onscreen == NULL) + return; + + kms_display->onscreen = NULL; + + kms_onscreen = egl_onscreen->platform; + + /* flip state takes a reference on the onscreen so there should + * never be outstanding flips when we reach here. */ + g_return_if_fail (kms_onscreen->next_fb_id == 0); + + free_current_bo (onscreen); + + if (egl_onscreen->egl_surface != EGL_NO_SURFACE) + { + eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface); + egl_onscreen->egl_surface = EGL_NO_SURFACE; + } + + if (kms_onscreen->surface) + { + gbm_surface_destroy (kms_onscreen->surface); + kms_onscreen->surface = NULL; + } + + g_slice_free (CoglOnscreenKMS, kms_onscreen); + g_slice_free (CoglOnscreenEGL, onscreen->winsys); + onscreen->winsys = NULL; +} + +static const CoglWinsysEGLVtable +_cogl_winsys_egl_vtable = + { + .display_setup = _cogl_winsys_egl_display_setup, + .display_destroy = _cogl_winsys_egl_display_destroy, + .context_created = _cogl_winsys_egl_context_created, + .cleanup_context = _cogl_winsys_egl_cleanup_context, + .context_init = _cogl_winsys_egl_context_init + }; + +const CoglWinsysVtable * +_cogl_winsys_egl_kms_get_vtable (void) +{ + static CoglBool vtable_inited = FALSE; + static CoglWinsysVtable vtable; + + if (!vtable_inited) + { + /* The EGL_KMS winsys is a subclass of the EGL winsys so we + start by copying its vtable */ + + parent_vtable = _cogl_winsys_egl_get_vtable (); + vtable = *parent_vtable; + + vtable.id = COGL_WINSYS_ID_EGL_KMS; + vtable.name = "EGL_KMS"; + + vtable.renderer_connect = _cogl_winsys_renderer_connect; + vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect; + + vtable.onscreen_init = _cogl_winsys_onscreen_init; + vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit; + + /* The KMS winsys doesn't support swap region */ + vtable.onscreen_swap_region = NULL; + vtable.onscreen_swap_buffers_with_damage = + _cogl_winsys_onscreen_swap_buffers_with_damage; + + vtable_inited = TRUE; + } + + return &vtable; +} + +void +cogl_kms_renderer_set_kms_fd (CoglRenderer *renderer, + int fd) +{ + _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer)); + /* NB: Renderers are considered immutable once connected */ + _COGL_RETURN_IF_FAIL (!renderer->connected); + + renderer->kms_fd = fd; +} + +struct gbm_device * +cogl_kms_renderer_get_gbm (CoglRenderer *renderer) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL); + if (renderer->connected) + { + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + return kms_renderer->gbm; + } + else + return NULL; +} + +int +cogl_kms_renderer_get_kms_fd (CoglRenderer *renderer) +{ + _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), -1); + + if (renderer->connected) + { + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + return kms_renderer->fd; + } + else + return -1; +} + +void +cogl_kms_display_queue_modes_reset (CoglDisplay *display) +{ + if (display->setup) + { + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + kms_display->pending_set_crtc = TRUE; + } +} + +CoglBool +cogl_kms_display_set_layout (CoglDisplay *display, + int width, + int height, + CoglKmsCrtc **crtcs, + int n_crtcs, + CoglError **error) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + GList *crtc_list; + int i; + + if ((width != kms_display->width || + height != kms_display->height) && + kms_display->onscreen) + { + CoglOnscreenEGL *egl_onscreen = kms_display->onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + struct gbm_surface *new_surface; + EGLSurface new_egl_surface; + + /* Need to drop the GBM surface and create a new one */ + + new_surface = gbm_surface_create (kms_renderer->gbm, + width, height, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | + GBM_BO_USE_RENDERING); + + if (!new_surface) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Failed to allocate new surface"); + return FALSE; + } + + new_egl_surface = + eglCreateWindowSurface (egl_renderer->edpy, + egl_display->egl_config, + (EGLNativeWindowType) new_surface, + NULL); + if (new_egl_surface == EGL_NO_SURFACE) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Failed to allocate new surface"); + gbm_surface_destroy (new_surface); + return FALSE; + } + + if (kms_onscreen->pending_egl_surface) + eglDestroySurface (egl_renderer->edpy, kms_onscreen->pending_egl_surface); + if (kms_onscreen->pending_surface) + gbm_surface_destroy (kms_onscreen->pending_surface); + + /* If there's already a surface, wait until the next swap to switch + * it out, otherwise, if we're just starting up we can use the new + * surface right away. + */ + if (kms_onscreen->surface != NULL) + { + kms_onscreen->pending_surface = new_surface; + kms_onscreen->pending_egl_surface = new_egl_surface; + } + else + { + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (kms_display->onscreen); + + kms_onscreen->surface = new_surface; + egl_onscreen->egl_surface = new_egl_surface; + + _cogl_framebuffer_winsys_update_size (framebuffer, width, height); + } + } + + kms_display->width = width; + kms_display->height = height; + + g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free); + + crtc_list = NULL; + for (i = 0; i < n_crtcs; i++) + { + crtc_list = g_list_prepend (crtc_list, crtc_copy (crtcs[i])); + } + crtc_list = g_list_reverse (crtc_list); + kms_display->crtcs = crtc_list; + + kms_display->pending_set_crtc = TRUE; + + return TRUE; +} + + +void +cogl_kms_display_set_ignore_crtc (CoglDisplay *display, + uint32_t id, + CoglBool ignore) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayKMS *kms_display = egl_display->platform; + GList *l; + + for (l = kms_display->crtcs; l; l = l->next) + { + CoglKmsCrtc *crtc = l->data; + if (crtc->id == id) + { + crtc->ignore = ignore; + break; + } + } +} diff --git a/cogl/cogl/winsys/cogl-winsys-egl-private.h b/cogl/cogl/winsys/cogl-winsys-egl-private.h new file mode 100644 index 000000000..5d21b4f4e --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-private.h @@ -0,0 +1,203 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_WINSYS_EGL_PRIVATE_H +#define __COGL_WINSYS_EGL_PRIVATE_H + +#include "cogl-defines.h" +#include "cogl-winsys-private.h" +#include "cogl-context.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" + +/* XXX: depending on what version of Mesa you have then + * eglQueryWaylandBuffer may take a wl_buffer or wl_resource argument + * and the EGL header will only forward declare the corresponding + * type. + * + * The use of wl_buffer has been deprecated and so internally we + * assume that eglQueryWaylandBuffer takes a wl_resource but for + * compatibility we forward declare wl_resource in case we are + * building with EGL headers that still use wl_buffer. + * + * Placing the forward declaration here means it comes before we + * #include cogl-winsys-egl-feature-functions.h bellow which + * declares lots of function pointers for accessing EGL extensions + * and cogl-winsys-egl.c will include this header before it also + * includes cogl-winsys-egl-feature-functions.h that may depend + * on this type. + */ +#ifdef EGL_WL_bind_wayland_display +struct wl_resource; +#endif + +typedef struct _CoglWinsysEGLVtable +{ + CoglBool + (* display_setup) (CoglDisplay *display, + CoglError **error); + void + (* display_destroy) (CoglDisplay *display); + + CoglBool + (* context_created) (CoglDisplay *display, + CoglError **error); + + void + (* cleanup_context) (CoglDisplay *display); + + CoglBool + (* context_init) (CoglContext *context, CoglError **error); + + void + (* context_deinit) (CoglContext *context); + + CoglBool + (* onscreen_init) (CoglOnscreen *onscreen, + EGLConfig config, + CoglError **error); + void + (* onscreen_deinit) (CoglOnscreen *onscreen); + + int + (* add_config_attributes) (CoglDisplay *display, + CoglFramebufferConfig *config, + EGLint *attributes); +} CoglWinsysEGLVtable; + +typedef enum _CoglEGLWinsysFeature +{ + COGL_EGL_WINSYS_FEATURE_SWAP_REGION =1L<<0, + COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP =1L<<1, + COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER =1L<<2, + COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT =1L<<3, + COGL_EGL_WINSYS_FEATURE_BUFFER_AGE =1L<<4, + COGL_EGL_WINSYS_FEATURE_FENCE_SYNC =1L<<5, + COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT =1L<<6 +} CoglEGLWinsysFeature; + +typedef struct _CoglRendererEGL +{ + CoglEGLWinsysFeature private_features; + + EGLDisplay edpy; + + EGLint egl_version_major; + EGLint egl_version_minor; + + CoglClosure *resize_notify_idle; + + /* Data specific to the EGL platform */ + void *platform; + /* vtable for platform specific parts */ + const CoglWinsysEGLVtable *platform_vtable; + + /* Function pointers for EGL specific extensions */ +#define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d) + +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ + ret (APIENTRY * pf_ ## name) args; + +#define COGL_WINSYS_FEATURE_END() + +#include "cogl-winsys-egl-feature-functions.h" + +#undef COGL_WINSYS_FEATURE_BEGIN +#undef COGL_WINSYS_FEATURE_FUNCTION +#undef COGL_WINSYS_FEATURE_END +} CoglRendererEGL; + +typedef struct _CoglDisplayEGL +{ + EGLContext egl_context; + EGLSurface dummy_surface; + EGLSurface egl_surface; + + EGLConfig egl_config; + CoglBool found_egl_config; + + EGLSurface current_read_surface; + EGLSurface current_draw_surface; + EGLContext current_context; + + /* Platform specific display data */ + void *platform; +} CoglDisplayEGL; + +typedef struct _CoglContextEGL +{ + EGLSurface saved_draw_surface; + EGLSurface saved_read_surface; +} CoglContextEGL; + +typedef struct _CoglOnscreenEGL +{ + EGLSurface egl_surface; + + CoglBool pending_resize_notify; + + /* Platform specific data */ + void *platform; +} CoglOnscreenEGL; + +const CoglWinsysVtable * +_cogl_winsys_egl_get_vtable (void); + +EGLBoolean +_cogl_winsys_egl_make_current (CoglDisplay *display, + EGLSurface draw, + EGLSurface read, + EGLContext context); + +#ifdef EGL_KHR_image_base +EGLImageKHR +_cogl_egl_create_image (CoglContext *ctx, + EGLenum target, + EGLClientBuffer buffer, + const EGLint *attribs); + +void +_cogl_egl_destroy_image (CoglContext *ctx, + EGLImageKHR image); +#endif + +#ifdef EGL_WL_bind_wayland_display +CoglBool +_cogl_egl_query_wayland_buffer (CoglContext *ctx, + struct wl_resource *buffer, + int attribute, + int *value); +#endif + +CoglBool +_cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer, + CoglError **error); + +#endif /* __COGL_WINSYS_EGL_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11-private.h b/cogl/cogl/winsys/cogl-winsys-egl-x11-private.h new file mode 100644 index 000000000..206d4850d --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-x11-private.h @@ -0,0 +1,39 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_WINSYS_EGL_X11_PRIVATE_H +#define __COGL_WINSYS_EGL_X11_PRIVATE_H + +#include "cogl-winsys-private.h" + +const CoglWinsysVtable * +_cogl_winsys_egl_xlib_get_vtable (void); + +#endif /* __COGL_WINSYS_EGL_X11_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/cogl/winsys/cogl-winsys-egl-x11.c new file mode 100644 index 000000000..724a4d01a --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl-x11.c @@ -0,0 +1,856 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-winsys-egl-x11-private.h" +#include "cogl-winsys-egl-private.h" +#include "cogl-xlib-renderer-private.h" +#include "cogl-xlib-renderer.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-display-private.h" +#include "cogl-renderer-private.h" + +#include "cogl-texture-pixmap-x11-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-error-private.h" +#include "cogl-poll-private.h" + +#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask) + +static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable; + +typedef struct _CoglDisplayXlib +{ + Window dummy_xwin; +} CoglDisplayXlib; + +typedef struct _CoglOnscreenXlib +{ + Window xwin; + CoglBool is_foreign_xwin; +} CoglOnscreenXlib; + +#ifdef EGL_KHR_image_pixmap +typedef struct _CoglTexturePixmapEGL +{ + EGLImageKHR image; + CoglTexture *texture; +} CoglTexturePixmapEGL; +#endif + +static CoglOnscreen * +find_onscreen_for_xid (CoglContext *context, uint32_t xid) +{ + GList *l; + + for (l = context->framebuffers; l; l = l->next) + { + CoglFramebuffer *framebuffer = l->data; + CoglOnscreenEGL *egl_onscreen; + CoglOnscreenXlib *xlib_onscreen; + + if (!framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + continue; + + egl_onscreen = COGL_ONSCREEN (framebuffer)->winsys; + xlib_onscreen = egl_onscreen->platform; + if (xlib_onscreen->xwin == (Window)xid) + return COGL_ONSCREEN (framebuffer); + } + + return NULL; +} + +static void +flush_pending_resize_notifications_cb (void *data, + void *user_data) +{ + CoglFramebuffer *framebuffer = data; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + + if (egl_onscreen->pending_resize_notify) + { + _cogl_onscreen_notify_resize (onscreen); + egl_onscreen->pending_resize_notify = FALSE; + } + } +} + +static void +flush_pending_resize_notifications_idle (void *user_data) +{ + CoglContext *context = user_data; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + + /* This needs to be disconnected before invoking the callbacks in + * case the callbacks cause it to be queued again */ + _cogl_closure_disconnect (egl_renderer->resize_notify_idle); + egl_renderer->resize_notify_idle = NULL; + + g_list_foreach (context->framebuffers, + flush_pending_resize_notifications_cb, + NULL); +} + +static void +notify_resize (CoglContext *context, + Window drawable, + int width, + int height) +{ + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreen *onscreen = find_onscreen_for_xid (context, drawable); + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEGL *egl_onscreen; + + if (!onscreen) + return; + + egl_onscreen = onscreen->winsys; + + _cogl_framebuffer_winsys_update_size (framebuffer, width, height); + + /* We only want to notify that a resize happened when the + * application calls cogl_context_dispatch so instead of immediately + * notifying we queue an idle callback */ + if (!egl_renderer->resize_notify_idle) + { + egl_renderer->resize_notify_idle = + _cogl_poll_renderer_add_idle (renderer, + flush_pending_resize_notifications_idle, + context, + NULL); + } + + egl_onscreen->pending_resize_notify = TRUE; +} + +static CoglFilterReturn +event_filter_cb (XEvent *xevent, void *data) +{ + CoglContext *context = data; + + if (xevent->type == ConfigureNotify) + { + notify_resize (context, + xevent->xconfigure.window, + xevent->xconfigure.width, + xevent->xconfigure.height); + } + else if (xevent->type == Expose) + { + CoglOnscreen *onscreen = + find_onscreen_for_xid (context, xevent->xexpose.window); + + if (onscreen) + { + CoglOnscreenDirtyInfo info; + + info.x = xevent->xexpose.x; + info.y = xevent->xexpose.y; + info.width = xevent->xexpose.width; + info.height = xevent->xexpose.height; + + _cogl_onscreen_queue_dirty (onscreen, &info); + } + } + + return COGL_FILTER_CONTINUE; +} + +static XVisualInfo * +get_visual_info (CoglDisplay *display, EGLConfig egl_config) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglRendererEGL *egl_renderer = display->renderer->winsys; + XVisualInfo visinfo_template; + int template_mask = 0; + XVisualInfo *visinfo = NULL; + int visinfos_count; + EGLint visualid, red_size, green_size, blue_size, alpha_size; + + eglGetConfigAttrib (egl_renderer->edpy, egl_config, + EGL_NATIVE_VISUAL_ID, &visualid); + + if (visualid != 0) + { + visinfo_template.visualid = visualid; + template_mask |= VisualIDMask; + } + else + { + /* some EGL drivers don't implement the EGL_NATIVE_VISUAL_ID + * attribute, so attempt to find the closest match. */ + + eglGetConfigAttrib (egl_renderer->edpy, egl_config, + EGL_RED_SIZE, &red_size); + eglGetConfigAttrib (egl_renderer->edpy, egl_config, + EGL_GREEN_SIZE, &green_size); + eglGetConfigAttrib (egl_renderer->edpy, egl_config, + EGL_BLUE_SIZE, &blue_size); + eglGetConfigAttrib (egl_renderer->edpy, egl_config, + EGL_ALPHA_SIZE, &alpha_size); + + visinfo_template.depth = red_size + green_size + blue_size + alpha_size; + template_mask |= VisualDepthMask; + + visinfo_template.screen = DefaultScreen (xlib_renderer->xdpy); + template_mask |= VisualScreenMask; + } + + visinfo = XGetVisualInfo (xlib_renderer->xdpy, + template_mask, + &visinfo_template, + &visinfos_count); + + return visinfo; +} + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + CoglRendererEGL *egl_renderer = renderer->winsys; + + _cogl_xlib_renderer_disconnect (renderer); + + eglTerminate (egl_renderer->edpy); + + g_slice_free (CoglRendererEGL, egl_renderer); +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + CoglError **error) +{ + CoglRendererEGL *egl_renderer; + CoglXlibRenderer *xlib_renderer; + + renderer->winsys = g_slice_new0 (CoglRendererEGL); + egl_renderer = renderer->winsys; + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable; + + if (!_cogl_xlib_renderer_connect (renderer, error)) + goto error; + + egl_renderer->edpy = + eglGetDisplay ((EGLNativeDisplayType) xlib_renderer->xdpy); + + if (!_cogl_winsys_egl_renderer_connect_common (renderer, error)) + goto error; + + return TRUE; + +error: + _cogl_winsys_renderer_disconnect (renderer); + return FALSE; +} + +static CoglBool +_cogl_winsys_egl_display_setup (CoglDisplay *display, + CoglError **error) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayXlib *xlib_display; + + xlib_display = g_slice_new0 (CoglDisplayXlib); + egl_display->platform = xlib_display; + + return TRUE; +} + +static void +_cogl_winsys_egl_display_destroy (CoglDisplay *display) +{ + CoglDisplayEGL *egl_display = display->winsys; + + g_slice_free (CoglDisplayXlib, egl_display->platform); +} + +static CoglBool +_cogl_winsys_egl_context_init (CoglContext *context, + CoglError **error) +{ + cogl_xlib_renderer_add_filter (context->display->renderer, + event_filter_cb, + context); + + context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE; + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE); + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, + TRUE); + + /* We'll manually handle queueing dirty events in response to + * Expose events from X */ + COGL_FLAGS_SET (context->private_features, + COGL_PRIVATE_FEATURE_DIRTY_EVENTS, + TRUE); + + return TRUE; +} + +static void +_cogl_winsys_egl_context_deinit (CoglContext *context) +{ + cogl_xlib_renderer_remove_filter (context->display->renderer, + event_filter_cb, + context); +} + +static CoglBool +_cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen, + EGLConfig egl_config, + CoglError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglOnscreenXlib *xlib_onscreen; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + Window xwin; + + /* FIXME: We need to explicitly Select for ConfigureNotify events. + * For foreign windows we need to be careful not to mess up any + * existing event mask. + * We need to document that for windows we create then toolkits + * must be careful not to clear event mask bits that we select. + */ + + /* XXX: Note we ignore the user's original width/height when + * given a foreign X window. */ + if (onscreen->foreign_xid) + { + Status status; + CoglXlibTrapState state; + XWindowAttributes attr; + int xerror; + + xwin = onscreen->foreign_xid; + + _cogl_xlib_renderer_trap_errors (display->renderer, &state); + + status = XGetWindowAttributes (xlib_renderer->xdpy, xwin, &attr); + xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, + &state); + if (status == 0 || xerror) + { + char message[1000]; + XGetErrorText (xlib_renderer->xdpy, xerror, + message, sizeof (message)); + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Unable to query geometry of foreign " + "xid 0x%08lX: %s", + xwin, message); + return FALSE; + } + + _cogl_framebuffer_winsys_update_size (framebuffer, + attr.width, attr.height); + + /* Make sure the app selects for the events we require... */ + onscreen->foreign_update_mask_callback (onscreen, + COGL_ONSCREEN_X11_EVENT_MASK, + onscreen-> + foreign_update_mask_data); + } + else + { + int width; + int height; + CoglXlibTrapState state; + XVisualInfo *xvisinfo; + XSetWindowAttributes xattr; + unsigned long mask; + int xerror; + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + + _cogl_xlib_renderer_trap_errors (display->renderer, &state); + + xvisinfo = get_visual_info (display, egl_config); + if (xvisinfo == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Unable to retrieve the X11 visual of context's " + "fbconfig"); + return FALSE; + } + + /* window attributes */ + xattr.background_pixel = + WhitePixel (xlib_renderer->xdpy, + DefaultScreen (xlib_renderer->xdpy)); + xattr.border_pixel = 0; + /* XXX: is this an X resource that we are leaking‽... */ + xattr.colormap = + XCreateColormap (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + xvisinfo->visual, + AllocNone); + xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK; + + mask = CWBorderPixel | CWColormap | CWEventMask; + + xwin = XCreateWindow (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + 0, 0, + width, height, + 0, + xvisinfo->depth, + InputOutput, + xvisinfo->visual, + mask, &xattr); + + XFree (xvisinfo); + + XSync (xlib_renderer->xdpy, False); + xerror = + _cogl_xlib_renderer_untrap_errors (display->renderer, &state); + if (xerror) + { + char message[1000]; + XGetErrorText (xlib_renderer->xdpy, xerror, + message, sizeof (message)); + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "X error while creating Window for CoglOnscreen: %s", + message); + return FALSE; + } + } + + xlib_onscreen = g_slice_new (CoglOnscreenXlib); + egl_onscreen->platform = xlib_onscreen; + + xlib_onscreen->xwin = xwin; + xlib_onscreen->is_foreign_xwin = onscreen->foreign_xid ? TRUE : FALSE; + + egl_onscreen->egl_surface = + eglCreateWindowSurface (egl_renderer->edpy, + egl_config, + (EGLNativeWindowType) xlib_onscreen->xwin, + NULL); + + return TRUE; +} + +static void +_cogl_winsys_egl_onscreen_deinit (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglRenderer *renderer = context->display->renderer; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglXlibTrapState old_state; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; + + _cogl_xlib_renderer_trap_errors (renderer, &old_state); + + if (!xlib_onscreen->is_foreign_xwin && xlib_onscreen->xwin != None) + { + XDestroyWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); + xlib_onscreen->xwin = None; + } + else + xlib_onscreen->xwin = None; + + XSync (xlib_renderer->xdpy, False); + + if (_cogl_xlib_renderer_untrap_errors (renderer, + &old_state) != Success) + g_warning ("X Error while destroying X window"); + + g_slice_free (CoglOnscreenXlib, xlib_onscreen); +} + +static void +_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, + CoglBool visibility) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglOnscreenEGL *onscreen_egl = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen_egl->platform; + + if (visibility) + XMapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); + else + XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); +} + +static void +_cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; + + XSizeHints *size_hints = XAllocSizeHints (); + + if (resizable) + { + /* TODO: Add cogl_onscreen_request_minimum_size () */ + size_hints->min_width = 1; + size_hints->min_height = 1; + + size_hints->max_width = INT_MAX; + size_hints->max_height = INT_MAX; + } + else + { + int width = cogl_framebuffer_get_width (framebuffer); + int height = cogl_framebuffer_get_height (framebuffer); + + size_hints->min_width = width; + size_hints->min_height = height; + + size_hints->max_width = width; + size_hints->max_height = height; + } + + XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints); + + XFree (size_hints); +} + +static uint32_t +_cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen) +{ + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; + + return xlib_onscreen->xwin; +} + +static CoglBool +_cogl_winsys_egl_context_created (CoglDisplay *display, + CoglError **error) +{ + CoglRenderer *renderer = display->renderer; + CoglDisplayEGL *egl_display = display->winsys; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglDisplayXlib *xlib_display = egl_display->platform; + XVisualInfo *xvisinfo; + XSetWindowAttributes attrs; + const char *error_message; + + xvisinfo = get_visual_info (display, egl_display->egl_config); + if (xvisinfo == NULL) + { + error_message = "Unable to find suitable X visual"; + goto fail; + } + + attrs.override_redirect = True; + attrs.colormap = XCreateColormap (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + xvisinfo->visual, + AllocNone); + attrs.border_pixel = 0; + + if ((egl_renderer->private_features & + COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0) + { + xlib_display->dummy_xwin = + XCreateWindow (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + -100, -100, 1, 1, + 0, + xvisinfo->depth, + CopyFromParent, + xvisinfo->visual, + CWOverrideRedirect | + CWColormap | + CWBorderPixel, + &attrs); + + egl_display->dummy_surface = + eglCreateWindowSurface (egl_renderer->edpy, + egl_display->egl_config, + (EGLNativeWindowType) xlib_display->dummy_xwin, + NULL); + + if (egl_display->dummy_surface == EGL_NO_SURFACE) + { + error_message = "Unable to create an EGL surface"; + XFree (xvisinfo); + goto fail; + } + } + + xlib_renderer->xvisinfo = xvisinfo; + + if (!_cogl_winsys_egl_make_current (display, + egl_display->dummy_surface, + egl_display->dummy_surface, + egl_display->egl_context)) + { + if (egl_display->dummy_surface == EGL_NO_SURFACE) + error_message = "Unable to eglMakeCurrent with no surface"; + else + error_message = "Unable to eglMakeCurrent with dummy surface"; + goto fail; + } + + return TRUE; + +fail: + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "%s", error_message); + return FALSE; +} + +static void +_cogl_winsys_egl_cleanup_context (CoglDisplay *display) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglDisplayXlib *xlib_display = egl_display->platform; + CoglRenderer *renderer = display->renderer; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (egl_display->dummy_surface != EGL_NO_SURFACE) + { + eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface); + egl_display->dummy_surface = EGL_NO_SURFACE; + } + + if (xlib_display->dummy_xwin) + { + XDestroyWindow (xlib_renderer->xdpy, xlib_display->dummy_xwin); + xlib_display->dummy_xwin = None; + } +} + +#ifdef EGL_KHR_image_pixmap + +static CoglBool +_cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + CoglContext *ctx = tex->context; + CoglTexturePixmapEGL *egl_tex_pixmap; + EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; + CoglPixelFormat texture_format; + CoglRendererEGL *egl_renderer; + + egl_renderer = ctx->display->renderer->winsys; + + if (!(egl_renderer->private_features & + COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP) || + !_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE)) + { + tex_pixmap->winsys = NULL; + return FALSE; + } + + egl_tex_pixmap = g_new0 (CoglTexturePixmapEGL, 1); + + egl_tex_pixmap->image = + _cogl_egl_create_image (ctx, + EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)tex_pixmap->pixmap, + attribs); + if (egl_tex_pixmap->image == EGL_NO_IMAGE_KHR) + { + g_free (egl_tex_pixmap); + return FALSE; + } + + texture_format = (tex_pixmap->depth >= 32 ? + COGL_PIXEL_FORMAT_RGBA_8888_PRE : + COGL_PIXEL_FORMAT_RGB_888); + + egl_tex_pixmap->texture = COGL_TEXTURE ( + _cogl_egl_texture_2d_new_from_image (ctx, + tex->width, + tex->height, + texture_format, + egl_tex_pixmap->image, + NULL)); + + tex_pixmap->winsys = egl_tex_pixmap; + + return TRUE; +} + +static void +_cogl_winsys_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexturePixmapEGL *egl_tex_pixmap; + + /* FIXME: It should be possible to get to a CoglContext from any + * CoglTexture pointer. */ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!tex_pixmap->winsys) + return; + + egl_tex_pixmap = tex_pixmap->winsys; + + if (egl_tex_pixmap->texture) + cogl_object_unref (egl_tex_pixmap->texture); + + if (egl_tex_pixmap->image != EGL_NO_IMAGE_KHR) + _cogl_egl_destroy_image (ctx, egl_tex_pixmap->image); + + tex_pixmap->winsys = NULL; + g_free (egl_tex_pixmap); +} + +static CoglBool +_cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode, + CoglBool needs_mipmap) +{ + if (needs_mipmap) + return FALSE; + + return TRUE; +} + +static void +_cogl_winsys_texture_pixmap_x11_damage_notify (CoglTexturePixmapX11 *tex_pixmap) +{ +} + +static CoglTexture * +_cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode) +{ + CoglTexturePixmapEGL *egl_tex_pixmap = tex_pixmap->winsys; + + return egl_tex_pixmap->texture; +} + +#endif /* EGL_KHR_image_pixmap */ + +static const CoglWinsysEGLVtable +_cogl_winsys_egl_vtable = + { + .display_setup = _cogl_winsys_egl_display_setup, + .display_destroy = _cogl_winsys_egl_display_destroy, + .context_created = _cogl_winsys_egl_context_created, + .cleanup_context = _cogl_winsys_egl_cleanup_context, + .context_init = _cogl_winsys_egl_context_init, + .context_deinit = _cogl_winsys_egl_context_deinit, + .onscreen_init = _cogl_winsys_egl_onscreen_init, + .onscreen_deinit = _cogl_winsys_egl_onscreen_deinit + }; + +const CoglWinsysVtable * +_cogl_winsys_egl_xlib_get_vtable (void) +{ + static CoglBool vtable_inited = FALSE; + static CoglWinsysVtable vtable; + + if (!vtable_inited) + { + /* The EGL_X11 winsys is a subclass of the EGL winsys so we + start by copying its vtable */ + + vtable = *_cogl_winsys_egl_get_vtable (); + + vtable.id = COGL_WINSYS_ID_EGL_XLIB; + vtable.name = "EGL_XLIB"; + vtable.constraints |= (COGL_RENDERER_CONSTRAINT_USES_X11 | + COGL_RENDERER_CONSTRAINT_USES_XLIB); + + vtable.renderer_connect = _cogl_winsys_renderer_connect; + vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect; + + vtable.onscreen_set_visibility = + _cogl_winsys_onscreen_set_visibility; + vtable.onscreen_set_resizable = + _cogl_winsys_onscreen_set_resizable; + + vtable.onscreen_x11_get_window_xid = + _cogl_winsys_onscreen_x11_get_window_xid; + +#ifdef EGL_KHR_image_pixmap + /* X11 tfp support... */ + /* XXX: instead of having a rather monolithic winsys vtable we could + * perhaps look for a way to separate these... */ + vtable.texture_pixmap_x11_create = + _cogl_winsys_texture_pixmap_x11_create; + vtable.texture_pixmap_x11_free = + _cogl_winsys_texture_pixmap_x11_free; + vtable.texture_pixmap_x11_update = + _cogl_winsys_texture_pixmap_x11_update; + vtable.texture_pixmap_x11_damage_notify = + _cogl_winsys_texture_pixmap_x11_damage_notify; + vtable.texture_pixmap_x11_get_texture = + _cogl_winsys_texture_pixmap_x11_get_texture; +#endif /* EGL_KHR_image_pixmap) */ + + vtable_inited = TRUE; + } + + return &vtable; +} diff --git a/cogl/cogl/winsys/cogl-winsys-egl.c b/cogl/cogl/winsys/cogl-winsys-egl.c new file mode 100644 index 000000000..a53c0c7d7 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-egl.c @@ -0,0 +1,1085 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-i18n-private.h" +#include "cogl-util.h" +#include "cogl-winsys-egl-private.h" +#include "cogl-winsys-private.h" +#include "cogl-feature-private.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer.h" +#include "cogl-onscreen-private.h" +#include "cogl-swap-chain-private.h" +#include "cogl-renderer-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-gles2-context-private.h" +#include "cogl-error-private.h" +#include "cogl-egl.h" + +#include "cogl-private.h" + +#include +#include +#include +#include +#include + + +#ifndef EGL_KHR_create_context +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB +#define EGL_CONTEXT_FLAGS_KHR 0x30FC +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE +#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#endif + + +#define MAX_EGL_CONFIG_ATTRIBS 30 + +/* Define a set of arrays containing the functions required from GL + for each winsys feature */ +#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \ + egl_private_flags) \ + static const CoglFeatureFunction \ + cogl_egl_feature_ ## name ## _funcs[] = { +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ + { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglRendererEGL, pf_ ## name) }, +#define COGL_WINSYS_FEATURE_END() \ + { NULL, 0 }, \ + }; +#include "cogl-winsys-egl-feature-functions.h" + +/* Define an array of features */ +#undef COGL_WINSYS_FEATURE_BEGIN +#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \ + egl_private_flags) \ + { 255, 255, 0, namespaces, extension_names, \ + 0, egl_private_flags, \ + 0, \ + cogl_egl_feature_ ## name ## _funcs }, +#undef COGL_WINSYS_FEATURE_FUNCTION +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) +#undef COGL_WINSYS_FEATURE_END +#define COGL_WINSYS_FEATURE_END() + +static const CoglFeatureData winsys_feature_data[] = + { +#include "cogl-winsys-egl-feature-functions.h" + }; + +static const char * +get_error_string (void) +{ + switch (eglGetError()){ + case EGL_BAD_DISPLAY: + return "Invalid display"; + case EGL_NOT_INITIALIZED: + return "Display not initialized"; + case EGL_BAD_ALLOC: + return "Not enough resources to allocate context"; + case EGL_BAD_ATTRIBUTE: + return "Invalid attribute"; + case EGL_BAD_CONFIG: + return "Invalid config"; + case EGL_BAD_CONTEXT: + return "Invalid context"; + case EGL_BAD_CURRENT_SURFACE: + return "Invalid current surface"; + case EGL_BAD_MATCH: + return "Bad match"; + case EGL_BAD_NATIVE_PIXMAP: + return "Invalid native pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "Invalid native window"; + case EGL_BAD_PARAMETER: + return "Invalid parameter"; + case EGL_BAD_SURFACE: + return "Invalid surface"; + default: + g_assert_not_reached (); + } +} + +static CoglFuncPtr +_cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, + const char *name, + CoglBool in_core) +{ + void *ptr = NULL; + + if (!in_core) + ptr = eglGetProcAddress (name); + + /* eglGetProcAddress doesn't support fetching core API so we need to + get that separately with GModule */ + if (ptr == NULL) + g_module_symbol (renderer->libgl_module, name, &ptr); + + return ptr; +} + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + /* This function must be overridden by a platform winsys */ + g_assert_not_reached (); +} + +/* Updates all the function pointers */ +static void +check_egl_extensions (CoglRenderer *renderer) +{ + CoglRendererEGL *egl_renderer = renderer->winsys; + const char *egl_extensions; + char **split_extensions; + int i; + + egl_extensions = eglQueryString (egl_renderer->edpy, EGL_EXTENSIONS); + split_extensions = g_strsplit (egl_extensions, " ", 0 /* max_tokens */); + + COGL_NOTE (WINSYS, " EGL Extensions: %s", egl_extensions); + + egl_renderer->private_features = 0; + for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++) + if (_cogl_feature_check (renderer, + "EGL", winsys_feature_data + i, 0, 0, + COGL_DRIVER_GL, /* the driver isn't used */ + split_extensions, + egl_renderer)) + { + egl_renderer->private_features |= + winsys_feature_data[i].feature_flags_private; + } + + g_strfreev (split_extensions); +} + +CoglBool +_cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer, + CoglError **error) +{ + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (!eglInitialize (egl_renderer->edpy, + &egl_renderer->egl_version_major, + &egl_renderer->egl_version_minor)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Couldn't initialize EGL"); + return FALSE; + } + + check_egl_extensions (renderer); + + return TRUE; +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + CoglError **error) +{ + /* This function must be overridden by a platform winsys */ + g_assert_not_reached (); +} + +static void +egl_attributes_from_framebuffer_config (CoglDisplay *display, + CoglFramebufferConfig *config, + EGLint *attributes) +{ + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + int i = 0; + + /* Let the platform add attributes first */ + if (egl_renderer->platform_vtable->add_config_attributes) + i = egl_renderer->platform_vtable->add_config_attributes (display, + config, + attributes); + + if (config->need_stencil) + { + attributes[i++] = EGL_STENCIL_SIZE; + attributes[i++] = 2; + } + + attributes[i++] = EGL_RED_SIZE; + attributes[i++] = 1; + attributes[i++] = EGL_GREEN_SIZE; + attributes[i++] = 1; + attributes[i++] = EGL_BLUE_SIZE; + attributes[i++] = 1; + + attributes[i++] = EGL_ALPHA_SIZE; + attributes[i++] = config->swap_chain->has_alpha ? 1 : EGL_DONT_CARE; + + attributes[i++] = EGL_DEPTH_SIZE; + attributes[i++] = 1; + + attributes[i++] = EGL_BUFFER_SIZE; + attributes[i++] = EGL_DONT_CARE; + + attributes[i++] = EGL_RENDERABLE_TYPE; + attributes[i++] = ((renderer->driver == COGL_DRIVER_GL || + renderer->driver == COGL_DRIVER_GL3) ? + EGL_OPENGL_BIT : + renderer->driver == COGL_DRIVER_GLES1 ? + EGL_OPENGL_ES_BIT : + EGL_OPENGL_ES2_BIT); + + attributes[i++] = EGL_SURFACE_TYPE; + attributes[i++] = EGL_WINDOW_BIT; + + if (config->samples_per_pixel) + { + attributes[i++] = EGL_SAMPLE_BUFFERS; + attributes[i++] = 1; + attributes[i++] = EGL_SAMPLES; + attributes[i++] = config->samples_per_pixel; + } + + attributes[i++] = EGL_NONE; + + g_assert (i < MAX_EGL_CONFIG_ATTRIBS); +} + +EGLBoolean +_cogl_winsys_egl_make_current (CoglDisplay *display, + EGLSurface draw, + EGLSurface read, + EGLContext context) +{ + CoglDisplayEGL *egl_display = display->winsys; + CoglRendererEGL *egl_renderer = display->renderer->winsys; + EGLBoolean ret; + + if (egl_display->current_draw_surface == draw && + egl_display->current_read_surface == read && + egl_display->current_context == context) + return EGL_TRUE; + + ret = eglMakeCurrent (egl_renderer->edpy, + draw, + read, + context); + + egl_display->current_draw_surface = draw; + egl_display->current_read_surface = read; + egl_display->current_context = context; + + return ret; +} + +static void +cleanup_context (CoglDisplay *display) +{ + CoglRenderer *renderer = display->renderer; + CoglDisplayEGL *egl_display = display->winsys; + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (egl_display->egl_context != EGL_NO_CONTEXT) + { + _cogl_winsys_egl_make_current (display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglDestroyContext (egl_renderer->edpy, egl_display->egl_context); + egl_display->egl_context = EGL_NO_CONTEXT; + } + + if (egl_renderer->platform_vtable->cleanup_context) + egl_renderer->platform_vtable->cleanup_context (display); +} + +static CoglBool +try_create_context (CoglDisplay *display, + CoglError **error) +{ + CoglRenderer *renderer = display->renderer; + CoglDisplayEGL *egl_display = display->winsys; + CoglRendererEGL *egl_renderer = renderer->winsys; + EGLDisplay edpy; + EGLConfig config; + EGLint config_count = 0; + EGLBoolean status; + EGLint attribs[9]; + EGLint cfg_attribs[MAX_EGL_CONFIG_ATTRIBS]; + const char *error_message; + + _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context == NULL, TRUE); + + if (renderer->driver == COGL_DRIVER_GL || + renderer->driver == COGL_DRIVER_GL3) + eglBindAPI (EGL_OPENGL_API); + + egl_attributes_from_framebuffer_config (display, + &display->onscreen_template->config, + cfg_attribs); + + edpy = egl_renderer->edpy; + + status = eglChooseConfig (edpy, + cfg_attribs, + &config, 1, + &config_count); + if (status != EGL_TRUE || config_count == 0) + { + error_message = "Unable to find a usable EGL configuration"; + goto fail; + } + + egl_display->egl_config = config; + + if (display->renderer->driver == COGL_DRIVER_GL3) + { + if (!(egl_renderer->private_features & + COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT)) + { + error_message = "Driver does not support GL 3 contexts"; + goto fail; + } + + /* Try to get a core profile 3.1 context with no deprecated features */ + attribs[0] = EGL_CONTEXT_MAJOR_VERSION_KHR; + attribs[1] = 3; + attribs[2] = EGL_CONTEXT_MINOR_VERSION_KHR; + attribs[3] = 1; + attribs[4] = EGL_CONTEXT_FLAGS_KHR; + attribs[5] = EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; + attribs[6] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + attribs[7] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + attribs[8] = EGL_NONE; + } + else if (display->renderer->driver == COGL_DRIVER_GLES2) + { + attribs[0] = EGL_CONTEXT_CLIENT_VERSION; + attribs[1] = 2; + attribs[2] = EGL_NONE; + } + else + attribs[0] = EGL_NONE; + + egl_display->egl_context = eglCreateContext (edpy, + config, + EGL_NO_CONTEXT, + attribs); + + if (egl_display->egl_context == EGL_NO_CONTEXT) + { + error_message = "Unable to create a suitable EGL context"; + goto fail; + } + + if (egl_renderer->platform_vtable->context_created && + !egl_renderer->platform_vtable->context_created (display, error)) + return FALSE; + + return TRUE; + +fail: + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "%s", error_message); + + cleanup_context (display); + + return FALSE; +} + +static void +_cogl_winsys_display_destroy (CoglDisplay *display) +{ + CoglRendererEGL *egl_renderer = display->renderer->winsys; + CoglDisplayEGL *egl_display = display->winsys; + + _COGL_RETURN_IF_FAIL (egl_display != NULL); + + cleanup_context (display); + + if (egl_renderer->platform_vtable->display_destroy) + egl_renderer->platform_vtable->display_destroy (display); + + g_slice_free (CoglDisplayEGL, display->winsys); + display->winsys = NULL; +} + +static CoglBool +_cogl_winsys_display_setup (CoglDisplay *display, + CoglError **error) +{ + CoglDisplayEGL *egl_display; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + + _COGL_RETURN_VAL_IF_FAIL (display->winsys == NULL, FALSE); + + egl_display = g_slice_new0 (CoglDisplayEGL); + display->winsys = egl_display; + +#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT + if (display->wayland_compositor_display) + { + struct wl_display *wayland_display = display->wayland_compositor_display; + CoglRendererEGL *egl_renderer = display->renderer->winsys; + + if (egl_renderer->pf_eglBindWaylandDisplay) + egl_renderer->pf_eglBindWaylandDisplay (egl_renderer->edpy, + wayland_display); + } +#endif + + if (egl_renderer->platform_vtable->display_setup && + !egl_renderer->platform_vtable->display_setup (display, error)) + goto error; + + if (!try_create_context (display, error)) + goto error; + + egl_display->found_egl_config = TRUE; + + return TRUE; + +error: + _cogl_winsys_display_destroy (display); + return FALSE; +} + +static CoglBool +_cogl_winsys_context_init (CoglContext *context, CoglError **error) +{ + CoglRenderer *renderer = context->display->renderer; + CoglDisplayEGL *egl_display = context->display->winsys; + CoglRendererEGL *egl_renderer = renderer->winsys; + + context->winsys = g_new0 (CoglContextEGL, 1); + + _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE); + + memset (context->winsys_features, 0, sizeof (context->winsys_features)); + + check_egl_extensions (renderer); + + if (!_cogl_context_update_features (context, error)) + return FALSE; + + if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SWAP_REGION) + { + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION, TRUE); + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); + } + + if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) && + _cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_OES_EGL_SYNC)) + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE); + + if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE) + { + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_BUFFER_AGE, + TRUE); + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE); + } + + /* NB: We currently only support creating standalone GLES2 contexts + * for offscreen rendering and so we need a dummy (non-visible) + * surface to be able to bind those contexts */ + if (egl_display->dummy_surface != EGL_NO_SURFACE && + context->driver == COGL_DRIVER_GLES2) + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_GLES2_CONTEXT, TRUE); + + if (egl_renderer->platform_vtable->context_init && + !egl_renderer->platform_vtable->context_init (context, error)) + return FALSE; + + return TRUE; +} + +static void +_cogl_winsys_context_deinit (CoglContext *context) +{ + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (egl_renderer->platform_vtable->context_deinit) + egl_renderer->platform_vtable->context_deinit (context); + + g_free (context->winsys); +} + +typedef struct _CoglGLES2ContextEGL +{ + EGLContext egl_context; + EGLSurface dummy_surface; +} CoglGLES2ContextEGL; + +static void * +_cogl_winsys_context_create_gles2_context (CoglContext *ctx, CoglError **error) +{ + CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; + CoglDisplayEGL *egl_display = ctx->display->winsys; + EGLint attribs[3]; + EGLContext egl_context; + + attribs[0] = EGL_CONTEXT_CLIENT_VERSION; + attribs[1] = 2; + attribs[2] = EGL_NONE; + + egl_context = eglCreateContext (egl_renderer->edpy, + egl_display->egl_config, + egl_display->egl_context, + attribs); + if (egl_context == EGL_NO_CONTEXT) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_GLES2_CONTEXT, + "%s", get_error_string ()); + return NULL; + } + + return (void *)egl_context; +} + +static void +_cogl_winsys_destroy_gles2_context (CoglGLES2Context *gles2_ctx) +{ + CoglContext *context = gles2_ctx->context; + CoglDisplay *display = context->display; + CoglDisplayEGL *egl_display = display->winsys; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + EGLContext egl_context = gles2_ctx->winsys; + + _COGL_RETURN_IF_FAIL (egl_display->current_context != egl_context); + + eglDestroyContext (egl_renderer->edpy, egl_context); +} + +static CoglBool +_cogl_winsys_onscreen_init (CoglOnscreen *onscreen, + CoglError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglDisplayEGL *egl_display = display->winsys; + CoglRenderer *renderer = display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + EGLint attributes[MAX_EGL_CONFIG_ATTRIBS]; + EGLConfig egl_config; + EGLint config_count = 0; + EGLBoolean status; + + _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE); + + egl_attributes_from_framebuffer_config (display, + &framebuffer->config, + attributes); + + status = eglChooseConfig (egl_renderer->edpy, + attributes, + &egl_config, 1, + &config_count); + if (status != EGL_TRUE || config_count == 0) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Failed to find a suitable EGL configuration"); + return FALSE; + } + + /* Update the real number of samples_per_pixel now that we have + * found an egl_config... */ + if (framebuffer->config.samples_per_pixel) + { + EGLint samples; + status = eglGetConfigAttrib (egl_renderer->edpy, + egl_config, + EGL_SAMPLES, &samples); + g_return_val_if_fail (status == EGL_TRUE, TRUE); + framebuffer->samples_per_pixel = samples; + } + + onscreen->winsys = g_slice_new0 (CoglOnscreenEGL); + + if (egl_renderer->platform_vtable->onscreen_init && + !egl_renderer->platform_vtable->onscreen_init (onscreen, + egl_config, + error)) + { + g_slice_free (CoglOnscreenEGL, onscreen->winsys); + return FALSE; + } + + return TRUE; +} + +static void +_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplayEGL *egl_display = context->display->winsys; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + + /* If we never successfully allocated then there's nothing to do */ + if (egl_onscreen == NULL) + return; + + if (egl_onscreen->egl_surface != EGL_NO_SURFACE) + { + /* Cogl always needs a valid context bound to something so if we + * are destroying the onscreen that is currently bound we'll + * switch back to the dummy drawable. */ + if ((egl_display->dummy_surface != EGL_NO_SURFACE || + (egl_renderer->private_features & + COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) != 0) && + (egl_display->current_draw_surface == egl_onscreen->egl_surface || + egl_display->current_read_surface == egl_onscreen->egl_surface)) + { + _cogl_winsys_egl_make_current (context->display, + egl_display->dummy_surface, + egl_display->dummy_surface, + egl_display->current_context); + } + + if (eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface) + == EGL_FALSE) + g_warning ("Failed to destroy EGL surface"); + egl_onscreen->egl_surface = EGL_NO_SURFACE; + } + + if (egl_renderer->platform_vtable->onscreen_deinit) + egl_renderer->platform_vtable->onscreen_deinit (onscreen); + + g_slice_free (CoglOnscreenEGL, onscreen->winsys); + onscreen->winsys = NULL; +} + +static CoglBool +bind_onscreen_with_context (CoglOnscreen *onscreen, + EGLContext egl_context) +{ + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = fb->context; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + + CoglBool status = _cogl_winsys_egl_make_current (context->display, + egl_onscreen->egl_surface, + egl_onscreen->egl_surface, + egl_context); + if (status) + { + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + + if (fb->config.swap_throttled) + eglSwapInterval (egl_renderer->edpy, 1); + else + eglSwapInterval (egl_renderer->edpy, 0); + } + + return status; +} + +static CoglBool +bind_onscreen (CoglOnscreen *onscreen) +{ + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = fb->context; + CoglDisplayEGL *egl_display = context->display->winsys; + + return bind_onscreen_with_context (onscreen, egl_display->egl_context); +} + +static void +_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) +{ + bind_onscreen (onscreen); +} + +#ifndef EGL_BUFFER_AGE_EXT +#define EGL_BUFFER_AGE_EXT 0x313D +#endif + +static int +_cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + EGLSurface surface = egl_onscreen->egl_surface; + int age; + + if (!(egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE)) + return 0; + + eglQuerySurface (egl_renderer->edpy, surface, EGL_BUFFER_AGE_EXT, &age); + + return age; +} + +static void +_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, + const int *user_rectangles, + int n_rectangles) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + int framebuffer_height = cogl_framebuffer_get_height (framebuffer); + int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); + int i; + + /* eglSwapBuffersRegion expects rectangles relative to the + * bottom left corner but we are given rectangles relative to + * the top left so we need to flip them... */ + memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4); + for (i = 0; i < n_rectangles; i++) + { + int *rect = &rectangles[4 * i]; + rect[1] = framebuffer_height - rect[1] - rect[3]; + } + + /* At least for eglSwapBuffers the EGL spec says that the surface to + swap must be bound to the current context. It looks like Mesa + also validates that this is the case for eglSwapBuffersRegion so + we must bind here too */ + _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen), + COGL_FRAMEBUFFER (onscreen), + COGL_FRAMEBUFFER_STATE_BIND); + + if (egl_renderer->pf_eglSwapBuffersRegion (egl_renderer->edpy, + egl_onscreen->egl_surface, + n_rectangles, + rectangles) == EGL_FALSE) + g_warning ("Error reported by eglSwapBuffersRegion"); +} + +static void +_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + + /* The specification for EGL (at least in 1.4) says that the surface + needs to be bound to the current context for the swap to work + although it may change in future. Mesa explicitly checks for this + and just returns an error if this is not the case so we can't + just pretend this isn't in the spec. */ + _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen), + COGL_FRAMEBUFFER (onscreen), + COGL_FRAMEBUFFER_STATE_BIND); + + if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage) + { + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + size_t size = n_rectangles * sizeof (int) * 4; + int *flipped = alloca (size); + int i; + + memcpy (flipped, rectangles, size); + for (i = 0; i < n_rectangles; i++) + { + const int *rect = rectangles + 4 * i; + int *flip_rect = flipped + 4 * i; + flip_rect[1] = fb->height - rect[1] - rect[3]; + } + + if (egl_renderer->pf_eglSwapBuffersWithDamage (egl_renderer->edpy, + egl_onscreen->egl_surface, + flipped, + n_rectangles) == EGL_FALSE) + g_warning ("Error reported by eglSwapBuffersWithDamage"); + } + else + eglSwapBuffers (egl_renderer->edpy, egl_onscreen->egl_surface); +} + +static void +_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglDisplayEGL *egl_display = context->display->winsys; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + + if (egl_display->current_draw_surface != egl_onscreen->egl_surface) + return; + + egl_display->current_draw_surface = EGL_NO_SURFACE; + + _cogl_winsys_onscreen_bind (onscreen); +} + +static void +_cogl_winsys_save_context (CoglContext *ctx) +{ + CoglContextEGL *egl_context = ctx->winsys; + CoglDisplayEGL *egl_display = ctx->display->winsys; + + egl_context->saved_draw_surface = egl_display->current_draw_surface; + egl_context->saved_read_surface = egl_display->current_read_surface; +} + +static CoglBool +_cogl_winsys_set_gles2_context (CoglGLES2Context *gles2_ctx, CoglError **error) +{ + CoglContext *ctx = gles2_ctx->context; + CoglDisplayEGL *egl_display = ctx->display->winsys; + CoglBool status; + + if (gles2_ctx->write_buffer && + cogl_is_onscreen (gles2_ctx->write_buffer)) + status = + bind_onscreen_with_context (COGL_ONSCREEN (gles2_ctx->write_buffer), + gles2_ctx->winsys); + else + status = _cogl_winsys_egl_make_current (ctx->display, + egl_display->dummy_surface, + egl_display->dummy_surface, + gles2_ctx->winsys); + + if (!status) + { + _cogl_set_error (error, + COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_MAKE_CURRENT, + "Failed to make gles2 context current"); + return FALSE; + } + + return TRUE; +} + +static void +_cogl_winsys_restore_context (CoglContext *ctx) +{ + CoglContextEGL *egl_context = ctx->winsys; + CoglDisplayEGL *egl_display = ctx->display->winsys; + + _cogl_winsys_egl_make_current (ctx->display, + egl_context->saved_draw_surface, + egl_context->saved_read_surface, + egl_display->egl_context); +} + +#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) +static void * +_cogl_winsys_fence_add (CoglContext *context) +{ + CoglRendererEGL *renderer = context->display->renderer->winsys; + void *ret; + + if (renderer->pf_eglCreateSync) + ret = renderer->pf_eglCreateSync (renderer->edpy, + EGL_SYNC_FENCE_KHR, + NULL); + else + ret = NULL; + + return ret; +} + +static CoglBool +_cogl_winsys_fence_is_complete (CoglContext *context, void *fence) +{ + CoglRendererEGL *renderer = context->display->renderer->winsys; + EGLint ret; + + ret = renderer->pf_eglClientWaitSync (renderer->edpy, + fence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + 0); + return (ret == EGL_CONDITION_SATISFIED_KHR); +} + +static void +_cogl_winsys_fence_destroy (CoglContext *context, void *fence) +{ + CoglRendererEGL *renderer = context->display->renderer->winsys; + + renderer->pf_eglDestroySync (renderer->edpy, fence); +} +#endif + +static CoglWinsysVtable _cogl_winsys_vtable = + { + .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL | + COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2, + + /* This winsys is only used as a base for the EGL-platform + winsys's so it does not have an ID or a name */ + + .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address, + .renderer_connect = _cogl_winsys_renderer_connect, + .renderer_disconnect = _cogl_winsys_renderer_disconnect, + .display_setup = _cogl_winsys_display_setup, + .display_destroy = _cogl_winsys_display_destroy, + .context_init = _cogl_winsys_context_init, + .context_deinit = _cogl_winsys_context_deinit, + .context_create_gles2_context = + _cogl_winsys_context_create_gles2_context, + .destroy_gles2_context = _cogl_winsys_destroy_gles2_context, + .onscreen_init = _cogl_winsys_onscreen_init, + .onscreen_deinit = _cogl_winsys_onscreen_deinit, + .onscreen_bind = _cogl_winsys_onscreen_bind, + .onscreen_swap_buffers_with_damage = + _cogl_winsys_onscreen_swap_buffers_with_damage, + .onscreen_swap_region = _cogl_winsys_onscreen_swap_region, + .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age, + .onscreen_update_swap_throttled = + _cogl_winsys_onscreen_update_swap_throttled, + + /* CoglGLES2Context related methods */ + .save_context = _cogl_winsys_save_context, + .set_gles2_context = _cogl_winsys_set_gles2_context, + .restore_context = _cogl_winsys_restore_context, + +#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) + .fence_add = _cogl_winsys_fence_add, + .fence_is_complete = _cogl_winsys_fence_is_complete, + .fence_destroy = _cogl_winsys_fence_destroy, +#endif + }; + +/* XXX: we use a function because no doubt someone will complain + * about using c99 member initializers because they aren't portable + * to windows. We want to avoid having to rigidly follow the real + * order of members since some members are #ifdefd and we'd have + * to mirror the #ifdefing to add padding etc. For any winsys that + * can assume the platform has a sane compiler then we can just use + * c99 initializers for insane platforms they can initialize + * the members by name in a function. + */ +const CoglWinsysVtable * +_cogl_winsys_egl_get_vtable (void) +{ + return &_cogl_winsys_vtable; +} + +#ifdef EGL_KHR_image_base +EGLImageKHR +_cogl_egl_create_image (CoglContext *ctx, + EGLenum target, + EGLClientBuffer buffer, + const EGLint *attribs) +{ + CoglDisplayEGL *egl_display = ctx->display->winsys; + CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; + EGLContext egl_ctx; + + _COGL_RETURN_VAL_IF_FAIL (egl_renderer->pf_eglCreateImage, EGL_NO_IMAGE_KHR); + + /* The EGL_KHR_image_pixmap spec explicitly states that EGL_NO_CONTEXT must + * always be used in conjunction with the EGL_NATIVE_PIXMAP_KHR target */ +#ifdef EGL_KHR_image_pixmap + if (target == EGL_NATIVE_PIXMAP_KHR) + egl_ctx = EGL_NO_CONTEXT; + else +#endif + egl_ctx = egl_display->egl_context; + + return egl_renderer->pf_eglCreateImage (egl_renderer->edpy, + egl_ctx, + target, + buffer, + attribs); +} + +void +_cogl_egl_destroy_image (CoglContext *ctx, + EGLImageKHR image) +{ + CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; + + _COGL_RETURN_IF_FAIL (egl_renderer->pf_eglDestroyImage); + + egl_renderer->pf_eglDestroyImage (egl_renderer->edpy, image); +} +#endif + +#ifdef EGL_WL_bind_wayland_display +CoglBool +_cogl_egl_query_wayland_buffer (CoglContext *ctx, + struct wl_resource *buffer, + int attribute, + int *value) +{ + CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; + + _COGL_RETURN_VAL_IF_FAIL (egl_renderer->pf_eglQueryWaylandBuffer, FALSE); + + return egl_renderer->pf_eglQueryWaylandBuffer (egl_renderer->edpy, + buffer, + attribute, + value); +} +#endif + +EGLDisplay +cogl_egl_context_get_egl_display (CoglContext *context) +{ + CoglRendererEGL *egl_renderer = context->display->renderer->winsys; + + return egl_renderer->edpy; +} + +EGLContext +cogl_egl_context_get_egl_context (CoglContext *context) +{ + CoglDisplayEGL *egl_display = context->display->winsys; + + return egl_display->egl_context; +} diff --git a/cogl/cogl/winsys/cogl-winsys-glx-feature-functions.h b/cogl/cogl/winsys/cogl-winsys-glx-feature-functions.h new file mode 100644 index 000000000..ed9df707f --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-glx-feature-functions.h @@ -0,0 +1,210 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +/* This can be included multiple times with different definitions for + * the COGL_WINSYS_FEATURE_* functions. + */ + +/* Macro prototypes: + * COGL_WINSYS_FEATURE_BEGIN (major_glx_version, minor_glx_version, + * name, namespaces, extension_names, + * implied_legacy_feature_flags, + * implied_winsys_feature) + * COGL_WINSYS_FEATURE_FUNCTION (return_type, function_name, + * (arguments)) + * ... + * COGL_WINSYS_FEATURE_END () + * + * Note: You can list multiple namespace and extension names if the + * corresponding _FEATURE_FUNCTIONS have the same semantics accross + * the different extension variants. + * + * XXX: NB: Don't add a trailing semicolon when using these macros + */ + +/* Base functions that we assume are always available */ +COGL_WINSYS_FEATURE_BEGIN (0, 0, /* always available */ + base_glx_functions, + "\0", + "\0", + 0, /* no implied public feature */ + 0 /* no winsys feature */) +COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyContext, + (Display *dpy, GLXContext ctx)) +COGL_WINSYS_FEATURE_FUNCTION (void, glXSwapBuffers, + (Display *dpy, GLXDrawable drawable)) +COGL_WINSYS_FEATURE_FUNCTION (Bool, glXIsDirect, + (Display *dpy, GLXContext ctx)) +COGL_WINSYS_FEATURE_FUNCTION (int, glXGetFBConfigAttrib, + (Display *dpy, GLXFBConfig config, + int attribute, int *value)) +COGL_WINSYS_FEATURE_FUNCTION (GLXWindow, glXCreateWindow, + (Display *dpy, GLXFBConfig config, + Window win, const int *attribList)) +COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyWindow, + (Display *dpy, GLXWindow window)) +COGL_WINSYS_FEATURE_FUNCTION (GLXPixmap, glXCreatePixmap, + (Display *dpy, GLXFBConfig config, + Pixmap pixmap, const int *attribList)) +COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyPixmap, + (Display *dpy, GLXPixmap pixmap)) +COGL_WINSYS_FEATURE_FUNCTION (GLXContext, glXCreateNewContext, + (Display *dpy, GLXFBConfig config, + int renderType, GLXContext shareList, + Bool direct)) +COGL_WINSYS_FEATURE_FUNCTION (Bool, glXMakeContextCurrent, + (Display *dpy, GLXDrawable draw, + GLXDrawable read, GLXContext ctx)) +COGL_WINSYS_FEATURE_FUNCTION (void, glXSelectEvent, + (Display *dpy, GLXDrawable drawable, + unsigned long mask)) +COGL_WINSYS_FEATURE_FUNCTION (GLXFBConfig *, glXGetFBConfigs, + (Display *dpy, int screen, int *nelements)) +COGL_WINSYS_FEATURE_FUNCTION (GLXFBConfig *, glXChooseFBConfig, + (Display *dpy, int screen, + const int *attrib_list, int *nelements)) +COGL_WINSYS_FEATURE_FUNCTION (XVisualInfo *, glXGetVisualFromFBConfig, + (Display *dpy, GLXFBConfig config)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + texture_from_pixmap, + "EXT\0", + "texture_from_pixmap\0", + 0, + COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP) +COGL_WINSYS_FEATURE_FUNCTION (void, glXBindTexImage, + (Display *display, + GLXDrawable drawable, + int buffer, + int *attribList)) +COGL_WINSYS_FEATURE_FUNCTION (void, glXReleaseTexImage, + (Display *display, + GLXDrawable drawable, + int buffer)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + video_sync, + "SGI\0", + "video_sync\0", + 0, + COGL_WINSYS_FEATURE_VBLANK_COUNTER) +COGL_WINSYS_FEATURE_FUNCTION (int, glXGetVideoSync, + (unsigned int *count)) +COGL_WINSYS_FEATURE_FUNCTION (int, glXWaitVideoSync, + (int divisor, + int remainder, + unsigned int *count)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + swap_control, + "SGI\0", + "swap_control\0", + 0, + COGL_WINSYS_FEATURE_SWAP_THROTTLE) +COGL_WINSYS_FEATURE_FUNCTION (int, glXSwapInterval, + (int interval)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + sync_control, + "OML\0", + "sync_control\0", + 0, + 0) +COGL_WINSYS_FEATURE_FUNCTION (Bool, glXGetSyncValues, + (Display* dpy, + GLXDrawable drawable, + int64_t* ust, + int64_t* msc, + int64_t* sbc)) +COGL_WINSYS_FEATURE_FUNCTION (Bool, glXWaitForMsc, + (Display* dpy, + GLXDrawable drawable, + int64_t target_msc, + int64_t divisor, + int64_t remainder, + int64_t* ust, + int64_t* msc, + int64_t* sbc)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + copy_sub_buffer, + "MESA\0", + "copy_sub_buffer\0", + 0, +/* We initially assumed that copy_sub_buffer is synchronized on + * which is only the case for a subset of GPUs for example it is not + * synchronized on INTEL gen6 and gen7, so we remove this assumption + * for now + */ +#if 0 + COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED) +#endif + 0) +COGL_WINSYS_FEATURE_FUNCTION (void, glXCopySubBuffer, + (Display *dpy, + GLXDrawable drawable, + int x, int y, int width, int height)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + swap_event, + "INTEL\0", + "swap_event\0", + 0, + COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT) + +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + create_context, + "ARB\0", + "create_context", + 0, + 0) +COGL_WINSYS_FEATURE_FUNCTION (GLXContext, glXCreateContextAttribs, + (Display *dpy, + GLXFBConfig config, + GLXContext share_context, + Bool direct, + const int *attrib_list)) +COGL_WINSYS_FEATURE_END () + +COGL_WINSYS_FEATURE_BEGIN (255, 255, + buffer_age, + "EXT\0", + "buffer_age\0", + 0, + COGL_WINSYS_FEATURE_BUFFER_AGE) +COGL_WINSYS_FEATURE_END () diff --git a/cogl/cogl/winsys/cogl-winsys-glx-private.h b/cogl/cogl/winsys/cogl-winsys-glx-private.h new file mode 100644 index 000000000..9fb386ff7 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-glx-private.h @@ -0,0 +1,37 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2012 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_WINSYS_GLX_PRIVATE_H +#define __COGL_WINSYS_GLX_PRIVATE_H + +const CoglWinsysVtable * +_cogl_winsys_glx_get_vtable (void); + +#endif /* __COGL_WINSYS_GLX_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-glx.c b/cogl/cogl/winsys/cogl-winsys-glx.c new file mode 100644 index 000000000..72d9e5627 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-glx.c @@ -0,0 +1,2748 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-i18n-private.h" +#include "cogl-util.h" +#include "cogl-winsys-private.h" +#include "cogl-feature-private.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer.h" +#include "cogl-swap-chain-private.h" +#include "cogl-renderer-private.h" +#include "cogl-glx-renderer-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-glx-display-private.h" +#include "cogl-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-rectangle-private.h" +#include "cogl-pipeline-opengl-private.h" +#include "cogl-frame-info-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-swap-chain-private.h" +#include "cogl-xlib-renderer.h" +#include "cogl-util.h" +#include "cogl-winsys-glx-private.h" +#include "cogl-error-private.h" +#include "cogl-poll-private.h" +#include "cogl-version.h" +#include "cogl-glx.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask) +#define MAX_GLX_CONFIG_ATTRIBS 30 + +typedef struct _CoglContextGLX +{ + GLXDrawable current_drawable; +} CoglContextGLX; + +typedef struct _CoglOnscreenXlib +{ + Window xwin; + int x, y; + CoglBool is_foreign_xwin; + CoglOutput *output; +} CoglOnscreenXlib; + +typedef struct _CoglOnscreenGLX +{ + CoglOnscreenXlib _parent; + GLXDrawable glxwin; + uint32_t last_swap_vsync_counter; + CoglBool pending_sync_notify; + CoglBool pending_complete_notify; + CoglBool pending_resize_notify; +} CoglOnscreenGLX; + +typedef struct _CoglPixmapTextureEyeGLX +{ + CoglTexture *glx_tex; + CoglBool bind_tex_image_queued; + CoglBool pixmap_bound; +} CoglPixmapTextureEyeGLX; + +typedef struct _CoglTexturePixmapGLX +{ + GLXPixmap glx_pixmap; + CoglBool has_mipmap_space; + CoglBool can_mipmap; + + CoglPixmapTextureEyeGLX left; + CoglPixmapTextureEyeGLX right; +} CoglTexturePixmapGLX; + +/* Define a set of arrays containing the functions required from GL + for each winsys feature */ +#define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version, \ + name, namespaces, extension_names, \ + feature_flags, \ + winsys_feature) \ + static const CoglFeatureFunction \ + cogl_glx_feature_ ## name ## _funcs[] = { +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ + { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglGLXRenderer, name) }, +#define COGL_WINSYS_FEATURE_END() \ + { NULL, 0 }, \ + }; +#include "cogl-winsys-glx-feature-functions.h" + +/* Define an array of features */ +#undef COGL_WINSYS_FEATURE_BEGIN +#define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version, \ + name, namespaces, extension_names, \ + feature_flags, \ + winsys_feature) \ + { major_version, minor_version, \ + 0, namespaces, extension_names, \ + feature_flags, \ + 0, \ + winsys_feature, \ + cogl_glx_feature_ ## name ## _funcs }, +#undef COGL_WINSYS_FEATURE_FUNCTION +#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) +#undef COGL_WINSYS_FEATURE_END +#define COGL_WINSYS_FEATURE_END() + +static const CoglFeatureData winsys_feature_data[] = + { +#include "cogl-winsys-glx-feature-functions.h" + }; + +static CoglFuncPtr +_cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, + const char *name, + CoglBool in_core) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + + /* The GLX_ARB_get_proc_address extension documents that this should + * work for core functions too so we don't need to do anything + * special with in_core */ + + return glx_renderer->glXGetProcAddress ((const GLubyte *) name); +} + +static CoglOnscreen * +find_onscreen_for_xid (CoglContext *context, uint32_t xid) +{ + GList *l; + + for (l = context->framebuffers; l; l = l->next) + { + CoglFramebuffer *framebuffer = l->data; + CoglOnscreenXlib *xlib_onscreen; + + if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN) + continue; + + /* Does the GLXEvent have the GLXDrawable or the X Window? */ + xlib_onscreen = COGL_ONSCREEN (framebuffer)->winsys; + if (xlib_onscreen != NULL && xlib_onscreen->xwin == (Window)xid) + return COGL_ONSCREEN (framebuffer); + } + + return NULL; +} + +static void +ensure_ust_type (CoglRenderer *renderer, + GLXDrawable drawable) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + int64_t ust; + int64_t msc; + int64_t sbc; + struct timeval tv; + struct timespec ts; + int64_t current_system_time; + int64_t current_monotonic_time; + + if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN) + return; + + glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER; + + if (glx_renderer->glXGetSyncValues == NULL) + goto out; + + if (!glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable, + &ust, &msc, &sbc)) + goto out; + + /* This is the time source that existing (buggy) linux drm drivers + * use */ + gettimeofday (&tv, NULL); + current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec; + + if (current_system_time > ust - 1000000 && + current_system_time < ust + 1000000) + { + glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY; + goto out; + } + + /* This is the time source that the newer (fixed) linux drm + * drivers use (Linux >= 3.8) */ + clock_gettime (CLOCK_MONOTONIC, &ts); + current_monotonic_time = (ts.tv_sec * G_GINT64_CONSTANT (1000000)) + + (ts.tv_nsec / G_GINT64_CONSTANT (1000)); + + if (current_monotonic_time > ust - 1000000 && + current_monotonic_time < ust + 1000000) + { + glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME; + goto out; + } + + out: + COGL_NOTE (WINSYS, "Classified OML system time as: %s", + glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" : + (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" : + "other")); + return; +} + +static int64_t +ust_to_nanoseconds (CoglRenderer *renderer, + GLXDrawable drawable, + int64_t ust) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + + ensure_ust_type (renderer, drawable); + + switch (glx_renderer->ust_type) + { + case COGL_GLX_UST_IS_UNKNOWN: + g_assert_not_reached (); + break; + case COGL_GLX_UST_IS_GETTIMEOFDAY: + case COGL_GLX_UST_IS_MONOTONIC_TIME: + return 1000 * ust; + case COGL_GLX_UST_IS_OTHER: + /* In this case the scale of UST is undefined so we can't easily + * scale to nanoseconds. + * + * For example the driver may be reporting the rdtsc CPU counter + * as UST values and so the scale would need to be determined + * empirically. + * + * Potentially we could block for a known duration within + * ensure_ust_type() to measure the timescale of UST but for now + * we just ignore unknown time sources */ + return 0; + } + + return 0; +} + +static int64_t +_cogl_winsys_get_clock_time (CoglContext *context) +{ + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + + /* We don't call ensure_ust_type() because we don't have a drawable + * to work with. cogl_get_clock_time() is documented to only work + * once a valid, non-zero, timestamp has been retrieved from Cogl. + */ + + switch (glx_renderer->ust_type) + { + case COGL_GLX_UST_IS_UNKNOWN: + case COGL_GLX_UST_IS_OTHER: + return 0; + case COGL_GLX_UST_IS_GETTIMEOFDAY: + { + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * G_GINT64_CONSTANT (1000000000) + + tv.tv_usec * G_GINT64_CONSTANT (1000); + } + case COGL_GLX_UST_IS_MONOTONIC_TIME: + { + struct timespec ts; + + clock_gettime (CLOCK_MONOTONIC, &ts); + return ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec; + } + } + + g_assert_not_reached(); + return 0; +} + +static void +flush_pending_notifications_cb (void *data, + void *user_data) +{ + CoglFramebuffer *framebuffer = data; + + if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglBool pending_sync_notify = glx_onscreen->pending_sync_notify; + CoglBool pending_complete_notify = glx_onscreen->pending_complete_notify; + + /* If swap_region is called then notifying the sync event could + * potentially immediately queue a subsequent pending notify so + * we need to clear the flag before invoking the callback */ + glx_onscreen->pending_sync_notify = FALSE; + glx_onscreen->pending_complete_notify = FALSE; + + if (pending_sync_notify) + { + CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos); + + _cogl_onscreen_notify_frame_sync (onscreen, info); + } + + if (pending_complete_notify) + { + CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos); + + _cogl_onscreen_notify_complete (onscreen, info); + + cogl_object_unref (info); + } + + if (glx_onscreen->pending_resize_notify) + { + _cogl_onscreen_notify_resize (onscreen); + glx_onscreen->pending_resize_notify = FALSE; + } + } +} + +static void +flush_pending_notifications_idle (void *user_data) +{ + CoglContext *context = user_data; + CoglRenderer *renderer = context->display->renderer; + CoglGLXRenderer *glx_renderer = renderer->winsys; + + /* This needs to be disconnected before invoking the callbacks in + * case the callbacks cause it to be queued again */ + _cogl_closure_disconnect (glx_renderer->flush_notifications_idle); + glx_renderer->flush_notifications_idle = NULL; + + g_list_foreach (context->framebuffers, + flush_pending_notifications_cb, + NULL); +} + +static void +set_sync_pending (CoglOnscreen *onscreen) +{ + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglGLXRenderer *glx_renderer = renderer->winsys; + + /* We only want to dispatch sync events when the application calls + * cogl_context_dispatch so instead of immediately notifying we + * queue an idle callback */ + if (!glx_renderer->flush_notifications_idle) + { + glx_renderer->flush_notifications_idle = + _cogl_poll_renderer_add_idle (renderer, + flush_pending_notifications_idle, + context, + NULL); + } + + glx_onscreen->pending_sync_notify = TRUE; +} + +static void +set_complete_pending (CoglOnscreen *onscreen) +{ + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglRenderer *renderer = context->display->renderer; + CoglGLXRenderer *glx_renderer = renderer->winsys; + + /* We only want to notify swap completion when the application calls + * cogl_context_dispatch so instead of immediately notifying we + * queue an idle callback */ + if (!glx_renderer->flush_notifications_idle) + { + glx_renderer->flush_notifications_idle = + _cogl_poll_renderer_add_idle (renderer, + flush_pending_notifications_idle, + context, + NULL); + } + + glx_onscreen->pending_complete_notify = TRUE; +} + +static void +notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event) +{ + CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable); + CoglOnscreenGLX *glx_onscreen; + + if (!onscreen) + return; + glx_onscreen = onscreen->winsys; + + /* We only want to notify that the swap is complete when the + application calls cogl_context_dispatch so instead of immediately + notifying we'll set a flag to remember to notify later */ + set_sync_pending (onscreen); + + if (swap_event->ust != 0) + { + CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos); + + info->presentation_time = + ust_to_nanoseconds (context->display->renderer, + glx_onscreen->glxwin, + swap_event->ust); + } + + set_complete_pending (onscreen); +} + +static void +update_output (CoglOnscreen *onscreen) +{ + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglOutput *output; + int width, height; + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + output = _cogl_xlib_renderer_output_for_rectangle (display->renderer, + xlib_onscreen->x, + xlib_onscreen->y, + width, height); + if (xlib_onscreen->output != output) + { + if (xlib_onscreen->output) + cogl_object_unref (xlib_onscreen->output); + + xlib_onscreen->output = output; + + if (output) + cogl_object_ref (xlib_onscreen->output); + } +} + +static void +notify_resize (CoglContext *context, + XConfigureEvent *configure_event) +{ + CoglOnscreen *onscreen = find_onscreen_for_xid (context, + configure_event->window); + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglRenderer *renderer = context->display->renderer; + CoglGLXRenderer *glx_renderer = renderer->winsys; + CoglOnscreenGLX *glx_onscreen; + CoglOnscreenXlib *xlib_onscreen; + + if (!onscreen) + return; + + glx_onscreen = onscreen->winsys; + xlib_onscreen = onscreen->winsys; + + _cogl_framebuffer_winsys_update_size (framebuffer, + configure_event->width, + configure_event->height); + + /* We only want to notify that a resize happened when the + * application calls cogl_context_dispatch so instead of immediately + * notifying we queue an idle callback */ + if (!glx_renderer->flush_notifications_idle) + { + glx_renderer->flush_notifications_idle = + _cogl_poll_renderer_add_idle (renderer, + flush_pending_notifications_idle, + context, + NULL); + } + + glx_onscreen->pending_resize_notify = TRUE; + + if (!xlib_onscreen->is_foreign_xwin) + { + int x, y; + + if (configure_event->send_event) + { + x = configure_event->x; + y = configure_event->y; + } + else + { + Window child; + XTranslateCoordinates (configure_event->display, + configure_event->window, + DefaultRootWindow (configure_event->display), + 0, 0, &x, &y, &child); + } + + xlib_onscreen->x = x; + xlib_onscreen->y = y; + + update_output (onscreen); + } +} + +static CoglFilterReturn +glx_event_filter_cb (XEvent *xevent, void *data) +{ + CoglContext *context = data; +#ifdef GLX_INTEL_swap_event + CoglGLXRenderer *glx_renderer; +#endif + + if (xevent->type == ConfigureNotify) + { + notify_resize (context, + &xevent->xconfigure); + + /* we let ConfigureNotify pass through */ + return COGL_FILTER_CONTINUE; + } + +#ifdef GLX_INTEL_swap_event + glx_renderer = context->display->renderer->winsys; + + if (xevent->type == (glx_renderer->glx_event_base + GLX_BufferSwapComplete)) + { + GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent; + + notify_swap_buffers (context, swap_event); + + /* remove SwapComplete events from the queue */ + return COGL_FILTER_REMOVE; + } +#endif /* GLX_INTEL_swap_event */ + + if (xevent->type == Expose) + { + CoglOnscreen *onscreen = + find_onscreen_for_xid (context, xevent->xexpose.window); + + if (onscreen) + { + CoglOnscreenDirtyInfo info; + + info.x = xevent->xexpose.x; + info.y = xevent->xexpose.y; + info.width = xevent->xexpose.width; + info.height = xevent->xexpose.height; + + _cogl_onscreen_queue_dirty (onscreen, &info); + } + + return COGL_FILTER_CONTINUE; + } + + return COGL_FILTER_CONTINUE; +} + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + + _cogl_xlib_renderer_disconnect (renderer); + + if (glx_renderer->libgl_module) + g_module_close (glx_renderer->libgl_module); + + g_slice_free (CoglGLXRenderer, renderer->winsys); +} + +static CoglBool +update_all_outputs (CoglRenderer *renderer) +{ + GList *l; + + _COGL_GET_CONTEXT (context, FALSE); + + if (context->display == NULL) /* during connection */ + return FALSE; + + if (context->display->renderer != renderer) + return FALSE; + + for (l = context->framebuffers; l; l = l->next) + { + CoglFramebuffer *framebuffer = l->data; + + if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN) + continue; + + update_output (COGL_ONSCREEN (framebuffer)); + } + + return TRUE; +} + +static void +_cogl_winsys_renderer_outputs_changed (CoglRenderer *renderer) +{ + update_all_outputs (renderer); +} + +static CoglBool +resolve_core_glx_functions (CoglRenderer *renderer, + CoglError **error) +{ + CoglGLXRenderer *glx_renderer; + + glx_renderer = renderer->winsys; + + if (!g_module_symbol (glx_renderer->libgl_module, "glXQueryExtension", + (void **) &glx_renderer->glXQueryExtension) || + !g_module_symbol (glx_renderer->libgl_module, "glXQueryVersion", + (void **) &glx_renderer->glXQueryVersion) || + !g_module_symbol (glx_renderer->libgl_module, "glXQueryExtensionsString", + (void **) &glx_renderer->glXQueryExtensionsString) || + (!g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddress", + (void **) &glx_renderer->glXGetProcAddress) && + !g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddressARB", + (void **) &glx_renderer->glXGetProcAddress)) || + !g_module_symbol (glx_renderer->libgl_module, "glXQueryDrawable", + (void **) &glx_renderer->glXQueryDrawable)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Failed to resolve required GLX symbol"); + return FALSE; + } + + return TRUE; +} + +static void +update_base_winsys_features (CoglRenderer *renderer) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (renderer); + const char *glx_extensions; + int default_screen; + char **split_extensions; + int i; + + default_screen = DefaultScreen (xlib_renderer->xdpy); + glx_extensions = + glx_renderer->glXQueryExtensionsString (xlib_renderer->xdpy, + default_screen); + + COGL_NOTE (WINSYS, " GLX Extensions: %s", glx_extensions); + + split_extensions = g_strsplit (glx_extensions, " ", 0 /* max_tokens */); + + for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++) + if (_cogl_feature_check (renderer, + "GLX", winsys_feature_data + i, + glx_renderer->glx_major, + glx_renderer->glx_minor, + COGL_DRIVER_GL, /* the driver isn't used */ + split_extensions, + glx_renderer)) + { + glx_renderer->legacy_feature_flags |= + winsys_feature_data[i].feature_flags; + if (winsys_feature_data[i].winsys_feature) + COGL_FLAGS_SET (glx_renderer->base_winsys_features, + winsys_feature_data[i].winsys_feature, + TRUE); + } + + g_strfreev (split_extensions); + + /* Note: the GLX_SGI_video_sync spec explicitly states this extension + * only works for direct contexts. */ + if (!glx_renderer->is_direct) + { + glx_renderer->glXGetVideoSync = NULL; + glx_renderer->glXWaitVideoSync = NULL; + COGL_FLAGS_SET (glx_renderer->base_winsys_features, + COGL_WINSYS_FEATURE_VBLANK_COUNTER, + FALSE); + } + + COGL_FLAGS_SET (glx_renderer->base_winsys_features, + COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, + TRUE); + + if (glx_renderer->glXWaitVideoSync || + glx_renderer->glXWaitForMsc) + COGL_FLAGS_SET (glx_renderer->base_winsys_features, + COGL_WINSYS_FEATURE_VBLANK_WAIT, + TRUE); +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + CoglError **error) +{ + CoglGLXRenderer *glx_renderer; + CoglXlibRenderer *xlib_renderer; + + renderer->winsys = g_slice_new0 (CoglGLXRenderer); + + glx_renderer = renderer->winsys; + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + if (!_cogl_xlib_renderer_connect (renderer, error)) + goto error; + + if (renderer->driver != COGL_DRIVER_GL && + renderer->driver != COGL_DRIVER_GL3) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "GLX Backend can only be used in conjunction with OpenGL"); + goto error; + } + + glx_renderer->libgl_module = g_module_open (COGL_GL_LIBNAME, + G_MODULE_BIND_LAZY); + + if (glx_renderer->libgl_module == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "Failed to dynamically open the OpenGL library"); + goto error; + } + + if (!resolve_core_glx_functions (renderer, error)) + goto error; + + if (!glx_renderer->glXQueryExtension (xlib_renderer->xdpy, + &glx_renderer->glx_error_base, + &glx_renderer->glx_event_base)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "XServer appears to lack required GLX support"); + goto error; + } + + /* XXX: Note: For a long time Mesa exported a hybrid GLX, exporting + * extensions specified to require GLX 1.3, but still reporting 1.2 + * via glXQueryVersion. */ + if (!glx_renderer->glXQueryVersion (xlib_renderer->xdpy, + &glx_renderer->glx_major, + &glx_renderer->glx_minor) + || !(glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 2)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "XServer appears to lack required GLX 1.2 support"); + goto error; + } + + update_base_winsys_features (renderer); + + glx_renderer->dri_fd = -1; + + return TRUE; + +error: + _cogl_winsys_renderer_disconnect (renderer); + return FALSE; +} + +static CoglBool +update_winsys_features (CoglContext *context, CoglError **error) +{ + CoglGLXDisplay *glx_display = context->display->winsys; + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + + _COGL_RETURN_VAL_IF_FAIL (glx_display->glx_context, FALSE); + + if (!_cogl_context_update_features (context, error)) + return FALSE; + + memcpy (context->winsys_features, + glx_renderer->base_winsys_features, + sizeof (context->winsys_features)); + + context->feature_flags |= glx_renderer->legacy_feature_flags; + + context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE; + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE); + + if (glx_renderer->glXCopySubBuffer || context->glBlitFramebuffer) + { + CoglGpuInfo *info = &context->gpu; + CoglGpuInfoArchitecture arch = info->architecture; + + COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION, TRUE); + + /* + * "The "drisw" binding in Mesa for loading sofware renderers is + * broken, and neither glBlitFramebuffer nor glXCopySubBuffer + * work correctly." + * - ajax + * - https://bugzilla.gnome.org/show_bug.cgi?id=674208 + * + * This is broken in software Mesa at least as of 7.10 and got + * fixed in Mesa 10.1 + */ + + if (info->driver_package == COGL_GPU_INFO_DRIVER_PACKAGE_MESA && + info->driver_package_version < COGL_VERSION_ENCODE (10, 1, 0) && + (arch == COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE || + arch == COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE || + arch == COGL_GPU_INFO_ARCHITECTURE_SWRAST)) + { + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION, FALSE); + } + } + + /* Note: glXCopySubBuffer and glBlitFramebuffer won't be throttled + * by the SwapInterval so we have to throttle swap_region requests + * manually... */ + if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION) && + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_WAIT)) + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); + + if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) + { + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE); + /* TODO: remove this deprecated feature */ + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, + TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_PRESENTATION_TIME, + TRUE); + } + + /* We'll manually handle queueing dirty events in response to + * Expose events from X */ + COGL_FLAGS_SET (context->private_features, + COGL_PRIVATE_FEATURE_DIRTY_EVENTS, + TRUE); + + if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE); + + return TRUE; +} + +static void +glx_attributes_from_framebuffer_config (CoglDisplay *display, + CoglFramebufferConfig *config, + int *attributes) +{ + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + int i = 0; + + attributes[i++] = GLX_DRAWABLE_TYPE; + attributes[i++] = GLX_WINDOW_BIT; + + attributes[i++] = GLX_RENDER_TYPE; + attributes[i++] = GLX_RGBA_BIT; + + attributes[i++] = GLX_DOUBLEBUFFER; + attributes[i++] = GL_TRUE; + + attributes[i++] = GLX_RED_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_GREEN_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_BLUE_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_ALPHA_SIZE; + attributes[i++] = config->swap_chain->has_alpha ? 1 : GLX_DONT_CARE; + attributes[i++] = GLX_DEPTH_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_STENCIL_SIZE; + attributes[i++] = config->need_stencil ? 1: GLX_DONT_CARE; + if (config->stereo_enabled) + { + attributes[i++] = GLX_STEREO; + attributes[i++] = TRUE; + } + + if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4 && + config->samples_per_pixel) + { + attributes[i++] = GLX_SAMPLE_BUFFERS; + attributes[i++] = 1; + attributes[i++] = GLX_SAMPLES; + attributes[i++] = config->samples_per_pixel; + } + + attributes[i++] = None; + + g_assert (i < MAX_GLX_CONFIG_ATTRIBS); +} + +/* It seems the GLX spec never defined an invalid GLXFBConfig that + * we could overload as an indication of error, so we have to return + * an explicit boolean status. */ +static CoglBool +find_fbconfig (CoglDisplay *display, + CoglFramebufferConfig *config, + GLXFBConfig *config_ret, + CoglError **error) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + GLXFBConfig *configs = NULL; + int n_configs; + static int attributes[MAX_GLX_CONFIG_ATTRIBS]; + CoglBool ret = TRUE; + int xscreen_num = DefaultScreen (xlib_renderer->xdpy); + + glx_attributes_from_framebuffer_config (display, config, attributes); + + configs = glx_renderer->glXChooseFBConfig (xlib_renderer->xdpy, + xscreen_num, + attributes, + &n_configs); + + if (!configs || n_configs == 0) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Failed to find any compatible fbconfigs"); + ret = FALSE; + goto done; + } + + if (config->swap_chain->has_alpha) + { + int i; + + for (i = 0; i < n_configs; i++) + { + XVisualInfo *vinfo; + + vinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, + configs[i]); + if (vinfo == NULL) + continue; + + if (vinfo->depth == 32 && + (vinfo->red_mask | vinfo->green_mask | vinfo->blue_mask) + != 0xffffffff) + { + COGL_NOTE (WINSYS, "Found an ARGB FBConfig [index:%d]", i); + *config_ret = configs[i]; + goto done; + } + } + + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to find fbconfig with rgba visual"); + ret = FALSE; + goto done; + } + else + { + COGL_NOTE (WINSYS, "Using the first available FBConfig"); + *config_ret = configs[0]; + } + +done: + XFree (configs); + return ret; +} + +static GLXContext +create_gl3_context (CoglDisplay *display, + GLXFBConfig fb_config) +{ + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + + /* We want a core profile 3.1 context with no deprecated features */ + static const int attrib_list[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + None + }; + + /* Make sure that the display supports the GLX_ARB_create_context + extension */ + if (glx_renderer->glXCreateContextAttribs == NULL) + return NULL; + + return glx_renderer->glXCreateContextAttribs (xlib_renderer->xdpy, + fb_config, + NULL /* share_context */, + True, /* direct */ + attrib_list); +} + +static CoglBool +create_context (CoglDisplay *display, CoglError **error) +{ + CoglGLXDisplay *glx_display = display->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + CoglBool support_transparent_windows = + display->onscreen_template->config.swap_chain->has_alpha; + GLXFBConfig config; + CoglError *fbconfig_error = NULL; + XSetWindowAttributes attrs; + XVisualInfo *xvisinfo; + GLXDrawable dummy_drawable; + CoglXlibTrapState old_state; + + _COGL_RETURN_VAL_IF_FAIL (glx_display->glx_context == NULL, TRUE); + + glx_display->found_fbconfig = + find_fbconfig (display, &display->onscreen_template->config, &config, + &fbconfig_error); + if (!glx_display->found_fbconfig) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to find suitable fbconfig for the GLX context: %s", + fbconfig_error->message); + cogl_error_free (fbconfig_error); + return FALSE; + } + + glx_display->fbconfig = config; + glx_display->fbconfig_has_rgba_visual = support_transparent_windows; + + COGL_NOTE (WINSYS, "Creating GLX Context (display: %p)", + xlib_renderer->xdpy); + + _cogl_xlib_renderer_trap_errors (display->renderer, &old_state); + + if (display->renderer->driver == COGL_DRIVER_GL3) + glx_display->glx_context = create_gl3_context (display, config); + else + glx_display->glx_context = + glx_renderer->glXCreateNewContext (xlib_renderer->xdpy, + config, + GLX_RGBA_TYPE, + NULL, + True); + + if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state) || + glx_display->glx_context == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to create suitable GL context"); + return FALSE; + } + + glx_renderer->is_direct = + glx_renderer->glXIsDirect (xlib_renderer->xdpy, glx_display->glx_context); + + COGL_NOTE (WINSYS, "Setting %s context", + glx_renderer->is_direct ? "direct" : "indirect"); + + /* XXX: GLX doesn't let us make a context current without a window + * so we create a dummy window that we can use while no CoglOnscreen + * framebuffer is in use. + */ + + xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, + config); + if (xvisinfo == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to retrieve the X11 visual"); + return FALSE; + } + + _cogl_xlib_renderer_trap_errors (display->renderer, &old_state); + + attrs.override_redirect = True; + attrs.colormap = XCreateColormap (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + xvisinfo->visual, + AllocNone); + attrs.border_pixel = 0; + + glx_display->dummy_xwin = + XCreateWindow (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + -100, -100, 1, 1, + 0, + xvisinfo->depth, + CopyFromParent, + xvisinfo->visual, + CWOverrideRedirect | CWColormap | CWBorderPixel, + &attrs); + + /* Try and create a GLXWindow to use with extensions dependent on + * GLX versions >= 1.3 that don't accept regular X Windows as GLX + * drawables. */ + if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3) + { + glx_display->dummy_glxwin = + glx_renderer->glXCreateWindow (xlib_renderer->xdpy, + config, + glx_display->dummy_xwin, + NULL); + } + + if (glx_display->dummy_glxwin) + dummy_drawable = glx_display->dummy_glxwin; + else + dummy_drawable = glx_display->dummy_xwin; + + COGL_NOTE (WINSYS, "Selecting dummy 0x%x for the GLX context", + (unsigned int) dummy_drawable); + + glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, + dummy_drawable, + dummy_drawable, + glx_display->glx_context); + + xlib_renderer->xvisinfo = xvisinfo; + + if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to select the newly created GLX context"); + return FALSE; + } + + return TRUE; +} + +static void +_cogl_winsys_display_destroy (CoglDisplay *display) +{ + CoglGLXDisplay *glx_display = display->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + + _COGL_RETURN_IF_FAIL (glx_display != NULL); + + if (glx_display->glx_context) + { + glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, + None, None, NULL); + glx_renderer->glXDestroyContext (xlib_renderer->xdpy, + glx_display->glx_context); + glx_display->glx_context = NULL; + } + + if (glx_display->dummy_glxwin) + { + glx_renderer->glXDestroyWindow (xlib_renderer->xdpy, + glx_display->dummy_glxwin); + glx_display->dummy_glxwin = None; + } + + if (glx_display->dummy_xwin) + { + XDestroyWindow (xlib_renderer->xdpy, glx_display->dummy_xwin); + glx_display->dummy_xwin = None; + } + + g_slice_free (CoglGLXDisplay, display->winsys); + display->winsys = NULL; +} + +static CoglBool +_cogl_winsys_display_setup (CoglDisplay *display, + CoglError **error) +{ + CoglGLXDisplay *glx_display; + int i; + + _COGL_RETURN_VAL_IF_FAIL (display->winsys == NULL, FALSE); + + glx_display = g_slice_new0 (CoglGLXDisplay); + display->winsys = glx_display; + + if (!create_context (display, error)) + goto error; + + for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++) + glx_display->glx_cached_configs[i].depth = -1; + + return TRUE; + +error: + _cogl_winsys_display_destroy (display); + return FALSE; +} + +static CoglBool +_cogl_winsys_context_init (CoglContext *context, CoglError **error) +{ + context->winsys = g_new0 (CoglContextGLX, 1); + + cogl_xlib_renderer_add_filter (context->display->renderer, + glx_event_filter_cb, + context); + return update_winsys_features (context, error); +} + +static void +_cogl_winsys_context_deinit (CoglContext *context) +{ + cogl_xlib_renderer_remove_filter (context->display->renderer, + glx_event_filter_cb, + context); + g_free (context->winsys); +} + +static CoglBool +_cogl_winsys_onscreen_init (CoglOnscreen *onscreen, + CoglError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglDisplay *display = context->display; + CoglGLXDisplay *glx_display = display->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (display->renderer); + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + Window xwin; + CoglOnscreenXlib *xlib_onscreen; + CoglOnscreenGLX *glx_onscreen; + GLXFBConfig fbconfig; + CoglError *fbconfig_error = NULL; + + _COGL_RETURN_VAL_IF_FAIL (glx_display->glx_context, FALSE); + + if (!find_fbconfig (display, &framebuffer->config, + &fbconfig, + &fbconfig_error)) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to find suitable fbconfig for the GLX context: %s", + fbconfig_error->message); + cogl_error_free (fbconfig_error); + return FALSE; + } + + /* Update the real number of samples_per_pixel now that we have + * found an fbconfig... */ + if (framebuffer->config.samples_per_pixel) + { + int samples; + int status = glx_renderer->glXGetFBConfigAttrib (xlib_renderer->xdpy, + fbconfig, + GLX_SAMPLES, + &samples); + g_return_val_if_fail (status == Success, TRUE); + framebuffer->samples_per_pixel = samples; + } + + /* FIXME: We need to explicitly Select for ConfigureNotify events. + * For foreign windows we need to be careful not to mess up any + * existing event mask. + * We need to document that for windows we create then toolkits + * must be careful not to clear event mask bits that we select. + */ + + /* XXX: Note we ignore the user's original width/height when + * given a foreign X window. */ + if (onscreen->foreign_xid) + { + Status status; + CoglXlibTrapState state; + XWindowAttributes attr; + int xerror; + + xwin = onscreen->foreign_xid; + + _cogl_xlib_renderer_trap_errors (display->renderer, &state); + + status = XGetWindowAttributes (xlib_renderer->xdpy, xwin, &attr); + XSync (xlib_renderer->xdpy, False); + xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state); + if (status == 0 || xerror) + { + char message[1000]; + XGetErrorText (xlib_renderer->xdpy, xerror, message, sizeof(message)); + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Unable to query geometry of foreign xid 0x%08lX: %s", + xwin, message); + return FALSE; + } + + _cogl_framebuffer_winsys_update_size (framebuffer, + attr.width, attr.height); + + /* Make sure the app selects for the events we require... */ + onscreen->foreign_update_mask_callback (onscreen, + COGL_ONSCREEN_X11_EVENT_MASK, + onscreen->foreign_update_mask_data); + } + else + { + int width; + int height; + CoglXlibTrapState state; + XVisualInfo *xvisinfo; + XSetWindowAttributes xattr; + unsigned long mask; + int xerror; + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + + _cogl_xlib_renderer_trap_errors (display->renderer, &state); + + xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, + fbconfig); + if (xvisinfo == NULL) + { + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "Unable to retrieve the X11 visual of context's " + "fbconfig"); + return FALSE; + } + + /* window attributes */ + xattr.background_pixel = WhitePixel (xlib_renderer->xdpy, + DefaultScreen (xlib_renderer->xdpy)); + xattr.border_pixel = 0; + /* XXX: is this an X resource that we are leaking‽... */ + xattr.colormap = XCreateColormap (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + xvisinfo->visual, + AllocNone); + xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK; + + mask = CWBorderPixel | CWColormap | CWEventMask; + + xwin = XCreateWindow (xlib_renderer->xdpy, + DefaultRootWindow (xlib_renderer->xdpy), + 0, 0, + width, height, + 0, + xvisinfo->depth, + InputOutput, + xvisinfo->visual, + mask, &xattr); + + XFree (xvisinfo); + + XSync (xlib_renderer->xdpy, False); + xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state); + if (xerror) + { + char message[1000]; + XGetErrorText (xlib_renderer->xdpy, xerror, + message, sizeof (message)); + _cogl_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "X error while creating Window for CoglOnscreen: %s", + message); + return FALSE; + } + } + + onscreen->winsys = g_slice_new0 (CoglOnscreenGLX); + xlib_onscreen = onscreen->winsys; + glx_onscreen = onscreen->winsys; + + xlib_onscreen->xwin = xwin; + xlib_onscreen->is_foreign_xwin = onscreen->foreign_xid ? TRUE : FALSE; + + /* Try and create a GLXWindow to use with extensions dependent on + * GLX versions >= 1.3 that don't accept regular X Windows as GLX + * drawables. */ + if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3) + { + glx_onscreen->glxwin = + glx_renderer->glXCreateWindow (xlib_renderer->xdpy, + fbconfig, + xlib_onscreen->xwin, + NULL); + } + +#ifdef GLX_INTEL_swap_event + if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) + { + GLXDrawable drawable = + glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + + /* similarly to above, we unconditionally select this event + * because we rely on it to advance the master clock, and + * drive redraw/relayout, animations and event handling. + */ + glx_renderer->glXSelectEvent (xlib_renderer->xdpy, + drawable, + GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK); + } +#endif /* GLX_INTEL_swap_event */ + + return TRUE; +} + +static void +_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglContextGLX *glx_context = context->winsys; + CoglGLXDisplay *glx_display = context->display->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + CoglXlibTrapState old_state; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + GLXDrawable drawable; + + /* If we never successfully allocated then there's nothing to do */ + if (glx_onscreen == NULL) + return; + + if (xlib_onscreen->output != NULL) + { + cogl_object_unref (xlib_onscreen->output); + xlib_onscreen->output = NULL; + } + + _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state); + + drawable = + glx_onscreen->glxwin == None ? xlib_onscreen->xwin : glx_onscreen->glxwin; + + /* Cogl always needs a valid context bound to something so if we are + * destroying the onscreen that is currently bound we'll switch back + * to the dummy drawable. Although the documentation for + * glXDestroyWindow states that a currently bound window won't + * actually be destroyed until it is unbound, it looks like this + * doesn't work if the X window itself is destroyed */ + if (drawable == glx_context->current_drawable) + { + GLXDrawable dummy_drawable = (glx_display->dummy_glxwin == None ? + glx_display->dummy_xwin : + glx_display->dummy_glxwin); + + glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, + dummy_drawable, + dummy_drawable, + glx_display->glx_context); + glx_context->current_drawable = dummy_drawable; + } + + if (glx_onscreen->glxwin != None) + { + glx_renderer->glXDestroyWindow (xlib_renderer->xdpy, + glx_onscreen->glxwin); + glx_onscreen->glxwin = None; + } + + if (!xlib_onscreen->is_foreign_xwin && xlib_onscreen->xwin != None) + { + XDestroyWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); + xlib_onscreen->xwin = None; + } + else + xlib_onscreen->xwin = None; + + XSync (xlib_renderer->xdpy, False); + + _cogl_xlib_renderer_untrap_errors (context->display->renderer, &old_state); + + g_slice_free (CoglOnscreenGLX, onscreen->winsys); + onscreen->winsys = NULL; +} + +static void +_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglContextGLX *glx_context = context->winsys; + CoglGLXDisplay *glx_display = context->display->winsys; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglXlibTrapState old_state; + GLXDrawable drawable; + + drawable = + glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + + if (glx_context->current_drawable == drawable) + return; + + _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state); + + COGL_NOTE (WINSYS, + "MakeContextCurrent dpy: %p, window: 0x%x (%s), context: %p", + xlib_renderer->xdpy, + (unsigned int) drawable, + xlib_onscreen->is_foreign_xwin ? "foreign" : "native", + glx_display->glx_context); + + glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, + drawable, + drawable, + glx_display->glx_context); + + /* In case we are using GLX_SGI_swap_control for vblank syncing + * we need call glXSwapIntervalSGI here to make sure that it + * affects the current drawable. + * + * Note: we explicitly set to 0 when we aren't using the swap + * interval to synchronize since some drivers have a default + * swap interval of 1. Sadly some drivers even ignore requests + * to disable the swap interval. + * + * NB: glXSwapIntervalSGI applies to the context not the + * drawable which is why we can't just do this once when the + * framebuffer is allocated. + * + * FIXME: We should check for GLX_EXT_swap_control which allows + * per framebuffer swap intervals. GLX_MESA_swap_control also + * allows per-framebuffer swap intervals but the semantics tend + * to be more muddled since Mesa drivers tend to expose both the + * MESA and SGI extensions which should technically be mutually + * exclusive. + */ + if (glx_renderer->glXSwapInterval) + { + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + if (fb->config.swap_throttled) + glx_renderer->glXSwapInterval (1); + else + glx_renderer->glXSwapInterval (0); + } + + XSync (xlib_renderer->xdpy, False); + + /* FIXME: We should be reporting a CoglError here + */ + if (_cogl_xlib_renderer_untrap_errors (context->display->renderer, + &old_state)) + { + g_warning ("X Error received while making drawable 0x%08lX current", + drawable); + return; + } + + glx_context->current_drawable = drawable; +} + +static void +_cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *ctx = framebuffer->context; + + ctx->glFinish (); +} + +static void +_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *ctx = framebuffer->context; + CoglGLXRenderer *glx_renderer; + CoglXlibRenderer *xlib_renderer; + + glx_renderer = ctx->display->renderer->winsys; + xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer); + + if (glx_renderer->glXWaitForMsc || + glx_renderer->glXGetVideoSync) + { + CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos); + + if (glx_renderer->glXWaitForMsc) + { + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + Drawable drawable = glx_onscreen->glxwin; + int64_t ust; + int64_t msc; + int64_t sbc; + + glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable, + 0, 1, 0, + &ust, &msc, &sbc); + info->presentation_time = ust_to_nanoseconds (ctx->display->renderer, + drawable, + ust); + } + else + { + uint32_t current_count; + struct timespec ts; + + glx_renderer->glXGetVideoSync (¤t_count); + glx_renderer->glXWaitVideoSync (2, + (current_count + 1) % 2, + ¤t_count); + + clock_gettime (CLOCK_MONOTONIC, &ts); + info->presentation_time = + ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec; + } + } +} + +static uint32_t +_cogl_winsys_get_vsync_counter (CoglContext *ctx) +{ + uint32_t video_sync_count; + CoglGLXRenderer *glx_renderer; + + glx_renderer = ctx->display->renderer->winsys; + + glx_renderer->glXGetVideoSync (&video_sync_count); + + return video_sync_count; +} + +#ifndef GLX_BACK_BUFFER_AGE_EXT +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif + +static int +_cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + GLXDrawable drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + unsigned int age; + + if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) + return 0; + + glx_renderer->glXQueryDrawable (xlib_renderer->xdpy, drawable, GLX_BACK_BUFFER_AGE_EXT, &age); + + return age; +} + +static void +set_frame_info_output (CoglOnscreen *onscreen, + CoglOutput *output) +{ + CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos); + + info->output = output; + + if (output) + { + float refresh_rate = cogl_output_get_refresh_rate (output); + if (refresh_rate != 0.0) + info->refresh_rate = refresh_rate; + } +} + +static void +_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, + const int *user_rectangles, + int n_rectangles) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + GLXDrawable drawable = + glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + uint32_t end_frame_vsync_counter = 0; + CoglBool have_counter; + CoglBool can_wait; + int x_min = 0, x_max = 0, y_min = 0, y_max = 0; + + /* + * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple + * blits per retrace if they can all be performed in the blanking period. If that's the + * case then we still want to use the vblank sync menchanism but + * we only need it to throttle redraws. + */ + CoglBool blit_sub_buffer_is_synchronized = + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED); + + int framebuffer_width = cogl_framebuffer_get_width (framebuffer); + int framebuffer_height = cogl_framebuffer_get_height (framebuffer); + int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); + int i; + + /* glXCopySubBuffer expects rectangles relative to the bottom left corner but + * we are given rectangles relative to the top left so we need to flip + * them... */ + memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4); + for (i = 0; i < n_rectangles; i++) + { + int *rect = &rectangles[4 * i]; + + if (i == 0) + { + x_min = rect[0]; + x_max = rect[0] + rect[2]; + y_min = rect[1]; + y_max = rect[1] + rect[3]; + } + else + { + x_min = MIN (x_min, rect[0]); + x_max = MAX (x_max, rect[0] + rect[2]); + y_min = MIN (y_min, rect[1]); + y_max = MAX (y_max, rect[1] + rect[3]); + } + + rect[1] = framebuffer_height - rect[1] - rect[3]; + + } + + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + + if (framebuffer->config.swap_throttled) + { + have_counter = + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_COUNTER); + can_wait = _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_WAIT); + } + else + { + have_counter = FALSE; + can_wait = FALSE; + } + + /* We need to ensure that all the rendering is done, otherwise + * redraw operations that are slower than the framerate can + * queue up in the pipeline during a heavy animation, causing a + * larger and larger backlog of rendering visible as lag to the + * user. + * + * For an exaggerated example consider rendering at 60fps (so 16ms + * per frame) and you have a really slow frame that takes 160ms to + * render, even though painting the scene and issuing the commands + * to the GPU takes no time at all. If all we did was use the + * video_sync extension to throttle the painting done by the CPU + * then every 16ms we would have another frame queued up even though + * the GPU has only rendered one tenth of the current frame. By the + * time the GPU would get to the 2nd frame there would be 9 frames + * waiting to be rendered. + * + * The problem is that we don't currently have a good way to throttle + * the GPU, only the CPU so we have to resort to synchronizing the + * GPU with the CPU to throttle it. + * + * Note: since calling glFinish() and synchronizing the CPU with + * the GPU is far from ideal, we hope that this is only a short + * term solution. + * - One idea is to using sync objects to track render + * completion so we can throttle the backlog (ideally with an + * additional extension that lets us get notifications in our + * mainloop instead of having to busy wait for the + * completion.) + * - Another option is to support clipped redraws by reusing the + * contents of old back buffers such that we can flip instead + * of using a blit and then we can use GLX_INTEL_swap_events + * to throttle. For this though we would still probably want an + * additional extension so we can report the limited region of + * the window damage to X/compositors. + */ + _cogl_winsys_wait_for_gpu (onscreen); + + if (blit_sub_buffer_is_synchronized && have_counter && can_wait) + { + end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context); + + /* If we have the GLX_SGI_video_sync extension then we can + * be a bit smarter about how we throttle blits by avoiding + * any waits if we can see that the video sync count has + * already progressed. */ + if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) + _cogl_winsys_wait_for_vblank (onscreen); + } + else if (can_wait) + _cogl_winsys_wait_for_vblank (onscreen); + + if (glx_renderer->glXCopySubBuffer) + { + Display *xdpy = xlib_renderer->xdpy; + int i; + for (i = 0; i < n_rectangles; i++) + { + int *rect = &rectangles[4 * i]; + glx_renderer->glXCopySubBuffer (xdpy, drawable, + rect[0], rect[1], rect[2], rect[3]); + } + } + else if (context->glBlitFramebuffer) + { + int i; + /* XXX: checkout how this state interacts with the code to use + * glBlitFramebuffer in Neil's texture atlasing branch */ + + /* glBlitFramebuffer is affected by the scissor so we need to + * ensure we have flushed an empty clip stack to get rid of it. + * We also mark that the clip state is dirty so that it will be + * flushed to the correct state the next time something is + * drawn */ + _cogl_clip_stack_flush (NULL, framebuffer); + context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + + context->glDrawBuffer (GL_FRONT); + for (i = 0; i < n_rectangles; i++) + { + int *rect = &rectangles[4 * i]; + int x2 = rect[0] + rect[2]; + int y2 = rect[1] + rect[3]; + context->glBlitFramebuffer (rect[0], rect[1], x2, y2, + rect[0], rect[1], x2, y2, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + context->glDrawBuffer (context->current_gl_draw_buffer); + } + + /* NB: unlike glXSwapBuffers, glXCopySubBuffer and + * glBlitFramebuffer don't issue an implicit glFlush() so we + * have to flush ourselves if we want the request to complete in + * a finite amount of time since otherwise the driver can batch + * the command indefinitely. */ + context->glFlush (); + + /* NB: It's important we save the counter we read before acting on + * the swap request since if we are mixing and matching different + * swap methods between frames we don't want to read the timer e.g. + * after calling glFinish() some times and not for others. + * + * In other words; this way we consistently save the time at the end + * of the applications frame such that the counter isn't muddled by + * the varying costs of different swap methods. + */ + if (have_counter) + glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter; + + if (!xlib_onscreen->is_foreign_xwin) + { + CoglOutput *output; + + x_min = CLAMP (x_min, 0, framebuffer_width); + x_max = CLAMP (x_max, 0, framebuffer_width); + y_min = CLAMP (y_min, 0, framebuffer_width); + y_max = CLAMP (y_max, 0, framebuffer_height); + + output = + _cogl_xlib_renderer_output_for_rectangle (context->display->renderer, + xlib_onscreen->x + x_min, + xlib_onscreen->y + y_min, + x_max - x_min, + y_max - y_min); + + set_frame_info_output (onscreen, output); + } + + /* XXX: we don't get SwapComplete events based on how we implement + * the _swap_region() API but if cogl-onscreen.c knows we are + * handling _SYNC and _COMPLETE events in the winsys then we need to + * send fake events in this case. + */ + if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) + { + set_sync_pending (onscreen); + set_complete_pending (onscreen); + } +} + +static void +_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglBool have_counter; + GLXDrawable drawable; + + /* XXX: theoretically this shouldn't be necessary but at least with + * the Intel drivers we have see that if we don't call + * glXMakeContextCurrent for the drawable we are swapping then + * we get a BadDrawable error from the X server. */ + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + + drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + + if (framebuffer->config.swap_throttled) + { + uint32_t end_frame_vsync_counter = 0; + + have_counter = + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_COUNTER); + + /* If the swap_region API is also being used then we need to track + * the vsync counter for each swap request so we can manually + * throttle swap_region requests. */ + if (have_counter) + end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context); + + if (!glx_renderer->glXSwapInterval) + { + CoglBool can_wait = + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_WAIT); + + /* If we are going to wait for VBLANK manually, we not only + * need to flush out pending drawing to the GPU before we + * sleep, we need to wait for it to finish. Otherwise, we + * may end up with the situation: + * + * - We finish drawing - GPU drawing continues + * - We go to sleep - GPU drawing continues + * VBLANK - We call glXSwapBuffers - GPU drawing continues + * - GPU drawing continues + * - Swap buffers happens + * + * Producing a tear. Calling glFinish() first will cause us + * to properly wait for the next VBLANK before we swap. This + * obviously does not happen when we use _GLX_SWAP and let + * the driver do the right thing + */ + _cogl_winsys_wait_for_gpu (onscreen); + + if (have_counter && can_wait) + { + if (glx_onscreen->last_swap_vsync_counter == + end_frame_vsync_counter) + _cogl_winsys_wait_for_vblank (onscreen); + } + else if (can_wait) + _cogl_winsys_wait_for_vblank (onscreen); + } + } + else + have_counter = FALSE; + + glx_renderer->glXSwapBuffers (xlib_renderer->xdpy, drawable); + + if (have_counter) + glx_onscreen->last_swap_vsync_counter = + _cogl_winsys_get_vsync_counter (context); + + set_frame_info_output (onscreen, xlib_onscreen->output); +} + +static uint32_t +_cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen) +{ + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + return xlib_onscreen->xwin; +} + +static void +_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglContextGLX *glx_context = context->winsys; + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + GLXDrawable drawable = + glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; + + if (glx_context->current_drawable != drawable) + return; + + glx_context->current_drawable = 0; + _cogl_winsys_onscreen_bind (onscreen); +} + +static void +_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, + CoglBool visibility) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + + if (visibility) + XMapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); + else + XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); +} + +static void +_cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = framebuffer->context; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (context->display->renderer); + CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; + + XSizeHints *size_hints = XAllocSizeHints (); + + if (resizable) + { + /* TODO: Add cogl_onscreen_request_minimum_size () */ + size_hints->min_width = 1; + size_hints->min_height = 1; + + size_hints->max_width = INT_MAX; + size_hints->max_height = INT_MAX; + } + else + { + int width = cogl_framebuffer_get_width (framebuffer); + int height = cogl_framebuffer_get_height (framebuffer); + + size_hints->min_width = width; + size_hints->min_height = height; + + size_hints->max_width = width; + size_hints->max_height = height; + } + + XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints); + + XFree (size_hints); +} + +static CoglBool +get_fbconfig_for_depth (CoglContext *context, + unsigned int depth, + CoglBool stereo, + GLXFBConfig *fbconfig_ret, + CoglBool *can_mipmap_ret) +{ + CoglXlibRenderer *xlib_renderer; + CoglGLXRenderer *glx_renderer; + CoglGLXDisplay *glx_display; + Display *dpy; + GLXFBConfig *fbconfigs; + int n_elements, i; + int db, stencil, alpha, mipmap, rgba, value; + int spare_cache_slot = 0; + CoglBool found = FALSE; + + xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); + glx_renderer = context->display->renderer->winsys; + glx_display = context->display->winsys; + + /* Check if we've already got a cached config for this depth and stereo */ + for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++) + if (glx_display->glx_cached_configs[i].depth == -1) + spare_cache_slot = i; + else if (glx_display->glx_cached_configs[i].depth == depth && + glx_display->glx_cached_configs[i].stereo == stereo) + { + *fbconfig_ret = glx_display->glx_cached_configs[i].fb_config; + *can_mipmap_ret = glx_display->glx_cached_configs[i].can_mipmap; + return glx_display->glx_cached_configs[i].found; + } + + dpy = xlib_renderer->xdpy; + + fbconfigs = glx_renderer->glXGetFBConfigs (dpy, DefaultScreen (dpy), + &n_elements); + + db = G_MAXSHORT; + stencil = G_MAXSHORT; + mipmap = 0; + rgba = 0; + + for (i = 0; i < n_elements; i++) + { + XVisualInfo *vi; + int visual_depth; + + vi = glx_renderer->glXGetVisualFromFBConfig (dpy, fbconfigs[i]); + if (vi == NULL) + continue; + + visual_depth = vi->depth; + + XFree (vi); + + if (visual_depth != depth) + continue; + + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_ALPHA_SIZE, + &alpha); + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_BUFFER_SIZE, + &value); + if (value != depth && (value - alpha) != depth) + continue; + + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_STEREO, + &value); + if (!!value != !!stereo) + continue; + + if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4) + { + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_SAMPLES, + &value); + if (value > 1) + continue; + } + + value = 0; + if (depth == 32) + { + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGBA_EXT, + &value); + if (value) + rgba = 1; + } + + if (!value) + { + if (rgba) + continue; + + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGB_EXT, + &value); + if (!value) + continue; + } + + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_DOUBLEBUFFER, + &value); + if (value > db) + continue; + + db = value; + + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_STENCIL_SIZE, + &value); + if (value > stencil) + continue; + + stencil = value; + + /* glGenerateMipmap is defined in the offscreen extension */ + if (cogl_has_feature (context, COGL_FEATURE_ID_OFFSCREEN)) + { + glx_renderer->glXGetFBConfigAttrib (dpy, + fbconfigs[i], + GLX_BIND_TO_MIPMAP_TEXTURE_EXT, + &value); + + if (value < mipmap) + continue; + + mipmap = value; + } + + *fbconfig_ret = fbconfigs[i]; + *can_mipmap_ret = mipmap; + found = TRUE; + } + + if (n_elements) + XFree (fbconfigs); + + glx_display->glx_cached_configs[spare_cache_slot].depth = depth; + glx_display->glx_cached_configs[spare_cache_slot].found = found; + glx_display->glx_cached_configs[spare_cache_slot].fb_config = *fbconfig_ret; + glx_display->glx_cached_configs[spare_cache_slot].can_mipmap = mipmap; + + return found; +} + +static CoglBool +should_use_rectangle (CoglContext *context) +{ + + if (context->rectangle_state == COGL_WINSYS_RECTANGLE_STATE_UNKNOWN) + { + if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RECTANGLE)) + { + const char *rect_env; + + /* Use the rectangle only if it is available and either: + + the COGL_PIXMAP_TEXTURE_RECTANGLE environment variable is + set to 'force' + + *or* + + the env var is set to 'allow' or not set and NPOTs textures + are not available */ + + context->rectangle_state = + cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_NPOT) ? + COGL_WINSYS_RECTANGLE_STATE_DISABLE : + COGL_WINSYS_RECTANGLE_STATE_ENABLE; + + if ((rect_env = g_getenv ("COGL_PIXMAP_TEXTURE_RECTANGLE")) || + /* For compatibility, we'll also look at the old Clutter + environment variable */ + (rect_env = g_getenv ("CLUTTER_PIXMAP_TEXTURE_RECTANGLE"))) + { + if (g_ascii_strcasecmp (rect_env, "force") == 0) + context->rectangle_state = + COGL_WINSYS_RECTANGLE_STATE_ENABLE; + else if (g_ascii_strcasecmp (rect_env, "disable") == 0) + context->rectangle_state = + COGL_WINSYS_RECTANGLE_STATE_DISABLE; + else if (g_ascii_strcasecmp (rect_env, "allow")) + g_warning ("Unknown value for COGL_PIXMAP_TEXTURE_RECTANGLE, " + "should be 'force' or 'disable'"); + } + } + else + context->rectangle_state = COGL_WINSYS_RECTANGLE_STATE_DISABLE; + } + + return context->rectangle_state == COGL_WINSYS_RECTANGLE_STATE_ENABLE; +} + +static CoglBool +try_create_glx_pixmap (CoglContext *context, + CoglTexturePixmapX11 *tex_pixmap, + CoglBool mipmap) +{ + CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; + CoglRenderer *renderer; + CoglXlibRenderer *xlib_renderer; + CoglGLXRenderer *glx_renderer; + Display *dpy; + /* We have to initialize this *opaque* variable because gcc tries to + * be too smart for its own good and warns that the variable may be + * used uninitialized otherwise. */ + GLXFBConfig fb_config = (GLXFBConfig)0; + int attribs[7]; + int i = 0; + GLenum target; + CoglXlibTrapState trap_state; + + unsigned int depth = tex_pixmap->depth; + Visual* visual = tex_pixmap->visual; + + renderer = context->display->renderer; + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + glx_renderer = renderer->winsys; + dpy = xlib_renderer->xdpy; + + if (!get_fbconfig_for_depth (context, depth, + tex_pixmap->stereo_mode != COGL_TEXTURE_PIXMAP_MONO, + &fb_config, + &glx_tex_pixmap->can_mipmap)) + { + COGL_NOTE (TEXTURE_PIXMAP, "No suitable FBConfig found for depth %i", + depth); + return FALSE; + } + + if (should_use_rectangle (context)) + { + target = GLX_TEXTURE_RECTANGLE_EXT; + glx_tex_pixmap->can_mipmap = FALSE; + } + else + target = GLX_TEXTURE_2D_EXT; + + if (!glx_tex_pixmap->can_mipmap) + mipmap = FALSE; + + attribs[i++] = GLX_TEXTURE_FORMAT_EXT; + + /* Check whether an alpha channel is used by comparing the total + * number of 1-bits in color masks against the color depth requested + * by the client. + */ + if (_cogl_util_popcountl (visual->red_mask | + visual->green_mask | + visual->blue_mask) == depth) + attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; + else + attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; + + attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; + attribs[i++] = mipmap; + + attribs[i++] = GLX_TEXTURE_TARGET_EXT; + attribs[i++] = target; + + attribs[i++] = None; + + /* We need to trap errors from glXCreatePixmap because it can + * sometimes fail during normal usage. For example on NVidia it gets + * upset if you try to create two GLXPixmaps for the same drawable. + */ + + _cogl_xlib_renderer_trap_errors (renderer, &trap_state); + + glx_tex_pixmap->glx_pixmap = + glx_renderer->glXCreatePixmap (dpy, + fb_config, + tex_pixmap->pixmap, + attribs); + glx_tex_pixmap->has_mipmap_space = mipmap; + + XSync (dpy, False); + + if (_cogl_xlib_renderer_untrap_errors (renderer, &trap_state)) + { + COGL_NOTE (TEXTURE_PIXMAP, "Failed to create pixmap for %p", tex_pixmap); + _cogl_xlib_renderer_trap_errors (renderer, &trap_state); + glx_renderer->glXDestroyPixmap (dpy, glx_tex_pixmap->glx_pixmap); + XSync (dpy, False); + _cogl_xlib_renderer_untrap_errors (renderer, &trap_state); + + glx_tex_pixmap->glx_pixmap = None; + return FALSE; + } + + return TRUE; +} + +static CoglBool +_cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexturePixmapGLX *glx_tex_pixmap; + CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context; + + if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP)) + { + tex_pixmap->winsys = NULL; + return FALSE; + } + + glx_tex_pixmap = g_new0 (CoglTexturePixmapGLX, 1); + + glx_tex_pixmap->glx_pixmap = None; + glx_tex_pixmap->can_mipmap = FALSE; + glx_tex_pixmap->has_mipmap_space = FALSE; + + glx_tex_pixmap->left.glx_tex = NULL; + glx_tex_pixmap->right.glx_tex = NULL; + + glx_tex_pixmap->left.bind_tex_image_queued = TRUE; + glx_tex_pixmap->right.bind_tex_image_queued = TRUE; + glx_tex_pixmap->left.pixmap_bound = FALSE; + glx_tex_pixmap->right.pixmap_bound = FALSE; + + tex_pixmap->winsys = glx_tex_pixmap; + + if (!try_create_glx_pixmap (ctx, tex_pixmap, FALSE)) + { + tex_pixmap->winsys = NULL; + g_free (glx_tex_pixmap); + return FALSE; + } + + return TRUE; +} + +static void +free_glx_pixmap (CoglContext *context, + CoglTexturePixmapGLX *glx_tex_pixmap) +{ + CoglXlibTrapState trap_state; + CoglRenderer *renderer; + CoglXlibRenderer *xlib_renderer; + CoglGLXRenderer *glx_renderer; + + renderer = context->display->renderer; + xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + glx_renderer = renderer->winsys; + + if (glx_tex_pixmap->left.pixmap_bound) + glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, + glx_tex_pixmap->glx_pixmap, + GLX_FRONT_LEFT_EXT); + if (glx_tex_pixmap->right.pixmap_bound) + glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, + glx_tex_pixmap->glx_pixmap, + GLX_FRONT_RIGHT_EXT); + + /* FIXME - we need to trap errors and synchronize here because + * of ordering issues between the XPixmap destruction and the + * GLXPixmap destruction. + * + * If the X pixmap is destroyed, the GLX pixmap is destroyed as + * well immediately, and thus, when Cogl calls glXDestroyPixmap() + * it'll cause a BadDrawable error. + * + * this is technically a bug in the X server, which should not + * destroy either pixmaps until the call to glXDestroyPixmap(); so + * at some point we should revisit this code and remove the + * trap+sync after verifying that the destruction is indeed safe. + * + * for reference, see: + * http://bugzilla.clutter-project.org/show_bug.cgi?id=2324 + */ + _cogl_xlib_renderer_trap_errors (renderer, &trap_state); + glx_renderer->glXDestroyPixmap (xlib_renderer->xdpy, + glx_tex_pixmap->glx_pixmap); + XSync (xlib_renderer->xdpy, False); + _cogl_xlib_renderer_untrap_errors (renderer, &trap_state); + + glx_tex_pixmap->glx_pixmap = None; + glx_tex_pixmap->left.pixmap_bound = FALSE; + glx_tex_pixmap->right.pixmap_bound = FALSE; +} + +static void +_cogl_winsys_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexturePixmapGLX *glx_tex_pixmap; + + if (!tex_pixmap->winsys) + return; + + glx_tex_pixmap = tex_pixmap->winsys; + + free_glx_pixmap (COGL_TEXTURE (tex_pixmap)->context, glx_tex_pixmap); + + if (glx_tex_pixmap->left.glx_tex) + cogl_object_unref (glx_tex_pixmap->left.glx_tex); + + if (glx_tex_pixmap->right.glx_tex) + cogl_object_unref (glx_tex_pixmap->right.glx_tex); + + tex_pixmap->winsys = NULL; + g_free (glx_tex_pixmap); +} + +static CoglBool +_cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode, + CoglBool needs_mipmap) +{ + CoglTexture *tex = COGL_TEXTURE (tex_pixmap); + CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context; + CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; + CoglPixmapTextureEyeGLX *texture_info; + int buffer; + CoglGLXRenderer *glx_renderer; + + if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + { + texture_info = &glx_tex_pixmap->right; + buffer = GLX_FRONT_RIGHT_EXT; + } + else + { + texture_info = &glx_tex_pixmap->left; + buffer = GLX_FRONT_LEFT_EXT; + } + + /* If we don't have a GLX pixmap then fallback */ + if (glx_tex_pixmap->glx_pixmap == None) + return FALSE; + + glx_renderer = ctx->display->renderer->winsys; + + /* Lazily create a texture to hold the pixmap */ + if (texture_info->glx_tex == NULL) + { + CoglPixelFormat texture_format; + CoglError *error = NULL; + + texture_format = (tex_pixmap->depth >= 32 ? + COGL_PIXEL_FORMAT_RGBA_8888_PRE : + COGL_PIXEL_FORMAT_RGB_888); + + if (should_use_rectangle (ctx)) + { + texture_info->glx_tex = COGL_TEXTURE ( + cogl_texture_rectangle_new_with_size (ctx, + tex->width, + tex->height)); + + _cogl_texture_set_internal_format (tex, texture_format); + + if (cogl_texture_allocate (texture_info->glx_tex, &error)) + COGL_NOTE (TEXTURE_PIXMAP, "Created a texture rectangle for %p", + tex_pixmap); + else + { + COGL_NOTE (TEXTURE_PIXMAP, "Falling back for %p because a " + "texture rectangle could not be created: %s", + tex_pixmap, error->message); + cogl_error_free (error); + free_glx_pixmap (ctx, glx_tex_pixmap); + return FALSE; + } + } + else + { + texture_info->glx_tex = COGL_TEXTURE ( + cogl_texture_2d_new_with_size (ctx, + tex->width, + tex->height)); + + _cogl_texture_set_internal_format (tex, texture_format); + + if (cogl_texture_allocate (texture_info->glx_tex, &error)) + COGL_NOTE (TEXTURE_PIXMAP, "Created a texture 2d for %p", + tex_pixmap); + else + { + COGL_NOTE (TEXTURE_PIXMAP, "Falling back for %p because a " + "texture 2d could not be created: %s", + tex_pixmap, error->message); + cogl_error_free (error); + free_glx_pixmap (ctx, glx_tex_pixmap); + return FALSE; + } + } + } + + if (needs_mipmap) + { + /* If we can't support mipmapping then temporarily fallback */ + if (!glx_tex_pixmap->can_mipmap) + return FALSE; + + /* Recreate the GLXPixmap if it wasn't previously created with a + * mipmap tree */ + if (!glx_tex_pixmap->has_mipmap_space) + { + free_glx_pixmap (ctx, glx_tex_pixmap); + + COGL_NOTE (TEXTURE_PIXMAP, "Recreating GLXPixmap with mipmap " + "support for %p", tex_pixmap); + if (!try_create_glx_pixmap (ctx, tex_pixmap, TRUE)) + + { + /* If the pixmap failed then we'll permanently fallback + * to using XImage. This shouldn't happen. */ + COGL_NOTE (TEXTURE_PIXMAP, "Falling back to XGetImage " + "updates for %p because creating the GLXPixmap " + "with mipmap support failed", tex_pixmap); + + if (texture_info->glx_tex) + cogl_object_unref (texture_info->glx_tex); + return FALSE; + } + + glx_tex_pixmap->left.bind_tex_image_queued = TRUE; + glx_tex_pixmap->right.bind_tex_image_queued = TRUE; + } + } + + if (texture_info->bind_tex_image_queued) + { + GLuint gl_handle, gl_target; + CoglXlibRenderer *xlib_renderer = + _cogl_xlib_renderer_get_data (ctx->display->renderer); + + cogl_texture_get_gl_texture (texture_info->glx_tex, + &gl_handle, &gl_target); + + COGL_NOTE (TEXTURE_PIXMAP, "Rebinding GLXPixmap for %p", tex_pixmap); + + _cogl_bind_gl_texture_transient (gl_target, gl_handle, FALSE); + + if (texture_info->pixmap_bound) + glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, + glx_tex_pixmap->glx_pixmap, + buffer); + + glx_renderer->glXBindTexImage (xlib_renderer->xdpy, + glx_tex_pixmap->glx_pixmap, + buffer, + NULL); + + /* According to the recommended usage in the spec for + * GLX_EXT_texture_pixmap we should release the texture after + * we've finished drawing with it and it is undefined what + * happens if you render to a pixmap that is bound to a texture. + * However that would require the texture backend to know when + * Cogl has finished painting and it may be more expensive to + * keep unbinding the texture. Leaving it bound appears to work + * on Mesa and NVidia drivers and it is also what Compiz does so + * it is probably ok */ + + texture_info->bind_tex_image_queued = FALSE; + texture_info->pixmap_bound = TRUE; + + _cogl_texture_2d_externally_modified (texture_info->glx_tex); + } + + return TRUE; +} + +static void +_cogl_winsys_texture_pixmap_x11_damage_notify (CoglTexturePixmapX11 *tex_pixmap) +{ + CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; + + glx_tex_pixmap->left.bind_tex_image_queued = TRUE; + glx_tex_pixmap->right.bind_tex_image_queued = TRUE; +} + +static CoglTexture * +_cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode) +{ + CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; + + if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) + return glx_tex_pixmap->right.glx_tex; + else + return glx_tex_pixmap->left.glx_tex; +} + +static CoglWinsysVtable _cogl_winsys_vtable = + { + .id = COGL_WINSYS_ID_GLX, + .name = "GLX", + .constraints = (COGL_RENDERER_CONSTRAINT_USES_X11 | + COGL_RENDERER_CONSTRAINT_USES_XLIB), + + .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address, + .renderer_connect = _cogl_winsys_renderer_connect, + .renderer_disconnect = _cogl_winsys_renderer_disconnect, + .renderer_outputs_changed = _cogl_winsys_renderer_outputs_changed, + .display_setup = _cogl_winsys_display_setup, + .display_destroy = _cogl_winsys_display_destroy, + .context_init = _cogl_winsys_context_init, + .context_deinit = _cogl_winsys_context_deinit, + .context_get_clock_time = _cogl_winsys_get_clock_time, + .onscreen_init = _cogl_winsys_onscreen_init, + .onscreen_deinit = _cogl_winsys_onscreen_deinit, + .onscreen_bind = _cogl_winsys_onscreen_bind, + .onscreen_swap_buffers_with_damage = + _cogl_winsys_onscreen_swap_buffers_with_damage, + .onscreen_swap_region = _cogl_winsys_onscreen_swap_region, + .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age, + .onscreen_update_swap_throttled = + _cogl_winsys_onscreen_update_swap_throttled, + .onscreen_x11_get_window_xid = + _cogl_winsys_onscreen_x11_get_window_xid, + .onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility, + .onscreen_set_resizable = + _cogl_winsys_onscreen_set_resizable, + + /* X11 tfp support... */ + /* XXX: instead of having a rather monolithic winsys vtable we could + * perhaps look for a way to separate these... */ + .texture_pixmap_x11_create = + _cogl_winsys_texture_pixmap_x11_create, + .texture_pixmap_x11_free = + _cogl_winsys_texture_pixmap_x11_free, + .texture_pixmap_x11_update = + _cogl_winsys_texture_pixmap_x11_update, + .texture_pixmap_x11_damage_notify = + _cogl_winsys_texture_pixmap_x11_damage_notify, + .texture_pixmap_x11_get_texture = + _cogl_winsys_texture_pixmap_x11_get_texture, + }; + +/* XXX: we use a function because no doubt someone will complain + * about using c99 member initializers because they aren't portable + * to windows. We want to avoid having to rigidly follow the real + * order of members since some members are #ifdefd and we'd have + * to mirror the #ifdefing to add padding etc. For any winsys that + * can assume the platform has a sane compiler then we can just use + * c99 initializers for insane platforms they can initialize + * the members by name in a function. + */ +const CoglWinsysVtable * +_cogl_winsys_glx_get_vtable (void) +{ + return &_cogl_winsys_vtable; +} + +GLXContext +cogl_glx_context_get_glx_context (CoglContext *context) +{ + CoglGLXDisplay *glx_display = context->display->winsys; + + return glx_display->glx_context; +} diff --git a/cogl/cogl/winsys/cogl-winsys-private.h b/cogl/cogl/winsys/cogl-winsys-private.h new file mode 100644 index 000000000..85a67c72a --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-private.h @@ -0,0 +1,196 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_WINSYS_PRIVATE_H +#define __COGL_WINSYS_PRIVATE_H + +#include "cogl-renderer.h" +#include "cogl-onscreen.h" +#include "cogl-gles2.h" + +#ifdef COGL_HAS_XLIB_SUPPORT +#include "cogl-texture-pixmap-x11-private.h" +#endif + +#ifdef COGL_HAS_XLIB_SUPPORT +#include +#include "cogl-texture-pixmap-x11-private.h" +#endif + +#ifdef COGL_HAS_EGL_SUPPORT +#include "cogl-egl-private.h" +#endif + +#include "cogl-poll.h" + +uint32_t +_cogl_winsys_error_quark (void); + +#define COGL_WINSYS_ERROR (_cogl_winsys_error_quark ()) + +typedef enum { /*< prefix=COGL_WINSYS_ERROR >*/ + COGL_WINSYS_ERROR_INIT, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + COGL_WINSYS_ERROR_MAKE_CURRENT, + COGL_WINSYS_ERROR_CREATE_GLES2_CONTEXT, +} CoglWinsysError; + +typedef enum +{ + COGL_WINSYS_RECTANGLE_STATE_UNKNOWN, + COGL_WINSYS_RECTANGLE_STATE_DISABLE, + COGL_WINSYS_RECTANGLE_STATE_ENABLE +} CoglWinsysRectangleState; + +typedef struct _CoglWinsysVtable +{ + CoglWinsysID id; + CoglRendererConstraint constraints; + + const char *name; + + /* Required functions */ + + CoglFuncPtr + (*renderer_get_proc_address) (CoglRenderer *renderer, + const char *name, + CoglBool in_core); + + CoglBool + (*renderer_connect) (CoglRenderer *renderer, CoglError **error); + + void + (*renderer_disconnect) (CoglRenderer *renderer); + + void + (*renderer_outputs_changed) (CoglRenderer *renderer); + + CoglBool + (*display_setup) (CoglDisplay *display, CoglError **error); + + void + (*display_destroy) (CoglDisplay *display); + + CoglBool + (*context_init) (CoglContext *context, CoglError **error); + + void + (*context_deinit) (CoglContext *context); + + void * + (*context_create_gles2_context) (CoglContext *ctx, CoglError **error); + + CoglBool + (*onscreen_init) (CoglOnscreen *onscreen, CoglError **error); + + void + (*onscreen_deinit) (CoglOnscreen *onscreen); + + void + (*onscreen_bind) (CoglOnscreen *onscreen); + + void + (*onscreen_swap_buffers_with_damage) (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles); + + void + (*onscreen_update_swap_throttled) (CoglOnscreen *onscreen); + + void + (*onscreen_set_visibility) (CoglOnscreen *onscreen, + CoglBool visibility); + + /* Optional functions */ + + int64_t + (*context_get_clock_time) (CoglContext *context); + + void + (*onscreen_swap_region) (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles); + + void + (*onscreen_set_resizable) (CoglOnscreen *onscreen, CoglBool resizable); + + int + (*onscreen_get_buffer_age) (CoglOnscreen *onscreen); + + uint32_t + (*onscreen_x11_get_window_xid) (CoglOnscreen *onscreen); + +#ifdef COGL_HAS_XLIB_SUPPORT + CoglBool + (*texture_pixmap_x11_create) (CoglTexturePixmapX11 *tex_pixmap); + void + (*texture_pixmap_x11_free) (CoglTexturePixmapX11 *tex_pixmap); + + CoglBool + (*texture_pixmap_x11_update) (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode, + CoglBool needs_mipmap); + + void + (*texture_pixmap_x11_damage_notify) (CoglTexturePixmapX11 *tex_pixmap); + + CoglTexture * + (*texture_pixmap_x11_get_texture) (CoglTexturePixmapX11 *tex_pixmap, + CoglTexturePixmapStereoMode stereo_mode); +#endif + + void + (*save_context) (CoglContext *ctx); + + CoglBool + (*set_gles2_context) (CoglGLES2Context *gles2_ctx, CoglError **error); + + void + (*restore_context) (CoglContext *ctx); + + void + (*destroy_gles2_context) (CoglGLES2Context *gles2_ctx); + + void * + (*fence_add) (CoglContext *ctx); + + CoglBool + (*fence_is_complete) (CoglContext *ctx, void *fence); + + void + (*fence_destroy) (CoglContext *ctx, void *fence); + +} CoglWinsysVtable; + +CoglBool +_cogl_winsys_has_feature (CoglWinsysFeature feature); + +#endif /* __COGL_WINSYS_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-stub-private.h b/cogl/cogl/winsys/cogl-winsys-stub-private.h new file mode 100644 index 000000000..4aaf798a3 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-stub-private.h @@ -0,0 +1,37 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef __COGL_WINSYS_STUB_PRIVATE_H +#define __COGL_WINSYS_STUB_PRIVATE_H + +const CoglWinsysVtable * +_cogl_winsys_stub_get_vtable (void); + +#endif /* __COGL_WINSYS_STUB_PRIVATE_H */ diff --git a/cogl/cogl/winsys/cogl-winsys-stub.c b/cogl/cogl/winsys/cogl-winsys-stub.c new file mode 100644 index 000000000..4fc8c90e5 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys-stub.c @@ -0,0 +1,197 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Authors: + * Robert Bragg + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-renderer-private.h" +#include "cogl-display-private.h" +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-private.h" +#include "cogl-winsys-stub-private.h" + +#include + +static int _cogl_winsys_stub_dummy_ptr; + +/* This provides a NOP winsys. This can be useful for debugging or for + * integrating with toolkits that already have window system + * integration code. + */ + +static CoglFuncPtr +_cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, + const char *name, + CoglBool in_core) +{ + static GModule *module = NULL; + + /* this should find the right function if the program is linked against a + * library providing it */ + if (G_UNLIKELY (module == NULL)) + module = g_module_open (NULL, 0); + + if (module) + { + void *symbol; + + if (g_module_symbol (module, name, &symbol)) + return symbol; + } + + return NULL; +} + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + renderer->winsys = NULL; +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + CoglError **error) +{ + renderer->winsys = &_cogl_winsys_stub_dummy_ptr; + return TRUE; +} + +static void +_cogl_winsys_display_destroy (CoglDisplay *display) +{ + display->winsys = NULL; +} + +static CoglBool +_cogl_winsys_display_setup (CoglDisplay *display, + CoglError **error) +{ + display->winsys = &_cogl_winsys_stub_dummy_ptr; + return TRUE; +} + +static CoglBool +_cogl_winsys_context_init (CoglContext *context, CoglError **error) +{ + context->winsys = &_cogl_winsys_stub_dummy_ptr; + + if (!_cogl_context_update_features (context, error)) + return FALSE; + + memset (context->winsys_features, 0, sizeof (context->winsys_features)); + + return TRUE; +} + +static void +_cogl_winsys_context_deinit (CoglContext *context) +{ + context->winsys = NULL; +} + +static CoglBool +_cogl_winsys_onscreen_init (CoglOnscreen *onscreen, + CoglError **error) +{ + return TRUE; +} + +static void +_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) +{ +} + +static void +_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) +{ +} + +static void +_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles) +{ +} + +static void +_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen) +{ +} + +static void +_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, + CoglBool visibility) +{ +} + +const CoglWinsysVtable * +_cogl_winsys_stub_get_vtable (void) +{ + static CoglBool vtable_inited = FALSE; + static CoglWinsysVtable vtable; + + /* It would be nice if we could use C99 struct initializers here + like the GLX backend does. However this code is more likely to be + compiled using Visual Studio which (still!) doesn't support them + so we initialize it in code instead */ + + if (!vtable_inited) + { + memset (&vtable, 0, sizeof (vtable)); + + vtable.id = COGL_WINSYS_ID_STUB; + vtable.name = "STUB"; + vtable.renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address; + vtable.renderer_connect = _cogl_winsys_renderer_connect; + vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect; + vtable.display_setup = _cogl_winsys_display_setup; + vtable.display_destroy = _cogl_winsys_display_destroy; + vtable.context_init = _cogl_winsys_context_init; + vtable.context_deinit = _cogl_winsys_context_deinit; + + vtable.onscreen_init = _cogl_winsys_onscreen_init; + vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit; + vtable.onscreen_bind = _cogl_winsys_onscreen_bind; + vtable.onscreen_swap_buffers_with_damage = + _cogl_winsys_onscreen_swap_buffers_with_damage; + vtable.onscreen_update_swap_throttled = + _cogl_winsys_onscreen_update_swap_throttled; + vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility; + + vtable_inited = TRUE; + } + + return &vtable; +} diff --git a/cogl/cogl/winsys/cogl-winsys.c b/cogl/cogl/winsys/cogl-winsys.c new file mode 100644 index 000000000..fccb94c25 --- /dev/null +++ b/cogl/cogl/winsys/cogl-winsys.c @@ -0,0 +1,52 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2010 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-context-private.h" + +#include + +uint32_t +_cogl_winsys_error_quark (void) +{ + return g_quark_from_static_string ("cogl-winsys-error-quark"); +} + +/* FIXME: we should distinguish renderer and context features */ +CoglBool +_cogl_winsys_has_feature (CoglWinsysFeature feature) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + return COGL_FLAGS_GET (ctx->winsys_features, feature); +} diff --git a/cogl/config-custom.h b/cogl/config-custom.h new file mode 100644 index 000000000..08fb9a40c --- /dev/null +++ b/cogl/config-custom.h @@ -0,0 +1,32 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* The contents of this file get #included by config.h so it is + intended for extra configuration that needs to be included by all + Cogl source files. */ + diff --git a/cogl/configure.ac b/cogl/configure.ac new file mode 100644 index 000000000..1ec4dbf10 --- /dev/null +++ b/cogl/configure.ac @@ -0,0 +1,1036 @@ +AC_PREREQ(2.59) + +dnl ================================================================ +dnl XXX: If you are making a release then you need to check these +dnl sections: +dnl » API versions (Only the 1.x version needs to change) +dnl (the pretty numbers that the users see) +dnl +dnl » Interface version details for libtool +dnl (the shared library versioning information) +dnl +dnl » Source code release status +dnl (mark the source code as being part of a "release" or from "git") +dnl ================================================================ + +dnl ================================================================ +dnl API versions (i.e. the pretty numbers that users see) +dnl ================================================================ +m4_define([cogl_major_version], [2]) +m4_define([cogl_minor_version], [0]) +m4_define([cogl_micro_version], [0]) +m4_define([cogl_version], + [cogl_major_version.cogl_minor_version.cogl_micro_version]) + +dnl Since the core Cogl library has to also maintain support for the +dnl Cogl 1.x API for Clutter then we track the 1.x version separately. +m4_define([cogl_1_minor_version], [22]) +m4_define([cogl_1_micro_version], [1]) +m4_define([cogl_1_version], [1.cogl_1_minor_version.cogl_1_micro_version]) + +dnl ================================================================ +dnl Interface version details for libtool +dnl ================================================================ +# Note: we don't automatically deduce the libtool version info from +# the pretty version number that users sees. This is because we want +# to update the pretty version number before making a release since it +# can affect the name of our pkg-config file and the naming or +# location of other installed files which we want to be able to verify +# as correct well before making a release. +# +# For reference on how the various numbers should be updated at +# release time these rules are adapted from the libtool info pages: +# +# 1. Update the version information only immediately before a public +# release. +# +# 2. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:r+1:A'). +# +# 3. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 4. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 5. If any interfaces have been removed since the last public release, +# then set AGE to 0. +m4_define([cogl_lt_current], 24) +m4_define([cogl_lt_revision], 1) +m4_define([cogl_lt_age], 4) +# We do also tell libtool the pretty version: +m4_define([cogl_lt_release], [cogl_version]) + + +dnl ================================================================ +dnl Source code release status +dnl ================================================================ +# Finally we explicitly track when we are building development source +# from Git vs building source corresponding to a release. As with the +# libtool version info we don't automatically derive this from the +# pretty version number because we want to test the results of +# updating the version number in advance of a release. +# +# Possible status values are: git, snapshot or release +# +m4_define([cogl_release_status], [git]) + +AC_INIT(cogl, [cogl_1_version]) +AC_CONFIG_SRCDIR(cogl/cogl.h) +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_MACRO_DIR([build/autotools]) +AC_CONFIG_HEADERS(config.h) +AC_GNU_SOURCE + +dnl ================================================================ +dnl Required versions for dependencies +dnl ================================================================ +m4_define([glib_req_version], [2.32.0]) +m4_define([pangocairo_req_version], [1.20]) +m4_define([gi_req_version], [0.9.5]) +m4_define([gdk_pixbuf_req_version], [2.0]) +m4_define([uprof_req_version], [0.3]) +m4_define([xfixes_req_version], [3]) +m4_define([xcomposite_req_version], [0.4]) +m4_define([xrandr_req_version], [1.2]) +m4_define([cairo_req_version], [1.10]) +m4_define([wayland_server_req_version], [1.1.90]) +m4_define([mirclient_req_version], [0.9.0]) + +dnl These variables get copied into the generated README +AC_SUBST([GLIB_REQ_VERSION], [glib_req_version]) +AC_SUBST([GDK_PIXBUF_REQ_VERSION], [gdk_pixbuf_req_version]) +AC_SUBST([CAIRO_REQ_VERSION], [cairo_req_version]) +AC_SUBST([PANGOCAIRO_REQ_VERSION], [pangocairo_req_version]) +AC_SUBST([XCOMPOSITE_REQ_VERSION], [xcomposite_req_version]) +AC_SUBST([XFIXES_REQ_VERSION], [xfixes_req_version]) +AC_SUBST([GI_REQ_VERSION], [gi_req_version]) +AC_SUBST([UPROF_REQ_VERSION], [uprof_req_version]) +AC_SUBST([WAYLAND_SERVER_REQ_VERSION], [wayland_server_req_version]) + +# Save this value here, since automake will set cflags later and we +# want to know if the user specified custom cflags or not. +cflags_set=${CFLAGS+set} + +AM_INIT_AUTOMAKE([1.14 foreign -Wno-portability no-define no-dist-gzip dist-xz tar-ustar subdir-objects]) +AM_SILENT_RULES([yes]) + +AH_BOTTOM([#include "config-custom.h"]) + +dnl ================================================================ +dnl Export the API versioning +dnl ================================================================ +AC_SUBST([COGL_MAJOR_VERSION],[cogl_major_version]) +AC_SUBST([COGL_MINOR_VERSION],[cogl_minor_version]) +AC_SUBST([COGL_MICRO_VERSION],[cogl_micro_version]) +AC_SUBST([COGL_VERSION],[cogl_version]) +AC_SUBST([COGL_API_VERSION],[cogl_major_version.0]) +AC_SUBST([COGL_API_VERSION_AM],[$COGL_MAJOR_VERSION\_0]) + +AC_SUBST([COGL_1_MINOR_VERSION],[cogl_1_minor_version]) +AC_SUBST([COGL_1_MICRO_VERSION],[cogl_1_micro_version]) +AC_SUBST([COGL_1_VERSION],[cogl_1_version]) + + +dnl ================================================================ +dnl Export the libtool versioning +dnl ================================================================ +AC_SUBST([COGL_LT_CURRENT], [cogl_lt_current]) +AC_SUBST([COGL_LT_REVISION], [cogl_lt_revision]) +AC_SUBST([COGL_LT_AGE], [cogl_lt_age]) +AC_SUBST([COGL_LT_RELEASE], [cogl_lt_release]) + + +dnl ================================================================ +dnl Export the source code release status +dnl ================================================================ +AC_SUBST([COGL_RELEASE_STATUS], [cogl_release_status]) + +dnl ================================================================ +dnl Compiler stuff. +dnl ================================================================ +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AM_PROG_CC_C_O +AC_ISC_POSIX +AC_C_CONST + +dnl ============================================================ +dnl Compiler features +dnl ============================================================ +AC_MSG_CHECKING([for _Static_assert]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([_Static_assert (1, "");], + [(void) 0])], + [AC_DEFINE([HAVE_STATIC_ASSERT], [1], + [Whether _Static_assert can be used or not]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + +dnl ================================================================ +dnl Libtool stuff. +dnl ================================================================ +dnl AC_PROG_LIBTOOL +dnl LIBTOOL="$LIBTOOL --preserve-dup-deps" +LT_PREREQ([2.2.6]) +LT_INIT([disable-static]) +dnl when using libtool 2.x create libtool early, because it's used in the +dnl internal glib configure (as-glibconfig.m4) +m4_ifdef([LT_OUTPUT], [LT_OUTPUT]) + +dnl ================================================================ +dnl Find an appropriate libm, for sin() etc. +dnl ================================================================ +LT_LIB_M +AC_SUBST(LIBM) + +dnl ================================================================ +dnl See what platform we are building for +dnl ================================================================ +AC_CANONICAL_HOST + +dnl ============================================================ +dnl Installed tests +dnl ============================================================ + +AC_ARG_ENABLE(installed_tests, + AS_HELP_STRING([--enable-installed-tests], + [Install test programs (default: no)]),, + [enable_installed_tests=no]) +AM_CONDITIONAL(ENABLE_INSTALLED_TESTS, test x$enable_installed_tests = xyes) + +dnl ============================================================ +dnl Enable debugging +dnl ============================================================ +m4_define([debug_default], [m4_if(cogl_release_status, [git], [yes], [no])]) +AC_ARG_ENABLE( + [debug], + [AC_HELP_STRING([--enable-debug=@<:@no/yes@:>@], [Control Cogl debugging level @<:@default=]debug_default[@:>@])], + [], + enable_debug=debug_default +) +AS_CASE( + [$enable_debug], + [yes], + [ + test "$cflags_set" = set || CFLAGS="$CFLAGS -g -O0" + COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS -DCOGL_GL_DEBUG -DCOGL_OBJECT_DEBUG -DCOGL_ENABLE_DEBUG" + ], + [no], + [ + COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS -DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS" + ], + [AC_MSG_ERROR([Unknown argument for --enable-debug])] +) + +AC_SUBST(COGL_DEBUG_CFLAGS) + +AC_ARG_ENABLE( + [unit-tests], + [AC_HELP_STRING([--enable-unit-tests=@<:@no/yes@:>@], [Build Cogl unit tests @<:@default=yes@:>@])], + [], + enable_unit_tests=yes +) +AS_IF([test "x$enable_unit_tests" = "xyes"], + [ + AC_DEFINE([ENABLE_UNIT_TESTS], [1], [Whether to enable building unit tests]) + ] +) +AM_CONDITIONAL(UNIT_TESTS, test "x$enable_unit_tests" = "xyes") + +dnl ============================================================ +dnl Enable cairo usage for debugging +dnl (debugging code can use cairo to dump the atlas) +dnl ============================================================ + +PKG_CHECK_EXISTS([CAIRO], [cairo >= cairo_req_version], [have_cairo=yes]) +AC_ARG_ENABLE( + [cairo], + [AC_HELP_STRING([--enable-cairo=@<:@no/yes@:>@], [Control Cairo usage in Cogl debugging code @<:@default=auto@:>@])], + [], + [ + AS_IF([test "x$enable_debug" = "xyes"], + [enable_cairo=$have_cairo], + [enable_cairo=no]) + ] +) +AS_IF([test "x$enable_cairo" = "xyes" && test "x$enable_debug" = "xyes"], + [ + AS_IF([test "x$have_cairo" != "xyes"], + [AC_MSG_ERROR([Could not find Cairo])]) + + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES cairo >= cairo_req_version" + AC_DEFINE([HAVE_CAIRO], [1], [Whether we have cairo or not]) + ]) + + +dnl ============================================================ +dnl Enable profiling +dnl ============================================================ +AC_ARG_ENABLE(profile, + [AC_HELP_STRING([--enable-profile=@<:@no/yes@:>@], + [Turn on uprof profiling support. yes; All UProf profiling probe points are compiled in and may be runtime enabled. no; No profiling support will built into cogl. @<:@default=no@:>@])], + [], + [enable_profile=no]) +AS_IF([test "x$enable_profile" = "xyes"], + [ + AS_IF([test "x$GCC" = "xyes"], + [ + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES uprof-0.3" + COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS -DCOGL_ENABLE_PROFILE" + AS_IF([test "x$enable_debug" = "xyes"], [COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS -DUPROF_DEBUG"]) + ], + [ + AC_MSG_ERROR([--enable-profile is currently only supported if using GCC]) + ]) + ]) +AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno") + + +dnl ============================================================ +dnl Enable strict compiler flags +dnl ============================================================ + +# use strict compiler flags only when building from git; the rules for +# distcheck will take care of turning this on when making a release +m4_define([maintainer_default], [m4_if(cogl_release_status, [git], [yes], [no])]) +AC_ARG_ENABLE( + [maintainer-flags], + [AC_HELP_STRING([--enable-maintainer-flags=@<:@no/yes/error@:>@], [Use strict compiler flags @<:@default=]maintainer_default[@:>@])], + [], + enable_maintainer_flags=maintainer_default +) + +MAINTAINER_COMPILER_FLAGS="-Wall -Wcast-align -Wformat -Wformat-security + -Werror=uninitialized -Werror=no-strict-aliasing + -Werror=empty-body -Werror=init-self -Werror=undef + -Werror=declaration-after-statement -Werror=vla + -Werror=pointer-arith -Werror=missing-declarations + -Werror=maybe-uninitialized" + +AS_CASE( + [$enable_maintainer_flags], + [yes], + [ + AS_COMPILER_FLAGS([MAINTAINER_CFLAGS], [$MAINTAINER_COMPILER_FLAGS]) + ], + [no], + [ + ], + [error], + [ + MAINTAINER_COMPILER_FLAGS="$MAINTAINER_COMPILER_FLAGS -Werror" + AS_COMPILER_FLAGS([MAINTAINER_CFLAGS], [$MAINTAINER_COMPILER_FLAGS]) + ], + [*], + [AC_MSG_ERROR([Invalid option for --enable-maintainer-flags])] +) + +# strip leading spaces +COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS ${MAINTAINER_CFLAGS#* }" + + +dnl ============================================================ +dnl Enable deprecation guards +dnl ============================================================ + +# disable deprecated options from Glib only when building from git; +# the rules for distcheck will take care of turning this on when +# making a release +m4_define([deprecated_default], + [m4_if(cogl_release_status, [git], [no], [yes])]) + +AC_ARG_ENABLE([deprecated], + [AS_HELP_STRING([--enable-deprecated=@<:@no/yes@:>@], + [Whether deprecated symbols should be disabled when compiling Cogl @<:@default=]deprecated_default[@:>@])], + [], + [enable_deprecated=deprecated_default]) + +AS_CASE([$enable_deprecated], + + [no], + [ + DEPRECATED_CFLAGS="-DG_DISABLE_DEPRECATED -DG_DISABLE_SINGLE_INCLUDES" + ], + + [yes], + [ + DEPRECATED_CFLAGS="" + ], + + [AC_MSG_ERROR([Unknown argument for --enable-deprecated])] +) + +# strip leading spaces +COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS ${DEPRECATED_CFLAGS#* }" + +dnl ================================================================ +dnl Check for dependency packages. +dnl ================================================================ + +AM_PATH_GLIB_2_0([glib_req_version], + [], + [AC_MSG_ERROR([glib-2.0 is required])], + [gobject gthread gmodule-no-export]) + +COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLIB_SUPPORT" +COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GTYPE_SUPPORT" +COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES gobject-2.0 gmodule-no-export-2.0" + +dnl ============================================================ +dnl Should cogl-pango be built? +dnl ============================================================ + +AC_ARG_ENABLE( + [cogl-pango], + [AC_HELP_STRING([--enable-cogl-pango=@<:@no/yes@:>@], [Enable pango support @<:@default=yes@:>@])], + [], + enable_cogl_pango=yes +) +AS_IF([test "x$enable_cogl_pango" = "xyes"], + [ + COGL_PANGO_PKG_REQUIRES="$COGL_PANGO_PKG_REQUIRES pangocairo >= pangocairo_req_version" + ] +) + +dnl ============================================================ +dnl Should cogl-path be built? +dnl ============================================================ + +AC_ARG_ENABLE( + [cogl-path], + [AC_HELP_STRING([--enable-cogl-path=@<:@no/yes@:>@], [Enable 2D path support @<:@default=yes@:>@])], + [], + enable_cogl_path=yes +) +AS_IF([test "x$enable_cogl_path" = "xyes"], + [ + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_COGL_PATH_SUPPORT" + ] +) + +dnl ============================================================ +dnl Choose image loading backend +dnl ============================================================ +COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES gdk-pixbuf-2.0 >= gdk_pixbuf_req_version" +COGL_IMAGE_BACKEND="gdk-pixbuf" + +dnl ============================================================ +dnl Determine which drivers and window systems we can support +dnl ============================================================ + +dnl ======================================================== +dnl Drivers first... +dnl ======================================================== +EGL_CHECKED=no + +enabled_drivers="" + +HAVE_GLES1=0 +AC_ARG_ENABLE( + [gles1], + [AC_HELP_STRING([--enable-gles1=@<:@no/yes@:>@], [Enable support for OpenGL-ES 1.1 @<:@default=no@:>@])], + [], + enable_gles1=no +) +AS_IF([test "x$enable_gles1" = "xyes"], + [ + enabled_drivers="$enabled_drivers gles1" + + cogl_gl_headers="GLES/gl.h GLES/glext.h" + + AC_DEFINE([HAVE_COGL_GLES], 1, [Have GLES 1.1 for rendering]) + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLES CLUTTER_COGL_HAS_GLES" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLES1" + HAVE_GLES1=1 + + PKG_CHECK_EXISTS([glesv1_cm], + [COGL_PKG_REQUIRES_GL="$COGL_PKG_REQUIRES_GL glesv1_cm" + COGL_GLES1_LIBNAME="libGLESv1_CM.so" + ], + [ + # We have to check the two headers independently as GLES/glext.h + # needs to include GLES/gl.h to have the GL types defined (eg. + # GLenum). + AC_CHECK_HEADER([GLES/gl.h], + [], + [AC_MSG_ERROR([Unable to locate GLES/gl.h])]) + AC_CHECK_HEADER([GLES/glext.h], + [], + [AC_MSG_ERROR([Unable to locate GLES/glext.h])], + [#include ]) + + # Early implementations provided only a GLES/egl.h while Khronos's + # implementer guide now states EGL/egl.h is the One. Some + # implementations keep a GLES/egl.h wrapper around EGL/egl.h for + # backward compatibility while others provide EGL/egl.h only. + AC_CHECK_HEADERS([GLES/egl.h EGL/egl.h]) + + AS_IF([test "x$ac_cv_header_GLES_egl_h" = "xyes"], + [COGL_EGL_INCLUDES="#include "], + [test "x$ac_cv_header_EGL_egl_h" = "xyes"], + [ + COGL_EGL_INCLUDES="#include " + ], + [AC_MSG_ERROR([Unable to locate EGL header])]) + AC_SUBST([COGL_EGL_INCLUDES]) + + AC_CHECK_HEADERS([EGL/eglext.h], + [COGL_EGL_INCLUDES="$COGL_EGL_INCLUDE +#include "], + [], + [$COGL_EGL_INCLUDES]) + + # Check for a GLES 1.x Common Profile library with/without EGL. + # + # Note: historically GLES 1 libraries shipped with the + # EGL and GLES symbols all bundled in one library. Now + # the Khronos Implementers Guide defines two naming + # schemes: -lGLES_CM should be used for a library that + # bundles the GLES and EGL API together and -lGLESv1_CM + # would be used for a standalone GLES API. + AC_CHECK_LIB(GLES_CM, [eglInitialize], + [COGL_GLES1_LIBNAME="libGLES_CM.so"], + [ + AC_CHECK_LIB(GLESv1_CM, [glFlush], + [COGL_GLES1_LIBNAME="libGLESv1_CM.so" + NEED_SEPARATE_EGL=yes + ], + [AC_MSG_ERROR([Unable to locate required GLES 1.x Common Profile library])]) + ]) + + EGL_CHECKED=yes + ]) + ]) + +HAVE_GLES2=0 +AC_ARG_ENABLE( + [gles2], + [AC_HELP_STRING([--enable-gles2=@<:@no/yes@:>@], [Enable support for OpenGL-ES 2.0 @<:@default=no@:>@])], + [], + enable_gles2=no +) +AS_IF([test "x$enable_gles2" = "xyes"], + [ + enabled_drivers="$enabled_drivers gles2" + + cogl_gl_headers="GLES2/gl2.h GLES2/gl2ext.h" + AC_DEFINE([HAVE_COGL_GLES2], 1, [Have GLES 2.0 for rendering]) + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLES CLUTTER_COGL_HAS_GLES" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLES2" + HAVE_GLES2=1 + + PKG_CHECK_EXISTS([glesv2], + [COGL_PKG_REQUIRES_GL="$COGL_PKG_REQUIRES_GL glesv2" + COGL_GLES2_LIBNAME="libGLESv2.so" + ], + [ + # We have to check the two headers independently as GLES2/gl2ext.h + # needs to include GLES2/gl2.h to have the GL types defined (eg. + # GLenum). + AC_CHECK_HEADER([GLES2/gl2.h], + [], + [AC_MSG_ERROR([Unable to locate GLES2/gl2.h])]) + AC_CHECK_HEADER([GLES2/gl2ext.h], + [], + [AC_MSG_ERROR([Unable to locate GLES2/gl2ext.h])], + [#include ]) + + COGL_GLES2_LIBNAME="libGLESv2.so" + ]) + ]) + +HAVE_GL=0 +AC_ARG_ENABLE( + [gl], + [AC_HELP_STRING([--enable-gl=@<:@no/yes@:>@], [Enable support for OpenGL @<:@default=yes@:>@])], + [], + [enable_gl=yes] +) +AS_IF([test "x$enable_gl" = "xyes"], + [ + enabled_drivers="$enabled_drivers gl" + + PKG_CHECK_EXISTS([x11], [ALLOW_GLX=yes]) + + cogl_gl_headers="GL/gl.h" + + PKG_CHECK_EXISTS([gl], + dnl We don't want to use COGL_PKG_REQUIRES here because we don't want to + dnl directly link against libGL + [COGL_PKG_REQUIRES_GL="$COGL_PKG_REQUIRES_GL gl"], + [AC_CHECK_LIB(GL, [glGetString], + , + [AC_MSG_ERROR([Unable to locate required GL library])]) + ]) + COGL_GL_LIBNAME="libGL.so.1" + + AC_DEFINE([HAVE_COGL_GL], [1], [Have GL for rendering]) + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GL" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS CLUTTER_COGL_HAS_GL" + HAVE_GL=1 + ]) + +AM_CONDITIONAL([COGL_DRIVER_GL_SUPPORTED], [test "x$enable_gl" = "xyes"]) +AM_CONDITIONAL([COGL_DRIVER_GLES_SUPPORTED], + [test "x$enable_gles1" = "xyes" || test "x$enable_gles2" = "xyes"]) + +dnl Allow the GL library names and default driver to be overridden with configure options +AC_ARG_WITH([gl-libname], + [AS_HELP_STRING([--with-gl-libname], + override the name of the GL library to dlopen)], + [COGL_GL_LIBNAME="$withval"]) +AC_ARG_WITH([gles1-libname], + [AS_HELP_STRING([--with-gles1-libname], + override the name of the GLESv1 library to dlopen)], + [COGL_GLES1_LIBNAME="$withval"]) +AC_ARG_WITH([gles2-libname], + [AS_HELP_STRING([--with-gles2-libname], + override the name of the GLESv2 library to dlopen)], + [COGL_GLES2_LIBNAME="$withval"]) +AC_ARG_WITH([default-driver], + [AS_HELP_STRING([--with-default-driver], + specify a default cogl driver)], + [COGL_DEFAULT_DRIVER="${withval}"], + [COGL_DEFAULT_DRIVER="" ]) + +AM_CONDITIONAL(HAVE_COGL_DEFAULT_DRIVER, + [ test "x$COGL_DEFAULT_DRIVER" != "x" ]) + + +AC_SUBST([COGL_GL_LIBNAME]) +AC_SUBST([HAVE_GL]) +AC_SUBST([COGL_GLES1_LIBNAME]) +AC_SUBST([HAVE_GLES1]) +AC_SUBST([COGL_GLES2_LIBNAME]) +AC_SUBST([HAVE_GLES2]) +AC_SUBST([COGL_DEFAULT_DRIVER]) + +AC_ARG_ENABLE( + [cogl-gles2], + [AC_HELP_STRING([--enable-cogl-gles2=@<:@no/yes@:>@], + [Enable libcogl-gles2 frontend api for OpenGL-ES 2.0 @<:@default=auto@:>@])], + [], + [ + AS_IF([test "x$HAVE_GLES2" = "x1"], + [enable_cogl_gles2=yes], + [enable_cogl_gles2=no]) + ] +) +AS_IF([test "x$enable_cogl_gles2" = "xyes"], + [ + AS_IF([test "x$HAVE_GLES2" != "x1"], + [ + AC_MSG_ERROR([libcogl-gles2 is currently only supported on systems with a native GLES 2.0 library]) + ]) + ]) +AM_CONDITIONAL([BUILD_COGL_GLES2], [test "x$enable_cogl_gles2" = "xyes"]) + + +dnl ======================================================== +dnl Check window system integration libraries... +dnl ======================================================== + +AC_ARG_ENABLE( + [glx], + [AC_HELP_STRING([--enable-glx=@<:@no/yes@:>@], [Enable support GLX @<:@default=auto@:>@])], + [], + [AS_IF([test "x$ALLOW_GLX" = "xyes"], [enable_glx=yes], [enable_glx=no])] +) +AS_IF([test "x$enable_glx" = "xyes"], + [ + AS_IF([test "x$ALLOW_GLX" != "xyes"], + [AC_MSG_ERROR([GLX not supported with this configuration])]) + + NEED_XLIB=yes + SUPPORT_GLX=yes + GL_WINSYS_APIS="$GL_WINSYS_APIS glx" + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GLX_SUPPORT" + ]) +AM_CONDITIONAL(SUPPORT_GLX, [test "x$SUPPORT_GLX" = "xyes"]) + +EGL_PLATFORM_COUNT=0 + +AC_ARG_ENABLE( + [kms-egl-platform], + [AC_HELP_STRING([--enable-kms-egl-platform=@<:@no/yes@:>@], [Enable support for the KMS egl platform @<:@default=no@:>@])], + [], + enable_kms_egl_platform=no +) +AS_IF([test "x$enable_kms_egl_platform" = "xyes"], + [ + EGL_PLATFORM_COUNT=$((EGL_PLATFORM_COUNT+1)) + NEED_EGL=yes + EGL_PLATFORMS="$EGL_PLATFORMS kms" + + PKG_CHECK_EXISTS([gbm], + [ + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES gbm" + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES libdrm" + ], + [AC_MSG_ERROR([Unable to locate required libgbm library for the KMS egl platform])]) + + GBM_VERSION=`$PKG_CONFIG --modversion gbm` + GBM_MAJOR=`echo $GBM_VERSION | cut -d'.' -f1` + GBM_MINOR=`echo $GBM_VERSION | cut -d'.' -f2` + GBM_MICRO=`echo $GBM_VERSION | cut -d'.' -f3 | sed 's/-.*//'` + + AC_DEFINE_UNQUOTED([COGL_GBM_MAJOR], [$GBM_MAJOR], [The major version for libgbm]) + AC_DEFINE_UNQUOTED([COGL_GBM_MINOR], [$GBM_MINOR], [The minor version for libgbm]) + AC_DEFINE_UNQUOTED([COGL_GBM_MICRO], [$GBM_MICRO], [The micro version for libgbm]) + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_EGL_PLATFORM_KMS_SUPPORT" + ]) +AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_KMS, + [test "x$enable_kms_egl_platform" = "xyes"]) + +AC_ARG_ENABLE( + [wayland-egl-server], + [AC_HELP_STRING([--enable-wayland-egl-server=@<:@no/yes@:>@], [Enable server side wayland support @<:@default=no@:>@])], + [], + enable_wayland_egl_server=no +) +AS_IF([test "x$enable_wayland_egl_server" = "xyes"], + [ + NEED_EGL=yes + + PKG_CHECK_MODULES(WAYLAND_SERVER, + [wayland-server >= wayland_server_req_version]) + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES wayland-server >= wayland_server_req_version" + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT" + ]) +AM_CONDITIONAL(SUPPORT_WAYLAND_EGL_SERVER, + [test "x$enable_wayland_egl_server" = "xyes"]) + +dnl This should go last, since it's the default fallback and we need +dnl to check the value of $EGL_PLATFORM_COUNT here. +AC_ARG_ENABLE( + [xlib-egl-platform], + [AC_HELP_STRING([--enable-xlib-egl-platform=@<:@no/yes@:>@], [Enable support for the Xlib egl platform @<:@default=auto@:>@])], + [], + AS_IF([test "x$enable_gles1" = "xyes" -o \ + "x$enable_gles2" = "xyes" && \ + test $EGL_PLATFORM_COUNT -eq 0], + [enable_xlib_egl_platform=yes], [enable_xlib_egl_platform=no]) +) +AS_IF([test "x$enable_xlib_egl_platform" = "xyes"], + [ + EGL_PLATFORM_COUNT=$((EGL_PLATFORM_COUNT+1)) + NEED_EGL=yes + NEED_XLIB=yes + EGL_PLATFORMS="$EGL_PLATFORMS xlib" + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT" + ]) +AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_XLIB, + [test "x$enable_xlib_egl_platform" = "xyes"]) + +AS_IF([test "x$NEED_EGL" = "xyes" && test "x$EGL_CHECKED" != "xyes"], + [ + PKG_CHECK_EXISTS([egl], + [COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES egl"], + [ + AC_CHECK_HEADERS( + [EGL/egl.h], + [], + [AC_MSG_ERROR([Unable to locate required EGL headers])]) + AC_CHECK_HEADERS( + [EGL/eglext.h], + [], + [AC_MSG_ERROR([Unable to locate required EGL headers])], + [#include ]) + + AC_CHECK_LIB(EGL, [eglInitialize], + [COGL_EXTRA_LDFLAGS="$COGL_EXTRA_LDFLAGS -lEGL"], + [AC_MSG_ERROR([Unable to locate required EGL library])]) + + COGL_EXTRA_LDFLAGS="$COGL_EXTRA_LDFLAGS -lEGL" + ] + ) + + COGL_EGL_INCLUDES="#include +#include " + AC_SUBST([COGL_EGL_INCLUDES]) + ]) + +AS_IF([test "x$NEED_EGL" = "xyes"], + [ + SUPPORT_EGL=yes + GL_WINSYS_APIS="$GL_WINSYS_APIS egl" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_EGL_SUPPORT" + ]) + +AM_CONDITIONAL(SUPPORT_EGL, [test "x$SUPPORT_EGL" = "xyes"]) + +dnl ======================================================== +dnl Check X11 dependencies if required +dnl ======================================================== +AS_IF([test "x$NEED_XLIB" = "xyes"], + [ + X11_MODULES="x11 xext xfixes >= xfixes_req_version xdamage xcomposite >= xcomposite_req_version xrandr >= xrandr_req_version" + PKG_CHECK_MODULES(DUMMY, [$X11_MODULES], + [COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES $X11_MODULES"]) + SUPPORT_X11=yes + SUPPORT_XLIB=yes + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_X11" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_X11_SUPPORT" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_XLIB" + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_XLIB_SUPPORT" + ]) + +AM_CONDITIONAL(X11_TESTS, [test "x$SUPPORT_X11" = "xyes"]) +AM_CONDITIONAL(SUPPORT_X11, [test "x$SUPPORT_X11" = "xyes"]) +AM_CONDITIONAL(SUPPORT_XLIB, [test "x$SUPPORT_XLIB" = "xyes"]) + +dnl ================================================================ +dnl Documentation stuff. +dnl ================================================================ +GLIB_PREFIX="`$PKG_CONFIG --variable=prefix glib-2.0`" +GDKPIXBUF_PREFIX="`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0`" +AC_SUBST(GLIB_PREFIX) +AC_SUBST(GDKPIXBUF_PREFIX) + + +AC_SUBST(COGL_PKG_REQUIRES) +if test -n "$COGL_PKG_REQUIRES"; then + PKG_CHECK_MODULES(COGL_DEP, [$COGL_PKG_REQUIRES]) + + if test -n "$COGL_PKG_REQUIRES_GL"; then + PKG_CHECK_MODULES(COGL_DEP_GL, [$COGL_PKG_REQUIRES_GL]) + + dnl Strip out the GL libraries from the GL pkg-config files so we can + dnl dynamically load them instead + gl_libs="" + for x in $COGL_DEP_GL_LIBS; do + AS_CASE([$x], + [-lGL], [], + [-lGLESv2], [], + [-lGLESv1_CM], [], + [*], [gl_libs="$gl_libs $x"]) + done + COGL_DEP_CFLAGS="$COGL_DEP_CFLAGS $COGL_DEP_CFLAGS_GL" + COGL_DEP_LIBS="$COGL_DEP_LIBS $gl_libs" + fi +fi +AC_SUBST(COGL_PANGO_PKG_REQUIRES) + +AS_IF([test "x$enable_cogl_pango" = "xyes"], + [PKG_CHECK_MODULES(COGL_PANGO_DEP, [$COGL_PANGO_PKG_REQUIRES])] +) +AM_CONDITIONAL([BUILD_COGL_PANGO], [test "x$enable_cogl_pango" = "xyes"]) + +AM_CONDITIONAL([BUILD_COGL_PATH], [test "x$enable_cogl_path" = "xyes"]) + +dnl ================================================================ +dnl Misc program dependencies. +dnl ================================================================ +AC_PROG_INSTALL + +dnl ================================================================ +dnl GObject-Introspection check +dnl ================================================================ +GOBJECT_INTROSPECTION_CHECK([gi_req_version]) + +dnl ================================================================ +dnl Checks for header files. +dnl ================================================================ +AC_PATH_X +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h limits.h unistd.h) +AC_CHECK_HEADER([endian.h], + [AC_CHECK_DECL([__FLOAT_WORD_ORDER], + AC_DEFINE([HAVE_FLOAT_WORD_ORDER], [1], + [Has the __FLOAT_WORD_ORDER macro]))]) + +dnl ================================================================ +dnl Checks for library functions. +dnl ================================================================ + +dnl The 'ffs' function is part of C99 so it isn't always +dnl available. Cogl has a fallback if needed. +AC_CHECK_FUNCS([ffs]) + +dnl 'memmem' is a GNU extension but we have a simple fallback +AC_CHECK_FUNCS([memmem]) + +dnl This is used in the cogl-gles2-gears example but it is a GNU extension +save_libs="$LIBS" +LIBS="$LIBS $LIBM" +AC_CHECK_FUNCS([sincos]) +LIBS="$save_libs" + +dnl ================================================================ +dnl Platform values +dnl ================================================================ + +dnl These are values from system headers that we want to copy into the +dnl public Cogl headers without having to include the system header +have_poll_h=no +AC_CHECK_HEADER(poll.h, + [ + AC_COMPUTE_INT(COGL_SYSDEF_POLLIN, POLLIN, [#include ], + AC_MSG_ERROR([Unable to get value of POLLIN])) + AC_COMPUTE_INT(COGL_SYSDEF_POLLPRI, POLLPRI, [#include ], + AC_MSG_ERROR([Unable to get value of POLLPRI])) + AC_COMPUTE_INT(COGL_SYSDEF_POLLOUT, POLLOUT, [#include ], + AC_MSG_ERROR([Unable to get value of POLLOUT])) + AC_COMPUTE_INT(COGL_SYSDEF_POLLERR, POLLERR, [#include ], + AC_MSG_ERROR([Unable to get value of POLLERR])) + AC_COMPUTE_INT(COGL_SYSDEF_POLLHUP, POLLHUP, [#include ], + AC_MSG_ERROR([Unable to get value of POLLHUP])) + AC_COMPUTE_INT(COGL_SYSDEF_POLLNVAL, POLLNVAL, [#include ], + AC_MSG_ERROR([Unable to get value of POLLNVAL])) + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_POLL_SUPPORT" + have_poll_h=yes + ]) + +AS_IF([test "x$have_poll_h" = "xno"], + [ + COGL_SYSDEF_POLLIN=1 + COGL_SYSDEF_POLLPRI=2 + COGL_SYSDEF_POLLOUT=4 + COGL_SYSDEF_POLLERR=8 + COGL_SYSDEF_POLLHUP=16 + COGL_SYSDEF_POLLNVAL=32 + ]) + +COGL_DEFINES_EXTRA="$COGL_DEFINES_EXTRA +#define COGL_SYSDEF_POLLIN $COGL_SYSDEF_POLLIN +#define COGL_SYSDEF_POLLPRI $COGL_SYSDEF_POLLPRI +#define COGL_SYSDEF_POLLOUT $COGL_SYSDEF_POLLOUT +#define COGL_SYSDEF_POLLERR $COGL_SYSDEF_POLLERR +#define COGL_SYSDEF_POLLHUP $COGL_SYSDEF_POLLHUP +#define COGL_SYSDEF_POLLNVAL $COGL_SYSDEF_POLLNVAL +" + +dnl ================================================================ +dnl What needs to be substituted in other files +dnl ================================================================ +COGL_DEFINES="$COGL_DEFINES_EXTRA" +for x in $COGL_DEFINES_SYMBOLS; do + COGL_DEFINES="$COGL_DEFINES +#define $x 1" +done; +AC_SUBST(COGL_DEFINES) +AM_SUBST_NOTMAKE(COGL_DEFINES) + +AS_IF([test "x$cogl_gl_headers" = "x"], + [AC_MSG_ERROR([Internal error: no GL header set])]) +dnl cogl_gl_headers is a space separate list of headers to +dnl include. We'll now convert them to a single variable with a +dnl #include line for each header +COGL_GL_HEADER_INCLUDES="" +for x in $cogl_gl_headers; do + COGL_GL_HEADER_INCLUDES="$COGL_GL_HEADER_INCLUDES +#include <$x>" +done; +AC_SUBST(COGL_GL_HEADER_INCLUDES) +AM_SUBST_NOTMAKE(COGL_GL_HEADER_INCLUDES) + +AC_DEFINE([COGL_ENABLE_EXPERIMENTAL_2_0_API], [1], + [Can use Cogl 2.0 API internally]) +AC_DEFINE([COGL_ENABLE_EXPERIMENTAL_API], [1], + [Can use experimental API internally]) + +AC_SUBST(COGL_DEP_CFLAGS) +AC_SUBST(COGL_DEP_LIBS) +AC_SUBST(COGL_PANGO_DEP_CFLAGS) +AC_SUBST(COGL_PANGO_DEP_LIBS) +AC_SUBST(COGL_GST_DEP_CFLAGS) +AC_SUBST(COGL_GST_DEP_LIBS) +AC_SUBST(COGL_EXTRA_CFLAGS) +AC_SUBST(COGL_EXTRA_LDFLAGS) + +# just for compatability with the clutter build... +MAINTAINER_CFLAGS= +AC_SUBST(MAINTAINER_CFLAGS) + +AC_OUTPUT( +Makefile +test-fixtures/Makefile +cogl/Makefile +cogl/mutter-cogl-1.0.pc +cogl/cogl-defines.h +cogl/cogl-gl-header.h +cogl/cogl-egl-defines.h +cogl-pango/Makefile +cogl-pango/mutter-cogl-pango-1.0.pc +cogl-path/Makefile +cogl-path/mutter-cogl-path-1.0.pc +cogl-gles2/Makefile +cogl-gles2/mutter-cogl-gles2-1.0.pc +tests/Makefile +tests/config.env +tests/conform/Makefile +tests/unit/Makefile +tests/micro-perf/Makefile +tests/data/Makefile +) + +dnl ================================================================ +dnl Dah Da! +dnl ================================================================ +echo "" +echo "Cogl - $COGL_1_VERSION/$COGL_VERSION (${COGL_RELEASE_STATUS})" + +# Global flags +echo "" +echo " • Global:" +echo " Prefix: ${prefix}" +if test "x$COGL_DEFAULT_DRIVER" != "x"; then +echo " Default driver: ${COGL_DEFAULT_DRIVER}" +fi + +echo "" +# Features +echo " • Features:" +echo " Drivers: ${enabled_drivers}" +for driver in $enabled_drivers; do + driver=`echo $driver | tr "[gles]" "[GLES]"` + libname=`eval echo \\$COGL_${driver}_LIBNAME` + echo " Library name for $driver: $libname" +done +echo " GL Window System APIs:${GL_WINSYS_APIS}" +if test "x$SUPPORT_EGL" = "xyes"; then +echo " EGL Platforms:${EGL_PLATFORMS}" +echo " Wayland compositor support: ${enable_wayland_egl_server}" +fi +echo " Build libcogl-gles2 GLES 2.0 frontend api: ${enable_cogl_gles2}" +echo " Image backend: ${COGL_IMAGE_BACKEND}" +echo " Cogl Pango: ${enable_cogl_pango}" +echo " Cogl Path: ${enable_cogl_path}" + +# Compiler/Debug related flags +echo "" +echo " • Build options:" +echo " Debugging: ${enable_debug}" +echo " Profiling: ${enable_profile}" +echo " Enable deprecated symbols: ${enable_deprecated}" +echo " Compiler flags: ${CFLAGS} ${COGL_EXTRA_CFLAGS}" +echo " Linker flags: ${LDFLAGS} ${COGL_EXTRA_LDFLAGS}" + +# Miscellaneous +echo "" +echo " • Extra:" +echo " Build introspection data: ${enable_introspection}" +echo " Build unit tests: ${enable_unit_tests}" + +echo "" + +# General warning about experimental features +if test "x$EXPERIMENTAL_CONFIG" = "xyes"; then +echo "" +echo "☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠" +echo " *WARNING*" +echo "" +echo " The stability of your build might be affected by one or more" +echo " experimental configuration options." +echo +echo " experimental options: $EXPERIMENTAL_OPTIONS" +echo "☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠" +echo "" +fi diff --git a/cogl/test-fixtures/Makefile.am b/cogl/test-fixtures/Makefile.am new file mode 100644 index 000000000..aa66216e1 --- /dev/null +++ b/cogl/test-fixtures/Makefile.am @@ -0,0 +1,21 @@ + +noinst_LTLIBRARIES = libtest-fixtures.la + +libtest_fixtures_la_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir)/cogl \ + -Wall \ + $(NULL) + +libtest_fixtures_la_CPPFLAGS += \ + -DCOGL_DISABLE_DEPRECATED \ + -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" \ + -DCOGL_COMPILATION + +libtest_fixtures_la_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) + +libtest_fixtures_la_SOURCES = \ + test-unit.h \ + test-utils.h \ + test-utils.c + diff --git a/cogl/test-fixtures/test-unit.h b/cogl/test-fixtures/test-unit.h new file mode 100644 index 000000000..270a94134 --- /dev/null +++ b/cogl/test-fixtures/test-unit.h @@ -0,0 +1,31 @@ +#ifndef _TEST_UNIT_H_ +#define _TEST_UNIT_H_ + +#include + +#ifdef ENABLE_UNIT_TESTS + +typedef struct _CoglUnitTest +{ + const char *name; + TestFlags requirement_flags; + TestFlags known_failure_flags; + void (*run) (void); +} CoglUnitTest; + +#define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \ + static void NAME (void); \ + \ + const CoglUnitTest unit_test_##NAME = \ + { #NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS, NAME }; \ + \ + static void NAME (void) + +#else /* ENABLE_UNIT_TESTS */ + +#define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \ + static inline void NAME (void) + +#endif /* ENABLE_UNIT_TESTS */ + +#endif /* _TEST_UNIT_H_ */ diff --git a/cogl/test-fixtures/test-utils.c b/cogl/test-fixtures/test-utils.c new file mode 100644 index 000000000..59e3fd8c9 --- /dev/null +++ b/cogl/test-fixtures/test-utils.c @@ -0,0 +1,535 @@ +#include + +#include + +#include "test-unit.h" +#include "test-utils.h" + +#define FB_WIDTH 512 +#define FB_HEIGHT 512 + +static CoglBool cogl_test_is_verbose; + +CoglContext *test_ctx; +CoglFramebuffer *test_fb; + +static CoglBool +check_flags (TestFlags flags, + CoglRenderer *renderer) +{ + if (flags & TEST_REQUIREMENT_GL && + cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL && + cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL3) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_NPOT && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_TEXTURE_3D && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_3D)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_TEXTURE_RECTANGLE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_TEXTURE_RG && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RG)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_POINT_SPRITE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_POINT_SPRITE)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_GLES2_CONTEXT && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLES2_CONTEXT)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_MAP_WRITE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_GLSL && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLSL)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_OFFSCREEN && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + return FALSE; + } + + if (flags & TEST_REQUIREMENT_FENCE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE)) + { + return FALSE; + } + + if (flags & TEST_KNOWN_FAILURE) + { + return FALSE; + } + + return TRUE; +} + +CoglBool +is_boolean_env_set (const char *variable) +{ + char *val = getenv (variable); + CoglBool ret; + + if (!val) + return FALSE; + + if (g_ascii_strcasecmp (val, "1") == 0 || + g_ascii_strcasecmp (val, "on") == 0 || + g_ascii_strcasecmp (val, "true") == 0) + ret = TRUE; + else if (g_ascii_strcasecmp (val, "0") == 0 || + g_ascii_strcasecmp (val, "off") == 0 || + g_ascii_strcasecmp (val, "false") == 0) + ret = FALSE; + else + { + g_critical ("Spurious boolean environment variable value (%s=%s)", + variable, val); + ret = TRUE; + } + + return ret; +} + +void +test_utils_init (TestFlags requirement_flags, + TestFlags known_failure_flags) +{ + static int counter = 0; + CoglError *error = NULL; + CoglOnscreen *onscreen = NULL; + CoglDisplay *display; + CoglRenderer *renderer; + CoglBool missing_requirement; + CoglBool known_failure; + + if (counter != 0) + g_critical ("We don't support running more than one test at a time\n" + "in a single test run due to the state leakage that can\n" + "cause subsequent tests to fail.\n" + "\n" + "If you want to run all the tests you should run\n" + "$ make test-report"); + counter++; + + if (is_boolean_env_set ("COGL_TEST_VERBOSE") || + is_boolean_env_set ("V")) + cogl_test_is_verbose = TRUE; + + /* NB: This doesn't have any effect since commit 47444dac of glib + * because the environment variable is read in a magic constructor + * so it is too late to set them here */ + if (g_getenv ("G_DEBUG")) + { + char *debug = g_strconcat (g_getenv ("G_DEBUG"), ",fatal-warnings", NULL); + g_setenv ("G_DEBUG", debug, TRUE); + g_free (debug); + } + else + g_setenv ("G_DEBUG", "fatal-warnings", TRUE); + + g_setenv ("COGL_X11_SYNC", "1", 0); + + test_ctx = cogl_context_new (NULL, &error); + if (!test_ctx) + g_critical ("Failed to create a CoglContext: %s", error->message); + + display = cogl_context_get_display (test_ctx); + renderer = cogl_display_get_renderer (display); + + missing_requirement = !check_flags (requirement_flags, renderer); + known_failure = !check_flags (known_failure_flags, renderer); + + if (is_boolean_env_set ("COGL_TEST_ONSCREEN")) + { + onscreen = cogl_onscreen_new (test_ctx, 640, 480); + test_fb = COGL_FRAMEBUFFER (onscreen); + } + else + { + CoglOffscreen *offscreen; + CoglTexture2D *tex = cogl_texture_2d_new_with_size (test_ctx, + FB_WIDTH, FB_HEIGHT); + offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (tex)); + test_fb = COGL_FRAMEBUFFER (offscreen); + } + + if (!cogl_framebuffer_allocate (test_fb, &error)) + g_critical ("Failed to allocate framebuffer: %s", error->message); + + if (onscreen) + cogl_onscreen_show (onscreen); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH | + COGL_BUFFER_BIT_STENCIL, + 0, 0, 0, 1); + + if (missing_requirement) + g_print ("WARNING: Missing required feature[s] for this test\n"); + else if (known_failure) + g_print ("WARNING: Test is known to fail\n"); +} + +void +test_utils_fini (void) +{ + if (test_fb) + cogl_object_unref (test_fb); + + if (test_ctx) + cogl_object_unref (test_ctx); +} + +static CoglBool +compare_component (int a, int b) +{ + return ABS (a - b) <= 1; +} + +void +test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, + uint32_t expected_pixel) +{ + /* Compare each component with a small fuzz factor */ + if (!compare_component (screen_pixel[0], expected_pixel >> 24) || + !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || + !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff) || + !compare_component (screen_pixel[3], (expected_pixel >> 0) & 0xff)) + { + uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); + char *screen_pixel_string = + g_strdup_printf ("#%08x", screen_pixel_num); + char *expected_pixel_string = + g_strdup_printf ("#%08x", expected_pixel); + + g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); + + g_free (screen_pixel_string); + g_free (expected_pixel_string); + } +} + +void +test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel) +{ + /* Compare each component with a small fuzz factor */ + if (!compare_component (screen_pixel[0], expected_pixel >> 24) || + !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || + !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff)) + { + uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); + char *screen_pixel_string = + g_strdup_printf ("#%06x", screen_pixel_num >> 8); + char *expected_pixel_string = + g_strdup_printf ("#%06x", expected_pixel >> 8); + + g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); + + g_free (screen_pixel_string); + g_free (expected_pixel_string); + } +} + +void +test_utils_check_pixel (CoglFramebuffer *test_fb, + int x, int y, uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + cogl_framebuffer_read_pixels (test_fb, + x, y, 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + + test_utils_compare_pixel (pixel, expected_pixel); +} + +void +test_utils_check_pixel_and_alpha (CoglFramebuffer *test_fb, + int x, int y, uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + cogl_framebuffer_read_pixels (test_fb, + x, y, 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + + test_utils_compare_pixel_and_alpha (pixel, expected_pixel); +} + +void +test_utils_check_pixel_rgb (CoglFramebuffer *test_fb, + int x, int y, int r, int g, int b) +{ + test_utils_check_pixel (test_fb, x, y, (r << 24) | (g << 16) | (b << 8)); +} + +void +test_utils_check_region (CoglFramebuffer *test_fb, + int x, int y, + int width, int height, + uint32_t expected_rgba) +{ + uint8_t *pixels, *p; + + pixels = p = g_malloc (width * height * 4); + cogl_framebuffer_read_pixels (test_fb, + x, + y, + width, + height, + COGL_PIXEL_FORMAT_RGBA_8888, + p); + + /* Check whether the center of each division is the right color */ + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + test_utils_compare_pixel (p, expected_rgba); + p += 4; + } + + g_free (pixels); +} + +CoglTexture * +test_utils_create_color_texture (CoglContext *context, + uint32_t color) +{ + CoglTexture2D *tex_2d; + + color = GUINT32_TO_BE (color); + + tex_2d = cogl_texture_2d_new_from_data (context, + 1, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + (uint8_t *) &color, + NULL); + + return COGL_TEXTURE (tex_2d); +} + +CoglBool +cogl_test_verbose (void) +{ + return cogl_test_is_verbose; +} + +static void +set_auto_mipmap_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture), + FALSE); +} + +CoglTexture * +test_utils_texture_new_with_size (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglTextureComponents components) +{ + CoglTexture *tex; + CoglError *skip_error = NULL; + + if ((test_utils_is_pot (width) && test_utils_is_pot (height)) || + (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + /* First try creating a fast-path non-sliced texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, + width, height)); + + cogl_texture_set_components (tex, components); + + if (!cogl_texture_allocate (tex, &skip_error)) + { + cogl_error_free (skip_error); + cogl_object_unref (tex); + tex = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + /* If it fails resort to sliced textures */ + int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? + -1 : COGL_TEXTURE_MAX_WASTE; + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_with_size (ctx, + width, + height, + max_waste); + tex = COGL_TEXTURE (tex_2ds); + + cogl_texture_set_components (tex, components); + } + + if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) + { + /* To be able to iterate the slices of a #CoglTexture2DSliced we + * need to ensure the texture is allocated... */ + cogl_texture_allocate (tex, NULL); /* don't catch exceptions */ + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); /* don't catch exceptions */ + } + + cogl_texture_allocate (tex, NULL); + + return tex; +} + +CoglTexture * +test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, + TestUtilsTextureFlags flags, + CoglBool premultiplied) +{ + CoglAtlasTexture *atlas_tex; + CoglTexture *tex; + CoglError *internal_error = NULL; + + if (!flags) + { + /* First try putting the texture in the atlas */ + atlas_tex = cogl_atlas_texture_new_from_bitmap (bitmap); + + cogl_texture_set_premultiplied (COGL_TEXTURE (atlas_tex), premultiplied); + + if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) + return COGL_TEXTURE (atlas_tex); + + cogl_error_free (internal_error); + cogl_object_unref (atlas_tex); + internal_error = NULL; + } + + /* If that doesn't work try a fast path 2D texture */ + if ((test_utils_is_pot (cogl_bitmap_get_width (bitmap)) && + test_utils_is_pot (cogl_bitmap_get_height (bitmap))) || + (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) + { + tex = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); + + cogl_texture_set_premultiplied (tex, premultiplied); + + if (cogl_error_matches (internal_error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY)) + { + g_assert_not_reached (); + return NULL; + } + + if (!tex) + { + cogl_error_free (internal_error); + internal_error = NULL; + } + } + else + tex = NULL; + + if (!tex) + { + /* Otherwise create a sliced texture */ + int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? + -1 : COGL_TEXTURE_MAX_WASTE; + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_from_bitmap (bitmap, max_waste); + tex = COGL_TEXTURE (tex_2ds); + + cogl_texture_set_premultiplied (tex, premultiplied); + } + + if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) + { + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); /* don't catch exceptions */ + } + + cogl_texture_allocate (tex, NULL); + + return tex; +} + +CoglTexture * +test_utils_texture_new_from_data (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglPixelFormat format, + int rowstride, + const uint8_t *data) +{ + CoglBitmap *bmp; + CoglTexture *tex; + + g_assert_cmpint (format, !=, COGL_PIXEL_FORMAT_ANY); + g_assert (data != NULL); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + tex = test_utils_texture_new_from_bitmap (bmp, flags, TRUE); + + cogl_object_unref (bmp); + + return tex; +} diff --git a/cogl/test-fixtures/test-utils.h b/cogl/test-fixtures/test-utils.h new file mode 100644 index 000000000..9c3ced9b3 --- /dev/null +++ b/cogl/test-fixtures/test-utils.h @@ -0,0 +1,287 @@ +#ifndef _TEST_UTILS_H_ +#define _TEST_UTILS_H_ + +/* NB: This header is for private and public api testing and so + * we need consider that if we are testing the public api we should + * just include but since that will only provide + * opaque typedefs we need to include the specific internal headers + * for testing private apis... + */ +#ifdef COGL_COMPILATION +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +#include + +/* We don't really care about functions that are defined without a + header for the unit tests so we can just disable it here */ +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#endif + +typedef enum _TestFlags +{ + TEST_KNOWN_FAILURE = 1<<0, + TEST_REQUIREMENT_GL = 1<<1, + TEST_REQUIREMENT_NPOT = 1<<2, + TEST_REQUIREMENT_TEXTURE_3D = 1<<3, + TEST_REQUIREMENT_TEXTURE_RECTANGLE = 1<<4, + TEST_REQUIREMENT_TEXTURE_RG = 1<<5, + TEST_REQUIREMENT_POINT_SPRITE = 1<<6, + TEST_REQUIREMENT_GLES2_CONTEXT = 1<<7, + TEST_REQUIREMENT_MAP_WRITE = 1<<8, + TEST_REQUIREMENT_GLSL = 1<<9, + TEST_REQUIREMENT_OFFSCREEN = 1<<10, + TEST_REQUIREMENT_FENCE = 1<<11, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE = 1<<12 +} TestFlags; + + /** + * TestUtilsTextureFlags: + * @TEST_UTILS_TEXTURE_NONE: No flags specified + * @TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP: Disables the automatic generation of + * the mipmap pyramid from the base level image whenever it is + * updated. The mipmaps are only generated when the texture is + * rendered with a mipmap filter so it should be free to leave out + * this flag when using other filtering modes + * @TEST_UTILS_TEXTURE_NO_SLICING: Disables the slicing of the texture + * @TEST_UTILS_TEXTURE_NO_ATLAS: Disables the insertion of the texture inside + * the texture atlas used by Cogl + * + * Flags to pass to the test_utils_texture_new_* family of functions. + */ +typedef enum { + TEST_UTILS_TEXTURE_NONE = 0, + TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, + TEST_UTILS_TEXTURE_NO_SLICING = 1 << 1, + TEST_UTILS_TEXTURE_NO_ATLAS = 1 << 2 +} TestUtilsTextureFlags; + +extern CoglContext *test_ctx; +extern CoglFramebuffer *test_fb; + +void +test_utils_init (TestFlags requirement_flags, + TestFlags known_failure_flags); + +void +test_utils_fini (void); + +/* + * test_utils_texture_new_with_size: + * @context: A #CoglContext + * @width: width of texture in pixels. + * @height: height of texture in pixels. + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @components: What texture components are required + * + * Creates a new #CoglTexture with the specified dimensions and pixel format. + * + * The storage for the texture is not necesarily created before this + * function returns. The storage can be explicitly allocated using + * cogl_texture_allocate() or preferably you can let Cogl + * automatically allocate the storage lazily when uploading data when + * Cogl may know more about how the texture will be used and can + * optimize how it is allocated. + * + * Return value: A newly created #CoglTexture + */ +CoglTexture * +test_utils_texture_new_with_size (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglTextureComponents components); + +/* + * test_utils_texture_new_from_data: + * @context: A #CoglContext + * @width: width of texture in pixels + * @height: height of texture in pixels + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @rowstride: the memory offset in bytes between the starts of + * scanlines in @data + * @data: pointer the memory region where the source buffer resides + * @error: A #CoglError to catch exceptional errors or %NULL + * + * Creates a new #CoglTexture based on data residing in memory. + * + * Note: If the given @format has an alpha channel then the data + * will be loaded into a premultiplied internal format. If you want + * to avoid having the source data be premultiplied then you can + * either specify that the data is already premultiplied or use + * test_utils_texture_new_from_bitmap which lets you explicitly + * request whether the data should internally be premultipled or not. + * + * Return value: A newly created #CoglTexture or %NULL on failure + */ +CoglTexture * +test_utils_texture_new_from_data (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglPixelFormat format, + int rowstride, + const uint8_t *data); + +/* + * test_utils_texture_new_from_bitmap: + * @bitmap: A #CoglBitmap pointer + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @premultiplied: Whether the texture should hold premultipled data. + * (if the bitmap already holds premultiplied data + * and %TRUE is given then no premultiplication will + * be done. The data will be premultipled while + * uploading if the bitmap has an alpha channel but + * does not already have a premultiplied format.) + * + * Creates a #CoglTexture from a #CoglBitmap. + * + * Return value: A newly created #CoglTexture or %NULL on failure + */ +CoglTexture * +test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, + TestUtilsTextureFlags flags, + CoglBool premultiplied); + +/* + * test_utils_check_pixel: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x co-ordinate of the pixel to test + * @y: y co-ordinate of the pixel to test + * @pixel: An integer of the form 0xRRGGBBAA representing the expected + * pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel of the + * color is ignored. The pixels are converted to a string and compared + * with g_assert_cmpstr so that if the comparison fails then the + * assert will display a meaningful message + */ +void +test_utils_check_pixel (CoglFramebuffer *framebuffer, + int x, int y, uint32_t expected_pixel); + +/** + * @framebuffer: The #CoglFramebuffer to read from + * @x: x co-ordinate of the pixel to test + * @y: y co-ordinate of the pixel to test + * @pixel: An integer of the form 0xRRGGBBAA representing the expected + * pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel is also + * checked unlike with test_utils_check_pixel(). The pixels are + * converted to a string and compared with g_assert_cmpstr so that if + * the comparison fails then the assert will display a meaningful + * message. + */ +void +test_utils_check_pixel_and_alpha (CoglFramebuffer *fb, + int x, int y, uint32_t expected_pixel); + +/* + * test_utils_check_pixel: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x co-ordinate of the pixel to test + * @y: y co-ordinate of the pixel to test + * @pixel: An integer of the form 0xrrggbb representing the expected pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel of the + * color is ignored. The pixels are converted to a string and compared + * with g_assert_cmpstr so that if the comparison fails then the + * assert will display a meaningful message + */ +void +test_utils_check_pixel_rgb (CoglFramebuffer *framebuffer, + int x, int y, int r, int g, int b); + +/* + * test_utils_check_region: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x co-ordinate of the region to test + * @y: y co-ordinate of the region to test + * @width: width of the region to test + * @height: height of the region to test + * @pixel: An integer of the form 0xrrggbb representing the expected region color + * + * Performs a read pixel on the specified region of the given cogl + * @framebuffer and asserts that it matches the given color. The alpha + * channel of the color is ignored. The pixels are converted to a + * string and compared with g_assert_cmpstr so that if the comparison + * fails then the assert will display a meaningful message + */ +void +test_utils_check_region (CoglFramebuffer *framebuffer, + int x, int y, + int width, int height, + uint32_t expected_rgba); + +/* + * test_utils_compare_pixel: + * @screen_pixel: A pixel stored in memory + * @expected_pixel: The expected RGBA value + * + * Compares a pixel from a buffer to an expected value. The pixels are + * converted to a string and compared with g_assert_cmpstr so that if + * the comparison fails then the assert will display a meaningful + * message. + */ +void +test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel); + +/* + * test_utils_compare_pixel_and_alpha: + * @screen_pixel: A pixel stored in memory + * @expected_pixel: The expected RGBA value + * + * Compares a pixel from a buffer to an expected value. This is + * similar to test_utils_compare_pixel() except that it doesn't ignore + * the alpha component. + */ +void +test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, + uint32_t expected_pixel); + +/* + * test_utils_create_color_texture: + * @context: A #CoglContext + * @color: A color to put in the texture + * + * Creates a 1x1-pixel RGBA texture filled with the given color. + */ +CoglTexture * +test_utils_create_color_texture (CoglContext *context, + uint32_t color); + +/* cogl_test_verbose: + * + * Queries if the user asked for verbose output or not. + */ +CoglBool +cogl_test_verbose (void); + +/* test_util_is_pot: + * @number: A number to test + * + * Returns whether the given integer is a power of two + */ +static inline CoglBool +test_utils_is_pot (unsigned int number) +{ + /* Make sure there is only one bit set */ + return (number & (number - 1)) == 0; +} + +#endif /* _TEST_UTILS_H_ */ diff --git a/cogl/tests/Makefile.am b/cogl/tests/Makefile.am new file mode 100644 index 000000000..94ba34a6f --- /dev/null +++ b/cogl/tests/Makefile.am @@ -0,0 +1,31 @@ +SUBDIRS = conform + +if UNIT_TESTS +SUBDIRS += unit +endif + +SUBDIRS += micro-perf data + +DIST_SUBDIRS = conform unit micro-perf data + +EXTRA_DIST = README test-launcher.sh run-tests.sh + +if UNIT_TESTS +test conform: + ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? + ( cd ./unit && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? +else +test conform: + ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? +endif + +.PHONY: test conform + +# run make test as part of make check +check-local: test + +if ENABLE_INSTALLED_TESTS +insttestdir = $(libexecdir)/installed-tests/$(PACKAGE) +insttest_SCRIPTS = run-tests.sh +insttest_DATA = config.env +endif diff --git a/cogl/tests/README b/cogl/tests/README new file mode 100644 index 000000000..cc6dbb97a --- /dev/null +++ b/cogl/tests/README @@ -0,0 +1,63 @@ +Outline of test categories: + +The conform/ tests: +------------------- +These tests should be non-interactive unit-tests that verify a single +feature is behaving as documented. See conform/ADDING_NEW_TESTS for more +details. + +Although it may seem a bit awkward; all the tests are built into a +single binary because it makes building the tests *much* faster by avoiding +lots of linking. + +Each test has a wrapper script generated though so running the individual tests +should be convenient enough. Running the wrapper script will also print out for +convenience how you could run the test under gdb or valgrind like this for +example: + + NOTE: For debugging purposes, you can run this single test as follows: + $ libtool --mode=execute \ + gdb --eval-command="b test_cogl_depth_test" \ + --args ./test-conformance -p /conform/cogl/test_cogl_depth_test + or: + $ env G_SLICE=always-malloc \ + libtool --mode=execute \ + valgrind ./test-conformance -p /conform/cogl/test_cogl_depth_test + +By default the conformance tests are run offscreen. This makes the tests run +much faster and they also don't interfere with other work you may want to do by +constantly stealing focus. CoglOnscreen framebuffers obviously don't get tested +this way so it's important that the tests also get run onscreen every once in a +while, especially if changes are being made to CoglFramebuffer related code. +Onscreen testing can be enabled by setting COGL_TEST_ONSCREEN=1 in your +environment. + +The micro-bench/ tests: +----------------------- +These should be focused performance tests, ideally testing a +single metric. Please never forget that these tests are synthetic and if you +are using them then you understand what metric is being tested. They probably +don't reflect any real world application loads and the intention is that you +use these tests once you have already determined the crux of your problem and +need focused feedback that your changes are indeed improving matters. There is +no exit status requirements for these tests, but they should give clear +feedback as to their performance. If the framerate is the feedback metric, then +the test should forcibly enable FPS debugging. + +The data/ directory: +-------------------- +This contains optional data (like images) that can be referenced by a test. + + +Misc notes: +----------- +• All tests should ideally include a detailed description in the source +explaining exactly what the test is for, how the test was designed to work, +and possibly a rationale for the approach taken for testing. + +• When running tests under Valgrind, you should follow the instructions +available here: + + http://live.gnome.org/Valgrind + +and also use the suppression file available inside the data/ directory. diff --git a/cogl/tests/config.env.in b/cogl/tests/config.env.in new file mode 100644 index 000000000..d3777565d --- /dev/null +++ b/cogl/tests/config.env.in @@ -0,0 +1,3 @@ +HAVE_GL=@HAVE_GL@ +HAVE_GLES1=@HAVE_GLES1@ +HAVE_GLES2=@HAVE_GLES2@ diff --git a/cogl/tests/conform/Makefile.am b/cogl/tests/conform/Makefile.am new file mode 100644 index 000000000..f1c4d6fa5 --- /dev/null +++ b/cogl/tests/conform/Makefile.am @@ -0,0 +1,176 @@ +NULL = + +noinst_PROGRAMS = test-conformance + +common_sources = \ + test-conform-main.c \ + $(NULL) + +unported_test_sources = \ + test-fixed.c \ + test-materials.c \ + test-viewport.c \ + test-multitexture.c \ + test-npot-texture.c \ + test-object.c \ + test-readpixels.c \ + test-texture-mipmaps.c \ + test-texture-pixmap-x11.c \ + test-texture-rectangle.c \ + test-vertex-buffer-contiguous.c \ + test-vertex-buffer-interleved.c \ + test-vertex-buffer-mutability.c \ + $(NULL) + +test_sources = \ + test-atlas-migration.c \ + test-blend-strings.c \ + test-blend.c \ + test-depth-test.c \ + test-color-hsl.c \ + test-color-mask.c \ + test-backface-culling.c \ + test-just-vertex-shader.c \ + test-pipeline-user-matrix.c \ + test-pipeline-uniforms.c \ + test-pixel-buffer.c \ + test-premult.c \ + test-snippets.c \ + test-wrap-modes.c \ + test-sub-texture.c \ + test-custom-attributes.c \ + test-offscreen.c \ + test-primitive.c \ + test-texture-3d.c \ + test-sparse-pipeline.c \ + test-read-texture-formats.c \ + test-write-texture-formats.c \ + test-point-size.c \ + test-point-size-attribute.c \ + test-point-sprite.c \ + test-no-gl-header.c \ + test-version.c \ + test-gles2-context.c \ + test-euler-quaternion.c \ + test-layer-remove.c \ + test-alpha-test.c \ + test-map-buffer-range.c \ + test-npot-texture.c \ + test-alpha-textures.c \ + test-wrap-rectangle-textures.c \ + test-texture-get-set-data.c \ + test-framebuffer-get-bits.c \ + test-primitive-and-journal.c \ + test-copy-replace-texture.c \ + test-pipeline-cache-unrefs-texture.c \ + test-texture-no-allocate.c \ + test-pipeline-shader-state.c \ + test-texture-rg.c \ + test-fence.c \ + $(NULL) + +if BUILD_COGL_PATH +test_sources += \ + test-path.c \ + test-path-clip.c +endif + +test_conformance_SOURCES = $(common_sources) $(test_sources) + +SHEXT = $(EXEEXT) + +# For convenience, this provides a way to easily run individual unit tests: +.PHONY: wrappers clean-wrappers + +wrappers: stamp-test-conformance + @true +stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c + @mkdir -p wrappers + @sed -n -e 's/^ \{1,\}ADD_TEST *( *\([a-zA-Z0-9_]\{1,\}\).*/\1/p' $(srcdir)/test-conform-main.c > unit-tests + @chmod +x $(top_srcdir)/tests/test-launcher.sh + @( echo "/stamp-test-conformance" ; \ + echo "/test-conformance$(EXEEXT)" ; \ + echo "*.o" ; \ + echo ".gitignore" ; \ + echo "unit-tests" ; ) > .gitignore + @for i in `cat unit-tests`; \ + do \ + unit=`basename $$i | sed -e s/_/-/g`; \ + echo " GEN $$unit"; \ + ( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-conformance$(EXEEXT) '' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \ + chmod +x $$unit$(SHEXT); \ + echo "/$$unit$(SHEXT)" >> .gitignore; \ + done \ + && echo timestamp > $(@F) + +clean-wrappers: + @for i in `cat unit-tests`; \ + do \ + unit=`basename $$i | sed -e s/_/-/g`; \ + echo " RM $$unit"; \ + rm -f $$unit$(SHEXT) ; \ + done \ + && rm -f unit-tests \ + && rm -f stamp-test-conformance + +# NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting +# a phony rule that will generate symlink scripts for running individual tests +BUILT_SOURCES = wrappers + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir)/cogl \ + -I$(top_srcdir)/test-fixtures + +AM_CPPFLAGS += \ + -DCOGL_ENABLE_EXPERIMENTAL_API \ + -DCOGL_DISABLE_DEPRECATED \ + -DCOGL_DISABLE_DEPRECATION_WARNINGS \ + -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" + +test_conformance_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) +test_conformance_LDADD = \ + $(COGL_DEP_LIBS) \ + $(top_builddir)/cogl/libmutter-cogl.la \ + $(LIBM) +if BUILD_COGL_PATH +test_conformance_LDADD += $(top_builddir)/cogl-path/libmutter-cogl-path.la +endif +test_conformance_LDFLAGS = -export-dynamic + +test: wrappers + @$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-conformance$(EXEEXT) + +# XXX: we could prevent the conformance test suite from running +# by simply defining this variable conditionally +TEST_PROGS = test-conformance + +.PHONY: test + +DISTCLEANFILES = .gitignore + +# we override the clean-generic target to clean up the wrappers so +# we cannot use CLEANFILES +clean-generic: clean-wrappers + $(QUIET_RM)rm -f .log + + +if ENABLE_INSTALLED_TESTS + +insttestdir = $(libexecdir)/installed-tests/$(PACKAGE)/conform +insttest_PROGRAMS = test-conformance +insttest_DATA = unit-tests + +testmetadir = $(datadir)/installed-tests/$(PACKAGE) +testmeta_DATA = conform.test + +conform.test: + echo " GEN $@"; \ + echo "[Test]" > $@.tmp; \ + echo "Type=session" >> $@.tmp; \ + echo "Exec=sh -c \"cd $(libexecdir)/installed-tests/$(PACKAGE)/conform; ../run-tests.sh ../config.env ./test-conformance\"" >> $@.tmp; \ + mv $@.tmp $@ + +CLEANFILES = conform.test + +endif diff --git a/cogl/tests/conform/test-alpha-test.c b/cogl/tests/conform/test-alpha-test.c new file mode 100644 index 000000000..e74f6d8e0 --- /dev/null +++ b/cogl/tests/conform/test-alpha-test.c @@ -0,0 +1,73 @@ +#include +#include + +#include "test-utils.h" + +static CoglTexture2D * +create_texture (CoglContext *context) +{ + static const uint8_t data[] = + { + 0xff, 0x00, 0x00, 0xff, + 0x00, 0xfa, 0x00, 0xfa + }; + + return cogl_texture_2d_new_from_data (context, + 2, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + data, + NULL /* error */); +} + +void +test_alpha_test (void) +{ + CoglTexture *tex = create_texture (test_ctx); + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglColor clear_color; + + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_alpha_test_function (pipeline, + COGL_PIPELINE_ALPHA_FUNC_GEQUAL, + 254 / 255.0f /* alpha reference */); + + cogl_color_init_from_4ub (&clear_color, 0x00, 0x00, 0xff, 0xff); + cogl_framebuffer_clear (test_fb, + COGL_BUFFER_BIT_COLOR, + &clear_color); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1, -1, + 1, 1); + + cogl_object_unref (pipeline); + cogl_object_unref (tex); + + /* The left side of the framebuffer should use the first pixel from + * the texture which is red */ + test_utils_check_region (test_fb, + 2, 2, + fb_width / 2 - 4, + fb_height - 4, + 0xff0000ff); + /* The right side of the framebuffer should use the clear color + * because the second pixel from the texture is clipped from the + * alpha test */ + test_utils_check_region (test_fb, + fb_width / 2 + 2, + 2, + fb_width / 2 - 4, + fb_height - 4, + 0x0000ffff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-alpha-textures.c b/cogl/tests/conform/test-alpha-textures.c new file mode 100644 index 000000000..dccd30e11 --- /dev/null +++ b/cogl/tests/conform/test-alpha-textures.c @@ -0,0 +1,123 @@ +#include + +#include + +#include "test-utils.h" + +static void +create_pipeline (CoglTexture **tex_out, + CoglPipeline **pipeline_out) +{ + CoglTexture2D *tex; + CoglPipeline *pipeline; + static const uint8_t tex_data[] = + { 0x00, 0x44, 0x88, 0xcc }; + + tex = cogl_texture_2d_new_from_data (test_ctx, + 2, 2, /* width/height */ + COGL_PIXEL_FORMAT_A_8, /* format */ + 2, /* rowstride */ + tex_data, + NULL); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_layer_wrap_mode (pipeline, + 0, /* layer */ + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); + + /* This is the layer combine used by cogl-pango */ + cogl_pipeline_set_layer_combine (pipeline, + 0, /* layer */ + "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", + NULL); + + cogl_pipeline_set_layer_texture (pipeline, + 0, /* layer */ + tex); + + *pipeline_out = pipeline; + *tex_out = tex; +} + +void +test_alpha_textures (void) +{ + CoglTexture *tex1, *tex2; + CoglPipeline *pipeline1, *pipeline2; + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + uint8_t replacement_data[1] = { 0xff }; + + create_pipeline (&tex1, &pipeline1); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline1, + -1.0f, 1.0f, /* x1/y1 */ + 1.0f, 0.0f /* x2/y2 */); + + create_pipeline (&tex2, &pipeline2); + + cogl_texture_set_region (tex2, + 0, 0, /* src_x/y */ + 1, 1, /* dst_x/y */ + 1, 1, /* dst_width / dst_height */ + 1, 1, /* width / height */ + COGL_PIXEL_FORMAT_A_8, + 1, /* rowstride */ + replacement_data); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline2, + -1.0f, 0.0f, /* x1/y1 */ + 1.0f, -1.0f /* x2/y2 */); + + cogl_object_unref (tex1); + cogl_object_unref (tex2); + cogl_object_unref (pipeline1); + cogl_object_unref (pipeline2); + + /* Unmodified texture */ + test_utils_check_pixel (test_fb, + fb_width / 4, + fb_height / 8, + 0x000000ff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, + fb_height / 8, + 0x444444ff); + test_utils_check_pixel (test_fb, + fb_width / 4, + fb_height * 3 / 8, + 0x888888ff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, + fb_height * 3 / 8, + 0xccccccff); + + /* Modified texture */ + test_utils_check_pixel (test_fb, + fb_width / 4, + fb_height * 5 / 8, + 0x000000ff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, + fb_height * 5 / 8, + 0x444444ff); + test_utils_check_pixel (test_fb, + fb_width / 4, + fb_height * 7 / 8, + 0x888888ff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, + fb_height * 7 / 8, + 0xffffffff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-atlas-migration.c b/cogl/tests/conform/test-atlas-migration.c new file mode 100644 index 000000000..39e8a3c1f --- /dev/null +++ b/cogl/tests/conform/test-atlas-migration.c @@ -0,0 +1,145 @@ +#include + +#include "test-utils.h" + +#define N_TEXTURES 128 + +#define OPACITY_FOR_ROW(y) \ + (0xff - ((y) & 0xf) * 0x10) + +#define COLOR_FOR_SIZE(size) \ + (colors + (size) % 3) + +typedef struct +{ + uint8_t red, green, blue, alpha; +} TestColor; + +static const TestColor colors[] = + { { 0xff, 0x00, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0x00, 0x00, 0xff, 0xff } }; + +static CoglTexture * +create_texture (int size) +{ + CoglTexture *texture; + const TestColor *color; + uint8_t *data, *p; + int x, y; + + /* Create a red, green or blue texture depending on the size */ + color = COLOR_FOR_SIZE (size); + + p = data = g_malloc (size * size * 4); + + /* Fill the data with the color but fade the opacity out with + increasing y coordinates so that we can see the blending it the + atlas migration accidentally blends with garbage in the + texture */ + for (y = 0; y < size; y++) + { + int opacity = OPACITY_FOR_ROW (y); + + for (x = 0; x < size; x++) + { + /* Store the colors premultiplied */ + p[0] = color->red * opacity / 255; + p[1] = color->green * opacity / 255; + p[2] = color->blue * opacity / 255; + p[3] = opacity; + + p += 4; + } + } + + texture = test_utils_texture_new_from_data (test_ctx, + size, /* width */ + size, /* height */ + TEST_UTILS_TEXTURE_NONE, /* flags */ + /* format */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + /* rowstride */ + size * 4, + data); + + g_free (data); + + return texture; +} + +static void +verify_texture (CoglTexture *texture, int size) +{ + uint8_t *data, *p; + int x, y; + const TestColor *color; + + color = COLOR_FOR_SIZE (size); + + p = data = g_malloc (size * size * 4); + + cogl_texture_get_data (texture, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + size * 4, + data); + + for (y = 0; y < size; y++) + { + int opacity = OPACITY_FOR_ROW (y); + + for (x = 0; x < size; x++) + { + TestColor real_color = + { + color->red * opacity / 255, + color->green * opacity / 255, + color->blue * opacity / 255 + }; + + test_utils_compare_pixel (p, + (real_color.red << 24) | + (real_color.green << 16) | + (real_color.blue << 8) | + opacity); + g_assert_cmpint (p[3], ==, opacity); + + p += 4; + } + } + + g_free (data); +} + +void +test_atlas_migration (void) +{ + CoglTexture *textures[N_TEXTURES]; + int i, tex_num; + + /* Create and destroy all of the textures a few times to increase + the chances that we'll end up reusing the buffers for previously + discarded atlases */ + for (i = 0; i < 5; i++) + { + for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) + textures[tex_num] = create_texture (tex_num + 1); + for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) + cogl_object_unref (textures[tex_num]); + } + + /* Create all the textures again */ + for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) + textures[tex_num] = create_texture (tex_num + 1); + + /* Verify that they all still have the right data */ + for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) + verify_texture (textures[tex_num], tex_num + 1); + + /* Destroy them all */ + for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) + cogl_object_unref (textures[tex_num]); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-backface-culling.c b/cogl/tests/conform/test-backface-culling.c new file mode 100644 index 000000000..e90c2f5ec --- /dev/null +++ b/cogl/tests/conform/test-backface-culling.c @@ -0,0 +1,311 @@ +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 + +#include + +#include + +#include "test-utils.h" + +/* Size the texture so that it is just off a power of two to encourage + it so use software tiling when NPOTs aren't available */ +#define TEXTURE_SIZE 257 + +/* Amount of pixels to skip off the top, bottom, left and right of the + texture when reading back the stage */ +#define TEST_INSET 2 + +/* Size to actually render the texture at */ +#define TEXTURE_RENDER_SIZE 8 + +typedef struct _TestState +{ + CoglTexture *texture; + CoglFramebuffer *offscreen; + CoglTexture *offscreen_tex; + int width, height; +} TestState; + +static void +validate_part (CoglFramebuffer *framebuffer, + int xnum, int ynum, CoglBool shown) +{ + test_utils_check_region (framebuffer, + xnum * TEXTURE_RENDER_SIZE + TEST_INSET, + ynum * TEXTURE_RENDER_SIZE + TEST_INSET, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + shown ? 0xff0000ff : 0x000000ff); +} + +/* We draw everything 16 times. The draw number is used as a bitmask + to test all of the combinations of enabling legacy state, both + winding orders and all four culling modes */ + +#define USE_LEGACY_STATE(draw_num) (((draw_num) & 0x01) >> 0) +#define FRONT_WINDING(draw_num) (((draw_num) & 0x02) >> 1) +#define CULL_FACE_MODE(draw_num) (((draw_num) & 0x0c) >> 2) + +static void +paint_test_backface_culling (TestState *state, + CoglFramebuffer *framebuffer) +{ + int draw_num; + CoglPipeline *base_pipeline = cogl_pipeline_new (test_ctx); + + cogl_framebuffer_orthographic (framebuffer, + 0, 0, + state->width, + state->height, + -1, + 100); + + cogl_framebuffer_clear4f (framebuffer, + COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_STENCIL, + 0, 0, 0, 1); + + cogl_pipeline_set_layer_texture (base_pipeline, 0, state->texture); + + cogl_pipeline_set_layer_filters (base_pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + cogl_push_framebuffer (framebuffer); + + /* Render the scene sixteen times to test all of the combinations of + cull face mode, legacy state and winding orders */ + for (draw_num = 0; draw_num < 16; draw_num++) + { + float x1 = 0, x2, y1 = 0, y2 = (float)(TEXTURE_RENDER_SIZE); + CoglTextureVertex verts[4]; + CoglPipeline *pipeline; + + cogl_push_matrix (); + cogl_translate (0, TEXTURE_RENDER_SIZE * draw_num, 0); + + pipeline = cogl_pipeline_copy (base_pipeline); + + cogl_set_backface_culling_enabled (USE_LEGACY_STATE (draw_num)); + cogl_pipeline_set_front_face_winding (pipeline, FRONT_WINDING (draw_num)); + cogl_pipeline_set_cull_face_mode (pipeline, CULL_FACE_MODE (draw_num)); + + cogl_push_source (pipeline); + + memset (verts, 0, sizeof (verts)); + + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + /* Draw a front-facing texture */ + cogl_rectangle (x1, y1, x2, y2); + + x1 = x2; + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + /* Draw a front-facing texture with flipped texcoords */ + cogl_rectangle_with_texture_coords (x1, y1, x2, y2, + 1.0, 0.0, 0.0, 1.0); + + x1 = x2; + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + /* Draw a back-facing texture */ + cogl_rectangle (x2, y1, x1, y2); + + x1 = x2; + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + /* If the texture is sliced then cogl_polygon doesn't work so + we'll just use a solid color instead */ + if (cogl_texture_is_sliced (state->texture)) + cogl_set_source_color4ub (255, 0, 0, 255); + + /* Draw a front-facing polygon */ + verts[0].x = x1; verts[0].y = y2; + verts[1].x = x2; verts[1].y = y2; + verts[2].x = x2; verts[2].y = y1; + verts[3].x = x1; verts[3].y = y1; + verts[0].tx = 0; verts[0].ty = 0; + verts[1].tx = 1.0; verts[1].ty = 0; + verts[2].tx = 1.0; verts[2].ty = 1.0; + verts[3].tx = 0; verts[3].ty = 1.0; + cogl_polygon (verts, 4, FALSE); + + x1 = x2; + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + /* Draw a back-facing polygon */ + verts[0].x = x1; verts[0].y = y1; + verts[1].x = x2; verts[1].y = y1; + verts[2].x = x2; verts[2].y = y2; + verts[3].x = x1; verts[3].y = y2; + verts[0].tx = 0; verts[0].ty = 0; + verts[1].tx = 1.0; verts[1].ty = 0; + verts[2].tx = 1.0; verts[2].ty = 1.0; + verts[3].tx = 0; verts[3].ty = 1.0; + cogl_polygon (verts, 4, FALSE); + + x1 = x2; + x2 = x1 + (float)(TEXTURE_RENDER_SIZE); + + cogl_pop_matrix (); + + cogl_pop_source (); + cogl_object_unref (pipeline); + } + + cogl_pop_framebuffer (); + + cogl_object_unref (base_pipeline); +} + +static void +validate_result (CoglFramebuffer *framebuffer, int y_offset) +{ + int draw_num; + + for (draw_num = 0; draw_num < 16; draw_num++) + { + CoglBool cull_front, cull_back; + CoglPipelineCullFaceMode cull_mode; + + if (USE_LEGACY_STATE (draw_num)) + cull_mode = COGL_PIPELINE_CULL_FACE_MODE_BACK; + else + cull_mode = CULL_FACE_MODE (draw_num); + + switch (cull_mode) + { + case COGL_PIPELINE_CULL_FACE_MODE_NONE: + cull_front = FALSE; + cull_back = FALSE; + break; + + case COGL_PIPELINE_CULL_FACE_MODE_FRONT: + cull_front = TRUE; + cull_back = FALSE; + break; + + case COGL_PIPELINE_CULL_FACE_MODE_BACK: + cull_front = FALSE; + cull_back = TRUE; + break; + + case COGL_PIPELINE_CULL_FACE_MODE_BOTH: + cull_front = TRUE; + cull_back = TRUE; + break; + } + + if (FRONT_WINDING (draw_num) == COGL_WINDING_CLOCKWISE) + { + CoglBool tmp = cull_front; + cull_front = cull_back; + cull_back = tmp; + } + + /* Front-facing texture */ + validate_part (framebuffer, + 0, y_offset + draw_num, !cull_front); + /* Front-facing texture with flipped tex coords */ + validate_part (framebuffer, + 1, y_offset + draw_num, !cull_front); + /* Back-facing texture */ + validate_part (framebuffer, + 2, y_offset + draw_num, !cull_back); + /* Front-facing texture polygon */ + validate_part (framebuffer, + 3, y_offset + draw_num, !cull_front); + /* Back-facing texture polygon */ + validate_part (framebuffer, + 4, y_offset + draw_num, !cull_back); + } +} + +static void +paint (TestState *state) +{ + CoglPipeline *pipeline; + + paint_test_backface_culling (state, test_fb); + + /* + * Now repeat the test but rendered to an offscreen + * framebuffer. Note that by default the conformance tests are + * always run to an offscreen buffer but we might as well have this + * check anyway in case it is being run with COGL_TEST_ONSCREEN=1 + */ + paint_test_backface_culling (state, state->offscreen); + + /* Copy the result of the offscreen rendering for validation and + * also so we can have visual feedback. */ + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (pipeline, 0, state->offscreen_tex); + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 0, TEXTURE_RENDER_SIZE * 16, + state->width, + state->height + TEXTURE_RENDER_SIZE * 16); + cogl_object_unref (pipeline); + + validate_result (test_fb, 0); + validate_result (test_fb, 16); +} + +static CoglTexture * +make_texture (void) +{ + guchar *tex_data, *p; + CoglTexture *tex; + + tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); + + for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;) + { + *(--p) = 255; + *(--p) = 0; + *(--p) = 0; + *(--p) = 255; + } + + tex = test_utils_texture_new_from_data (test_ctx, + TEXTURE_SIZE, + TEXTURE_SIZE, + TEST_UTILS_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGBA_8888, + TEXTURE_SIZE * 4, + tex_data); + + g_free (tex_data); + + return tex; +} + +void +test_backface_culling (void) +{ + TestState state; + CoglTexture *tex; + + state.width = cogl_framebuffer_get_width (test_fb); + state.height = cogl_framebuffer_get_height (test_fb); + + state.offscreen = NULL; + + state.texture = make_texture (); + + tex = test_utils_texture_new_with_size (test_ctx, + state.width, state.height, + TEST_UTILS_TEXTURE_NO_SLICING, + COGL_TEXTURE_COMPONENTS_RGBA); + state.offscreen = cogl_offscreen_new_with_texture (tex); + state.offscreen_tex = tex; + + paint (&state); + + cogl_object_unref (state.offscreen); + cogl_object_unref (state.offscreen_tex); + cogl_object_unref (state.texture); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-blend-strings.c b/cogl/tests/conform/test-blend-strings.c new file mode 100644 index 000000000..f49c8603b --- /dev/null +++ b/cogl/tests/conform/test-blend-strings.c @@ -0,0 +1,430 @@ +#include + +#include + +#include "test-utils.h" + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) +#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) +#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) +#define MASK_ALPHA(COLOR) (COLOR & 0xff) + +#define BLEND_CONSTANT_UNUSED 0xDEADBEEF +#define TEX_CONSTANT_UNUSED 0xDEADBEEF + +typedef struct _TestState +{ + CoglContext *ctx; +} TestState; + + +static void +test_blend (TestState *state, + int x, + int y, + uint32_t src_color, + uint32_t dst_color, + const char *blend_string, + uint32_t blend_constant, + uint32_t expected_result) +{ + /* src color */ + uint8_t Sr = MASK_RED (src_color); + uint8_t Sg = MASK_GREEN (src_color); + uint8_t Sb = MASK_BLUE (src_color); + uint8_t Sa = MASK_ALPHA (src_color); + /* dest color */ + uint8_t Dr = MASK_RED (dst_color); + uint8_t Dg = MASK_GREEN (dst_color); + uint8_t Db = MASK_BLUE (dst_color); + uint8_t Da = MASK_ALPHA (dst_color); + /* blend constant - when applicable */ + uint8_t Br = MASK_RED (blend_constant); + uint8_t Bg = MASK_GREEN (blend_constant); + uint8_t Bb = MASK_BLUE (blend_constant); + uint8_t Ba = MASK_ALPHA (blend_constant); + CoglColor blend_const_color; + + CoglHandle material; + CoglPipeline *pipeline; + CoglBool status; + CoglError *error = NULL; + int y_off; + int x_off; + + /* First write out the destination color without any blending... */ + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, Dr, Dg, Db, Da); + cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); + cogl_set_source (pipeline); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_object_unref (pipeline); + + /* + * Now blend a rectangle over our well defined destination: + */ + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, Sr, Sg, Sb, Sa); + + status = cogl_pipeline_set_blend (pipeline, blend_string, &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this blend string. */ + if (cogl_test_verbose ()) + { + g_debug ("Failed to test blend string %s: %s", + blend_string, error->message); + g_print ("Skipping\n"); + } + return; + } + + cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba); + cogl_pipeline_set_blend_constant (pipeline, &blend_const_color); + + cogl_set_source (pipeline); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_object_unref (pipeline); + + /* See what we got... */ + + y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2); + x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); + + if (cogl_test_verbose ()) + { + g_print ("test_blend (%d, %d):\n%s\n", x, y, blend_string); + g_print (" src color = %02x, %02x, %02x, %02x\n", Sr, Sg, Sb, Sa); + g_print (" dst color = %02x, %02x, %02x, %02x\n", Dr, Dg, Db, Da); + if (blend_constant != BLEND_CONSTANT_UNUSED) + g_print (" blend constant = %02x, %02x, %02x, %02x\n", + Br, Bg, Bb, Ba); + else + g_print (" blend constant = UNUSED\n"); + } + + test_utils_check_pixel (test_fb, x_off, y_off, expected_result); + + + /* + * Test with legacy API + */ + + /* Clear previous work */ + cogl_set_source_color4ub (0, 0, 0, 0xff); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + + /* First write out the destination color without any blending... */ + material = cogl_material_new (); + cogl_material_set_color4ub (material, Dr, Dg, Db, Da); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_handle_unref (material); + + /* + * Now blend a rectangle over our well defined destination: + */ + + material = cogl_material_new (); + cogl_material_set_color4ub (material, Sr, Sg, Sb, Sa); + + status = cogl_material_set_blend (material, blend_string, &error); + if (!status) + { + /* This is a failure as it must be equivalent to the new API */ + g_warning ("Error setting blend string %s: %s", + blend_string, error->message); + g_assert_not_reached (); + } + + cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba); + cogl_material_set_blend_constant (material, &blend_const_color); + + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_handle_unref (material); + + /* See what we got... */ + + test_utils_check_pixel (test_fb, x_off, y_off, expected_result); +} + +static CoglTexture * +make_texture (uint32_t color) +{ + guchar *tex_data, *p; + uint8_t r = MASK_RED (color); + uint8_t g = MASK_GREEN (color); + uint8_t b = MASK_BLUE (color); + uint8_t a = MASK_ALPHA (color); + CoglTexture *tex; + + tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4); + + for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;) + { + *(--p) = a; + *(--p) = b; + *(--p) = g; + *(--p) = r; + } + + /* Note: we claim that the data is premultiplied so that Cogl won't + * premultiply the data on upload */ + tex = test_utils_texture_new_from_data (test_ctx, + QUAD_WIDTH, + QUAD_WIDTH, + TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + QUAD_WIDTH * 4, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +test_tex_combine (TestState *state, + int x, + int y, + uint32_t tex0_color, + uint32_t tex1_color, + uint32_t combine_constant, + const char *combine_string, + uint32_t expected_result) +{ + CoglTexture *tex0, *tex1; + + /* combine constant - when applicable */ + uint8_t Cr = MASK_RED (combine_constant); + uint8_t Cg = MASK_GREEN (combine_constant); + uint8_t Cb = MASK_BLUE (combine_constant); + uint8_t Ca = MASK_ALPHA (combine_constant); + CoglColor combine_const_color; + + CoglHandle material; + CoglBool status; + CoglError *error = NULL; + int y_off; + int x_off; + + + tex0 = make_texture (tex0_color); + tex1 = make_texture (tex1_color); + + material = cogl_material_new (); + + cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + + cogl_material_set_layer (material, 0, tex0); + cogl_material_set_layer_combine (material, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + + cogl_material_set_layer (material, 1, tex1); + status = cogl_material_set_layer_combine (material, 1, + combine_string, &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this texture combine string. */ + g_debug ("Failed to test texture combine string %s: %s", + combine_string, error->message); + } + + cogl_color_init_from_4ub (&combine_const_color, Cr, Cg, Cb, Ca); + cogl_material_set_layer_combine_constant (material, 1, &combine_const_color); + + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + + cogl_handle_unref (material); + cogl_object_unref (tex0); + cogl_object_unref (tex1); + + /* See what we got... */ + + y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2); + x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); + + if (cogl_test_verbose ()) + { + g_print ("test_tex_combine (%d, %d):\n%s\n", x, y, combine_string); + g_print (" texture 0 color = 0x%08lX\n", (unsigned long)tex0_color); + g_print (" texture 1 color = 0x%08lX\n", (unsigned long)tex1_color); + if (combine_constant != TEX_CONSTANT_UNUSED) + g_print (" combine constant = %02x, %02x, %02x, %02x\n", + Cr, Cg, Cb, Ca); + else + g_print (" combine constant = UNUSED\n"); + } + + test_utils_check_pixel (test_fb, x_off, y_off, expected_result); +} + +static void +paint (TestState *state) +{ + test_blend (state, 0, 0, /* position */ + 0xff0000ff, /* src */ + 0xffffffff, /* dst */ + "RGBA = ADD (SRC_COLOR, 0)", + BLEND_CONSTANT_UNUSED, + 0xff0000ff); /* expected */ + + test_blend (state, 1, 0, /* position */ + 0x11223344, /* src */ + 0x11223344, /* dst */ + "RGBA = ADD (SRC_COLOR, DST_COLOR)", + BLEND_CONSTANT_UNUSED, + 0x22446688); /* expected */ + + test_blend (state, 2, 0, /* position */ + 0x80808080, /* src */ + 0xffffffff, /* dst */ + "RGBA = ADD (SRC_COLOR * (CONSTANT), 0)", + 0x80808080, /* constant (RGBA all = 0.5 when normalized) */ + 0x40404040); /* expected */ + + test_blend (state, 3, 0, /* position */ + 0x80000080, /* src (alpha = 0.5 when normalized) */ + 0x40000000, /* dst */ + "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A])," + " DST_COLOR * (1-SRC_COLOR[A]))", + BLEND_CONSTANT_UNUSED, + 0x60000040); /* expected */ + + /* XXX: + * For all texture combine tests tex0 will use a combine mode of + * "RGBA = REPLACE (TEXTURE)" + */ + + test_tex_combine (state, 4, 0, /* position */ + 0x11111111, /* texture 0 color */ + 0x22222222, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = ADD (PREVIOUS, TEXTURE)", /* tex combine */ + 0x33333333); /* expected */ + + test_tex_combine (state, 5, 0, /* position */ + 0x40404040, /* texture 0 color */ + 0x80808080, /* texture 1 color (RGBA all = 0.5) */ + TEX_CONSTANT_UNUSED, + "RGBA = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ + 0x20202020); /* expected */ + + test_tex_combine (state, 6, 0, /* position */ + 0xffffff80, /* texture 0 color (alpha = 0.5) */ + 0xDEADBE40, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGB = REPLACE (PREVIOUS)" + "A = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ + 0xffffff20); /* expected */ + + /* XXX: we are assuming test_tex_combine creates a material with + * a color of 0x80808080 (i.e. the "PRIMARY" color) */ + test_tex_combine (state, 7, 0, /* position */ + 0xffffff80, /* texture 0 color (alpha = 0.5) */ + 0xDEADBE20, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGB = REPLACE (PREVIOUS)" + "A = MODULATE (PRIMARY, TEXTURE)", /* tex combine */ + 0xffffff10); /* expected */ + + test_tex_combine (state, 8, 0, /* position */ + 0x11111111, /* texture 0 color */ + 0x22222222, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = ADD (PREVIOUS, 1-TEXTURE)", /* tex combine */ + 0xeeeeeeee); /* expected */ + + /* this is again assuming a primary color of 0x80808080 */ + test_tex_combine (state, 9, 0, /* position */ + 0x10101010, /* texture 0 color */ + 0x20202020, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = INTERPOLATE (PREVIOUS, TEXTURE, PRIMARY)", + 0x18181818); /* expected */ + +#if 0 /* using TEXTURE_N appears to be broken in cogl-blend-string.c */ + test_tex_combine (state, 0, 1, /* position */ + 0xDEADBEEF, /* texture 0 color (not used) */ + 0x11223344, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = ADD (TEXTURE_1, TEXTURE)", /* tex combine */ + 0x22446688); /* expected */ +#endif + + test_tex_combine (state, 1, 1, /* position */ + 0x21314151, /* texture 0 color */ + 0x99999999, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = ADD_SIGNED (PREVIOUS, TEXTURE)", /* tex combine */ + 0x3a4a5a6a); /* expected */ + + test_tex_combine (state, 2, 1, /* position */ + 0xfedcba98, /* texture 0 color */ + 0x11111111, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = SUBTRACT (PREVIOUS, TEXTURE)", /* tex combine */ + 0xedcba987); /* expected */ + + test_tex_combine (state, 3, 1, /* position */ + 0x8899aabb, /* texture 0 color */ + 0xbbaa9988, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGB = DOT3_RGBA (PREVIOUS, TEXTURE)" + "A = REPLACE (PREVIOUS)", + 0x2a2a2abb); /* expected */ +} + +void +test_blend_strings (void) +{ + TestState state; + + cogl_framebuffer_orthographic (test_fb, 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + /* XXX: we have to push/pop a framebuffer since this test currently + * uses the legacy cogl_rectangle() api. */ + cogl_push_framebuffer (test_fb); + paint (&state); + cogl_pop_framebuffer (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-blend.c b/cogl/tests/conform/test-blend.c new file mode 100644 index 000000000..3c6235b5f --- /dev/null +++ b/cogl/tests/conform/test-blend.c @@ -0,0 +1,64 @@ +#include + +#include + +#include "test-utils.h" + +static void +paint (void) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + int width = cogl_framebuffer_get_width (test_fb); + int half_width = width / 2; + int height = cogl_framebuffer_get_height (test_fb); + CoglVertexP2 tri0_vertices[] = { + { 0, 0 }, + { 0, height }, + { half_width, height }, + }; + CoglVertexP2C4 tri1_vertices[] = { + { half_width, 0, 0x80, 0x80, 0x80, 0x80 }, + { half_width, height, 0x80, 0x80, 0x80, 0x80 }, + { width, height, 0x80, 0x80, 0x80, 0x80 }, + }; + CoglPrimitive *tri0; + CoglPrimitive *tri1; + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); + + tri0 = cogl_primitive_new_p2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, + 3, tri0_vertices); + tri1 = cogl_primitive_new_p2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, + 3, tri1_vertices); + + /* Check that cogl correctly handles the case where we draw + * different primitives same pipeline and switch from using the + * opaque color associated with the pipeline and using a colour + * attribute with an alpha component which implies blending is + * required. + * + * If Cogl gets this wrong then then in all likelyhood the second + * primitive will be drawn with blending still disabled. + */ + + cogl_primitive_draw (tri0, test_fb, pipeline); + cogl_primitive_draw (tri1, test_fb, pipeline); + + test_utils_check_pixel_and_alpha (test_fb, + half_width + 5, + height - 5, + 0x80808080); +} + +void +test_blend (void) +{ + cogl_framebuffer_orthographic (test_fb, 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (); +} + diff --git a/cogl/tests/conform/test-color-hsl.c b/cogl/tests/conform/test-color-hsl.c new file mode 100644 index 000000000..651ce5208 --- /dev/null +++ b/cogl/tests/conform/test-color-hsl.c @@ -0,0 +1,45 @@ +#include +#include + +#include + +#include "test-utils.h" + +#define cogl_assert_float(a, b) \ + do { \ + if (fabsf ((a) - (b)) >= 0.0001f) \ + g_assert_cmpfloat ((a), ==, (b)); \ + } while (0) + +void +test_color_hsl (void) +{ + CoglColor color; + float hue, saturation, luminance; + + cogl_color_init_from_4ub(&color, 108, 198, 78, 255); + cogl_color_to_hsl(&color, &hue, &saturation, &luminance); + + cogl_assert_float(hue, 105.f); + cogl_assert_float(saturation, 0.512821); + cogl_assert_float(luminance, 0.541176); + + memset(&color, 0, sizeof (CoglColor)); + cogl_color_init_from_hsl(&color, hue, saturation, luminance); + + g_assert_cmpint (cogl_color_get_red_byte (&color), ==, 108); + g_assert_cmpint (cogl_color_get_green_byte (&color), ==, 198); + g_assert_cmpint (cogl_color_get_blue_byte (&color), ==, 78); + g_assert_cmpint (cogl_color_get_alpha_byte (&color), ==, 255); + + memset(&color, 0, sizeof (CoglColor)); + cogl_color_init_from_hsl(&color, hue, 0, luminance); + + cogl_assert_float (cogl_color_get_red_float (&color), luminance); + cogl_assert_float (cogl_color_get_green_float (&color), luminance); + cogl_assert_float (cogl_color_get_blue_float (&color), luminance); + cogl_assert_float (cogl_color_get_alpha_float (&color), 1.0f); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-color-mask.c b/cogl/tests/conform/test-color-mask.c new file mode 100644 index 000000000..e80f46dae --- /dev/null +++ b/cogl/tests/conform/test-color-mask.c @@ -0,0 +1,110 @@ +#include + +#include "test-utils.h" + +#define TEX_SIZE 128 + +#define NUM_FBOS 3 + +typedef struct _TestState +{ + int width; + int height; + + CoglTexture *tex[NUM_FBOS]; + CoglFramebuffer *fbo[NUM_FBOS]; +} TestState; + +static void +paint (TestState *state) +{ + CoglColor bg; + int i; + + cogl_set_source_color4ub (255, 255, 255, 255); + + /* We push the third framebuffer first so that later we can switch + back to it by popping to test that that works */ + cogl_push_framebuffer (state->fbo[2]); + + cogl_push_framebuffer (state->fbo[0]); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + cogl_pop_framebuffer (); + + cogl_push_framebuffer (state->fbo[1]); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + cogl_pop_framebuffer (); + + /* We should now be back on the third framebuffer */ + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + cogl_pop_framebuffer (); + + cogl_color_init_from_4ub (&bg, 128, 128, 128, 255); + cogl_clear (&bg, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); + + /* Render all of the textures to the screen */ + for (i = 0; i < NUM_FBOS; i++) + { + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (pipeline, 0, state->tex[i]); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, + 2.0f / NUM_FBOS * i - 1.0f, -1.0f, + 2.0f / NUM_FBOS * (i + 1) - 1.0f, 1.0f); + cogl_object_unref (pipeline); + } + + /* Verify all of the fbos drew the right color */ + for (i = 0; i < NUM_FBOS; i++) + { + uint8_t expected_colors[NUM_FBOS][4] = + { { 0xff, 0x00, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0x00, 0x00, 0xff, 0xff } }; + + test_utils_check_pixel_rgb (test_fb, + state->width * (i + 0.5f) / NUM_FBOS, + state->height / 2, + expected_colors[i][0], + expected_colors[i][1], + expected_colors[i][2]); + } +} + +void +test_color_mask (void) +{ + TestState state; + int i; + + state.width = cogl_framebuffer_get_width (test_fb); + state.height = cogl_framebuffer_get_height (test_fb); + + for (i = 0; i < NUM_FBOS; i++) + { + state.tex[i] = test_utils_texture_new_with_size (test_ctx, 128, 128, + TEST_UTILS_TEXTURE_NO_ATLAS, + COGL_TEXTURE_COMPONENTS_RGB); + + + state.fbo[i] = cogl_offscreen_new_with_texture (state.tex[i]); + + /* Clear the texture color bits */ + cogl_framebuffer_clear4f (state.fbo[i], + COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + cogl_framebuffer_set_color_mask (state.fbo[i], + i == 0 ? COGL_COLOR_MASK_RED : + i == 1 ? COGL_COLOR_MASK_GREEN : + COGL_COLOR_MASK_BLUE); + } + + /* XXX: we have to push/pop a framebuffer since this test currently + * uses the legacy cogl_rectangle() api. */ + cogl_push_framebuffer (test_fb); + paint (&state); + cogl_pop_framebuffer (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-conform-main.c b/cogl/tests/conform/test-conform-main.c new file mode 100644 index 000000000..9b6573d92 --- /dev/null +++ b/cogl/tests/conform/test-conform-main.c @@ -0,0 +1,157 @@ +#include "config.h" + +#include + +#include +#include +#include +#include + +#include "test-utils.h" + +/* A bit of sugar for adding new conformance tests */ +#define ADD_TEST(FUNC, REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS) \ + G_STMT_START { \ + extern void FUNC (void); \ + if (strcmp (#FUNC, argv[1]) == 0) \ + { \ + test_utils_init (REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS); \ + FUNC (); \ + test_utils_fini (); \ + exit (0); \ + } \ + } G_STMT_END + +#define UNPORTED_TEST(FUNC) + +int +main (int argc, char **argv) +{ + int i; + + if (argc != 2) + { + g_printerr ("usage %s UNIT_TEST\n", argv[0]); + exit (1); + } + + /* Just for convenience in case people try passing the wrapper + * filenames for the UNIT_TEST argument we normalize '-' characters + * to '_' characters... */ + for (i = 0; argv[1][i]; i++) + { + if (argv[1][i] == '-') + argv[1][i] = '_'; + } + + /* This file is run through a sed script during the make step so the + * lines containing the tests need to be formatted on a single line + * each. + */ + + UNPORTED_TEST (test_object); + UNPORTED_TEST (test_fixed); + UNPORTED_TEST (test_materials); + ADD_TEST (test_pipeline_user_matrix, 0, 0); + ADD_TEST (test_blend_strings, 0, 0); + ADD_TEST (test_blend, 0, 0); + ADD_TEST (test_premult, 0, TEST_KNOWN_FAILURE); + UNPORTED_TEST (test_readpixels); +#ifdef COGL_HAS_COGL_PATH_SUPPORT + ADD_TEST (test_path, 0, 0); + ADD_TEST (test_path_clip, 0, 0); +#endif + ADD_TEST (test_depth_test, 0, 0); + ADD_TEST (test_color_mask, 0, 0); + ADD_TEST (test_backface_culling, 0, TEST_REQUIREMENT_NPOT); + ADD_TEST (test_layer_remove, 0, 0); + + ADD_TEST (test_sparse_pipeline, 0, 0); + + ADD_TEST (test_npot_texture, 0, 0); + UNPORTED_TEST (test_multitexture); + UNPORTED_TEST (test_texture_mipmaps); + ADD_TEST (test_sub_texture, 0, 0); + ADD_TEST (test_pixel_buffer_map, 0, 0); + ADD_TEST (test_pixel_buffer_set_data, 0, 0); + ADD_TEST (test_pixel_buffer_sub_region, 0, 0); + UNPORTED_TEST (test_texture_rectangle); + ADD_TEST (test_texture_3d, TEST_REQUIREMENT_TEXTURE_3D, 0); + ADD_TEST (test_wrap_modes, 0, 0); + UNPORTED_TEST (test_texture_pixmap_x11); + ADD_TEST (test_texture_get_set_data, 0, 0); + ADD_TEST (test_atlas_migration, 0, 0); + ADD_TEST (test_read_texture_formats, 0, TEST_KNOWN_FAILURE); + ADD_TEST (test_write_texture_formats, 0, 0); + ADD_TEST (test_alpha_textures, 0, 0); + ADD_TEST (test_wrap_rectangle_textures, + TEST_REQUIREMENT_TEXTURE_RECTANGLE, + TEST_KNOWN_FAILURE); + + UNPORTED_TEST (test_vertex_buffer_contiguous); + UNPORTED_TEST (test_vertex_buffer_interleved); + UNPORTED_TEST (test_vertex_buffer_mutability); + + ADD_TEST (test_primitive, 0, 0); + + ADD_TEST (test_just_vertex_shader, TEST_REQUIREMENT_GLSL, 0); + ADD_TEST (test_pipeline_uniforms, TEST_REQUIREMENT_GLSL, 0); + ADD_TEST (test_snippets, TEST_REQUIREMENT_GLSL, 0); + ADD_TEST (test_custom_attributes, TEST_REQUIREMENT_GLSL, 0); + + ADD_TEST (test_offscreen, 0, 0); + ADD_TEST (test_framebuffer_get_bits, + TEST_REQUIREMENT_OFFSCREEN | TEST_REQUIREMENT_GL, + 0); + + ADD_TEST (test_point_size, 0, 0); + ADD_TEST (test_point_size_attribute, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE, 0); + ADD_TEST (test_point_size_attribute_snippet, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE | + TEST_REQUIREMENT_GLSL, 0); + ADD_TEST (test_point_sprite, + TEST_REQUIREMENT_POINT_SPRITE, + 0); + ADD_TEST (test_point_sprite_orientation, + TEST_REQUIREMENT_POINT_SPRITE, + TEST_KNOWN_FAILURE); + ADD_TEST (test_point_sprite_glsl, + TEST_REQUIREMENT_POINT_SPRITE | + TEST_REQUIREMENT_GLSL, + 0); + + ADD_TEST (test_version, 0, 0); + + ADD_TEST (test_alpha_test, 0, 0); + + ADD_TEST (test_map_buffer_range, TEST_REQUIREMENT_MAP_WRITE, 0); + + ADD_TEST (test_primitive_and_journal, 0, 0); + + ADD_TEST (test_copy_replace_texture, 0, 0); + + ADD_TEST (test_pipeline_cache_unrefs_texture, 0, 0); + ADD_TEST (test_pipeline_shader_state, TEST_REQUIREMENT_GLSL, 0); + + UNPORTED_TEST (test_viewport); + + ADD_TEST (test_gles2_context, TEST_REQUIREMENT_GLES2_CONTEXT, 0); + ADD_TEST (test_gles2_context_fbo, TEST_REQUIREMENT_GLES2_CONTEXT, 0); + ADD_TEST (test_gles2_context_copy_tex_image, + TEST_REQUIREMENT_GLES2_CONTEXT, + 0); + + ADD_TEST (test_euler_quaternion, 0, 0); + ADD_TEST (test_color_hsl, 0, 0); + + ADD_TEST (test_fence, TEST_REQUIREMENT_FENCE, 0); + + ADD_TEST (test_texture_no_allocate, 0, 0); + + ADD_TEST (test_texture_rg, TEST_REQUIREMENT_TEXTURE_RG, 0); + + g_printerr ("Unknown test name \"%s\"\n", argv[1]); + + return 1; +} diff --git a/cogl/tests/conform/test-copy-replace-texture.c b/cogl/tests/conform/test-copy-replace-texture.c new file mode 100644 index 000000000..f11070ee8 --- /dev/null +++ b/cogl/tests/conform/test-copy-replace-texture.c @@ -0,0 +1,120 @@ +#include +#include + +#include "test-utils.h" + +/* Keep track of the number of textures that we've created and are + * still alive */ +static int alive_texture_mask = 0; + +#define N_LAYERS 3 +#define N_PIPELINES 4 + +#define PIPELINE_LAYER_MASK(pipeline_num) \ + (((1 << N_LAYERS) - 1) << (N_LAYERS * (pipeline_num) + 1)) +#define LAST_PIPELINE_MASK PIPELINE_LAYER_MASK (N_PIPELINES - 1) +#define FIRST_PIPELINE_MASK PIPELINE_LAYER_MASK (0) + +static void +free_texture_cb (void *user_data) +{ + int texture_num = GPOINTER_TO_INT (user_data); + + alive_texture_mask &= ~(1 << texture_num); +} + +static CoglTexture * +create_texture (void) +{ + static const guint8 data[] = + { 0xff, 0xff, 0xff, 0xff }; + static CoglUserDataKey texture_data_key; + CoglTexture2D *tex_2d; + static int texture_num = 1; + + alive_texture_mask |= (1 << texture_num); + + tex_2d = cogl_texture_2d_new_from_data (test_ctx, + 1, 1, /* width / height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + data, + NULL); + + /* Set some user data on the texture so we can track when it has + * been destroyed */ + cogl_object_set_user_data (COGL_OBJECT (tex_2d), + &texture_data_key, + GINT_TO_POINTER (texture_num), + free_texture_cb); + + texture_num++; + + return tex_2d; +} + +void +test_copy_replace_texture (void) +{ + CoglPipeline *pipelines[N_PIPELINES]; + int pipeline_num; + + /* Create a set of pipeline copies each with three of their own + * replacement textures */ + for (pipeline_num = 0; pipeline_num < N_PIPELINES; pipeline_num++) + { + int layer_num; + + if (pipeline_num == 0) + pipelines[pipeline_num] = cogl_pipeline_new (test_ctx); + else + pipelines[pipeline_num] = + cogl_pipeline_copy (pipelines[pipeline_num - 1]); + + for (layer_num = 0; layer_num < N_LAYERS; layer_num++) + { + CoglTexture *tex = create_texture (); + cogl_pipeline_set_layer_texture (pipelines[pipeline_num], + layer_num, + tex); + cogl_object_unref (tex); + } + } + + /* Unref everything but the last pipeline */ + for (pipeline_num = 0; pipeline_num < N_PIPELINES - 1; pipeline_num++) + cogl_object_unref (pipelines[pipeline_num]); + + if (alive_texture_mask && cogl_test_verbose ()) + { + int i; + + g_print ("Alive textures:"); + + for (i = 0; i < N_PIPELINES * N_LAYERS; i++) + if ((alive_texture_mask & (1 << (i + 1)))) + g_print (" %i", i); + + g_print ("\n"); + } + + /* Ideally there should only be the textures from the last pipeline + * left alive. We also let Cogl keep the textures from the first + * texture alive because currently the child of the third layer in + * the first pipeline will retain its authority on the unit index + * state so that it can set it to 2. If there are more textures then + * it means the pipeline isn't correctly pruning redundant + * ancestors */ + g_assert_cmpint (alive_texture_mask & ~FIRST_PIPELINE_MASK, + ==, + LAST_PIPELINE_MASK); + + /* Clean up the last pipeline */ + cogl_object_unref (pipelines[N_PIPELINES - 1]); + + /* That should get rid of the last of the textures */ + g_assert_cmpint (alive_texture_mask, ==, 0); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-custom-attributes.c b/cogl/tests/conform/test-custom-attributes.c new file mode 100644 index 000000000..633dc2ad8 --- /dev/null +++ b/cogl/tests/conform/test-custom-attributes.c @@ -0,0 +1,301 @@ +#include + +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + CoglPipeline *pipeline; +} TestState; + +typedef struct +{ + int16_t x, y; + float r, g, b, a; +} FloatVert; + +typedef struct +{ + int16_t x, y; + uint8_t r, g, b, a; +} ByteVert; + +typedef struct +{ + int16_t x, y; + int16_t r, g, b, a; +} ShortVert; + +static void +test_float_verts (TestState *state, int offset_x, int offset_y) +{ + CoglAttribute *attributes[2]; + CoglAttributeBuffer *buffer; + CoglPrimitive *primitive; + + static const FloatVert float_verts[] = + { + { 0, 10, /**/ 1, 0, 0, 1 }, + { 10, 10, /**/ 1, 0, 0, 1 }, + { 5, 0, /**/ 1, 0, 0, 1 }, + + { 10, 10, /**/ 0, 1, 0, 1 }, + { 20, 10, /**/ 0, 1, 0, 1 }, + { 15, 0, /**/ 0, 1, 0, 1 } + }; + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (float_verts), float_verts); + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (FloatVert), + G_STRUCT_OFFSET (FloatVert, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_SHORT); + attributes[1] = cogl_attribute_new (buffer, + "color", + sizeof (FloatVert), + G_STRUCT_OFFSET (FloatVert, r), + 4, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f); + + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 6, /* n_vertices */ + attributes, + 2); /* n_attributes */ + cogl_primitive_draw (primitive, test_fb, state->pipeline); + cogl_object_unref (primitive); + + cogl_framebuffer_pop_matrix (test_fb); + + cogl_object_unref (attributes[1]); + cogl_object_unref (attributes[0]); + cogl_object_unref (buffer); + + test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); + test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); +} + +static void +test_byte_verts (TestState *state, int offset_x, int offset_y) +{ + CoglAttribute *attributes[2]; + CoglAttributeBuffer *buffer, *unnorm_buffer; + CoglPrimitive *primitive; + + static const ByteVert norm_verts[] = + { + { 0, 10, /**/ 255, 0, 0, 255 }, + { 10, 10, /**/ 255, 0, 0, 255 }, + { 5, 0, /**/ 255, 0, 0, 255 }, + + { 10, 10, /**/ 0, 255, 0, 255 }, + { 20, 10, /**/ 0, 255, 0, 255 }, + { 15, 0, /**/ 0, 255, 0, 255 } + }; + + static const ByteVert unnorm_verts[] = + { + { 0, 0, /**/ 0, 0, 1, 1 }, + { 0, 0, /**/ 0, 0, 1, 1 }, + { 0, 0, /**/ 0, 0, 1, 1 }, + }; + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (norm_verts), norm_verts); + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (ByteVert), + G_STRUCT_OFFSET (ByteVert, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_SHORT); + attributes[1] = cogl_attribute_new (buffer, + "color", + sizeof (ByteVert), + G_STRUCT_OFFSET (ByteVert, r), + 4, /* n_components */ + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + cogl_attribute_set_normalized (attributes[1], TRUE); + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f); + + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 6, /* n_vertices */ + attributes, + 2); /* n_attributes */ + cogl_primitive_draw (primitive, test_fb, state->pipeline); + cogl_object_unref (primitive); + + cogl_object_unref (attributes[1]); + + /* Test again with unnormalized attributes */ + unnorm_buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (unnorm_verts), + unnorm_verts); + attributes[1] = cogl_attribute_new (unnorm_buffer, + "color", + sizeof (ByteVert), + G_STRUCT_OFFSET (ByteVert, r), + 4, /* n_components */ + COGL_ATTRIBUTE_TYPE_BYTE); + + cogl_framebuffer_translate (test_fb, 20, 0, 0); + + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + attributes, + 2); /* n_attributes */ + cogl_primitive_draw (primitive, test_fb, state->pipeline); + cogl_object_unref (primitive); + + cogl_framebuffer_pop_matrix (test_fb); + + cogl_object_unref (attributes[0]); + cogl_object_unref (attributes[1]); + cogl_object_unref (buffer); + cogl_object_unref (unnorm_buffer); + + test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); + test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); + test_utils_check_pixel (test_fb, offset_x + 25, offset_y + 5, 0x0000ffff); +} + +static void +test_short_verts (TestState *state, int offset_x, int offset_y) +{ + CoglAttribute *attributes[2]; + CoglAttributeBuffer *buffer; + CoglPipeline *pipeline, *pipeline2; + CoglSnippet *snippet; + CoglPrimitive *primitive; + + static const ShortVert short_verts[] = + { + { -10, -10, /**/ 0xffff, 0, 0, 0xffff }, + { -1, -10, /**/ 0xffff, 0, 0, 0xffff }, + { -5, -1, /**/ 0xffff, 0, 0, 0xffff } + }; + + + pipeline = cogl_pipeline_copy (state->pipeline); + + cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (short_verts), short_verts); + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (ShortVert), + G_STRUCT_OFFSET (ShortVert, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_SHORT); + attributes[1] = cogl_attribute_new (buffer, + "color", + sizeof (ShortVert), + G_STRUCT_OFFSET (ShortVert, r), + 4, /* n_components */ + COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT); + cogl_attribute_set_normalized (attributes[1], TRUE); + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, + offset_x + 10.0f, + offset_y + 10.0f, + 0.0f); + + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + attributes, + 2); /* n_attributes */ + cogl_primitive_draw (primitive, test_fb, pipeline); + cogl_object_unref (primitive); + + cogl_framebuffer_pop_matrix (test_fb); + + cogl_object_unref (attributes[0]); + + /* Test again treating the attribute as unsigned */ + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (ShortVert), + G_STRUCT_OFFSET (ShortVert, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT); + + /* XXX: this is a hack to force the pipeline to use the glsl backend + * because we know it's not possible to test short vertex position + * components with the legacy GL backend since which might otherwise + * be used internally... */ + pipeline2 = cogl_pipeline_new (test_ctx); + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, + "attribute vec4 color;", + "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);"); + cogl_pipeline_add_snippet (pipeline2, snippet); + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, + offset_x + 10.0f - 65525.0f, + offset_y - 65525, + 0.0f); + + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + attributes, + 1); /* n_attributes */ + cogl_primitive_draw (primitive, test_fb, pipeline2); + cogl_object_unref (primitive); + + cogl_framebuffer_pop_matrix (test_fb); + + cogl_object_unref (attributes[0]); + + cogl_object_unref (pipeline2); + cogl_object_unref (pipeline); + cogl_object_unref (buffer); + + test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); + test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); +} + +static void +paint (TestState *state) +{ + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + test_float_verts (state, 0, 0); + test_byte_verts (state, 0, 10); + test_short_verts (state, 0, 20); +} + +void +test_custom_attributes (void) +{ + CoglSnippet *snippet; + TestState state; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + state.pipeline = cogl_pipeline_new (test_ctx); + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, + "attribute vec4 color;", + "cogl_color_out = color;"); + cogl_pipeline_add_snippet (state.pipeline, snippet); + + paint (&state); + + cogl_object_unref (state.pipeline); + cogl_object_unref (snippet); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-depth-test.c b/cogl/tests/conform/test-depth-test.c new file mode 100644 index 000000000..bfa9d0e1f --- /dev/null +++ b/cogl/tests/conform/test-depth-test.c @@ -0,0 +1,301 @@ +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 + +#include + +#include + +#include "test-utils.h" + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) +#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) +#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) +#define MASK_ALPHA(COLOR) (COLOR & 0xff) + +typedef struct _TestState +{ + int padding; +} TestState; + +typedef struct +{ + uint32_t color; + float depth; + CoglBool test_enable; + CoglDepthTestFunction test_function; + CoglBool write_enable; + CoglBool fb_write_enable; + float range_near; + float range_far; +} TestDepthState; + +static CoglBool +draw_rectangle (TestState *state, + int x, + int y, + TestDepthState *rect_state, + CoglBool legacy_mode) +{ + uint8_t Cr = MASK_RED (rect_state->color); + uint8_t Cg = MASK_GREEN (rect_state->color); + uint8_t Cb = MASK_BLUE (rect_state->color); + uint8_t Ca = MASK_ALPHA (rect_state->color); + CoglPipeline *pipeline; + CoglDepthState depth_state; + + cogl_depth_state_init (&depth_state); + cogl_depth_state_set_test_enabled (&depth_state, rect_state->test_enable); + cogl_depth_state_set_test_function (&depth_state, rect_state->test_function); + cogl_depth_state_set_write_enabled (&depth_state, rect_state->write_enable); + cogl_depth_state_set_range (&depth_state, + rect_state->range_near, + rect_state->range_far); + + pipeline = cogl_pipeline_new (test_ctx); + if (!cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL)) + { + cogl_object_unref (pipeline); + return FALSE; + } + + if (!legacy_mode) + { + cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca); + + cogl_framebuffer_set_depth_write_enabled (test_fb, + rect_state->fb_write_enable); + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, 0, 0, rect_state->depth); + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_framebuffer_pop_matrix (test_fb); + } + else + { + cogl_push_framebuffer (test_fb); + cogl_push_matrix (); + cogl_set_source_color4ub (Cr, Cg, Cb, Ca); + cogl_translate (0, 0, rect_state->depth); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_pop_matrix (); + cogl_pop_framebuffer (); + } + + cogl_object_unref (pipeline); + + return TRUE; +} + +static void +test_depth (TestState *state, + int x, + int y, + TestDepthState *rect0_state, + TestDepthState *rect1_state, + TestDepthState *rect2_state, + CoglBool legacy_mode, + uint32_t expected_result) +{ + CoglBool missing_feature = FALSE; + + if (rect0_state) + missing_feature |= !draw_rectangle (state, x, y, rect0_state, legacy_mode); + if (rect1_state) + missing_feature |= !draw_rectangle (state, x, y, rect1_state, legacy_mode); + if (rect2_state) + missing_feature |= !draw_rectangle (state, x, y, rect2_state, legacy_mode); + + /* We don't consider it an error that we can't test something + * the driver doesn't support. */ + if (missing_feature) + return; + + test_utils_check_pixel (test_fb, + x * QUAD_WIDTH + (QUAD_WIDTH / 2), + y * QUAD_WIDTH + (QUAD_WIDTH / 2), + expected_result); +} + +static void +paint (TestState *state) +{ + /* Sanity check a few of the different depth test functions + * and that depth writing can be disabled... */ + + { + /* Closest */ + TestDepthState rect0_state = { + 0xff0000ff, /* rgba color */ + -10, /* depth */ + FALSE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_ALWAYS, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 1 /* depth range */ + }; + /* Furthest */ + TestDepthState rect1_state = { + 0x00ff00ff, /* rgba color */ + -70, /* depth */ + TRUE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_ALWAYS, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 1 /* depth range */ + }; + /* In the middle */ + TestDepthState rect2_state = { + 0x0000ffff, /* rgba color */ + -20, /* depth */ + TRUE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_NEVER, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 1 /* depth range */ + }; + + test_depth (state, 0, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x00ff00ff); /* expected */ + + rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS; + test_depth (state, 1, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x0000ffff); /* expected */ + + rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS; + test_depth (state, 2, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x0000ffff); /* expected */ + + rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER; + test_depth (state, 3, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x00ff00ff); /* expected */ + + rect0_state.test_enable = TRUE; + rect1_state.write_enable = FALSE; + test_depth (state, 4, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x0000ffff); /* expected */ + + rect1_state.write_enable = TRUE; + rect1_state.fb_write_enable = FALSE; + test_depth (state, 4, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x0000ffff); /* expected */ + + /* Re-enable FB depth writing to verify state flush */ + rect1_state.write_enable = TRUE; + rect1_state.fb_write_enable = TRUE; + test_depth (state, 4, 0, /* position */ + &rect0_state, &rect1_state, &rect2_state, + FALSE, /* legacy mode */ + 0x00ff00ff); /* expected */ + } + + /* Check that the depth buffer values can be mapped into different + * ranges... */ + + { + /* Closest by depth, furthest by depth range */ + TestDepthState rect0_state = { + 0xff0000ff, /* rgba color */ + -10, /* depth */ + TRUE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_ALWAYS, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0.5, 1 /* depth range */ + }; + /* Furthest by depth, nearest by depth range */ + TestDepthState rect1_state = { + 0x00ff00ff, /* rgba color */ + -70, /* depth */ + TRUE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_GREATER, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 0.5 /* depth range */ + }; + + test_depth (state, 0, 1, /* position */ + &rect0_state, &rect1_state, NULL, + FALSE, /* legacy mode */ + 0xff0000ff); /* expected */ + } + + /* Test that the legacy cogl_set_depth_test_enabled() API still + * works... */ + + { + /* Nearest */ + TestDepthState rect0_state = { + 0xff0000ff, /* rgba color */ + -10, /* depth */ + FALSE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_LESS, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 1 /* depth range */ + }; + /* Furthest */ + TestDepthState rect1_state = { + 0x00ff00ff, /* rgba color */ + -70, /* depth */ + FALSE, /* depth test enable */ + COGL_DEPTH_TEST_FUNCTION_LESS, + TRUE, /* depth write enable */ + TRUE, /* FB depth write enable */ + 0, 1 /* depth range */ + }; + + cogl_set_depth_test_enabled (TRUE); + test_depth (state, 0, 2, /* position */ + &rect0_state, &rect1_state, NULL, + TRUE, /* legacy mode */ + 0xff0000ff); /* expected */ + cogl_set_depth_test_enabled (FALSE); + test_depth (state, 1, 2, /* position */ + &rect0_state, &rect1_state, NULL, + TRUE, /* legacy mode */ + 0x00ff00ff); /* expected */ + } +} + +void +test_depth_test (void) +{ + TestState state; + + cogl_framebuffer_orthographic (test_fb, 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-euler-quaternion.c b/cogl/tests/conform/test-euler-quaternion.c new file mode 100644 index 000000000..c250bec0b --- /dev/null +++ b/cogl/tests/conform/test-euler-quaternion.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "test-utils.h" + +/* Macros are used here instead of functions so that the + * g_assert_cmpfloat will give a more interesting message when it + * fails */ + +#define COMPARE_FLOATS(a, b) \ + do { \ + if (fabsf ((a) - (b)) >= 0.0001f) \ + g_assert_cmpfloat ((a), ==, (b)); \ + } while (0) + +#define COMPARE_MATRICES(a, b) \ + do { \ + COMPARE_FLOATS ((a)->xx, (b)->xx); \ + COMPARE_FLOATS ((a)->yx, (b)->yx); \ + COMPARE_FLOATS ((a)->zx, (b)->zx); \ + COMPARE_FLOATS ((a)->wx, (b)->wx); \ + COMPARE_FLOATS ((a)->xy, (b)->xy); \ + COMPARE_FLOATS ((a)->yy, (b)->yy); \ + COMPARE_FLOATS ((a)->zy, (b)->zy); \ + COMPARE_FLOATS ((a)->wy, (b)->wy); \ + COMPARE_FLOATS ((a)->xz, (b)->xz); \ + COMPARE_FLOATS ((a)->yz, (b)->yz); \ + COMPARE_FLOATS ((a)->zz, (b)->zz); \ + COMPARE_FLOATS ((a)->wz, (b)->wz); \ + COMPARE_FLOATS ((a)->xw, (b)->xw); \ + COMPARE_FLOATS ((a)->yw, (b)->yw); \ + COMPARE_FLOATS ((a)->zw, (b)->zw); \ + COMPARE_FLOATS ((a)->ww, (b)->ww); \ + } while (0) + +void +test_euler_quaternion (void) +{ + CoglEuler euler; + CoglQuaternion quaternion; + CoglMatrix matrix_a, matrix_b; + + /* Try doing the rotation with three separate rotations */ + cogl_matrix_init_identity (&matrix_a); + cogl_matrix_rotate (&matrix_a, -30.0f, 0.0f, 1.0f, 0.0f); + cogl_matrix_rotate (&matrix_a, 40.0f, 1.0f, 0.0f, 0.0f); + cogl_matrix_rotate (&matrix_a, 50.0f, 0.0f, 0.0f, 1.0f); + + /* And try the same rotation with a euler */ + cogl_euler_init (&euler, -30, 40, 50); + cogl_matrix_init_from_euler (&matrix_b, &euler); + + /* Verify that the matrices are approximately the same */ + COMPARE_MATRICES (&matrix_a, &matrix_b); + + /* Try converting the euler to a matrix via a quaternion */ + cogl_quaternion_init_from_euler (&quaternion, &euler); + memset (&matrix_b, 0, sizeof (matrix_b)); + cogl_matrix_init_from_quaternion (&matrix_b, &quaternion); + COMPARE_MATRICES (&matrix_a, &matrix_b); + + /* Try applying the rotation from a euler to a framebuffer */ + cogl_framebuffer_identity_matrix (test_fb); + cogl_framebuffer_rotate_euler (test_fb, &euler); + memset (&matrix_b, 0, sizeof (matrix_b)); + cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b); + COMPARE_MATRICES (&matrix_a, &matrix_b); + + /* And again with a quaternion */ + cogl_framebuffer_identity_matrix (test_fb); + cogl_framebuffer_rotate_quaternion (test_fb, &quaternion); + memset (&matrix_b, 0, sizeof (matrix_b)); + cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b); + COMPARE_MATRICES (&matrix_a, &matrix_b); + + /* FIXME: This needs a lot more tests! */ + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-fence.c b/cogl/tests/conform/test-fence.c new file mode 100644 index 000000000..d5e3586f5 --- /dev/null +++ b/cogl/tests/conform/test-fence.c @@ -0,0 +1,63 @@ +#include + +/* These will be redefined in config.h */ +#undef COGL_ENABLE_EXPERIMENTAL_2_0_API +#undef COGL_ENABLE_EXPERIMENTAL_API + +#include "test-utils.h" +#include "config.h" + +/* I'm writing this on the train after having dinner at a churrascuria. */ +#define MAGIC_CHUNK_O_DATA ((void *) 0xdeadbeef) + +static GMainLoop *loop; + +gboolean +timeout (void *user_data) +{ + g_assert (!"timeout not reached"); + + return FALSE; +} + +void +callback (CoglFence *fence, + void *user_data) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + + test_utils_check_pixel (test_fb, fb_width - 1, fb_height - 1, 0x00ff0000); + g_assert (user_data == MAGIC_CHUNK_O_DATA && "callback data not mangled"); + + g_main_loop_quit (loop); +} + +void +test_fence (void) +{ + GSource *cogl_source; + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglFenceClosure *closure; + + cogl_source = cogl_glib_source_new (test_ctx, G_PRIORITY_DEFAULT); + g_source_attach (cogl_source, NULL); + loop = g_main_loop_new (NULL, TRUE); + + cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100); + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, + 0.0f, 1.0f, 0.0f, 0.0f); + + closure = cogl_framebuffer_add_fence_callback (test_fb, + callback, + MAGIC_CHUNK_O_DATA); + g_assert (closure != NULL); + + g_timeout_add_seconds (5, timeout, NULL); + + g_main_loop_run (loop); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-fixed.c b/cogl/tests/conform/test-fixed.c new file mode 100644 index 000000000..175b2d195 --- /dev/null +++ b/cogl/tests/conform/test-fixed.c @@ -0,0 +1,18 @@ +#include +#include + +#include "test-conform-common.h" + +void +test_fixed (TestUtilsGTestFixture *fixture, + void *data) +{ + g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_FLOAT (1.0)); + g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_INT (1)); + + g_assert_cmpint (COGL_FIXED_0_5, ==, COGL_FIXED_FROM_FLOAT (0.5)); + + g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_1), ==, 1.0); + g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_0_5), ==, 0.5); +} + diff --git a/cogl/tests/conform/test-fixtures.c b/cogl/tests/conform/test-fixtures.c new file mode 100644 index 000000000..dfc20437d --- /dev/null +++ b/cogl/tests/conform/test-fixtures.c @@ -0,0 +1,12 @@ + +#include +#include + +void +test_simple_rig (void) +{ + ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); +} diff --git a/cogl/tests/conform/test-framebuffer-get-bits.c b/cogl/tests/conform/test-framebuffer-get-bits.c new file mode 100644 index 000000000..31c220d78 --- /dev/null +++ b/cogl/tests/conform/test-framebuffer-get-bits.c @@ -0,0 +1,40 @@ +#include + +#include "test-utils.h" + +void +test_framebuffer_get_bits (void) +{ + CoglTexture2D *tex_a = + cogl_texture_2d_new_with_size (test_ctx, + 16, 16); /* width/height */ + CoglOffscreen *offscreen_a = + cogl_offscreen_new_with_texture (tex_a); + CoglFramebuffer *fb_a = offscreen_a; + CoglTexture2D *tex_rgba = + cogl_texture_2d_new_with_size (test_ctx, + 16, 16); /* width/height */ + CoglOffscreen *offscreen_rgba = + cogl_offscreen_new_with_texture (tex_rgba); + CoglFramebuffer *fb_rgba = offscreen_rgba; + + cogl_texture_set_components (tex_a, + COGL_TEXTURE_COMPONENTS_A); + cogl_framebuffer_allocate (fb_a, NULL); + cogl_framebuffer_allocate (fb_rgba, NULL); + + g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_a), ==, 0); + g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_a), ==, 0); + g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_a), ==, 0); + g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_a), >=, 1); + + g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_rgba), >=, 1); + g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_rgba), >=, 1); + g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_rgba), >=, 1); + g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_rgba), >=, 1); + + cogl_object_unref (fb_rgba); + cogl_object_unref (tex_rgba); + cogl_object_unref (fb_a); + cogl_object_unref (tex_a); +} diff --git a/cogl/tests/conform/test-gles2-context.c b/cogl/tests/conform/test-gles2-context.c new file mode 100644 index 000000000..bedc30a02 --- /dev/null +++ b/cogl/tests/conform/test-gles2-context.c @@ -0,0 +1,962 @@ + +#include +#include +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglGLES2Context *gles2_ctx; + const CoglGLES2Vtable *gles2; +} TestState; + +static void +test_push_pop_single_context (void) +{ + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + CoglGLES2Context *gles2_ctx; + const CoglGLES2Vtable *gles2; + CoglError *error = NULL; + + offscreen_texture = + cogl_texture_2d_new_with_size (test_ctx, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb)); + offscreen = cogl_offscreen_new_with_texture (offscreen_texture); + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (pipeline, 0, offscreen_texture); + + gles2_ctx = cogl_gles2_context_new (test_ctx, &error); + if (!gles2_ctx) + g_error ("Failed to create GLES2 context: %s\n", error->message); + + gles2 = cogl_gles2_context_get_vtable (gles2_ctx); + + /* Clear onscreen to 0xffff00 using GLES2 */ + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + test_fb, + test_fb, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glClearColor (1, 1, 0, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xffff00ff); + + /* Clear offscreen to 0xff0000 using GLES2 and then copy the result + * onscreen. + * + * If we fail to bind the new context here then we'd probably end up + * clearing onscreen to 0xff0000 and copying 0xffff00 to onscreen + * instead. + */ + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + offscreen, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glClearColor (1, 0, 0, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + cogl_pop_gles2_context (test_ctx); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1, 1, 1, -1); + /* NB: Cogl doesn't automatically support mid-scene modifications + * of textures and so we explicitly flush the drawn rectangle to the + * framebuffer now otherwise it may be batched until after the + * offscreen texture has been modified again. */ + cogl_flush (); + + /* Clear the offscreen framebuffer to blue using GLES2 before + * reading back from the onscreen framebuffer in case we mistakenly + * read from the offscreen framebuffer and get a false positive + */ + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + offscreen, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glClearColor (0, 0, 1, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xff0000ff); + + /* Now copy the offscreen blue clear to the onscreen framebufer and + * check that too */ + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1, 1, 1, -1); + + test_utils_check_pixel (test_fb, 0, 0, 0x0000ffff); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + test_fb, + test_fb, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glClearColor (1, 0, 1, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xff00ffff); + + + cogl_object_unref (gles2_ctx); + + cogl_object_unref (pipeline); +} + +static void +create_gles2_context (CoglTexture **offscreen_texture, + CoglOffscreen **offscreen, + CoglPipeline **pipeline, + CoglGLES2Context **gles2_ctx, + const CoglGLES2Vtable **gles2) +{ + CoglError *error = NULL; + + *offscreen_texture = + cogl_texture_2d_new_with_size (test_ctx, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb)); + *offscreen = cogl_offscreen_new_with_texture (*offscreen_texture); + + *pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (*pipeline, 0, *offscreen_texture); + + *gles2_ctx = cogl_gles2_context_new (test_ctx, &error); + if (!*gles2_ctx) + g_error ("Failed to create GLES2 context: %s\n", error->message); + + *gles2 = cogl_gles2_context_get_vtable (*gles2_ctx); +} + +static void +test_push_pop_multi_context (void) +{ + CoglTexture *offscreen_texture0; + CoglOffscreen *offscreen0; + CoglPipeline *pipeline0; + CoglGLES2Context *gles2_ctx0; + const CoglGLES2Vtable *gles20; + CoglTexture *offscreen_texture1; + CoglOffscreen *offscreen1; + CoglPipeline *pipeline1; + CoglGLES2Context *gles2_ctx1; + const CoglGLES2Vtable *gles21; + CoglError *error = NULL; + + create_gles2_context (&offscreen_texture0, + &offscreen0, + &pipeline0, + &gles2_ctx0, + &gles20); + + create_gles2_context (&offscreen_texture1, + &offscreen1, + &pipeline1, + &gles2_ctx1, + &gles21); + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx0, + offscreen0, + offscreen0, + &error)) + { + g_error ("Failed to push gles2 context 0: %s\n", error->message); + } + + gles20->glClearColor (1, 0, 0, 1); + gles20->glClear (GL_COLOR_BUFFER_BIT); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx1, + offscreen1, + offscreen1, + &error)) + { + g_error ("Failed to push gles2 context 1: %s\n", error->message); + } + + gles21->glClearColor (0, 1, 0, 1); + gles21->glClear (GL_COLOR_BUFFER_BIT); + + cogl_pop_gles2_context (test_ctx); + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xffffffff); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline0, + -1, 1, 1, -1); + + test_utils_check_pixel (test_fb, 0, 0, 0xff0000ff); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline1, + -1, 1, 1, -1); + + test_utils_check_pixel (test_fb, 0, 0, 0x00ff00ff); +} + +static GLuint +create_gles2_framebuffer (const CoglGLES2Vtable *gles2, + int width, + int height) +{ + GLuint texture_handle; + GLuint fbo_handle; + GLenum status; + + gles2->glGenTextures (1, &texture_handle); + gles2->glGenFramebuffers (1, &fbo_handle); + + gles2->glBindTexture (GL_TEXTURE_2D, texture_handle); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gles2->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + gles2->glBindTexture (GL_TEXTURE_2D, 0); + + gles2->glBindFramebuffer (GL_FRAMEBUFFER, fbo_handle); + gles2->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture_handle, 0); + + status = gles2->glCheckFramebufferStatus (GL_FRAMEBUFFER); + if (cogl_test_verbose ()) + g_print ("status for gles2 framebuffer = 0x%x %s\n", + status, status == GL_FRAMEBUFFER_COMPLETE ? "(complete)" : "(?)"); + + gles2->glBindFramebuffer (GL_FRAMEBUFFER, 0); + + return fbo_handle; +} + +static void +test_gles2_read_pixels (void) +{ + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + CoglGLES2Context *gles2_ctx; + const CoglGLES2Vtable *gles2; + CoglError *error = NULL; + GLubyte pixel[4]; + GLuint fbo_handle; + + create_gles2_context (&offscreen_texture, + &offscreen, + &pipeline, + &gles2_ctx, + &gles2); + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + offscreen, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glClearColor (1, 0, 0, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + + test_utils_compare_pixel (pixel, 0xff0000ff); + + fbo_handle = create_gles2_framebuffer (gles2, 256, 256); + + gles2->glBindFramebuffer (GL_FRAMEBUFFER, fbo_handle); + + gles2->glClearColor (0, 1, 0, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + + test_utils_compare_pixel (pixel, 0x00ff00ff); + + gles2->glBindFramebuffer (GL_FRAMEBUFFER, 0); + + gles2->glClearColor (0, 1, 1, 1); + gles2->glClear (GL_COLOR_BUFFER_BIT); + gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + + test_utils_compare_pixel (pixel, 0x00ffffff); + + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xffffffff); + + /* Bind different read and write buffers */ + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + test_fb, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + + test_utils_compare_pixel (pixel, 0x00ffffff); + + cogl_pop_gles2_context (test_ctx); + + test_utils_check_pixel (test_fb, 0, 0, 0xffffffff); + + /* Bind different read and write buffers (the other way around from + * before so when we test with COGL_TEST_ONSCREEN=1 we will read + * from an onscreen framebuffer) */ + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + test_fb, + offscreen, + &error)) + { + g_error ("Failed to push gles2 context: %s\n", error->message); + } + + gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + + test_utils_compare_pixel (pixel, 0xffffffff); + + cogl_pop_gles2_context (test_ctx); +} + +void +test_gles2_context (void) +{ + test_push_pop_single_context (); + test_push_pop_multi_context (); + test_gles2_read_pixels (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +static GLuint +create_shader (const CoglGLES2Vtable *gles2, + GLenum type, + const char *source) +{ + GLuint shader; + GLint status; + int length = strlen (source); + + shader = gles2->glCreateShader (type); + gles2->glShaderSource (shader, 1, &source, &length); + gles2->glCompileShader (shader); + gles2->glGetShaderiv (shader, GL_COMPILE_STATUS, &status); + + if (!status) + { + char buf[512]; + + gles2->glGetShaderInfoLog (shader, sizeof (buf), NULL, buf); + + g_error ("Shader compilation failed:\n%s", buf); + } + + return shader; +} + +static GLuint +create_program (const CoglGLES2Vtable *gles2, + const char *vertex_shader_source, + const char *fragment_shader_source) +{ + GLuint fragment_shader, vertex_shader, program; + GLint status; + + vertex_shader = + create_shader (gles2, GL_VERTEX_SHADER, vertex_shader_source); + fragment_shader = + create_shader (gles2, GL_FRAGMENT_SHADER, fragment_shader_source); + + program = gles2->glCreateProgram (); + gles2->glAttachShader (program, vertex_shader); + gles2->glAttachShader (program, fragment_shader); + gles2->glLinkProgram (program); + + gles2->glGetProgramiv (program, GL_LINK_STATUS, &status); + + if (!status) + { + char buf[512]; + + gles2->glGetProgramInfoLog (program, sizeof (buf), NULL, buf); + + g_error ("Program linking failed:\n%s", buf); + } + + return program; +} + +typedef struct +{ + const CoglGLES2Vtable *gles2; + GLint color_location; + GLint pos_location; + int fb_width, fb_height; +} PaintData; + +typedef void (* PaintMethod) (PaintData *data); + +/* Top vertices are counter-clockwise */ +static const float top_vertices[] = + { + -1.0f, 0.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, + 1.0f, 1.0f + }; +/* Bottom vertices are clockwise */ +static const float bottom_vertices[] = + { + 1.0f, 0.0f, + 1.0f, -1.0f, + -1.0f, 0.0f, + -1.0f, -1.0f + }; + +static void +paint_quads (PaintData *data) +{ + const CoglGLES2Vtable *gles2 = data->gles2; + + gles2->glEnableVertexAttribArray (data->pos_location); + + /* Paint the top half in red */ + gles2->glUniform4f (data->color_location, + 1.0f, 0.0f, 0.0f, 1.0f); + gles2->glVertexAttribPointer (data->pos_location, + 2, /* size */ + GL_FLOAT, + GL_FALSE, /* not normalized */ + sizeof (float) * 2, + top_vertices); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + /* Paint the bottom half in blue */ + gles2->glUniform4f (data->color_location, + 0.0f, 0.0f, 1.0f, 1.0f); + gles2->glVertexAttribPointer (data->pos_location, + 2, /* size */ + GL_FLOAT, + GL_FALSE, /* not normalized */ + sizeof (float) * 2, + bottom_vertices); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); +} + +static void +paint_viewport (PaintData *data) +{ + const CoglGLES2Vtable *gles2 = data->gles2; + int viewport[4]; + + /* Vertices to fill the entire framebuffer */ + static const float vertices[] = + { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f + }; + + gles2->glEnableVertexAttribArray (data->pos_location); + gles2->glVertexAttribPointer (data->pos_location, + 2, /* size */ + GL_FLOAT, + GL_FALSE, /* not normalized */ + sizeof (float) * 2, + vertices); + + /* Paint the top half in red */ + gles2->glViewport (0, data->fb_height / 2, + data->fb_width, data->fb_height / 2); + gles2->glUniform4f (data->color_location, + 1.0f, 0.0f, 0.0f, 1.0f); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + /* Paint the bottom half in blue */ + gles2->glViewport (0, 0, data->fb_width, data->fb_height / 2); + gles2->glUniform4f (data->color_location, + 0.0f, 0.0f, 1.0f, 1.0f); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + gles2->glGetIntegerv (GL_VIEWPORT, viewport); + g_assert_cmpint (viewport[0], ==, 0.0f); + g_assert_cmpint (viewport[1], ==, 0.0f); + g_assert_cmpint (viewport[2], ==, data->fb_width); + g_assert_cmpint (viewport[3], ==, data->fb_height / 2); +} + +static void +paint_scissor (PaintData *data) +{ + const CoglGLES2Vtable *gles2 = data->gles2; + float scissor[4]; + + gles2->glEnable (GL_SCISSOR_TEST); + + /* Paint the top half in red */ + gles2->glScissor (0, data->fb_height / 2, + data->fb_width, data->fb_height / 2); + gles2->glClearColor (1.0, 0.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Paint the bottom half in blue */ + gles2->glScissor (0, 0, data->fb_width, data->fb_height / 2); + gles2->glClearColor (0.0, 0.0, 1.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + gles2->glGetFloatv (GL_SCISSOR_BOX, scissor); + g_assert_cmpfloat (scissor[0], ==, 0.0f); + g_assert_cmpfloat (scissor[1], ==, 0.0f); + g_assert_cmpfloat (scissor[2], ==, data->fb_width); + g_assert_cmpfloat (scissor[3], ==, data->fb_height / 2); +} + +static void +paint_cull (PaintData *data) +{ + const CoglGLES2Vtable *gles2 = data->gles2; + GLint front_face; + int i; + + gles2->glEnableVertexAttribArray (data->pos_location); + gles2->glEnable (GL_CULL_FACE); + + /* First time round we'll use GL_CCW as the front face so that the + * bottom quad will be culled */ + gles2->glFrontFace (GL_CCW); + gles2->glUniform4f (data->color_location, + 1.0f, 0.0f, 0.0f, 1.0f); + + gles2->glGetIntegerv (GL_FRONT_FACE, &front_face); + g_assert_cmpint (front_face, ==, GL_CCW); + + for (i = 0; i < 2; i++) + { + /* Paint both quads in the same color. One of these will be + * culled */ + gles2->glVertexAttribPointer (data->pos_location, + 2, /* size */ + GL_FLOAT, + GL_FALSE, /* not normalized */ + sizeof (float) * 2, + top_vertices); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + gles2->glVertexAttribPointer (data->pos_location, + 2, /* size */ + GL_FLOAT, + GL_FALSE, /* not normalized */ + sizeof (float) * 2, + bottom_vertices); + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + /* Second time round we'll use GL_CW as the front face so that the + * top quad will be culled */ + gles2->glFrontFace (GL_CW); + gles2->glUniform4f (data->color_location, + 0.0f, 0.0f, 1.0f, 1.0f); + + gles2->glGetIntegerv (GL_FRONT_FACE, &front_face); + g_assert_cmpint (front_face, ==, GL_CW); + } +} + +static void +verify_read_pixels (const PaintData *data) +{ + int stride = data->fb_width * 4; + uint8_t *buf = g_malloc (data->fb_height * stride); + + data->gles2->glReadPixels (0, 0, /* x/y */ + data->fb_width, data->fb_height, + GL_RGBA, + GL_UNSIGNED_BYTE, + buf); + + /* In GL, the lines earlier in the buffer are the bottom */ + /* Bottom should be blue */ + test_utils_compare_pixel (buf + data->fb_width / 2 * 4 + + data->fb_height / 4 * stride, + 0x0000ffff); + /* Top should be red */ + test_utils_compare_pixel (buf + data->fb_width / 2 * 4 + + data->fb_height * 3 / 4 * stride, + 0xff0000ff); + + g_free (buf); +} + +void +test_gles2_context_fbo (void) +{ + static const char vertex_shader_source[] = + "attribute vec2 pos;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_Position = vec4 (pos, 0.0, 1.0);\n" + "}\n"; + static const char fragment_shader_source[] = + "precision mediump float;\n" + "uniform vec4 color;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + static const PaintMethod paint_methods[] = + { + paint_quads, + paint_viewport, + paint_scissor, + paint_cull + }; + int i; + PaintData data; + + data.fb_width = cogl_framebuffer_get_width (test_fb); + data.fb_height = cogl_framebuffer_get_height (test_fb); + + for (i = 0; i < G_N_ELEMENTS (paint_methods); i++) + { + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + CoglGLES2Context *gles2_ctx; + GLuint program; + CoglError *error = NULL; + + create_gles2_context (&offscreen_texture, + &offscreen, + &pipeline, + &gles2_ctx, + &data.gles2); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + offscreen, + &error)) + g_error ("Failed to push gles2 context: %s\n", error->message); + + program = create_program (data.gles2, + vertex_shader_source, + fragment_shader_source); + + data.gles2->glClearColor (1.0, 1.0, 0.0, 1.0); + data.gles2->glClear (GL_COLOR_BUFFER_BIT); + + data.gles2->glUseProgram (program); + + data.color_location = data.gles2->glGetUniformLocation (program, "color"); + if (data.color_location == -1) + g_error ("Couldn't find ‘color’ uniform"); + + data.pos_location = data.gles2->glGetAttribLocation (program, "pos"); + if (data.pos_location == -1) + g_error ("Couldn't find ‘pos’ attribute"); + + paint_methods[i] (&data); + + verify_read_pixels (&data); + + cogl_pop_gles2_context (test_ctx); + + cogl_object_unref (offscreen); + cogl_object_unref (gles2_ctx); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1.0f, 1.0f, + 1.0f, -1.0f); + + cogl_object_unref (pipeline); + cogl_object_unref (offscreen_texture); + + /* Top half of the framebuffer should be red */ + test_utils_check_pixel (test_fb, + data.fb_width / 2, data.fb_height / 4, + 0xff0000ff); + /* Bottom half should be blue */ + test_utils_check_pixel (test_fb, + data.fb_width / 2, data.fb_height * 3 / 4, + 0x0000ffff); + } +} + +/* Position to draw a rectangle in. The top half of this rectangle + * will be red, and the bottom will be blue */ +#define RECTANGLE_DRAW_X 10 +#define RECTANGLE_DRAW_Y 15 + +/* Position to copy the rectangle to in the destination texture */ +#define RECTANGLE_COPY_X 110 +#define RECTANGLE_COPY_Y 115 + +#define RECTANGLE_WIDTH 30 +#define RECTANGLE_HEIGHT 40 + +static void +verify_region (const CoglGLES2Vtable *gles2, + int x, + int y, + int width, + int height, + uint32_t expected_pixel) +{ + uint8_t *buf, *p; + + buf = g_malloc (width * height * 4); + + gles2->glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buf); + + for (p = buf + width * height * 4; p > buf; p -= 4) + test_utils_compare_pixel (p - 4, expected_pixel); + + g_free (buf); +} + +void +test_gles2_context_copy_tex_image (void) +{ + static const char vertex_shader_source[] = + "attribute vec2 pos;\n" + "attribute vec2 tex_coord_attrib;\n" + "varying vec2 tex_coord_varying;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_Position = vec4 (pos, 0.0, 1.0);\n" + " tex_coord_varying = tex_coord_attrib;\n" + "}\n"; + static const char fragment_shader_source[] = + "precision mediump float;\n" + "varying vec2 tex_coord_varying;\n" + "uniform sampler2D tex;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_FragColor = texture2D (tex, tex_coord_varying);\n" + "}\n"; + static const float verts[] = + { + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + CoglGLES2Context *gles2_ctx; + const CoglGLES2Vtable *gles2; + CoglError *error = NULL; + GLuint tex; + GLint tex_uniform_location; + GLint pos_location; + GLint tex_coord_location; + GLuint program; + + create_gles2_context (&offscreen_texture, + &offscreen, + &pipeline, + &gles2_ctx, + &gles2); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + offscreen, + offscreen, + &error)) + g_error ("Failed to push gles2 context: %s\n", error->message); + + gles2->glClearColor (1.0, 1.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Draw a rectangle using clear and the scissor so that we don't + * have to create a shader */ + gles2->glEnable (GL_SCISSOR_TEST); + + /* Top half red */ + gles2->glScissor (RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2); + gles2->glClearColor (1.0, 0.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + /* Bottom half blue */ + gles2->glScissor (RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2); + gles2->glClearColor (0.0, 0.0, 1.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Draw where the rectangle would be if the coordinates were flipped + * in white to make it obvious that that is the problem if the + * assertion fails */ + gles2->glScissor (RECTANGLE_DRAW_X, + fb_width - (RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT), + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT); + gles2->glClearColor (1.0, 1.0, 1.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + gles2->glDisable (GL_SCISSOR_TEST); + + /* Create a texture */ + gles2->glGenTextures (1, &tex); + gles2->glBindTexture (GL_TEXTURE_2D, tex); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + /* Copy the entire framebuffer into the texture */ + gles2->glCopyTexImage2D (GL_TEXTURE_2D, + 0, /* level */ + GL_RGBA, + 0, 0, /* x/y */ + fb_width, fb_height, + 0 /* border */); + + /* Copy the rectangle into another part of the texture */ + gles2->glCopyTexSubImage2D (GL_TEXTURE_2D, + 0, /* level */ + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT); + + /* Clear the framebuffer to make the test more thorough */ + gles2->glClearColor (1.0, 1.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Create a program to render the texture */ + program = create_program (gles2, + vertex_shader_source, + fragment_shader_source); + + pos_location = + gles2->glGetAttribLocation (program, "pos"); + if (pos_location == -1) + g_error ("Couldn't find ‘pos’ attribute"); + + tex_coord_location = + gles2->glGetAttribLocation (program, "tex_coord_attrib"); + if (tex_coord_location == -1) + g_error ("Couldn't find ‘tex_coord_attrib’ attribute"); + + tex_uniform_location = + gles2->glGetUniformLocation (program, "tex"); + if (tex_uniform_location == -1) + g_error ("Couldn't find ‘tex’ uniform"); + + gles2->glUseProgram (program); + + gles2->glUniform1i (tex_uniform_location, 0); + + /* Render the texture to fill the framebuffer */ + gles2->glEnableVertexAttribArray (pos_location); + gles2->glVertexAttribPointer (pos_location, + 2, /* n_components */ + GL_FLOAT, + FALSE, /* normalized */ + sizeof (float) * 4, + verts); + gles2->glEnableVertexAttribArray (tex_coord_location); + gles2->glVertexAttribPointer (tex_coord_location, + 2, /* n_components */ + GL_FLOAT, + FALSE, /* normalized */ + sizeof (float) * 4, + verts + 2); + + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + /* Verify top of drawn rectangle is red */ + verify_region (gles2, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0xff0000ff); + /* Verify bottom of drawn rectangle is blue */ + verify_region (gles2, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0x0000ffff); + /* Verify top of copied rectangle is red */ + verify_region (gles2, + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0xff0000ff); + /* Verify bottom of copied rectangle is blue */ + verify_region (gles2, + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0x0000ffff); + + cogl_pop_gles2_context (test_ctx); + + cogl_object_unref (offscreen); + cogl_object_unref (gles2_ctx); + cogl_object_unref (pipeline); + cogl_object_unref (offscreen_texture); +} diff --git a/cogl/tests/conform/test-just-vertex-shader.c b/cogl/tests/conform/test-just-vertex-shader.c new file mode 100644 index 000000000..60fcaf74c --- /dev/null +++ b/cogl/tests/conform/test-just-vertex-shader.c @@ -0,0 +1,205 @@ +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 + +#include + +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + int paddiing; +} TestState; + +static CoglTexture * +create_dummy_texture (void) +{ + /* Create a dummy 1x1 green texture to replace the color from the + vertex shader */ + static const uint8_t data[4] = { 0x00, 0xff, 0x00, 0xff }; + + return test_utils_texture_new_from_data (test_ctx, + 1, 1, /* size */ + TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGB_888, + 4, /* rowstride */ + data); +} + +static void +paint_legacy (TestState *state) +{ + CoglHandle material = cogl_material_new (); + CoglTexture *tex; + CoglColor color; + CoglError *error = NULL; + CoglHandle shader, program; + + cogl_color_init_from_4ub (&color, 0, 0, 0, 255); + cogl_clear (&color, COGL_BUFFER_BIT_COLOR); + + /* Set the primary vertex color as red */ + cogl_color_set_from_4ub (&color, 0xff, 0x00, 0x00, 0xff); + cogl_material_set_color (material, &color); + + /* Override the vertex color in the texture environment with a + constant green color provided by a texture */ + tex = create_dummy_texture (); + cogl_material_set_layer (material, 0, tex); + cogl_object_unref (tex); + if (!cogl_material_set_layer_combine (material, 0, + "RGBA=REPLACE(TEXTURE)", + &error)) + { + g_warning ("Error setting layer combine: %s", error->message); + g_assert_not_reached (); + } + + /* Set up a dummy vertex shader that does nothing but the usual + fixed function transform */ + shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX); + cogl_shader_source (shader, + "void\n" + "main ()\n" + "{\n" + " cogl_position_out = " + "cogl_modelview_projection_matrix * " + "cogl_position_in;\n" + " cogl_color_out = cogl_color_in;\n" + " cogl_tex_coord_out[0] = cogl_tex_coord_in;\n" + "}\n"); + cogl_shader_compile (shader); + if (!cogl_shader_is_compiled (shader)) + { + char *log = cogl_shader_get_info_log (shader); + g_warning ("Shader compilation failed:\n%s", log); + g_free (log); + g_assert_not_reached (); + } + + program = cogl_create_program (); + cogl_program_attach_shader (program, shader); + cogl_program_link (program); + + cogl_handle_unref (shader); + + /* Draw something using the material */ + cogl_set_source (material); + cogl_rectangle (0, 0, 50, 50); + + /* Draw it again using the program. It should look exactly the same */ + cogl_program_use (program); + cogl_rectangle (50, 0, 100, 50); + cogl_program_use (COGL_INVALID_HANDLE); + + cogl_handle_unref (material); + cogl_handle_unref (program); +} + +static void +paint (TestState *state) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglTexture *tex; + CoglColor color; + CoglError *error = NULL; + CoglHandle shader, program; + + cogl_color_init_from_4ub (&color, 0, 0, 0, 255); + cogl_clear (&color, COGL_BUFFER_BIT_COLOR); + + /* Set the primary vertex color as red */ + cogl_color_set_from_4ub (&color, 0xff, 0x00, 0x00, 0xff); + cogl_pipeline_set_color (pipeline, &color); + + /* Override the vertex color in the texture environment with a + constant green color provided by a texture */ + tex = create_dummy_texture (); + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_object_unref (tex); + if (!cogl_pipeline_set_layer_combine (pipeline, 0, + "RGBA=REPLACE(TEXTURE)", + &error)) + { + g_warning ("Error setting layer combine: %s", error->message); + g_assert_not_reached (); + } + + /* Set up a dummy vertex shader that does nothing but the usual + fixed function transform */ + shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX); + cogl_shader_source (shader, + "void\n" + "main ()\n" + "{\n" + " cogl_position_out = " + "cogl_modelview_projection_matrix * " + "cogl_position_in;\n" + " cogl_color_out = cogl_color_in;\n" + " cogl_tex_coord_out[0] = cogl_tex_coord_in;\n" + "}\n"); + cogl_shader_compile (shader); + if (!cogl_shader_is_compiled (shader)) + { + char *log = cogl_shader_get_info_log (shader); + g_warning ("Shader compilation failed:\n%s", log); + g_free (log); + g_assert_not_reached (); + } + + program = cogl_create_program (); + cogl_program_attach_shader (program, shader); + cogl_program_link (program); + + cogl_handle_unref (shader); + + /* Draw something without the program */ + cogl_set_source (pipeline); + cogl_rectangle (0, 0, 50, 50); + + /* Draw it again using the program. It should look exactly the same */ + cogl_pipeline_set_user_program (pipeline, program); + cogl_handle_unref (program); + + cogl_rectangle (50, 0, 100, 50); + cogl_pipeline_set_user_program (pipeline, COGL_INVALID_HANDLE); + + cogl_object_unref (pipeline); +} + +static void +validate_result (CoglFramebuffer *framebuffer) +{ + /* Non-shader version */ + test_utils_check_pixel (framebuffer, 25, 25, 0x00ff0000); + /* Shader version */ + test_utils_check_pixel (framebuffer, 75, 25, 0x00ff0000); +} + +void +test_just_vertex_shader (void) +{ + TestState state; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + /* XXX: we have to push/pop a framebuffer since this test currently + * uses the legacy cogl_rectangle() api. */ + cogl_push_framebuffer (test_fb); + + paint_legacy (&state); + validate_result (test_fb); + + paint (&state); + validate_result (test_fb); + + cogl_pop_framebuffer (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-layer-remove.c b/cogl/tests/conform/test-layer-remove.c new file mode 100644 index 000000000..de1efeccd --- /dev/null +++ b/cogl/tests/conform/test-layer-remove.c @@ -0,0 +1,145 @@ +#include + +#include "test-utils.h" + +#define TEST_SQUARE_SIZE 10 + +static CoglPipeline * +create_two_layer_pipeline (void) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglColor color; + + /* The pipeline is initially black */ + cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); + + /* The first layer adds a full red component */ + cogl_color_init_from_4ub (&color, 255, 0, 0, 255); + cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); + cogl_pipeline_set_layer_combine (pipeline, + 0, /* layer_num */ + "RGBA=ADD(PREVIOUS,CONSTANT)", + NULL); + + /* The second layer adds a full green component */ + cogl_color_init_from_4ub (&color, 0, 255, 0, 255); + cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); + cogl_pipeline_set_layer_combine (pipeline, + 1, /* layer_num */ + "RGBA=ADD(PREVIOUS,CONSTANT)", + NULL); + + return pipeline; +} + +static void +test_color (CoglPipeline *pipeline, + uint32_t color, + int pos) +{ + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + pos * TEST_SQUARE_SIZE, + 0, + pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE, + TEST_SQUARE_SIZE); + test_utils_check_pixel (test_fb, + pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE / 2, + TEST_SQUARE_SIZE / 2, + color); +} + +void +test_layer_remove (void) +{ + CoglPipeline *pipeline0, *pipeline1; + CoglColor color; + int pos = 0; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + /** TEST 1 **/ + /* Basic sanity check that the pipeline combines the two colors + * together properly */ + pipeline0 = create_two_layer_pipeline (); + test_color (pipeline0, 0xffff00ff, pos++); + cogl_object_unref (pipeline0); + + /** TEST 2 **/ + /* Check that we can remove the second layer */ + pipeline0 = create_two_layer_pipeline (); + cogl_pipeline_remove_layer (pipeline0, 1); + test_color (pipeline0, 0xff0000ff, pos++); + cogl_object_unref (pipeline0); + + /** TEST 3 **/ + /* Check that we can remove the first layer */ + pipeline0 = create_two_layer_pipeline (); + cogl_pipeline_remove_layer (pipeline0, 0); + test_color (pipeline0, 0x00ff00ff, pos++); + cogl_object_unref (pipeline0); + + /** TEST 4 **/ + /* Check that we can make a copy and remove a layer from the + * original pipeline */ + pipeline0 = create_two_layer_pipeline (); + pipeline1 = cogl_pipeline_copy (pipeline0); + cogl_pipeline_remove_layer (pipeline0, 1); + test_color (pipeline0, 0xff0000ff, pos++); + test_color (pipeline1, 0xffff00ff, pos++); + cogl_object_unref (pipeline0); + cogl_object_unref (pipeline1); + + /** TEST 5 **/ + /* Check that we can make a copy and remove the second layer from the + * new pipeline */ + pipeline0 = create_two_layer_pipeline (); + pipeline1 = cogl_pipeline_copy (pipeline0); + cogl_pipeline_remove_layer (pipeline1, 1); + test_color (pipeline0, 0xffff00ff, pos++); + test_color (pipeline1, 0xff0000ff, pos++); + cogl_object_unref (pipeline0); + cogl_object_unref (pipeline1); + + /** TEST 6 **/ + /* Check that we can make a copy and remove the first layer from the + * new pipeline */ + pipeline0 = create_two_layer_pipeline (); + pipeline1 = cogl_pipeline_copy (pipeline0); + cogl_pipeline_remove_layer (pipeline1, 0); + test_color (pipeline0, 0xffff00ff, pos++); + test_color (pipeline1, 0x00ff00ff, pos++); + cogl_object_unref (pipeline0); + cogl_object_unref (pipeline1); + + /** TEST 7 **/ + /* Check that we can modify a layer in a child pipeline */ + pipeline0 = create_two_layer_pipeline (); + pipeline1 = cogl_pipeline_copy (pipeline0); + cogl_color_init_from_4ub (&color, 0, 0, 255, 255); + cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color); + test_color (pipeline0, 0xffff00ff, pos++); + test_color (pipeline1, 0x00ffffff, pos++); + cogl_object_unref (pipeline0); + cogl_object_unref (pipeline1); + + /** TEST 8 **/ + /* Check that we can modify a layer in a child pipeline but then remove it */ + pipeline0 = create_two_layer_pipeline (); + pipeline1 = cogl_pipeline_copy (pipeline0); + cogl_color_init_from_4ub (&color, 0, 0, 255, 255); + cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color); + cogl_pipeline_remove_layer (pipeline1, 0); + test_color (pipeline0, 0xffff00ff, pos++); + test_color (pipeline1, 0x00ff00ff, pos++); + cogl_object_unref (pipeline0); + cogl_object_unref (pipeline1); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-map-buffer-range.c b/cogl/tests/conform/test-map-buffer-range.c new file mode 100644 index 000000000..e9792405f --- /dev/null +++ b/cogl/tests/conform/test-map-buffer-range.c @@ -0,0 +1,123 @@ +#include + +#include + +#include "test-utils.h" + +static uint8_t +tex_data[2 * 2 * 4] = + { + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff + }; + +/* Vertex data for a quad with all of the texture coordinates set to + * the top left (red) pixel */ +static CoglVertexP2T2 +vertex_data[4] = + { + { -1, -1, 0, 0 }, + { 1, -1, 0, 0 }, + { -1, 1, 0, 0 }, + { 1, 1, 0, 0 } + }; + +void +test_map_buffer_range (void) +{ + CoglTexture2D *tex; + CoglPipeline *pipeline; + int fb_width, fb_height; + CoglAttributeBuffer *buffer; + CoglVertexP2T2 *data; + CoglAttribute *pos_attribute; + CoglAttribute *tex_coord_attribute; + CoglPrimitive *primitive; + + tex = cogl_texture_2d_new_from_data (test_ctx, + 2, 2, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 2 * 4, /* rowstride */ + tex_data, + NULL /* error */); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_layer_wrap_mode (pipeline, + 0, /* layer */ + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); + + fb_width = cogl_framebuffer_get_width (test_fb); + fb_height = cogl_framebuffer_get_height (test_fb); + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (vertex_data), + vertex_data); + + /* Replace the texture coordinates of the third vertex with the + * coordinates for a green texel */ + data = cogl_buffer_map_range (buffer, + sizeof (vertex_data[0]) * 2, + sizeof (vertex_data[0]), + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD_RANGE, + NULL); /* don't catch errors */ + g_assert (data != NULL); + + data->x = vertex_data[2].x; + data->y = vertex_data[2].y; + data->s = 1.0f; + data->t = 0.0f; + + cogl_buffer_unmap (buffer); + + pos_attribute = + cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (vertex_data[0]), + offsetof (CoglVertexP2T2, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + tex_coord_attribute = + cogl_attribute_new (buffer, + "cogl_tex_coord_in", + sizeof (vertex_data[0]), + offsetof (CoglVertexP2T2, s), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 0, 0, 0, 1); + + primitive = + cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLE_STRIP, + 4, /* n_vertices */ + pos_attribute, + tex_coord_attribute, + NULL); + cogl_primitive_draw (primitive, test_fb, pipeline); + cogl_object_unref (primitive); + + /* Top left pixel should be the one that is replaced to be green */ + test_utils_check_pixel (test_fb, 1, 1, 0x00ff00ff); + /* The other three corners should be left as red */ + test_utils_check_pixel (test_fb, fb_width - 2, 1, 0xff0000ff); + test_utils_check_pixel (test_fb, 1, fb_height - 2, 0xff0000ff); + test_utils_check_pixel (test_fb, fb_width - 2, fb_height - 2, 0xff0000ff); + + cogl_object_unref (buffer); + cogl_object_unref (pos_attribute); + cogl_object_unref (tex_coord_attribute); + + cogl_object_unref (pipeline); + cogl_object_unref (tex); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-materials.c b/cogl/tests/conform/test-materials.c new file mode 100644 index 000000000..69c9c746f --- /dev/null +++ b/cogl/tests/conform/test-materials.c @@ -0,0 +1,253 @@ +#include "config.h" + +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) +#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) +#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) +#define MASK_ALPHA(COLOR) (COLOR & 0xff) + +typedef struct _TestState +{ + ClutterGeometry stage_geom; +} TestState; + +static void +check_quad (int quad_x, int quad_y, uint32_t color) +{ + test_utils_check_pixel (x * QUAD_WIDTH + (QUAD_WIDTH / 2), + y * QUAD_WIDTH + (QUAD_WIDTH / 2), + color); +} + +static void +test_material_with_primitives (TestState *state, + int x, int y, + uint32_t color) +{ + CoglTextureVertex verts[4] = { + { .x = 0, .y = 0, .z = 0 }, + { .x = 0, .y = QUAD_WIDTH, .z = 0 }, + { .x = QUAD_WIDTH, .y = QUAD_WIDTH, .z = 0 }, + { .x = QUAD_WIDTH, .y = 0, .z = 0 }, + }; + CoglHandle vbo; + + cogl_push_matrix (); + + cogl_translate (x * QUAD_WIDTH, y * QUAD_WIDTH, 0); + + cogl_rectangle (0, 0, QUAD_WIDTH, QUAD_WIDTH); + + cogl_translate (0, QUAD_WIDTH, 0); + cogl_polygon (verts, 4, FALSE); + + cogl_translate (0, QUAD_WIDTH, 0); + vbo = cogl_vertex_buffer_new (4); + cogl_vertex_buffer_add (vbo, + "gl_Vertex", + 2, /* n components */ + COGL_ATTRIBUTE_TYPE_FLOAT, + FALSE, /* normalized */ + sizeof (CoglTextureVertex), /* stride */ + verts); + cogl_vertex_buffer_draw (vbo, + COGL_VERTICES_MODE_TRIANGLE_FAN, + 0, /* first */ + 4); /* count */ + cogl_handle_unref (vbo); + + cogl_pop_matrix (); + + check_quad (x, y, color); + check_quad (x, y+1, color); + check_quad (x, y+2, color); +} + +static void +test_invalid_texture_layers (TestState *state, int x, int y) +{ + CoglHandle material = cogl_material_new (); + + /* explicitly create a layer with an invalid handle. This may be desireable + * if the user also sets a texture combine string that e.g. refers to a + * constant color. */ + cogl_material_set_layer (material, 0, NULL); + + cogl_set_source (material); + + cogl_handle_unref (material); + + /* We expect a white fallback material to be used */ + test_material_with_primitives (state, x, y, 0xffffffff); +} + +static void +test_using_all_layers (TestState *state, int x, int y) +{ + CoglHandle material = cogl_material_new (); + uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff }; + uint8_t red_pixel[] = { 0xff, 0x00, 0x00, 0xff }; + CoglHandle white_texture; + CoglHandle red_texture; + GLint n_layers; + int i; + + /* Create a material that uses the maximum number of layers. All but + the last layer will use a solid white texture. The last layer + will use a red texture. The layers will all be modulated together + so the final fragment should be red. */ + + white_texture = test_utils_texture_new_from_data (1, 1, TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + 4, white_pixel); + red_texture = test_utils_texture_new_from_data (1, 1, TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + 4, red_pixel); + + /* FIXME: Cogl doesn't provide a way to query the maximum number of + texture layers so for now we'll just ask GL directly. */ +#ifdef HAVE_COGL_GLES2 + { + GLint n_image_units, n_attribs; + /* GLES 2 doesn't have GL_MAX_TEXTURE_UNITS and it uses + GL_MAX_TEXTURE_IMAGE_UNITS instead */ + glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n_image_units); + /* Cogl needs a vertex attrib for each layer to upload the texture + coordinates */ + glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, &n_attribs); + /* We can't use two of the attribs because they are used by the + position and color */ + n_attribs -= 2; + n_layers = MIN (n_attribs, n_image_units); + } +#else + glGetIntegerv (GL_MAX_TEXTURE_UNITS, &n_layers); +#endif + /* FIXME: is this still true? */ + /* Cogl currently can't cope with more than 32 layers so we'll also + limit the maximum to that. */ + if (n_layers > 32) + n_layers = 32; + + for (i = 0; i < n_layers; i++) + { + cogl_material_set_layer_filters (material, i, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_material_set_layer (material, i, + i == n_layers - 1 ? red_texture : white_texture); + } + + cogl_set_source (material); + + cogl_handle_unref (material); + cogl_handle_unref (white_texture); + cogl_handle_unref (red_texture); + + /* We expect the final fragment to be red */ + test_material_with_primitives (state, x, y, 0xff0000ff); +} + +static void +test_invalid_texture_layers_with_constant_colors (TestState *state, + int x, int y) +{ + CoglHandle material = cogl_material_new (); + CoglColor constant_color; + + /* explicitly create a layer with an invalid handle */ + cogl_material_set_layer (material, 0, NULL); + + /* ignore the fallback texture on the layer and use a constant color + instead */ + cogl_color_init_from_4ub (&constant_color, 0, 0, 255, 255); + cogl_material_set_layer_combine (material, 0, + "RGBA=REPLACE(CONSTANT)", + NULL); + cogl_material_set_layer_combine_constant (material, 0, &constant_color); + + cogl_set_source (material); + + cogl_handle_unref (material); + + /* We expect the final fragments to be green */ + test_material_with_primitives (state, x, y, 0x0000ffff); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + test_invalid_texture_layers (state, + 0, 0 /* position */ + ); + test_invalid_texture_layers_with_constant_colors (state, + 1, 0 /* position */ + ); + test_using_all_layers (state, + 2, 0 /* position */ + ); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + clutter_main_quit (); +#endif +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_materials (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + unsigned int idle_source; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_get_geometry (stage, &state.stage_geom); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-multitexture.c b/cogl/tests/conform/test-multitexture.c new file mode 100644 index 000000000..da38766aa --- /dev/null +++ b/cogl/tests/conform/test-multitexture.c @@ -0,0 +1,206 @@ +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +typedef struct _TestState +{ + unsigned int padding; +} TestState; + +static void +assert_region_color (int x, + int y, + int width, + int height, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + uint8_t *data = g_malloc0 (width * height * 4); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + data); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + uint8_t *pixel = &data[y * width * 4 + x * 4]; +#if 1 + g_assert (pixel[RED] == red && + pixel[GREEN] == green && + pixel[BLUE] == blue); +#endif + } + g_free (data); +} + +/* Creates a texture divided into 4 quads with colors arranged as follows: + * (The same value are used in all channels for each texel) + * + * |-----------| + * |0x11 |0x00 | + * |+ref | | + * |-----------| + * |0x00 |0x33 | + * | |+ref | + * |-----------| + * + * + */ +static CoglHandle +make_texture (guchar ref) +{ + int x; + int y; + guchar *tex_data, *p; + CoglHandle tex; + guchar val; + + tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 16); + + for (y = 0; y < QUAD_WIDTH * 2; y++) + for (x = 0; x < QUAD_WIDTH * 2; x++) + { + p = tex_data + (QUAD_WIDTH * 8 * y) + x * 4; + if (x < QUAD_WIDTH && y < QUAD_WIDTH) + val = 0x11 + ref; + else if (x >= QUAD_WIDTH && y >= QUAD_WIDTH) + val = 0x33 + ref; + else + val = 0x00; + p[0] = p[1] = p[2] = p[3] = val; + } + + /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here + * since we don't want to allow Cogl to premultiply our data. */ + tex = test_utils_texture_new_from_data (QUAD_WIDTH * 2, + QUAD_WIDTH * 2, + TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_RGBA_8888, + QUAD_WIDTH * 8, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglHandle tex0, tex1; + CoglHandle material; + CoglBool status; + CoglError *error = NULL; + float tex_coords[] = { + 0, 0, 0.5, 0.5, /* tex0 */ + 0.5, 0.5, 1, 1 /* tex1 */ + }; + + tex0 = make_texture (0x00); + tex1 = make_texture (0x11); + + material = cogl_material_new (); + + /* An arbitrary color which should be replaced by the first texture layer */ + cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + + cogl_material_set_layer (material, 0, tex0); + cogl_material_set_layer_combine (material, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + /* We'll use nearest filtering mode on the textures, otherwise the + edge of the quad can pull in texels from the neighbouring + quarters of the texture due to imprecision */ + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + + cogl_material_set_layer (material, 1, tex1); + cogl_material_set_layer_filters (material, 1, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + status = cogl_material_set_layer_combine (material, 1, + "RGBA = ADD (PREVIOUS, TEXTURE)", + &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this texture combine string. */ + g_debug ("Failed to setup texture combine string " + "RGBA = ADD (PREVIOUS, TEXTURE): %s", + error->message); + } + + cogl_set_source (material); + cogl_rectangle_with_multitexture_coords (0, 0, QUAD_WIDTH, QUAD_WIDTH, + tex_coords, 8); + + cogl_handle_unref (material); + cogl_handle_unref (tex0); + cogl_handle_unref (tex1); + + /* See what we got... */ + + assert_region_color (0, 0, QUAD_WIDTH, QUAD_WIDTH, + 0x55, 0x55, 0x55, 0x55); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + clutter_main_quit (); +#endif +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_multitexture (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + unsigned int idle_source; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing incase someone comments out the + * clutter_main_quit and wants visual feedback for the test since we + * wont be doing anything else that will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-no-gl-header.c b/cogl/tests/conform/test-no-gl-header.c new file mode 100644 index 000000000..9618d840e --- /dev/null +++ b/cogl/tests/conform/test-no-gl-header.c @@ -0,0 +1,16 @@ +#undef COGL_COMPILATION +#include + +/* If you just include cogl/cogl.h, you shouldn't end up including any + GL headers */ +#ifdef GL_TRUE +#error "Including cogl.h shouldn't be including any GL headers" +#endif + +void test_no_gl_header (void); + +void +test_no_gl_header (void) +{ +} + diff --git a/cogl/tests/conform/test-npot-texture.c b/cogl/tests/conform/test-npot-texture.c new file mode 100644 index 000000000..85c16c960 --- /dev/null +++ b/cogl/tests/conform/test-npot-texture.c @@ -0,0 +1,170 @@ +#include + +#include + +#include "test-utils.h" + +/* Non-power-of-two sized texture that should cause slicing */ +#define TEXTURE_SIZE 384 +/* Number of times to split the texture up on each axis */ +#define PARTS 2 +/* The texture is split into four parts, each with a different colour */ +#define PART_SIZE (TEXTURE_SIZE / PARTS) + +/* Amount of pixels to skip off the top, bottom, left and right of the + texture when reading back the stage */ +#define TEST_INSET 4 + +/* Size to actually render the texture at */ +#define TEXTURE_RENDER_SIZE TEXTURE_SIZE +/* The size of a part once rendered */ +#define PART_RENDER_SIZE (TEXTURE_RENDER_SIZE / PARTS) + +static const uint32_t corner_colors[PARTS * PARTS] = + { + /* Top left - red */ 0xff0000ff, + /* Top right - green */ 0x00ff00ff, + /* Bottom left - blue */ 0x0000ffff, + /* Bottom right - yellow */ 0xffff00ff + }; + +static void +validate_part (int xnum, + int ynum, + uint32_t color) +{ + test_utils_check_region (test_fb, + xnum * PART_RENDER_SIZE + TEST_INSET, + ynum * PART_RENDER_SIZE + TEST_INSET, + PART_RENDER_SIZE - TEST_INSET * 2, + PART_RENDER_SIZE - TEST_INSET * 2, + color); +} + +static void +validate_result (void) +{ + /* Validate that all four corners of the texture are drawn in the + right color */ + validate_part (0, 0, corner_colors[0]); + validate_part (1, 0, corner_colors[1]); + validate_part (0, 1, corner_colors[2]); + validate_part (1, 1, corner_colors[3]); +} + +static CoglTexture * +make_texture (void) +{ + void *tex_data; + uint32_t *p; + CoglTexture *tex; + int partx, party, width, height; + + p = tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); + + /* Make a texture with a different color for each part */ + for (party = 0; party < PARTS; party++) + { + height = (party < PARTS - 1 + ? PART_SIZE + : TEXTURE_SIZE - PART_SIZE * (PARTS - 1)); + + for (partx = 0; partx < PARTS; partx++) + { + uint32_t color = corner_colors[party * PARTS + partx]; + width = (partx < PARTS - 1 + ? PART_SIZE + : TEXTURE_SIZE - PART_SIZE * (PARTS - 1)); + + while (width-- > 0) + *(p++) = GUINT32_TO_BE (color); + } + + while (--height > 0) + { + memcpy (p, p - TEXTURE_SIZE, TEXTURE_SIZE * 4); + p += TEXTURE_SIZE; + } + } + + tex = test_utils_texture_new_from_data (test_ctx, + TEXTURE_SIZE, + TEXTURE_SIZE, + TEST_UTILS_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + TEXTURE_SIZE * 4, + tex_data); + + g_free (tex_data); + + if (cogl_test_verbose ()) + { + if (cogl_texture_is_sliced (tex)) + g_print ("Texture is sliced\n"); + else + g_print ("Texture is not sliced\n"); + } + + /* The texture should be sliced unless NPOTs are supported */ + g_assert (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT) + ? !cogl_texture_is_sliced (tex) + : cogl_texture_is_sliced (tex)); + + return tex; +} + +static void +paint (void) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglTexture *texture = make_texture (); + int y, x; + + cogl_pipeline_set_layer_texture (pipeline, 0, texture); + + /* Just render the texture in the top left corner */ + /* Render the texture using four separate rectangles */ + for (y = 0; y < 2; y++) + for (x = 0; x < 2; x++) + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + x * TEXTURE_RENDER_SIZE / 2, + y * TEXTURE_RENDER_SIZE / 2, + (x + 1) * + TEXTURE_RENDER_SIZE / 2, + (y + 1) * + TEXTURE_RENDER_SIZE / 2, + x / 2.0f, + y / 2.0f, + (x + 1) / 2.0f, + (y + 1) / 2.0f); + + cogl_object_unref (pipeline); + cogl_object_unref (texture); +} + +void +test_npot_texture (void) +{ + if (cogl_test_verbose ()) + { + if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT)) + g_print ("NPOT textures are supported\n"); + else + g_print ("NPOT textures are not supported\n"); + } + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (); + validate_result (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-object.c b/cogl/tests/conform/test-object.c new file mode 100644 index 000000000..0a6dcab62 --- /dev/null +++ b/cogl/tests/conform/test-object.c @@ -0,0 +1,86 @@ + +#include +#include +#include + +#include "test-conform-common.h" + +CoglUserDataKey private_key0; +CoglUserDataKey private_key1; +CoglUserDataKey private_key2; + +static int user_data0; +static int user_data1; +static int user_data2; + +static int destroy0_count = 0; +static int destroy1_count = 0; +static int destroy2_count = 0; + +static void +destroy0_cb (void *user_data) +{ + g_assert (user_data == &user_data0); + destroy0_count++; +} + +static void +destroy1_cb (void *user_data) +{ + g_assert (user_data == &user_data1); + destroy1_count++; +} + +static void +destroy2_cb (void *user_data) +{ + g_assert (user_data == &user_data2); + destroy2_count++; +} + +void +test_object (TestUtilsGTestFixture *fixture, + void *data) +{ + CoglPath *path; + + /* Assuming that COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES == 2 + * test associating 2 pointers to private data with an object */ + cogl_path_new (); + path = cogl_get_path (); + + cogl_object_set_user_data (COGL_OBJECT (path), + &private_key0, + &user_data0, + destroy0_cb); + + cogl_object_set_user_data (COGL_OBJECT (path), + &private_key1, + &user_data1, + destroy1_cb); + + cogl_object_set_user_data (COGL_OBJECT (path), + &private_key2, + &user_data2, + destroy2_cb); + + cogl_object_set_user_data (COGL_OBJECT (path), + &private_key1, + NULL, + destroy1_cb); + + cogl_object_set_user_data (COGL_OBJECT (path), + &private_key1, + &user_data1, + destroy1_cb); + + cogl_object_unref (path); + + g_assert_cmpint (destroy0_count, ==, 1); + g_assert_cmpint (destroy1_count, ==, 2); + g_assert_cmpint (destroy2_count, ==, 1); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-offscreen.c b/cogl/tests/conform/test-offscreen.c new file mode 100644 index 000000000..9bc14b7da --- /dev/null +++ b/cogl/tests/conform/test-offscreen.c @@ -0,0 +1,199 @@ +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 + +#include + +#include "test-utils.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +typedef struct _TestState +{ + int fb_width; + int fb_height; +} TestState; + +static void +check_quadrant (TestState *state, + int qx, + int qy, + uint32_t expected_rgba) +{ + /* The quadrants are all stuffed into the top right corner of the + framebuffer */ + int x = state->fb_width * qx / 4 + state->fb_width / 2; + int y = state->fb_height * qy / 4; + int width = state->fb_width / 4; + int height = state->fb_height / 4; + + /* Subtract a two-pixel gap around the edges to allow some rounding + differences */ + x += 2; + y += 2; + width -= 4; + height -= 4; + + test_utils_check_region (test_fb, x, y, width, height, expected_rgba); +} + +static void +test_paint (TestState *state) +{ + CoglTexture2D *tex_2d; + CoglTexture *tex; + CoglOffscreen *offscreen; + + tex_2d = cogl_texture_2d_new_with_size (test_ctx, + state->fb_width, + state->fb_height); + tex = tex_2d; + + offscreen = cogl_offscreen_new_with_texture (tex); + + /* Set a scale and translate transform on the window framebuffer + * before switching to the offscreen framebuffer so we can verify it + * gets restored when we switch back + * + * The test is going to draw a grid of 4 colors to a texture which + * we subsequently draw to the window with a fullscreen rectangle. + * This transform will flip the texture left to right, scale it to a + * quarter of the window size and slide it to the top right of the + * window. + */ + cogl_push_matrix (); + cogl_translate (0.5, 0.5, 0); + cogl_scale (-0.5, 0.5, 1); + + cogl_push_framebuffer (offscreen); + + /* Cogl should release the last reference when we call cogl_pop_framebuffer() + */ + cogl_object_unref (offscreen); + + /* Setup something other than the identity matrix for the modelview so we can + * verify it gets restored when we call cogl_pop_framebuffer () */ + cogl_scale (2, 2, 1); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-0.5, 0.5, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 0.5, 0.5, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-0.5, 0, 0, -0.5); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 0.5, -0.5); + + cogl_pop_framebuffer (); + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + cogl_object_unref (tex_2d); + + cogl_pop_matrix (); + + /* NB: The texture is drawn flipped horizontally and scaled to fit in the + * top right corner of the window. */ + + /* red, top right */ + check_quadrant (state, 1, 0, 0xff0000ff); + /* green, top left */ + check_quadrant (state, 0, 0, 0x00ff00ff); + /* blue, bottom right */ + check_quadrant (state, 1, 1, 0x0000ffff); + /* white, bottom left */ + check_quadrant (state, 0, 1, 0xffffffff); +} + +static void +test_flush (TestState *state) +{ + CoglTexture2D *tex_2d; + CoglTexture *tex; + CoglOffscreen *offscreen; + CoglColor clear_color; + int i; + + for (i = 0; i < 3; i++) + { + /* This tests that rendering to a framebuffer and then reading back + the contents of the texture will automatically flush the + journal */ + + tex_2d = cogl_texture_2d_new_with_size (test_ctx, + 16, 16); /* width/height */ + tex = tex_2d; + + offscreen = cogl_offscreen_new_with_texture (tex); + + cogl_push_framebuffer (offscreen); + + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 255); + cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); + + cogl_set_source_color4ub (255, 0, 0, 255); + cogl_rectangle (-1, -1, 1, 1); + + if (i == 0) + /* First time check using read pixels on the offscreen */ + test_utils_check_region (offscreen, + 1, 1, 15, 15, 0xff0000ff); + else if (i == 1) + { + uint8_t data[16 * 4 * 16]; + int x, y; + + /* Second time try reading back the texture contents */ + cogl_texture_get_data (tex, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 16 * 4, /* rowstride */ + data); + + for (y = 1; y < 15; y++) + for (x = 1; x < 15; x++) + test_utils_compare_pixel (data + x * 4 + y * 16 * 4, + 0xff0000ff); + } + + cogl_pop_framebuffer (); + + if (i == 2) + { + /* Third time try drawing the texture to the screen */ + cogl_set_source_texture (tex); + cogl_rectangle (-1, -1, 1, 1); + test_utils_check_region (test_fb, + 2, 2, /* x/y */ + state->fb_width - 4, + state->fb_height - 4, + 0xff0000ff); + } + + cogl_object_unref (tex_2d); + cogl_object_unref (offscreen); + } +} + +void +test_offscreen (void) +{ + TestState state; + + state.fb_width = cogl_framebuffer_get_width (test_fb); + state.fb_height = cogl_framebuffer_get_height (test_fb); + + /* XXX: we have to push/pop a framebuffer since this test currently + * uses the legacy cogl_rectangle() api. */ + cogl_push_framebuffer (test_fb); + test_paint (&state); + test_flush (&state); + cogl_pop_framebuffer (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-path-clip.c b/cogl/tests/conform/test-path-clip.c new file mode 100644 index 000000000..95ad00b96 --- /dev/null +++ b/cogl/tests/conform/test-path-clip.c @@ -0,0 +1,68 @@ +#define COGL_ENABLE_EXPERIMENTAL_2_0_API +#include +#include + +#include + +#include "test-utils.h" + +void +test_path_clip (void) +{ + CoglPath *path; + CoglPipeline *pipeline; + int fb_width, fb_height; + + fb_width = cogl_framebuffer_get_width (test_fb); + fb_height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, fb_width, fb_height, -1, 100); + + path = cogl_path_new (); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 0.0f, 0.0f, 1.0f); + + /* Make an L-shape with the top right corner left untouched */ + cogl_path_move_to (path, 0, fb_height); + cogl_path_line_to (path, fb_width, fb_height); + cogl_path_line_to (path, fb_width, fb_height / 2); + cogl_path_line_to (path, fb_width / 2, fb_height / 2); + cogl_path_line_to (path, fb_width / 2, 0); + cogl_path_line_to (path, 0, 0); + cogl_path_close (path); + + cogl_framebuffer_push_path_clip (test_fb, path); + + /* Try to fill the framebuffer with a blue rectangle. This should be + * clipped to leave the top right quadrant as is */ + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, 0, 0, 255, 255); + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 0, 0, fb_width, fb_height); + + cogl_framebuffer_pop_clip (test_fb); + + cogl_object_unref (pipeline); + cogl_object_unref (path); + + /* Check each of the four quadrants */ + test_utils_check_pixel (test_fb, + fb_width / 4, fb_height / 4, + 0x0000ffff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, fb_height / 4, + 0xff0000ff); + test_utils_check_pixel (test_fb, + fb_width / 4, fb_height * 3 / 4, + 0x0000ffff); + test_utils_check_pixel (test_fb, + fb_width * 3 / 4, fb_height * 3 / 4, + 0x0000ffff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-path.c b/cogl/tests/conform/test-path.c new file mode 100644 index 000000000..11f7f1583 --- /dev/null +++ b/cogl/tests/conform/test-path.c @@ -0,0 +1,215 @@ +#define COGL_ENABLE_EXPERIMENTAL_2_0_API +#include +#include + +#include + +#include "test-utils.h" + +#define BLOCK_SIZE 16 + +/* Number of pixels at the border of a block quadrant to skip when verifying */ +#define TEST_INSET 1 + +typedef struct _TestState +{ + int dummy; +} TestState; + +static void +draw_path_at (CoglPath *path, CoglPipeline *pipeline, int x, int y) +{ + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f); + + cogl_set_framebuffer (test_fb); + cogl_set_source (pipeline); + cogl_path_fill (path); + + cogl_framebuffer_pop_matrix (test_fb); +} + +static void +check_block (int block_x, int block_y, int block_mask) +{ + uint32_t data[BLOCK_SIZE * BLOCK_SIZE]; + int qx, qy; + + /* Block mask represents which quarters of the block should be + filled. The bits from 0->3 represent the top left, top right, + bottom left and bottom right respectively */ + + cogl_framebuffer_read_pixels (test_fb, + block_x * BLOCK_SIZE, + block_y * BLOCK_SIZE, + BLOCK_SIZE, BLOCK_SIZE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + (uint8_t *)data); + + for (qy = 0; qy < 2; qy++) + for (qx = 0; qx < 2; qx++) + { + int bit = qx | (qy << 1); + const char *intended_pixel = ((block_mask & (1 << bit)) ? "#ffffff" : "#000000"); + int x, y; + + for (x = 0; x < BLOCK_SIZE / 2 - TEST_INSET * 2; x++) + for (y = 0; y < BLOCK_SIZE / 2 - TEST_INSET * 2; y++) + { + const uint32_t *p = data + (qx * BLOCK_SIZE / 2 + + qy * BLOCK_SIZE * BLOCK_SIZE / 2 + + (x + TEST_INSET) + + (y + TEST_INSET) * BLOCK_SIZE); + char *screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8); + g_assert_cmpstr (screen_pixel, ==, intended_pixel); + g_free (screen_pixel); + } + } +} + +static void +paint (TestState *state) +{ + CoglPath *path_a, *path_b, *path_c; + CoglPipeline *white = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4f (white, 1, 1, 1, 1); + + /* Create a path filling just a quarter of a block. It will use two + rectangles so that we have a sub path in the path */ + path_a = cogl_path_new (); + cogl_path_rectangle (path_a, + BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2, + BLOCK_SIZE, BLOCK_SIZE); + cogl_path_rectangle (path_a, + BLOCK_SIZE / 2, BLOCK_SIZE / 2, + BLOCK_SIZE * 3 / 4, BLOCK_SIZE); + draw_path_at (path_a, white, 0, 0); + + /* Create another path filling the whole block */ + path_b = cogl_path_new (); + cogl_path_rectangle (path_b, 0, 0, BLOCK_SIZE, BLOCK_SIZE); + draw_path_at (path_b, white, 1, 0); + + /* Draw the first path again */ + draw_path_at (path_a, white, 2, 0); + + /* Draw a copy of path a */ + path_c = cogl_path_copy (path_a); + draw_path_at (path_c, white, 3, 0); + + /* Add another rectangle to path a. We'll use line_to's instead of + cogl_rectangle so that we don't create another sub-path because + that is more likely to break the copy */ + cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, 0, 0); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); + draw_path_at (path_a, white, 4, 0); + + /* Draw the copy again. It should not have changed */ + draw_path_at (path_c, white, 5, 0); + + /* Add another rectangle to path c. It will be added in two halves, + one as an extension of the previous path and the other as a new + sub path */ + cogl_path_line_to (path_c, BLOCK_SIZE / 2, 0); + cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, 0); + cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2); + cogl_path_line_to (path_c, BLOCK_SIZE / 2, BLOCK_SIZE / 2); + cogl_path_rectangle (path_c, + BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2); + draw_path_at (path_c, white, 6, 0); + + /* Draw the original path again. It should not have changed */ + draw_path_at (path_a, white, 7, 0); + + cogl_object_unref (path_a); + cogl_object_unref (path_b); + cogl_object_unref (path_c); + + /* Draw a self-intersecting path. The part that intersects should be + inverted */ + path_a = cogl_path_new (); + cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE); + cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); + cogl_path_close (path_a); + draw_path_at (path_a, white, 8, 0); + cogl_object_unref (path_a); + + /* Draw two sub paths. Where the paths intersect it should be + inverted */ + path_a = cogl_path_new (); + cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE); + cogl_path_rectangle (path_a, + BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE); + draw_path_at (path_a, white, 9, 0); + cogl_object_unref (path_a); + + /* Draw a clockwise outer path */ + path_a = cogl_path_new (); + cogl_path_move_to (path_a, 0, 0); + cogl_path_line_to (path_a, BLOCK_SIZE, 0); + cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE); + cogl_path_line_to (path_a, 0, BLOCK_SIZE); + cogl_path_close (path_a); + /* Add a clockwise sub path in the upper left quadrant */ + cogl_path_move_to (path_a, 0, 0); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); + cogl_path_close (path_a); + /* Add a counter-clockwise sub path in the upper right quadrant */ + cogl_path_move_to (path_a, BLOCK_SIZE / 2, 0); + cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE / 2); + cogl_path_line_to (path_a, BLOCK_SIZE, 0); + cogl_path_close (path_a); + /* Retain the path for the next test */ + draw_path_at (path_a, white, 10, 0); + + /* Draw the same path again with the other fill rule */ + cogl_path_set_fill_rule (path_a, COGL_PATH_FILL_RULE_NON_ZERO); + draw_path_at (path_a, white, 11, 0); + + cogl_object_unref (path_a); +} + +static void +validate_result () +{ + check_block (0, 0, 0x8 /* bottom right */); + check_block (1, 0, 0xf /* all of them */); + check_block (2, 0, 0x8 /* bottom right */); + check_block (3, 0, 0x8 /* bottom right */); + check_block (4, 0, 0x9 /* top left and bottom right */); + check_block (5, 0, 0x8 /* bottom right */); + check_block (6, 0, 0xa /* bottom right and top right */); + check_block (7, 0, 0x9 /* top_left and bottom right */); + check_block (8, 0, 0xe /* all but top left */); + check_block (9, 0, 0x7 /* all but bottom right */); + check_block (10, 0, 0xc /* bottom two */); + check_block (11, 0, 0xd /* all but top right */); +} + +void +test_path (void) +{ + TestState state; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (&state); + validate_result (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c b/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c new file mode 100644 index 000000000..5d278dcd0 --- /dev/null +++ b/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c @@ -0,0 +1,91 @@ +#include + +#include "test-utils.h" + +/* Keep track of the number of textures that we've created and are + * still alive */ +static int destroyed_texture_count = 0; + +#define N_TEXTURES 3 + +static void +free_texture_cb (void *user_data) +{ + destroyed_texture_count++; +} + +static CoglTexture * +create_texture (void) +{ + static const guint8 data[] = + { 0xff, 0xff, 0xff, 0xff }; + static CoglUserDataKey texture_data_key; + CoglTexture2D *tex_2d; + + tex_2d = cogl_texture_2d_new_from_data (test_ctx, + 1, 1, /* width / height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + data, + NULL); + + /* Set some user data on the texture so we can track when it has + * been destroyed */ + cogl_object_set_user_data (COGL_OBJECT (tex_2d), + &texture_data_key, + GINT_TO_POINTER (1), + free_texture_cb); + + return tex_2d; +} + +void +test_pipeline_cache_unrefs_texture (void) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglPipeline *simple_pipeline; + int i; + + /* Create a pipeline with three texture layers. That way we can be + * pretty sure the pipeline will cause a unique shader to be + * generated in the cache */ + for (i = 0; i < N_TEXTURES; i++) + { + CoglTexture *tex = create_texture (); + cogl_pipeline_set_layer_texture (pipeline, i, tex); + cogl_object_unref (tex); + } + + /* Draw something with the pipeline to ensure it gets into the + * pipeline cache */ + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 0, 0, 10, 10); + cogl_framebuffer_finish (test_fb); + + /* Draw something else so that it is no longer the current flushed + * pipeline, and the units have a different texture bound */ + simple_pipeline = cogl_pipeline_new (test_ctx); + for (i = 0; i < N_TEXTURES; i++) + { + CoglColor combine_constant; + cogl_color_init_from_4ub (&combine_constant, i, 0, 0, 255); + cogl_pipeline_set_layer_combine_constant (simple_pipeline, + i, + &combine_constant); + } + cogl_framebuffer_draw_rectangle (test_fb, simple_pipeline, 0, 0, 10, 10); + cogl_framebuffer_finish (test_fb); + cogl_object_unref (simple_pipeline); + + g_assert_cmpint (destroyed_texture_count, ==, 0); + + /* Destroy the pipeline. This should immediately cause the textures + * to be freed */ + cogl_object_unref (pipeline); + + g_assert_cmpint (destroyed_texture_count, ==, N_TEXTURES); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-pipeline-shader-state.c b/cogl/tests/conform/test-pipeline-shader-state.c new file mode 100644 index 000000000..4d1e5f2b7 --- /dev/null +++ b/cogl/tests/conform/test-pipeline-shader-state.c @@ -0,0 +1,93 @@ +#include + +#include + +#include "test-utils.h" + +void +test_pipeline_shader_state (void) +{ + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglPipeline *base_pipeline; + CoglPipeline *draw_pipeline; + CoglTexture2D *tex; + CoglSnippet *snippet; + + float width = cogl_framebuffer_get_width (test_fb); + float height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, width, height, + -1, + 100); + + tex = cogl_texture_2d_new_with_size (test_ctx, 128, 128); + offscreen = cogl_offscreen_new_with_texture (tex); + fb = offscreen; + cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + cogl_object_unref (offscreen); + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 0, 1); + + + /* Setup a template pipeline... */ + + base_pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (base_pipeline, 1, tex); + cogl_pipeline_set_color4f (base_pipeline, 1, 0, 0, 1); + + + /* Derive a pipeline from the template, making a change that affects + * fragment processing but making sure not to affect vertex + * processing... */ + + draw_pipeline = cogl_pipeline_copy (base_pipeline); + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + "cogl_color_out = vec4 (0.0, 1.0, 0.1, 1.1);"); + cogl_pipeline_add_snippet (draw_pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline, + 0, 0, width, height); + + cogl_object_unref (draw_pipeline); + + cogl_framebuffer_finish (test_fb); + + + /* At this point we should have provoked cogl to cache some vertex + * shader state for the draw_pipeline with the base_pipeline because + * none of the changes made to the draw_pipeline affected vertex + * processing. (NB: cogl will cache shader state with the oldest + * ancestor that the state is still valid for to maximize the chance + * that it can be used with other derived pipelines) + * + * Now we make a change to the base_pipeline to make sure that this + * cached vertex shader gets invalidated. + */ + + cogl_pipeline_set_layer_texture (base_pipeline, 0, tex); + + + /* Now we derive another pipeline from base_pipeline to verify that + * it doesn't end up re-using the old cached state + */ + + draw_pipeline = cogl_pipeline_copy (base_pipeline); + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + "cogl_color_out = vec4 (0.0, 0.0, 1.1, 1.1);"); + cogl_pipeline_add_snippet (draw_pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline, + 0, 0, width, height); + + cogl_object_unref (draw_pipeline); + + + test_utils_check_region (test_fb, 0, 0, width, height, + 0x0000ffff); +} diff --git a/cogl/tests/conform/test-pipeline-uniforms.c b/cogl/tests/conform/test-pipeline-uniforms.c new file mode 100644 index 000000000..4d27558d2 --- /dev/null +++ b/cogl/tests/conform/test-pipeline-uniforms.c @@ -0,0 +1,415 @@ +#include + +#include + +#include "test-utils.h" + +#define LONG_ARRAY_SIZE 128 + +typedef struct _TestState +{ + CoglPipeline *pipeline_red; + CoglPipeline *pipeline_green; + CoglPipeline *pipeline_blue; + + CoglPipeline *matrix_pipeline; + CoglPipeline *vector_pipeline; + CoglPipeline *int_pipeline; + + CoglPipeline *long_pipeline; + int long_uniform_locations[LONG_ARRAY_SIZE]; +} TestState; + +static const char +color_source[] = + "uniform float red, green, blue;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " cogl_color_out = vec4 (red, green, blue, 1.0);\n" + "}\n"; + +static const char +matrix_source[] = + "uniform mat4 matrix_array[4];\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " vec4 color = vec4 (0.0, 0.0, 0.0, 1.0);\n" + " int i;\n" + "\n" + " for (i = 0; i < 4; i++)\n" + " color = matrix_array[i] * color;\n" + "\n" + " cogl_color_out = color;\n" + "}\n"; + +static const char +vector_source[] = + "uniform vec4 vector_array[2];\n" + "uniform vec3 short_vector;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " cogl_color_out = (vector_array[0] +\n" + " vector_array[1] +\n" + " vec4 (short_vector, 1.0));\n" + "}\n"; + +static const char +int_source[] = + "uniform ivec4 vector_array[2];\n" + "uniform int single_value;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " cogl_color_out = (vec4 (vector_array[0]) +\n" + " vec4 (vector_array[1]) +\n" + " vec4 (float (single_value), 0.0, 0.0, 255.0)) / 255.0;\n" + "}\n"; + +static const char +long_source[] = + "uniform int long_array[" G_STRINGIFY (LONG_ARRAY_SIZE) "];\n" + "const int last_index = " G_STRINGIFY (LONG_ARRAY_SIZE) " - 1;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " cogl_color_out = vec4 (float (long_array[last_index]), 0.0, 0.0, 1.0);\n" + "}\n"; + +static CoglPipeline * +create_pipeline_for_shader (TestState *state, const char *shader_source) +{ + CoglPipeline *pipeline; + CoglHandle shader; + CoglHandle program; + + pipeline = cogl_pipeline_new (test_ctx); + + shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); + cogl_shader_source (shader, shader_source); + + program = cogl_create_program (); + cogl_program_attach_shader (program, shader); + + cogl_pipeline_set_user_program (pipeline, program); + + cogl_handle_unref (shader); + cogl_handle_unref (program); + + return pipeline; +} + +static void +init_state (TestState *state) +{ + int uniform_location; + + state->pipeline_red = create_pipeline_for_shader (state, color_source); + + uniform_location = + cogl_pipeline_get_uniform_location (state->pipeline_red, "red"); + cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 1.0f); + uniform_location = + cogl_pipeline_get_uniform_location (state->pipeline_red, "green"); + cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f); + uniform_location = + cogl_pipeline_get_uniform_location (state->pipeline_red, "blue"); + cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f); + + state->pipeline_green = cogl_pipeline_copy (state->pipeline_red); + uniform_location = + cogl_pipeline_get_uniform_location (state->pipeline_green, "green"); + cogl_pipeline_set_uniform_1f (state->pipeline_green, uniform_location, 1.0f); + + state->pipeline_blue = cogl_pipeline_copy (state->pipeline_red); + uniform_location = + cogl_pipeline_get_uniform_location (state->pipeline_blue, "blue"); + cogl_pipeline_set_uniform_1f (state->pipeline_blue, uniform_location, 1.0f); + + state->matrix_pipeline = create_pipeline_for_shader (state, matrix_source); + state->vector_pipeline = create_pipeline_for_shader (state, vector_source); + state->int_pipeline = create_pipeline_for_shader (state, int_source); + + state->long_pipeline = NULL; +} + +static void +init_long_pipeline_state (TestState *state) +{ + int i; + + state->long_pipeline = create_pipeline_for_shader (state, long_source); + + /* This tries to lookup a large number of uniform names to make sure + that the bitmask of overriden uniforms flows over the size of a + single long so that it has to resort to allocating it */ + for (i = 0; i < LONG_ARRAY_SIZE; i++) + { + char *uniform_name = g_strdup_printf ("long_array[%i]", i); + state->long_uniform_locations[i] = + cogl_pipeline_get_uniform_location (state->long_pipeline, + uniform_name); + g_free (uniform_name); + } +} + +static void +destroy_state (TestState *state) +{ + cogl_object_unref (state->pipeline_red); + cogl_object_unref (state->pipeline_green); + cogl_object_unref (state->pipeline_blue); + cogl_object_unref (state->matrix_pipeline); + cogl_object_unref (state->vector_pipeline); + cogl_object_unref (state->int_pipeline); + + if (state->long_pipeline) + cogl_object_unref (state->long_pipeline); +} + +static void +paint_pipeline (CoglPipeline *pipeline, int pos) +{ + cogl_framebuffer_draw_rectangle (test_fb, pipeline, + pos * 10, 0, pos * 10 + 10, 10); +} + +static void +paint_color_pipelines (TestState *state) +{ + CoglPipeline *temp_pipeline; + int uniform_location; + int i; + + /* Paint with the first pipeline that sets the uniforms to bright + red */ + paint_pipeline (state->pipeline_red, 0); + + /* Paint with the two other pipelines. These inherit from the red + pipeline and only override one other component. The values for + the two other components should be inherited from the red + pipeline. */ + paint_pipeline (state->pipeline_green, 1); + paint_pipeline (state->pipeline_blue, 2); + + /* Try modifying a single pipeline for multiple rectangles */ + temp_pipeline = cogl_pipeline_copy (state->pipeline_green); + uniform_location = cogl_pipeline_get_uniform_location (temp_pipeline, + "green"); + + for (i = 0; i <= 8; i++) + { + cogl_pipeline_set_uniform_1f (temp_pipeline, uniform_location, + i / 8.0f); + paint_pipeline (temp_pipeline, i + 3); + } + + cogl_object_unref (temp_pipeline); +} + +static void +paint_matrix_pipeline (CoglPipeline *pipeline) +{ + CoglMatrix matrices[4]; + float matrix_floats[16 * 4]; + int uniform_location; + int i; + + for (i = 0; i < 4; i++) + cogl_matrix_init_identity (matrices + i); + + /* Use the first matrix to make the color red */ + cogl_matrix_translate (matrices + 0, 1.0f, 0.0f, 0.0f); + + /* Rotate the vertex so that it ends up green */ + cogl_matrix_rotate (matrices + 1, 90.0f, 0.0f, 0.0f, 1.0f); + + /* Scale the vertex so it ends up halved */ + cogl_matrix_scale (matrices + 2, 0.5f, 0.5f, 0.5f); + + /* Add a blue component in the final matrix. The final matrix is + uploaded as transposed so we need to transpose first to cancel + that out */ + cogl_matrix_translate (matrices + 3, 0.0f, 0.0f, 1.0f); + cogl_matrix_transpose (matrices + 3); + + for (i = 0; i < 4; i++) + memcpy (matrix_floats + i * 16, + cogl_matrix_get_array (matrices + i), + sizeof (float) * 16); + + /* Set the first three matrices as transposed */ + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "matrix_array"); + cogl_pipeline_set_uniform_matrix (pipeline, + uniform_location, + 4, /* dimensions */ + 3, /* count */ + FALSE, /* not transposed */ + matrix_floats); + + /* Set the last matrix as untransposed */ + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "matrix_array[3]"); + cogl_pipeline_set_uniform_matrix (pipeline, + uniform_location, + 4, /* dimensions */ + 1, /* count */ + TRUE, /* transposed */ + matrix_floats + 16 * 3); + + paint_pipeline (pipeline, 12); +} + +static void +paint_vector_pipeline (CoglPipeline *pipeline) +{ + float vector_array_values[] = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f }; + float short_vector_values[] = { 0.0f, 0.0f, 1.0f }; + int uniform_location; + + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "vector_array"); + cogl_pipeline_set_uniform_float (pipeline, + uniform_location, + 4, /* n_components */ + 2, /* count */ + vector_array_values); + + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "short_vector"); + cogl_pipeline_set_uniform_float (pipeline, + uniform_location, + 3, /* n_components */ + 1, /* count */ + short_vector_values); + + paint_pipeline (pipeline, 13); +} + +static void +paint_int_pipeline (CoglPipeline *pipeline) +{ + int vector_array_values[] = { 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00 }; + int single_value = 0x80; + int uniform_location; + + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "vector_array"); + cogl_pipeline_set_uniform_int (pipeline, + uniform_location, + 4, /* n_components */ + 2, /* count */ + vector_array_values); + + uniform_location = + cogl_pipeline_get_uniform_location (pipeline, "single_value"); + cogl_pipeline_set_uniform_1i (pipeline, + uniform_location, + single_value); + + paint_pipeline (pipeline, 14); +} + +static void +paint_long_pipeline (TestState *state) +{ + int i; + + for (i = 0; i < LONG_ARRAY_SIZE; i++) + { + int location = state->long_uniform_locations[i]; + + cogl_pipeline_set_uniform_1i (state->long_pipeline, + location, + i == LONG_ARRAY_SIZE - 1); + } + + paint_pipeline (state->long_pipeline, 15); +} + +static void +paint (TestState *state) +{ + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + paint_color_pipelines (state); + paint_matrix_pipeline (state->matrix_pipeline); + paint_vector_pipeline (state->vector_pipeline); + paint_int_pipeline (state->int_pipeline); +} + +static void +check_pos (int pos, uint32_t color) +{ + test_utils_check_pixel (test_fb, pos * 10 + 5, 5, color); +} + +static void +validate_result (void) +{ + int i; + + check_pos (0, 0xff0000ff); + check_pos (1, 0xffff00ff); + check_pos (2, 0xff00ffff); + + for (i = 0; i <= 8; i++) + { + int green_value = i / 8.0f * 255.0f + 0.5f; + check_pos (i + 3, 0xff0000ff + (green_value << 16)); + } + + check_pos (12, 0x0080ffff); + check_pos (13, 0xffffffff); + check_pos (14, 0x80ffffff); +} + +static void +validate_long_pipeline_result (void) +{ + check_pos (15, 0xff0000ff); +} + +void +test_pipeline_uniforms (void) +{ + TestState state; + + init_state (&state); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (&state); + validate_result (); + + /* Try the test again after querying the location of a large + number of uniforms. This should verify that the bitmasks + still work even if they have to allocate a separate array to + store the bits */ + + init_long_pipeline_state (&state); + paint (&state); + paint_long_pipeline (&state); + validate_result (); + validate_long_pipeline_result (); + + destroy_state (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-pipeline-user-matrix.c b/cogl/tests/conform/test-pipeline-user-matrix.c new file mode 100644 index 000000000..f7cdee8c8 --- /dev/null +++ b/cogl/tests/conform/test-pipeline-user-matrix.c @@ -0,0 +1,140 @@ +#include + +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + int width; + int height; +} TestState; + +static void +validate_result (TestState *state) +{ + uint32_t *pixels, *p; + char *screen_pixel; + const char *intended_pixel = "#ffffff"; + + /* The textures are setup so that when added together with the + correct matrices then all of the pixels should be white. We can + verify this by reading back the entire stage */ + pixels = g_malloc (state->width * state->height * 4); + + cogl_framebuffer_read_pixels (test_fb, 0, 0, state->width, state->height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + (uint8_t *)pixels); + + for (p = pixels; p < pixels + state->width * state->height; p++) + { + screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8); + g_assert_cmpstr (screen_pixel, ==, intended_pixel); + g_free (screen_pixel); + } +} + +static void +paint (TestState *state) +{ + /* This texture is painted mirrored around the x-axis */ + uint8_t data0[] = { + 0xff, 0x00, 0x00, /* red -> becomes bottom left */ + 0x00, 0xff, 0x00, /* green -> becomes bottom right */ + 0x00, 0x00, 0xff, /* blue -> becomes top left */ + 0xff, 0x00, 0xff /* magenta -> becomes top right */ + }; + /* This texture is painted mirrored about the y-axis */ + uint8_t data1[] = { + 0x00, 0xff, 0x00, /* green -> becomes top right */ + 0xff, 0xff, 0x00, /* yellow -> becomes top left */ + 0xff, 0x00, 0xff, /* magenta -> becomes bottom right */ + 0x00, 0xff, 0xff /* cyan -> becomes bottom left */ + }; + CoglTexture *tex0, *tex1; + CoglPipeline *pipeline; + CoglMatrix matrix; + CoglError *error = NULL; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + state->width, + state->height, + -1, + 100); + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + cogl_matrix_init_identity (&matrix); + cogl_framebuffer_set_modelview_matrix (test_fb, &matrix); + + tex0 = cogl_texture_new_from_data (2, 2, + COGL_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGB_888, + COGL_PIXEL_FORMAT_ANY, + 6, + data0); + tex1 = cogl_texture_new_from_data (2, 2, + COGL_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGB_888, + COGL_PIXEL_FORMAT_ANY, + 6, + data1); + + pipeline = cogl_pipeline_new (test_ctx); + + /* Set the two textures as layers */ + cogl_pipeline_set_layer_texture (pipeline, 0, tex0); + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_layer_texture (pipeline, 1, tex1); + cogl_pipeline_set_layer_filters (pipeline, 1, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + /* Set a combine mode so that the two textures get added together */ + if (!cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA=ADD(PREVIOUS, TEXTURE)", + &error)) + { + g_warning ("Error setting blend string: %s", error->message); + g_assert_not_reached (); + } + + /* Set a matrix on the first layer so that it will mirror about the y-axis */ + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); + cogl_matrix_scale (&matrix, 1.0f, -1.0f, 1.0f); + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + + /* Set a matrix on the second layer so that it will mirror about the x-axis */ + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, 1.0f, 0.0f, 0.0f); + cogl_matrix_scale (&matrix, -1.0f, 1.0f, 1.0f); + cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 0, 0, + state->width, state->height); + + cogl_object_unref (tex1); + cogl_object_unref (tex0); + cogl_object_unref (pipeline); +} + +void +test_pipeline_user_matrix (void) +{ + TestState state; + + state.width = cogl_framebuffer_get_width (test_fb); + state.height = cogl_framebuffer_get_height (test_fb); + + paint (&state); + validate_result (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-pixel-buffer.c b/cogl/tests/conform/test-pixel-buffer.c new file mode 100644 index 000000000..a78516d06 --- /dev/null +++ b/cogl/tests/conform/test-pixel-buffer.c @@ -0,0 +1,269 @@ +#include +#include + +#include "test-utils.h" + +#define BITMAP_SIZE 256 + +/* + * Creates a 256 x 256 with image data split into four quadrants. The + * colours of these in reading order will be: blue, green, cyan, + * red */ +static void +generate_bitmap_data (uint8_t *data, + int stride) +{ + int y, x; + + for (y = 0; y < BITMAP_SIZE; y++) + { + for (x = 0; x < BITMAP_SIZE; x++) + { + int color_num = x / (BITMAP_SIZE / 2) + y / (BITMAP_SIZE / 2) * 2 + 1; + *(data++) = (color_num & 4) ? 255 : 0; + *(data++) = (color_num & 2) ? 255 : 0; + *(data++) = (color_num & 1) ? 255 : 0; + *(data++) = 255; + } + data += stride - BITMAP_SIZE * 4; + } +} + +static CoglBitmap * +create_bitmap (void) +{ + CoglBitmap *bitmap; + CoglBuffer *buffer; + + bitmap = cogl_bitmap_new_with_size (test_ctx, + BITMAP_SIZE, + BITMAP_SIZE, + COGL_PIXEL_FORMAT_RGBA_8888); + buffer = cogl_bitmap_get_buffer (bitmap); + + g_assert (cogl_is_pixel_buffer (buffer)); + g_assert (cogl_is_buffer (buffer)); + + cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); + g_assert_cmpint (cogl_buffer_get_update_hint (buffer), + ==, + COGL_BUFFER_UPDATE_HINT_DYNAMIC); + + return bitmap; +} + +static CoglBitmap * +create_and_fill_bitmap (void) +{ + CoglBitmap *bitmap = create_bitmap (); + CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap); + uint8_t *map; + unsigned int stride; + + stride = cogl_bitmap_get_rowstride (bitmap); + + map = cogl_buffer_map (buffer, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + g_assert (map); + + generate_bitmap_data (map, stride); + + cogl_buffer_unmap (buffer); + + return bitmap; +} + +static CoglTexture * +create_texture_from_bitmap (CoglBitmap *bitmap) +{ + CoglTexture2D *texture; + + texture = cogl_texture_2d_new_from_bitmap (bitmap); + + g_assert (texture != NULL); + + return texture; +} + +static CoglPipeline * +create_pipeline_from_texture (CoglTexture *texture) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 0, texture); + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer_num */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + return pipeline; +} + +static void +check_colours (uint32_t color0, + uint32_t color1, + uint32_t color2, + uint32_t color3) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + + test_utils_check_region (test_fb, + 1, 1, /* x/y */ + fb_width / 2 - 2, /* width */ + fb_height / 2 - 2, /* height */ + color0); + test_utils_check_region (test_fb, + fb_width / 2 + 1, /* x */ + 1, /* y */ + fb_width / 2 - 2, /* width */ + fb_height / 2 - 2, /* height */ + color1); + test_utils_check_region (test_fb, + 1, /* x */ + fb_height / 2 + 1, /* y */ + fb_width / 2 - 2, /* width */ + fb_height / 2 - 2, /* height */ + color2); + test_utils_check_region (test_fb, + fb_width / 2 + 1, /* x */ + fb_height / 2 + 1, /* y */ + fb_width / 2 - 2, /* width */ + fb_height / 2 - 2, /* height */ + color3); +} + +void +test_pixel_buffer_map (void) +{ + CoglBitmap *bitmap = create_and_fill_bitmap (); + CoglPipeline *pipeline; + CoglTexture *texture; + + texture = create_texture_from_bitmap (bitmap); + pipeline = create_pipeline_from_texture (texture); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1.0f, 1.0f, + 1.0f, -1.0f); + + cogl_object_unref (bitmap); + cogl_object_unref (texture); + cogl_object_unref (pipeline); + + check_colours (0x0000ffff, + 0x00ff00ff, + 0x00ffffff, + 0xff0000ff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +void +test_pixel_buffer_set_data (void) +{ + CoglBitmap *bitmap = create_bitmap (); + CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap); + CoglPipeline *pipeline; + CoglTexture *texture; + uint8_t *data; + unsigned int stride; + + stride = cogl_bitmap_get_rowstride (bitmap); + + data = g_malloc (stride * BITMAP_SIZE); + + generate_bitmap_data (data, stride); + + cogl_buffer_set_data (buffer, + 0, /* offset */ + data, + stride * (BITMAP_SIZE - 1) + + BITMAP_SIZE * 4); + + g_free (data); + + texture = create_texture_from_bitmap (bitmap); + pipeline = create_pipeline_from_texture (texture); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1.0f, 1.0f, + 1.0f, -1.0f); + + cogl_object_unref (bitmap); + cogl_object_unref (texture); + cogl_object_unref (pipeline); + + check_colours (0x0000ffff, + 0x00ff00ff, + 0x00ffffff, + 0xff0000ff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +static CoglTexture * +create_white_texture (void) +{ + CoglTexture2D *texture; + uint8_t *data = g_malloc (BITMAP_SIZE * BITMAP_SIZE * 4); + + memset (data, 255, BITMAP_SIZE * BITMAP_SIZE * 4); + + texture = cogl_texture_2d_new_from_data (test_ctx, + BITMAP_SIZE, + BITMAP_SIZE, + COGL_PIXEL_FORMAT_RGBA_8888, + BITMAP_SIZE * 4, /* rowstride */ + data, + NULL); /* don't catch errors */ + + g_free (data); + + return texture; +} + +void +test_pixel_buffer_sub_region (void) +{ + CoglBitmap *bitmap = create_and_fill_bitmap (); + CoglPipeline *pipeline; + CoglTexture *texture; + + texture = create_white_texture (); + + /* Replace the top-right quadrant of the texture with the red part + * of the bitmap */ + cogl_texture_set_region_from_bitmap (texture, + BITMAP_SIZE / 2, /* src_x */ + BITMAP_SIZE / 2, /* src_y */ + BITMAP_SIZE / 2, /* dst_x */ + 0, /* dst_y */ + BITMAP_SIZE / 2, /* width */ + BITMAP_SIZE / 2, /* height */ + bitmap); + + pipeline = create_pipeline_from_texture (texture); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1.0f, 1.0f, + 1.0f, -1.0f); + + cogl_object_unref (bitmap); + cogl_object_unref (texture); + cogl_object_unref (pipeline); + + check_colours (0xffffffff, + 0xff0000ff, + 0xffffffff, + 0xffffffff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-point-size-attribute.c b/cogl/tests/conform/test-point-size-attribute.c new file mode 100644 index 000000000..a08d1daa9 --- /dev/null +++ b/cogl/tests/conform/test-point-size-attribute.c @@ -0,0 +1,166 @@ +#include + +#include "test-utils.h" + +/* This test assumes the GL driver supports point sizes up to 16 + pixels. Cogl should probably have some way of querying the size so + we start from that instead */ +#define MAX_POINT_SIZE 16 +#define MIN_POINT_SIZE 4 +#define N_POINTS (MAX_POINT_SIZE - MIN_POINT_SIZE + 1) +/* The size of the area that we'll paint each point in */ +#define POINT_BOX_SIZE (MAX_POINT_SIZE * 2) + +typedef struct +{ + float x, y; + float point_size; +} PointVertex; + +static int +calc_coord_offset (int pos, int pos_index, int point_size) +{ + switch (pos_index) + { + case 0: return pos - point_size / 2 - 2; + case 1: return pos - point_size / 2 + 2; + case 2: return pos + point_size / 2 - 2; + case 3: return pos + point_size / 2 + 2; + } + + g_assert_not_reached (); +} + +static void +verify_point_size (CoglFramebuffer *test_fb, + int x_pos, + int y_pos, + int point_size) +{ + int y, x; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + { + CoglBool in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2; + uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff; + + test_utils_check_pixel (test_fb, + calc_coord_offset (x_pos, x, point_size), + calc_coord_offset (y_pos, y, point_size), + expected_pixel); + } +} + +static CoglPrimitive * +create_primitive (const char *attribute_name) +{ + PointVertex vertices[N_POINTS]; + CoglAttributeBuffer *buffer; + CoglAttribute *attributes[2]; + CoglPrimitive *prim; + int i; + + for (i = 0; i < N_POINTS; i++) + { + vertices[i].x = i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2; + vertices[i].y = POINT_BOX_SIZE / 2; + vertices[i].point_size = MAX_POINT_SIZE - i; + } + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (vertices), + vertices); + + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (PointVertex), + G_STRUCT_OFFSET (PointVertex, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (buffer, + attribute_name, + sizeof (PointVertex), + G_STRUCT_OFFSET (PointVertex, point_size), + 1, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS, + N_POINTS, + attributes, + 2 /* n_attributes */); + + for (i = 0; i < 2; i++) + cogl_object_unref (attributes[i]); + + return prim; +} + +static void +do_test (const char *attribute_name, + void (* pipeline_setup_func) (CoglPipeline *pipeline)) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglPrimitive *primitive; + CoglPipeline *pipeline; + int i; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + fb_width, /* x_2 */ + fb_height /* y_2 */, + -1, 100 /* near/far */); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 0.0f, 0.0f, 1.0f); + + primitive = create_primitive (attribute_name); + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, 0x00, 0xff, 0x00, 0xff); + cogl_pipeline_set_per_vertex_point_size (pipeline, TRUE, NULL); + if (pipeline_setup_func) + pipeline_setup_func (pipeline); + cogl_primitive_draw (primitive, test_fb, pipeline); + cogl_object_unref (pipeline); + cogl_object_unref (primitive); + + /* Verify all of the points where drawn at the right size */ + for (i = 0; i < N_POINTS; i++) + verify_point_size (test_fb, + i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2, /* x */ + POINT_BOX_SIZE / 2, /* y */ + MAX_POINT_SIZE - i /* point size */); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +void +test_point_size_attribute (void) +{ + do_test ("cogl_point_size_in", NULL); +} + +static void +setup_snippet (CoglPipeline *pipeline) +{ + CoglSnippet *snippet; + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_POINT_SIZE, + "attribute float " + "my_super_duper_point_size_attrib;\n", + NULL); + cogl_snippet_set_replace (snippet, + "cogl_point_size_out = " + "my_super_duper_point_size_attrib;\n"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); +} + +void +test_point_size_attribute_snippet (void) +{ + do_test ("my_super_duper_point_size_attrib", setup_snippet); +} diff --git a/cogl/tests/conform/test-point-size.c b/cogl/tests/conform/test-point-size.c new file mode 100644 index 000000000..3c3af0f5e --- /dev/null +++ b/cogl/tests/conform/test-point-size.c @@ -0,0 +1,99 @@ +#include + +#include "test-utils.h" + +/* This test assumes the GL driver supports point sizes up to 16 + pixels. Cogl should probably have some way of querying the size so + we start from that instead */ +#define MAX_POINT_SIZE 16 +/* The size of the area that we'll paint each point in */ +#define POINT_BOX_SIZE (MAX_POINT_SIZE * 2) + +static int +calc_coord_offset (int pos, int pos_index, int point_size) +{ + switch (pos_index) + { + case 0: return pos - point_size / 2 - 2; + case 1: return pos - point_size / 2 + 2; + case 2: return pos + point_size / 2 - 2; + case 3: return pos + point_size / 2 + 2; + } + + g_assert_not_reached (); +} + +static void +verify_point_size (CoglFramebuffer *test_fb, + int x_pos, + int y_pos, + int point_size) +{ + int y, x; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + { + CoglBool in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2; + uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff; + + test_utils_check_pixel (test_fb, + calc_coord_offset (x_pos, x, point_size), + calc_coord_offset (y_pos, y, point_size), + expected_pixel); + } +} + +void +test_point_size (void) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + int point_size; + int x_pos; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + fb_width, /* x_2 */ + fb_height /* y_2 */, + -1, 100 /* near/far */); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 0.0f, 0.0f, 1.0f); + + /* Try a rendering a single point with a few different point + sizes */ + for (x_pos = 0, point_size = MAX_POINT_SIZE; + point_size >= 4; + x_pos += POINT_BOX_SIZE, point_size /= 2) + { + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + CoglVertexP2 point = { x_pos + POINT_BOX_SIZE / 2, + POINT_BOX_SIZE / 2 }; + CoglPrimitive *prim = + cogl_primitive_new_p2 (test_ctx, + COGL_VERTICES_MODE_POINTS, + 1, /* n_vertices */ + &point); + + cogl_pipeline_set_point_size (pipeline, point_size); + cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255); + cogl_primitive_draw (prim, test_fb, pipeline); + + cogl_object_unref (prim); + cogl_object_unref (pipeline); + } + + /* Verify all of the points where drawn at the right size */ + for (x_pos = 0, point_size = MAX_POINT_SIZE; + point_size >= 4; + x_pos += POINT_BOX_SIZE, point_size /= 2) + verify_point_size (test_fb, + x_pos + POINT_BOX_SIZE / 2, + POINT_BOX_SIZE / 2, + point_size); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-point-sprite.c b/cogl/tests/conform/test-point-sprite.c new file mode 100644 index 000000000..eb80cfb0a --- /dev/null +++ b/cogl/tests/conform/test-point-sprite.c @@ -0,0 +1,194 @@ +#include + +#include "test-utils.h" + +#define POINT_SIZE 8 + +static const CoglVertexP2T2 +point = + { + POINT_SIZE, POINT_SIZE, + 0.0f, 0.0f + }; + +static const uint8_t +tex_data[3 * 2 * 2] = + { + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00 + }; + +static void +do_test (CoglBool check_orientation, + CoglBool use_glsl) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglPrimitive *prim; + CoglError *error = NULL; + CoglTexture2D *tex_2d; + CoglPipeline *pipeline, *solid_pipeline; + int tex_height; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + fb_width, /* x_2 */ + fb_height /* y_2 */, + -1, 100 /* near/far */); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 1.0f, 1.0f, 1.0f); + + /* If we're not checking the orientation of the point sprite then + * we'll set the height of the texture to 1 so that the vertical + * orientation does not matter */ + if (check_orientation) + tex_height = 2; + else + tex_height = 1; + + tex_2d = cogl_texture_2d_new_from_data (test_ctx, + 2, tex_height, /* width/height */ + COGL_PIXEL_FORMAT_RGB_888, + 6, /* row stride */ + tex_data, + &error); + g_assert (tex_2d != NULL); + g_assert (error == NULL); + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (pipeline, 0, tex_2d); + + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer_index */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_point_size (pipeline, POINT_SIZE); + + /* If we're using GLSL then we don't need to enable point sprite + * coords and we can just directly reference cogl_point_coord in the + * snippet */ + if (use_glsl) + { + CoglSnippet *snippet = + cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, + NULL, /* declarations */ + NULL /* post */); + static const char source[] = + " cogl_texel = texture2D (cogl_sampler, cogl_point_coord);\n"; + + cogl_snippet_set_replace (snippet, source); + + /* Keep a reference to the original pipeline because there is no + * way to remove a snippet in order to recreate the solid + * pipeline */ + solid_pipeline = cogl_pipeline_copy (pipeline); + + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + + cogl_object_unref (snippet); + } + else + { + CoglBool res = + cogl_pipeline_set_layer_point_sprite_coords_enabled (pipeline, + /* layer_index */ + 0, + /* enable */ + TRUE, + &error); + g_assert (res == TRUE); + g_assert (error == NULL); + + solid_pipeline = cogl_pipeline_copy (pipeline); + + res = + cogl_pipeline_set_layer_point_sprite_coords_enabled (solid_pipeline, + /* layer_index */ + 0, + /* enable */ + FALSE, + &error); + + g_assert (res == TRUE); + g_assert (error == NULL); + } + + prim = cogl_primitive_new_p2t2 (test_ctx, + COGL_VERTICES_MODE_POINTS, + 1, /* n_vertices */ + &point); + + cogl_primitive_draw (prim, test_fb, pipeline); + + /* Render the primitive again without point sprites to make sure + disabling it works */ + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, + POINT_SIZE * 2, /* x */ + 0.0f, /* y */ + 0.0f /* z */); + cogl_primitive_draw (prim, test_fb, solid_pipeline); + cogl_framebuffer_pop_matrix (test_fb); + + cogl_object_unref (prim); + cogl_object_unref (solid_pipeline); + cogl_object_unref (pipeline); + cogl_object_unref (tex_2d); + + test_utils_check_pixel (test_fb, + POINT_SIZE - POINT_SIZE / 4, + POINT_SIZE - POINT_SIZE / 4, + 0x0000ffff); + test_utils_check_pixel (test_fb, + POINT_SIZE + POINT_SIZE / 4, + POINT_SIZE - POINT_SIZE / 4, + 0x00ff00ff); + test_utils_check_pixel (test_fb, + POINT_SIZE - POINT_SIZE / 4, + POINT_SIZE + POINT_SIZE / 4, + check_orientation ? + 0x00ffffff : + 0x0000ffff); + test_utils_check_pixel (test_fb, + POINT_SIZE + POINT_SIZE / 4, + POINT_SIZE + POINT_SIZE / 4, + check_orientation ? + 0xff0000ff : + 0x00ff00ff); + + /* When rendering without the point sprites all of the texture + coordinates should be 0,0 so it should get the top-left texel + which is blue */ + test_utils_check_region (test_fb, + POINT_SIZE * 3 - POINT_SIZE / 2 + 1, + POINT_SIZE - POINT_SIZE / 2 + 1, + POINT_SIZE - 2, POINT_SIZE - 2, + 0x0000ffff); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +void +test_point_sprite (void) +{ + do_test (FALSE /* don't check orientation */, + FALSE /* don't use GLSL */); +} + +void +test_point_sprite_orientation (void) +{ + do_test (TRUE /* check orientation */, + FALSE /* don't use GLSL */); +} + +void +test_point_sprite_glsl (void) +{ + do_test (FALSE /* don't check orientation */, + TRUE /* use GLSL */); +} diff --git a/cogl/tests/conform/test-premult.c b/cogl/tests/conform/test-premult.c new file mode 100644 index 000000000..fa60bdf1e --- /dev/null +++ b/cogl/tests/conform/test-premult.c @@ -0,0 +1,301 @@ +#include + +#include + +#include "test-utils.h" + +#define QUAD_WIDTH 32 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) +#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) +#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) +#define MASK_ALPHA(COLOR) (COLOR & 0xff) + +typedef enum _MakeTextureFlags +{ + TEXTURE_FLAG_SET_PREMULTIPLIED = 1, + TEXTURE_FLAG_SET_UNPREMULTIPLIED = 1<<1, +} MakeTextureFlags; + +static guchar * +gen_tex_data (uint32_t color) +{ + guchar *tex_data, *p; + uint8_t r = MASK_RED (color); + uint8_t g = MASK_GREEN (color); + uint8_t b = MASK_BLUE (color); + uint8_t a = MASK_ALPHA (color); + + tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4); + + for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;) + { + *(--p) = a; + *(--p) = b; + *(--p) = g; + *(--p) = r; + } + + return tex_data; +} + +static CoglTexture * +make_texture (uint32_t color, + CoglPixelFormat src_format, + MakeTextureFlags flags) +{ + CoglTexture2D *tex_2d; + guchar *tex_data = gen_tex_data (color); + CoglBitmap *bmp = cogl_bitmap_new_for_data (test_ctx, + QUAD_WIDTH, + QUAD_WIDTH, + src_format, + QUAD_WIDTH * 4, + tex_data); + + tex_2d = cogl_texture_2d_new_from_bitmap (bmp); + + if (flags & TEXTURE_FLAG_SET_PREMULTIPLIED) + cogl_texture_set_premultiplied (tex_2d, TRUE); + else if (flags & TEXTURE_FLAG_SET_UNPREMULTIPLIED) + cogl_texture_set_premultiplied (tex_2d, FALSE); + + cogl_object_unref (bmp); + g_free (tex_data); + + return tex_2d; +} + +static void +set_region (CoglTexture *tex, + uint32_t color, + CoglPixelFormat format) +{ + guchar *tex_data = gen_tex_data (color); + + cogl_texture_set_region (tex, + 0, 0, /* src x, y */ + 0, 0, /* dst x, y */ + QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */ + QUAD_WIDTH, QUAD_WIDTH, /* src width, height */ + format, + 0, /* auto compute row stride */ + tex_data); +} + +static void +check_texture (CoglPipeline *pipeline, + CoglHandle material, + int x, + int y, + CoglTexture *tex, + uint32_t expected_result) +{ + /* Legacy */ + cogl_push_framebuffer (test_fb); + cogl_material_set_layer (material, 0, tex); + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result); + cogl_pop_framebuffer (); + + /* New API */ + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, + x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result); +} + +void +test_premult (void) +{ + CoglPipeline *pipeline; + CoglHandle material; + CoglTexture *tex; + + cogl_framebuffer_orthographic (test_fb, 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 1.0f, 1.0f, 1.0f); + + /* Legacy */ + material = cogl_material_new (); + cogl_material_set_blend (material, + "RGBA = ADD (SRC_COLOR, 0)", NULL); + cogl_material_set_layer_combine (material, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + + /* New API */ + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_blend (pipeline, + "RGBA = ADD (SRC_COLOR, 0)", NULL); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + + /* If the user explicitly specifies an unmultiplied internal format then + * Cogl shouldn't automatically premultiply the given texture data... */ + if (cogl_test_verbose ()) + g_print ("make_texture (0xff00ff80, " + "src = RGBA_8888, internal = RGBA_8888)\n"); + tex = make_texture (0xff00ff80, + COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ + TEXTURE_FLAG_SET_UNPREMULTIPLIED); + check_texture (pipeline, material, 0, 0, /* position */ + tex, + 0xff00ff80); /* expected */ + + /* If the user explicitly requests a premultiplied internal format and + * gives unmultiplied src data then Cogl should always premultiply that + * for us */ + if (cogl_test_verbose ()) + g_print ("make_texture (0xff00ff80, " + "src = RGBA_8888, internal = RGBA_8888_PRE)\n"); + tex = make_texture (0xff00ff80, + COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ + TEXTURE_FLAG_SET_PREMULTIPLIED); + check_texture (pipeline, material, 1, 0, /* position */ + tex, + 0x80008080); /* expected */ + + /* If the user doesn't explicitly declare that the texture is premultiplied + * then Cogl should assume it is by default should premultiply + * unpremultiplied texture data... + */ + if (cogl_test_verbose ()) + g_print ("make_texture (0xff00ff80, " + "src = RGBA_8888, internal = ANY)\n"); + tex = make_texture (0xff00ff80, + COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ + 0); /* default premultiplied status */ + check_texture (pipeline, material, 2, 0, /* position */ + tex, + 0x80008080); /* expected */ + + /* If the user requests a premultiplied internal texture format and supplies + * premultiplied source data, Cogl should never modify that source data... + */ + if (cogl_test_verbose ()) + g_print ("make_texture (0x80008080, " + "src = RGBA_8888_PRE, " + "internal = RGBA_8888_PRE)\n"); + tex = make_texture (0x80008080, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ + TEXTURE_FLAG_SET_PREMULTIPLIED); + check_texture (pipeline, material, 3, 0, /* position */ + tex, + 0x80008080); /* expected */ + + /* If the user requests an unmultiplied internal texture format, but + * supplies premultiplied source data, then Cogl should always + * un-premultiply the source data... */ + if (cogl_test_verbose ()) + g_print ("make_texture (0x80008080, " + "src = RGBA_8888_PRE, internal = RGBA_8888)\n"); + tex = make_texture (0x80008080, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ + TEXTURE_FLAG_SET_UNPREMULTIPLIED); + check_texture (pipeline, material, 4, 0, /* position */ + tex, + 0xff00ff80); /* expected */ + + /* If the user allows any internal texture format and provides premultipled + * source data then by default Cogl shouldn't modify the source data... + * (In the future there will be additional Cogl API to control this + * behaviour) */ + if (cogl_test_verbose ()) + g_print ("make_texture (0x80008080, " + "src = RGBA_8888_PRE, internal = ANY)\n"); + tex = make_texture (0x80008080, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ + 0); /* default premultiplied status */ + check_texture (pipeline, material, 5, 0, /* position */ + tex, + 0x80008080); /* expected */ + + /* + * Test cogl_texture_set_region() .... + */ + + if (cogl_test_verbose ()) + g_print ("make_texture (0xDEADBEEF, " + "src = RGBA_8888, internal = RGBA_8888)\n"); + tex = make_texture (0xDEADBEEF, + COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ + TEXTURE_FLAG_SET_UNPREMULTIPLIED); + if (cogl_test_verbose ()) + g_print ("set_region (0xff00ff80, RGBA_8888)\n"); + set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888); + check_texture (pipeline, material, 6, 0, /* position */ + tex, + 0xff00ff80); /* expected */ + + /* Updating a texture region for an unmultiplied texture using premultiplied + * region data should result in Cogl unmultiplying the given region data... + */ + if (cogl_test_verbose ()) + g_print ("make_texture (0xDEADBEEF, " + "src = RGBA_8888, internal = RGBA_8888)\n"); + tex = make_texture (0xDEADBEEF, + COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ + TEXTURE_FLAG_SET_UNPREMULTIPLIED); + if (cogl_test_verbose ()) + g_print ("set_region (0x80008080, RGBA_8888_PRE)\n"); + set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE); + check_texture (pipeline, material, 7, 0, /* position */ + tex, + 0xff00ff80); /* expected */ + + + if (cogl_test_verbose ()) + g_print ("make_texture (0xDEADBEEF, " + "src = RGBA_8888_PRE, " + "internal = RGBA_8888_PRE)\n"); + tex = make_texture (0xDEADBEEF, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ + TEXTURE_FLAG_SET_PREMULTIPLIED); + if (cogl_test_verbose ()) + g_print ("set_region (0x80008080, RGBA_8888_PRE)\n"); + set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE); + check_texture (pipeline, material, 8, 0, /* position */ + tex, + 0x80008080); /* expected */ + + + /* Updating a texture region for a premultiplied texture using unmultiplied + * region data should result in Cogl premultiplying the given region data... + */ + if (cogl_test_verbose ()) + g_print ("make_texture (0xDEADBEEF, " + "src = RGBA_8888_PRE, " + "internal = RGBA_8888_PRE)\n"); + tex = make_texture (0xDEADBEEF, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ + TEXTURE_FLAG_SET_PREMULTIPLIED); + if (cogl_test_verbose ()) + g_print ("set_region (0xff00ff80, RGBA_8888)\n"); + set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888); + check_texture (pipeline, material, 9, 0, /* position */ + tex, + 0x80008080); /* expected */ + + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-primitive-and-journal.c b/cogl/tests/conform/test-primitive-and-journal.c new file mode 100644 index 000000000..f978cd5ee --- /dev/null +++ b/cogl/tests/conform/test-primitive-and-journal.c @@ -0,0 +1,122 @@ +#include + +#include "test-utils.h" + +typedef CoglVertexP2C4 Vertex; + +static void +setup_orthographic_modelview (void) +{ + CoglMatrix matrix; + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + + /* Set up a non-identity modelview matrix. When the journal is + * flushed it will usually flush the identity matrix. Using the + * non-default matrix ensures that we test that Cogl restores the + * matrix we asked for. The matrix sets up an orthographic transform + * in the modelview matrix */ + + cogl_matrix_init_identity (&matrix); + cogl_matrix_orthographic (&matrix, + 0.0f, 0.0f, /* x_1 y_1 */ + fb_width, + fb_height, + -1.0f, /* nearval */ + 1.0f /* farval */); + cogl_framebuffer_set_modelview_matrix (test_fb, &matrix); +} + +static void +create_primitives (CoglPrimitive *primitives[2]) +{ + static const Vertex vertex_data[8] = + { + /* triangle strip 1 */ + { 0, 0, 255, 0, 0, 255 }, + { 0, 100, 255, 0, 0, 255 }, + { 100, 0, 255, 0, 0, 255 }, + { 100, 100, 255, 0, 0, 255 }, + /* triangle strip 2 */ + { 200, 0, 0, 0, 255, 255 }, + { 200, 100, 0, 0, 255, 255 }, + { 300, 0, 0, 0, 255, 255 }, + { 300, 100, 0, 0, 255, 255 }, + }; + + primitives[0] = cogl_primitive_new_p2c4 (test_ctx, + COGL_VERTICES_MODE_TRIANGLE_STRIP, + G_N_ELEMENTS (vertex_data), + vertex_data); + cogl_primitive_set_n_vertices (primitives[0], 4); + + primitives[1] = cogl_primitive_copy (primitives[0]); + cogl_primitive_set_first_vertex (primitives[1], 4); + cogl_primitive_set_n_vertices (primitives[1], 4); +} + +static CoglPipeline * +create_pipeline (void) +{ + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255); + + return pipeline; +} + +void +test_primitive_and_journal (void) +{ + CoglPrimitive *primitives[2]; + CoglPipeline *pipeline; + + setup_orthographic_modelview (); + create_primitives (primitives); + pipeline = create_pipeline (); + + /* Set a clip to clip all three rectangles to just the bottom half. + * The journal flushes its own clip state so this verifies that the + * clip state is correctly restored for the second primitive. */ + cogl_framebuffer_push_rectangle_clip (test_fb, + 0, 50, 300, 100); + + cogl_primitive_draw (primitives[0], test_fb, pipeline); + + /* Draw a rectangle using the journal in-between the two primitives. + * This should test that the journal gets flushed correctly and that + * the modelview matrix is restored. Half of the rectangle should be + * overriden by the second primitive */ + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 100, 0, /* x1/y1 */ + 300, 100 /* x2/y2 */); + + cogl_primitive_draw (primitives[1], test_fb, pipeline); + + /* Check the three rectangles */ + test_utils_check_region (test_fb, + 1, 51, + 98, 48, + 0xff0000ff); + test_utils_check_region (test_fb, + 101, 51, + 98, 48, + 0x00ff00ff); + test_utils_check_region (test_fb, + 201, 51, + 98, 48, + 0x0000ffff); + + /* Check that the top half of all of the rectangles was clipped */ + test_utils_check_region (test_fb, + 1, 1, + 298, 48, + 0x000000ff); + + cogl_framebuffer_pop_clip (test_fb); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-primitive.c b/cogl/tests/conform/test-primitive.c new file mode 100644 index 000000000..db264fc1c --- /dev/null +++ b/cogl/tests/conform/test-primitive.c @@ -0,0 +1,334 @@ +#include +#include +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + int fb_width; + int fb_height; +} TestState; + +#define PRIM_COLOR 0xff00ffff +#define TEX_COLOR 0x0000ffff + +#define N_ATTRIBS 8 + +typedef CoglPrimitive * (* TestPrimFunc) (CoglContext *ctx, uint32_t *expected_color); + +static CoglPrimitive * +test_prim_p2 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP2 verts[] = + { { 0, 0 }, { 0, 10 }, { 10, 0 } }; + + return cogl_primitive_new_p2 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p3 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP3 verts[] = + { { 0, 0, 0 }, { 0, 10, 0 }, { 10, 0, 0 } }; + + return cogl_primitive_new_p3 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p2c4 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP2C4 verts[] = + { { 0, 0, 255, 255, 0, 255 }, + { 0, 10, 255, 255, 0, 255 }, + { 10, 0, 255, 255, 0, 255 } }; + + *expected_color = 0xffff00ff; + + return cogl_primitive_new_p2c4 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p3c4 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP3C4 verts[] = + { { 0, 0, 0, 255, 255, 0, 255 }, + { 0, 10, 0, 255, 255, 0, 255 }, + { 10, 0, 0, 255, 255, 0, 255 } }; + + *expected_color = 0xffff00ff; + + return cogl_primitive_new_p3c4 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p2t2 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP2T2 verts[] = + { { 0, 0, 1, 0 }, + { 0, 10, 1, 0 }, + { 10, 0, 1, 0 } }; + + *expected_color = TEX_COLOR; + + return cogl_primitive_new_p2t2 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p3t2 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP3T2 verts[] = + { { 0, 0, 0, 1, 0 }, + { 0, 10, 0, 1, 0 }, + { 10, 0, 0, 1, 0 } }; + + *expected_color = TEX_COLOR; + + return cogl_primitive_new_p3t2 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p2t2c4 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP2T2C4 verts[] = + { { 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, + { 0, 10, 1, 0, 0xff, 0xff, 0xf0, 0xff }, + { 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } }; + + /* The blue component of the texture color should be replaced with 0xf0 */ + *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000; + + return cogl_primitive_new_p2t2c4 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static CoglPrimitive * +test_prim_p3t2c4 (CoglContext *ctx, uint32_t *expected_color) +{ + static const CoglVertexP3T2C4 verts[] = + { { 0, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, + { 0, 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, + { 10, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } }; + + /* The blue component of the texture color should be replaced with 0xf0 */ + *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000; + + return cogl_primitive_new_p3t2c4 (test_ctx, + COGL_VERTICES_MODE_TRIANGLES, + 3, /* n_vertices */ + verts); +} + +static const TestPrimFunc +test_prim_funcs[] = + { + test_prim_p2, + test_prim_p3, + test_prim_p2c4, + test_prim_p3c4, + test_prim_p2t2, + test_prim_p3t2, + test_prim_p2t2c4, + test_prim_p3t2c4 + }; + +static void +test_paint (TestState *state) +{ + CoglPipeline *pipeline; + CoglTexture *tex; + uint8_t tex_data[6]; + int i; + + /* Create a two pixel texture. The first pixel is white and the + second pixel is tex_color. The assumption is that if no texture + coordinates are specified then it will default to 0,0 and get + white */ + tex_data[0] = 255; + tex_data[1] = 255; + tex_data[2] = 255; + tex_data[3] = (TEX_COLOR >> 24) & 0xff; + tex_data[4] = (TEX_COLOR >> 16) & 0xff; + tex_data[5] = (TEX_COLOR >> 8) & 0xff; + tex = test_utils_texture_new_from_data (test_ctx, + 2, 1, /* size */ + TEST_UTILS_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGB_888, + 6, /* rowstride */ + tex_data); + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, + (PRIM_COLOR >> 24) & 0xff, + (PRIM_COLOR >> 16) & 0xff, + (PRIM_COLOR >> 8) & 0xff, + (PRIM_COLOR >> 0) & 0xff); + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_object_unref (tex); + + for (i = 0; i < G_N_ELEMENTS (test_prim_funcs); i++) + { + CoglPrimitive *prim; + uint32_t expected_color = PRIM_COLOR; + + prim = test_prim_funcs[i] (test_ctx, &expected_color); + + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, i * 10, 0, 0); + cogl_primitive_draw (prim, test_fb, pipeline); + cogl_framebuffer_pop_matrix (test_fb); + + test_utils_check_pixel (test_fb, i * 10 + 2, 2, expected_color); + + cogl_object_unref (prim); + } + + cogl_object_unref (pipeline); +} + +static CoglBool +get_attributes_cb (CoglPrimitive *prim, + CoglAttribute *attrib, + void *user_data) +{ + CoglAttribute ***p = user_data; + *((* p)++) = attrib; + return TRUE; +} + +static int +compare_pointers (const void *a, const void *b) +{ + CoglAttribute *pa = *(CoglAttribute **) a; + CoglAttribute *pb = *(CoglAttribute **) b; + + if (pa < pb) + return -1; + else if (pa > pb) + return 1; + else + return 0; +} + +static void +test_copy (TestState *state) +{ + static const uint16_t indices_data[2] = { 1, 2 }; + CoglAttributeBuffer *buffer = + cogl_attribute_buffer_new (test_ctx, 100, NULL); + CoglAttribute *attributes[N_ATTRIBS]; + CoglAttribute *attributes_a[N_ATTRIBS], *attributes_b[N_ATTRIBS]; + CoglAttribute **p; + CoglPrimitive *prim_a, *prim_b; + CoglIndices *indices; + int i; + + for (i = 0; i < N_ATTRIBS; i++) + { + char *name = g_strdup_printf ("foo_%i", i); + attributes[i] = cogl_attribute_new (buffer, + name, + 16, /* stride */ + 16, /* offset */ + 2, /* components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + g_free (name); + } + + prim_a = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 8, /* n_vertices */ + attributes, + N_ATTRIBS); + + indices = cogl_indices_new (test_ctx, + COGL_INDICES_TYPE_UNSIGNED_SHORT, + indices_data, + 2 /* n_indices */); + + cogl_primitive_set_first_vertex (prim_a, 12); + cogl_primitive_set_indices (prim_a, indices, 2); + + prim_b = cogl_primitive_copy (prim_a); + + p = attributes_a; + cogl_primitive_foreach_attribute (prim_a, + get_attributes_cb, + &p); + g_assert_cmpint (p - attributes_a, ==, N_ATTRIBS); + + p = attributes_b; + cogl_primitive_foreach_attribute (prim_b, + get_attributes_cb, + &p); + g_assert_cmpint (p - attributes_b, ==, N_ATTRIBS); + + qsort (attributes_a, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers); + qsort (attributes_b, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers); + + g_assert (memcmp (attributes_a, attributes_b, sizeof (attributes_a)) == 0); + + g_assert_cmpint (cogl_primitive_get_first_vertex (prim_a), + ==, + cogl_primitive_get_first_vertex (prim_b)); + + g_assert_cmpint (cogl_primitive_get_n_vertices (prim_a), + ==, + cogl_primitive_get_n_vertices (prim_b)); + + g_assert_cmpint (cogl_primitive_get_mode (prim_a), + ==, + cogl_primitive_get_mode (prim_b)); + + g_assert (cogl_primitive_get_indices (prim_a) == + cogl_primitive_get_indices (prim_b)); + + cogl_object_unref (prim_a); + cogl_object_unref (prim_b); + cogl_object_unref (indices); + + for (i = 0; i < N_ATTRIBS; i++) + cogl_object_unref (attributes[i]); + + cogl_object_unref (buffer); +} + +void +test_primitive (void) +{ + TestState state; + + state.fb_width = cogl_framebuffer_get_width (test_fb); + state.fb_height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + state.fb_width, + state.fb_height, + -1, + 100); + + test_paint (&state); + test_copy (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-read-texture-formats.c b/cogl/tests/conform/test-read-texture-formats.c new file mode 100644 index 000000000..3fa4d8eea --- /dev/null +++ b/cogl/tests/conform/test-read-texture-formats.c @@ -0,0 +1,222 @@ +#include +#include + +#include "test-utils.h" + +/* + * This tests reading back an RGBA texture in all of the available + * pixel formats + */ + +static const uint8_t tex_data[4] = { 0x12, 0x34, 0x56, 0x78 }; + +static void +test_read_byte (CoglTexture2D *tex_2d, + CoglPixelFormat format, + uint8_t expected_byte) +{ + uint8_t received_byte; + + cogl_texture_get_data (tex_2d, + format, + 1, /* rowstride */ + &received_byte); + + g_assert_cmpint (expected_byte, ==, received_byte); +} + +static void +test_read_short (CoglTexture2D *tex_2d, + CoglPixelFormat format, + ...) +{ + va_list ap; + int bits; + uint16_t received_value; + uint16_t expected_value = 0; + char *received_value_str; + char *expected_value_str; + int bits_sum = 0; + + cogl_texture_get_data (tex_2d, + format, + 2, /* rowstride */ + (uint8_t *) &received_value); + + va_start (ap, format); + + /* Convert the va args into a single 16-bit expected value */ + while ((bits = va_arg (ap, int)) != -1) + { + int value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255; + + bits_sum += bits; + + expected_value |= value << (16 - bits_sum); + } + + va_end (ap); + + received_value_str = g_strdup_printf ("0x%04x", received_value); + expected_value_str = g_strdup_printf ("0x%04x", expected_value); + g_assert_cmpstr (received_value_str, ==, expected_value_str); + g_free (received_value_str); + g_free (expected_value_str); +} + +static void +test_read_888 (CoglTexture2D *tex_2d, + CoglPixelFormat format, + uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + cogl_texture_get_data (tex_2d, + format, + 4, /* rowstride */ + pixel); + + test_utils_compare_pixel (pixel, expected_pixel); +} + +static void +test_read_88 (CoglTexture2D *tex_2d, + CoglPixelFormat format, + uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + pixel[2] = 0x00; + + cogl_texture_get_data (tex_2d, + format, + 2, /* rowstride */ + pixel); + + test_utils_compare_pixel (pixel, expected_pixel); +} + +static void +test_read_8888 (CoglTexture2D *tex_2d, + CoglPixelFormat format, + uint32_t expected_pixel) +{ + uint32_t received_pixel; + char *received_value_str; + char *expected_value_str; + + cogl_texture_get_data (tex_2d, + format, + 4, /* rowstride */ + (uint8_t *) &received_pixel); + + received_pixel = GUINT32_FROM_BE (received_pixel); + + received_value_str = g_strdup_printf ("0x%08x", received_pixel); + expected_value_str = g_strdup_printf ("0x%08x", expected_pixel); + g_assert_cmpstr (received_value_str, ==, expected_value_str); + g_free (received_value_str); + g_free (expected_value_str); +} + +static void +test_read_int (CoglTexture2D *tex_2d, + CoglPixelFormat format, + ...) +{ + va_list ap; + int bits; + uint32_t received_value; + uint32_t expected_value = 0; + char *received_value_str; + char *expected_value_str; + int bits_sum = 0; + + cogl_texture_get_data (tex_2d, + format, + 4, /* rowstride */ + (uint8_t *) &received_value); + + va_start (ap, format); + + /* Convert the va args into a single 32-bit expected value */ + while ((bits = va_arg (ap, int)) != -1) + { + uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255; + + bits_sum += bits; + + expected_value |= value << (32 - bits_sum); + } + + va_end (ap); + + received_value_str = g_strdup_printf ("0x%08x", received_value); + expected_value_str = g_strdup_printf ("0x%08x", expected_value); + g_assert_cmpstr (received_value_str, ==, expected_value_str); + g_free (received_value_str); + g_free (expected_value_str); +} + +void +test_read_texture_formats (void) +{ + CoglTexture2D *tex_2d; + + tex_2d = cogl_texture_2d_new_from_data (test_ctx, + 1, 1, /* width / height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + tex_data, + NULL); + + test_read_byte (tex_2d, COGL_PIXEL_FORMAT_A_8, 0x78); + +#if 0 + /* I'm not sure what's the right value to put here because Nvidia + and Mesa seem to behave differently so one of them must be + wrong. */ + test_read_byte (tex_2d, COGL_PIXEL_FORMAT_G_8, 0x9c); +#endif + + /* We should always be able to read into an RG buffer regardless of + * whether RG textures are supported because Cogl will do the + * conversion for us */ + test_read_88 (tex_2d, COGL_PIXEL_FORMAT_RG_88, 0x123400ff); + + test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGB_565, + 5, 0x12, 6, 0x34, 5, 0x56, + -1); + test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_4444_PRE, + 4, 0x12, 4, 0x34, 4, 0x56, 4, 0x78, + -1); + test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_5551_PRE, + 5, 0x12, 5, 0x34, 5, 0x56, 1, 0x78, + -1); + + test_read_888 (tex_2d, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff); + test_read_888 (tex_2d, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff); + + test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 0x12345678); + test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_BGRA_8888_PRE, 0x56341278); + test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ARGB_8888_PRE, 0x78123456); + test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ABGR_8888_PRE, 0x78563412); + + test_read_int (tex_2d, COGL_PIXEL_FORMAT_RGBA_1010102_PRE, + 10, 0x12, 10, 0x34, 10, 0x56, 2, 0x78, + -1); + test_read_int (tex_2d, COGL_PIXEL_FORMAT_BGRA_1010102_PRE, + 10, 0x56, 10, 0x34, 10, 0x12, 2, 0x78, + -1); + test_read_int (tex_2d, COGL_PIXEL_FORMAT_ARGB_2101010_PRE, + 2, 0x78, 10, 0x12, 10, 0x34, 10, 0x56, + -1); + test_read_int (tex_2d, COGL_PIXEL_FORMAT_ABGR_2101010_PRE, + 2, 0x78, 10, 0x56, 10, 0x34, 10, 0x12, + -1); + + cogl_object_unref (tex_2d); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-readpixels.c b/cogl/tests/conform/test-readpixels.c new file mode 100644 index 000000000..131b08b23 --- /dev/null +++ b/cogl/tests/conform/test-readpixels.c @@ -0,0 +1,178 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define FRAMEBUFFER_WIDTH 640 +#define FRAMEBUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + uint32_t *pixels; + uint8_t *pixelsc; + + /* Save the Clutter viewport/matrices and load identity matrices */ + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* All offscreen rendering is done upside down so the first thing we + * verify is reading back grid of colors from a CoglOffscreen framebuffer + */ + + data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); + tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + TEST_UTILS_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + FRAMEBUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_with_texture (tex); + + cogl_push_framebuffer (offscreen); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-1, 1, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 1, 1, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 0, 0, -1); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 1, -1); + + pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); + cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + (guchar *)pixels); + + g_assert_cmpint (pixels[0], ==, 0xff0000ff); + g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00); + g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000); + g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff); + g_free (pixels); + + cogl_pop_framebuffer (); + cogl_handle_unref (offscreen); + + /* Now verify reading back from an onscreen framebuffer... + */ + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); + cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + (guchar *)pixels); + + g_assert_cmpint (pixels[0], ==, 0xff0000ff); + g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00); + g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000); + g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff); + g_free (pixels); + + /* Verify using BGR format */ + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + pixelsc = g_malloc0 (FRAMEBUFFER_WIDTH * 3 * FRAMEBUFFER_HEIGHT); + cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_BGR_888, + (guchar *)pixelsc); + + g_assert_cmpint (pixelsc[0], ==, 0x00); + g_assert_cmpint (pixelsc[1], ==, 0x00); + g_assert_cmpint (pixelsc[2], ==, 0xff); + + g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 0], ==, 0x00); + g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 1], ==, 0xff); + g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 2], ==, 0x00); + + g_free (pixelsc); + + cogl_handle_unref (tex); + + /* Restore the viewport and matrices state */ + cogl_set_viewport (saved_viewport[0], + saved_viewport[1], + saved_viewport[2], + saved_viewport[3]); + cogl_set_projection_matrix (&saved_projection); + cogl_pop_matrix (); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_readpixels (TestUtilsGTestFixture *fixture, + void *data) +{ + unsigned int idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-snippets.c b/cogl/tests/conform/test-snippets.c new file mode 100644 index 000000000..a251fc162 --- /dev/null +++ b/cogl/tests/conform/test-snippets.c @@ -0,0 +1,815 @@ +#include + +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + int fb_width, fb_height; +} TestState; + +typedef void (* SnippetTestFunc) (TestState *state); + +static CoglPipeline * +create_texture_pipeline (TestState *state) +{ + CoglPipeline *pipeline; + CoglTexture *tex; + static const uint8_t tex_data[] = + { + 0xff, 0x00, 0x00, 0xff, /* red */ 0x00, 0xff, 0x00, 0xff, /* green */ + 0x00, 0x00, 0xff, 0xff, /* blue */ 0xff, 0xff, 0x00, 0xff, /* yellow */ + }; + + tex = test_utils_texture_new_from_data (test_ctx, + 2, 2, /* width/height */ + TEST_UTILS_TEXTURE_NO_ATLAS, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 8, /* rowstride */ + tex_data); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + cogl_object_unref (tex); + + return pipeline; +} + +static void +simple_fragment_snippet (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Simple fragment snippet */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + "cogl_color_out.g += 1.0;"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); +} + +static void +simple_vertex_snippet (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Simple vertex snippet */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, + NULL, + "cogl_color_out.b += 1.0;"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 10, 0, 20, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 15, 5, 0xff00ffff); +} + +static void +shared_uniform (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + int location; + + /* Snippets sharing a uniform across the vertex and fragment + hooks */ + pipeline = cogl_pipeline_new (test_ctx); + + location = cogl_pipeline_get_uniform_location (pipeline, "a_value"); + cogl_pipeline_set_uniform_1f (pipeline, location, 0.25f); + + cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, + "uniform float a_value;", + "cogl_color_out.b += a_value;"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + "uniform float a_value;", + "cogl_color_out.b += a_value;"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 20, 0, 30, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 25, 5, 0xff0080ff); +} + +static void +lots_snippets (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + int location; + int i; + + /* Lots of snippets on one pipeline */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); + + for (i = 0; i < 3; i++) + { + char letter = 'x' + i; + char *uniform_name = g_strdup_printf ("%c_value", letter); + char *declarations = g_strdup_printf ("uniform float %s;\n", + uniform_name); + char *code = g_strdup_printf ("cogl_color_out.%c = %s;\n", + letter, + uniform_name); + + location = cogl_pipeline_get_uniform_location (pipeline, uniform_name); + cogl_pipeline_set_uniform_1f (pipeline, location, (i + 1) * 0.1f); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + declarations, + code); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + g_free (code); + g_free (uniform_name); + g_free (declarations); + } + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 30, 0, 40, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 35, 5, 0x19334cff); +} + +static void +shared_variable_pre_post (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Test that the pre string can declare variables used by the post + string */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 255, 255, 255, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + "cogl_color_out = redvec;"); + cogl_snippet_set_pre (snippet, "vec4 redvec = vec4 (1.0, 0.0, 0.0, 1.0);"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 40, 0, 50, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 45, 5, 0xff0000ff); +} + +static void +test_pipeline_caching (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Check that the pipeline caching works when unrelated pipelines + share snippets state. It's too hard to actually assert this in + the conformance test but at least it should be possible to see by + setting COGL_DEBUG=show-source to check whether this shader gets + generated twice */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + "/* This comment should only be seen ONCE\n" + " when COGL_DEBUG=show-source is TRUE\n" + " even though it is used in two different\n" + " unrelated pipelines */", + "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);\n"); + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 50, 0, 60, 10); + cogl_object_unref (pipeline); + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 60, 0, 70, 10); + cogl_object_unref (pipeline); + + cogl_object_unref (snippet); + + test_utils_check_pixel (test_fb, 55, 5, 0x00ff00ff); + test_utils_check_pixel (test_fb, 65, 5, 0x00ff00ff); +} + +static void +test_replace_string (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Check the replace string */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); + cogl_snippet_set_pre (snippet, + "cogl_color_out = vec4 (0.0, 0.5, 0.0, 1.0);"); + /* Remove the generated output. If the replace string isn't working + then the code from the pre string would get overwritten with + white */ + cogl_snippet_set_replace (snippet, "/* do nothing */"); + cogl_snippet_set_post (snippet, + "cogl_color_out += vec4 (0.5, 0.0, 0.0, 1.0);"); + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 70, 0, 80, 10); + cogl_object_unref (pipeline); + + cogl_object_unref (snippet); + + test_utils_check_pixel (test_fb, 75, 5, 0x808000ff); +} + +static void +test_texture_lookup_hook (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Check the texture lookup hook */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, + NULL, + "cogl_texel.b += 1.0;"); + /* Flip the texture coordinates around the y axis so that it will + get the green texel */ + cogl_snippet_set_pre (snippet, "cogl_tex_coord.x = 1.0 - cogl_tex_coord.x;"); + + pipeline = create_texture_pipeline (state); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 80, 0, 90, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + cogl_object_unref (snippet); + + test_utils_check_pixel (test_fb, 85, 5, 0x00ffffff); +} + +static void +test_multiple_samples (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Check that we can use the passed in sampler in the texture lookup + to sample multiple times */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, + NULL, + NULL); + cogl_snippet_set_replace (snippet, + "cogl_texel = " + "texture2D (cogl_sampler, vec2 (0.25, 0.25)) + " + "texture2D (cogl_sampler, vec2 (0.75, 0.25));"); + + pipeline = create_texture_pipeline (state); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); + cogl_object_unref (pipeline); + + cogl_object_unref (snippet); + + test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); +} + +static void +test_replace_lookup_hook (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Check replacing the texture lookup hook */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, NULL); + cogl_snippet_set_replace (snippet, "cogl_texel = vec4 (0.0, 0.0, 1.0, 0.0);"); + + pipeline = create_texture_pipeline (state); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 90, 0, 100, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + cogl_object_unref (snippet); + + test_utils_check_pixel (test_fb, 95, 5, 0x0000ffff); +} + +static void +test_replace_snippet (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Test replacing a previous snippet */ + pipeline = create_texture_pipeline (state); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + "cogl_color_out = vec4 (0.5, 0.5, 0.5, 1.0);"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); + cogl_snippet_set_pre (snippet, "cogl_color_out = vec4 (1.0, 1.0, 1.0, 1.0);"); + cogl_snippet_set_replace (snippet, + "cogl_color_out *= vec4 (1.0, 0.0, 0.0, 1.0);"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 100, 0, 110, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 105, 5, 0xff0000ff); +} + +static void +test_replace_fragment_layer (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Test replacing the fragment layer code */ + pipeline = create_texture_pipeline (state); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL); + cogl_snippet_set_replace (snippet, "cogl_layer = vec4 (0.0, 0.0, 1.0, 1.0);"); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_object_unref (snippet); + + /* Add a second layer which samples from the texture in the first + layer. The snippet override should cause the first layer not to + generate the code for the texture lookup but this second layer + should still be able to cause it to be generated */ + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGB = ADD(TEXTURE_0, PREVIOUS)" + "A = REPLACE(PREVIOUS)", + NULL); + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 110, 0, 120, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 115, 5, 0xff00ffff); +} + +static void +test_modify_fragment_layer (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Test modifying the fragment layer code */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_uniform_1f (pipeline, + cogl_pipeline_get_uniform_location (pipeline, + "a_value"), + 0.5); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, + "uniform float a_value;", + "cogl_layer.g = a_value;"); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 120, 0, 130, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 125, 5, 0xff80ffff); +} + +static void +test_modify_vertex_layer (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + CoglMatrix matrix; + + /* Test modifying the vertex layer code */ + pipeline = create_texture_pipeline (state); + + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM, + NULL, + "cogl_tex_coord.x = 1.0;"); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 130, 0, 140, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 135, 5, 0xffff00ff); +} + +static void +test_replace_vertex_layer (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + CoglMatrix matrix; + + /* Test replacing the vertex layer code */ + pipeline = create_texture_pipeline (state); + + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM, + NULL, + NULL); + cogl_snippet_set_replace (snippet, "cogl_tex_coord.x = 1.0;\n"); + cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + 140, 0, 150, 10, + 0, 0, 0, 0); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 145, 5, 0x00ff00ff); +} + +static void +test_vertex_transform_hook (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + CoglMatrix identity_matrix; + CoglMatrix matrix; + int location; + + /* Test the vertex transform hook */ + + cogl_matrix_init_identity (&identity_matrix); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 255, 0, 255, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_TRANSFORM, + "uniform mat4 pmat;", + NULL); + cogl_snippet_set_replace (snippet, "cogl_position_out = " + "pmat * cogl_position_in;"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + /* Copy the current projection matrix to a uniform */ + cogl_framebuffer_get_projection_matrix (test_fb, &matrix); + location = cogl_pipeline_get_uniform_location (pipeline, "pmat"); + cogl_pipeline_set_uniform_matrix (pipeline, + location, + 4, /* dimensions */ + 1, /* count */ + FALSE, /* don't transpose */ + cogl_matrix_get_array (&matrix)); + + /* Replace the real projection matrix with the identity. This should + mess up the drawing unless the snippet replacement is working */ + cogl_framebuffer_set_projection_matrix (test_fb, &identity_matrix); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 150, 0, 160, 10); + cogl_object_unref (pipeline); + + /* Restore the projection matrix */ + cogl_framebuffer_set_projection_matrix (test_fb, &matrix); + + test_utils_check_pixel (test_fb, 155, 5, 0xff00ffff); +} + +static void +test_global_vertex_hook (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + pipeline = cogl_pipeline_new (test_ctx); + + /* Creates a function in the global declarations hook which is used + * by a subsequent snippet. The subsequent snippets replace any + * previous snippets but this shouldn't prevent the global + * declarations from being generated */ + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS, + /* declarations */ + "float\n" + "multiply_by_two (float number)\n" + "{\n" + " return number * 2.0;\n" + "}\n", + /* post */ + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_snippet_set_pre (snippet, + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_snippet_set_replace (snippet, + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, + NULL, /* declarations */ + NULL /* replace */); + cogl_snippet_set_replace (snippet, + "cogl_color_out.r = multiply_by_two (0.5);\n" + "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n" + "cogl_position_out = cogl_position_in;\n"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1, 1, + 10.0f * 2.0f / state->fb_width - 1.0f, + 10.0f * 2.0f / state->fb_height - 1.0f); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff); +} + +static void +test_global_fragment_hook (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + pipeline = cogl_pipeline_new (test_ctx); + + /* Creates a function in the global declarations hook which is used + * by a subsequent snippet. The subsequent snippets replace any + * previous snippets but this shouldn't prevent the global + * declarations from being generated */ + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS, + /* declarations */ + "float\n" + "multiply_by_four (float number)\n" + "{\n" + " return number * 4.0;\n" + "}\n", + /* post */ + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_snippet_set_pre (snippet, + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_snippet_set_replace (snippet, + "This string shouldn't be used so " + "we can safely put garbage in here."); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, /* declarations */ + NULL /* replace */); + cogl_snippet_set_replace (snippet, + "cogl_color_out.r = multiply_by_four (0.25);\n" + "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + 0, 0, 10, 10); + + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff); +} + +static void +test_snippet_order (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + + /* Verify that the snippets are executed in the right order. We'll + replace the r component of the color in the pre sections of the + snippets and the g component in the post. The pre sections should + be executed in the reverse order they were added and the post + sections in the same order as they were added. Therefore the r + component should be taken from the the second snippet and the g + component from the first */ + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + "cogl_color_out.g = 0.5;\n"); + cogl_snippet_set_pre (snippet, "cogl_color_out.r = 0.5;\n"); + cogl_snippet_set_replace (snippet, "cogl_color_out.ba = vec2 (0.0, 1.0);"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + "cogl_color_out.g = 1.0;\n"); + cogl_snippet_set_pre (snippet, "cogl_color_out.r = 1.0;\n"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 160, 0, 170, 10); + cogl_object_unref (pipeline); + + test_utils_check_pixel (test_fb, 165, 5, 0x80ff00ff); +} + +static void +test_naming_texture_units (TestState *state) +{ + CoglPipeline *pipeline; + CoglSnippet *snippet; + CoglTexture *tex1, *tex2; + + /* Test that we can sample from an arbitrary texture unit by naming + its layer number */ + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + NULL); + cogl_snippet_set_replace (snippet, + "cogl_color_out = " + "texture2D (cogl_sampler100, vec2 (0.0, 0.0)) + " + "texture2D (cogl_sampler200, vec2 (0.0, 0.0));"); + + tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff); + tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 100, tex1); + cogl_pipeline_set_layer_texture (pipeline, 200, tex2); + + cogl_pipeline_add_snippet (pipeline, snippet); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); + + cogl_object_unref (pipeline); + cogl_object_unref (snippet); + cogl_object_unref (tex1); + cogl_object_unref (tex2); + + test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); +} + +static void +test_snippet_properties (TestState *state) +{ + CoglSnippet *snippet; + + /* Sanity check modifying the snippet */ + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "foo", "bar"); + g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "foo"); + g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar"); + g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); + g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); + + cogl_snippet_set_declarations (snippet, "fu"); + g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); + g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar"); + g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); + g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); + + cogl_snippet_set_post (snippet, "ba"); + g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); + g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); + g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); + g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); + + cogl_snippet_set_pre (snippet, "fuba"); + g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); + g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); + g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); + g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba"); + + cogl_snippet_set_replace (snippet, "baba"); + g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); + g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); + g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, "baba"); + g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba"); + + g_assert_cmpint (cogl_snippet_get_hook (snippet), + ==, + COGL_SNIPPET_HOOK_FRAGMENT); +} + +static SnippetTestFunc +tests[] = + { + simple_fragment_snippet, + simple_vertex_snippet, + shared_uniform, + lots_snippets, + shared_variable_pre_post, + test_pipeline_caching, + test_replace_string, + test_texture_lookup_hook, + test_multiple_samples, + test_replace_lookup_hook, + test_replace_snippet, + test_replace_fragment_layer, + test_modify_fragment_layer, + test_modify_vertex_layer, + test_replace_vertex_layer, + test_vertex_transform_hook, + test_global_fragment_hook, + test_global_vertex_hook, + test_snippet_order, + test_naming_texture_units, + test_snippet_properties + }; + +static void +run_tests (TestState *state) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 0, 0, 0, 1); + + tests[i] (state); + } +} + +void +test_snippets (void) +{ + TestState state; + + state.fb_width = cogl_framebuffer_get_width (test_fb); + state.fb_height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + state.fb_width, + state.fb_height, + -1, + 100); + + run_tests (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-sparse-pipeline.c b/cogl/tests/conform/test-sparse-pipeline.c new file mode 100644 index 000000000..04c81d27c --- /dev/null +++ b/cogl/tests/conform/test-sparse-pipeline.c @@ -0,0 +1,62 @@ +#include +#include + +#include "test-utils.h" + +typedef struct _TestState +{ + int fb_width; + int fb_height; +} TestState; + +static void +test_sparse_layer_combine (TestState *state) +{ + CoglPipeline *pipeline; + CoglTexture *tex1, *tex2; + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + /* This tests that the TEXTURE_* numbers used in the layer combine + string refer to the layer number rather than the unit numbers by + creating a pipeline with very large layer numbers. This should + end up being mapped to much smaller unit numbers */ + + tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff); + tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 50, tex1); + cogl_pipeline_set_layer_texture (pipeline, 100, tex2); + cogl_pipeline_set_layer_combine (pipeline, 200, + "RGBA = ADD(TEXTURE_50, TEXTURE_100)", + NULL); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, -1, 1, 1); + + test_utils_check_pixel (test_fb, 2, 2, 0xffff00ff); + + cogl_object_unref (pipeline); + cogl_object_unref (tex1); + cogl_object_unref (tex2); +} + +void +test_sparse_pipeline (void) +{ + TestState state; + + state.fb_width = cogl_framebuffer_get_width (test_fb); + state.fb_height = cogl_framebuffer_get_height (test_fb); + + test_sparse_layer_combine (&state); + + /* FIXME: This should have a lot more tests, for example testing + whether using an attribute with sparse texture coordinates will + work */ + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-sub-texture.c b/cogl/tests/conform/test-sub-texture.c new file mode 100644 index 000000000..f049f3fae --- /dev/null +++ b/cogl/tests/conform/test-sub-texture.c @@ -0,0 +1,325 @@ +#include +#include + +#include "test-utils.h" + +#define SOURCE_SIZE 32 +#define SOURCE_DIVISIONS_X 2 +#define SOURCE_DIVISIONS_Y 2 +#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) +#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) + +#define TEST_INSET 1 + +static const uint32_t +corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = + { + 0xff0000ff, /* red top left */ + 0x00ff00ff, /* green top right */ + 0x0000ffff, /* blue bottom left */ + 0xff00ffff /* purple bottom right */ + }; + +typedef struct _TestState +{ + CoglTexture2D *tex; +} TestState; + +static CoglTexture2D * +create_source (TestState *state) +{ + int dx, dy; + uint8_t *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4); + CoglTexture2D *tex; + + /* Create a texture with a different coloured rectangle at each + corner */ + for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++) + for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++) + { + uint8_t *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 + + dx * DIVISION_WIDTH * 4); + int x, y; + + for (y = 0; y < DIVISION_HEIGHT; y++) + { + for (x = 0; x < DIVISION_WIDTH; x++) + { + uint32_t color = GUINT32_FROM_BE (corner_colors[dx + dy * SOURCE_DIVISIONS_X]); + memcpy (p, &color, 4); + p += 4; + } + + p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4; + } + } + + tex = cogl_texture_2d_new_from_data (test_ctx, + SOURCE_SIZE, SOURCE_SIZE, + COGL_PIXEL_FORMAT_RGBA_8888, + SOURCE_SIZE * 4, + data, + NULL); + return tex; +} + +static CoglTexture2D * +create_test_texture (TestState *state) +{ + CoglTexture2D *tex; + uint8_t *data = g_malloc (256 * 256 * 4), *p = data; + int x, y; + + /* Create a texture that is 256x256 where the red component ranges + from 0->255 along the x axis and the green component ranges from + 0->255 along the y axis. The blue and alpha components are all + 255 */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = x; + *(p++) = y; + *(p++) = 255; + *(p++) = 255; + } + + tex = cogl_texture_2d_new_from_data (test_ctx, + 256, 256, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 256 * 4, + data, + NULL); + g_free (data); + + return tex; +} + +static void +paint (TestState *state) +{ + CoglTexture2D *full_texture; + CoglSubTexture *sub_texture, *sub_sub_texture; + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + + /* Create a sub texture of the bottom right quarter of the texture */ + sub_texture = cogl_sub_texture_new (test_ctx, + state->tex, + DIVISION_WIDTH, + DIVISION_HEIGHT, + DIVISION_WIDTH, + DIVISION_HEIGHT); + + /* Paint it */ + cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture); + cogl_object_unref (sub_texture); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, + 0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT); + + + /* Repeat a sub texture of the top half of the full texture. This is + documented to be undefined so it doesn't technically have to work + but it will with the current implementation */ + sub_texture = cogl_sub_texture_new (test_ctx, + state->tex, + 0, 0, + SOURCE_SIZE, + DIVISION_HEIGHT); + cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture); + cogl_object_unref (sub_texture); + cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, + 0.0f, + SOURCE_SIZE, + SOURCE_SIZE * 2.0f, + SOURCE_SIZE * 1.5f, + 0.0f, 0.0f, + 2.0f, 1.0f); + + /* Create a sub texture of a sub texture */ + full_texture = create_test_texture (state); + sub_texture = cogl_sub_texture_new (test_ctx, + full_texture, + 20, 10, 30, 20); + cogl_object_unref (full_texture); + sub_sub_texture = cogl_sub_texture_new (test_ctx, + sub_texture, + 20, 10, 10, 10); + cogl_object_unref (sub_texture); + cogl_pipeline_set_layer_texture (pipeline, 0, sub_sub_texture); + cogl_object_unref (sub_sub_texture); + cogl_framebuffer_draw_rectangle (test_fb, pipeline, + 0.0f, SOURCE_SIZE * 2.0f, + 10.0f, SOURCE_SIZE * 2.0f + 10.0f); + + cogl_object_unref (pipeline); +} + +static void +validate_part (int xpos, int ypos, + int width, int height, + uint32_t color) +{ + test_utils_check_region (test_fb, + xpos + TEST_INSET, + ypos + TEST_INSET, + width - TEST_INSET - 2, + height - TEST_INSET - 2, + color); +} + +static uint8_t * +create_update_data (void) +{ + uint8_t *data = g_malloc (256 * 256 * 4), *p = data; + int x, y; + + /* Create some image data that is 256x256 where the blue component + ranges from 0->255 along the x axis and the alpha component + ranges from 0->255 along the y axis. The red and green components + are all zero */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = 0; + *(p++) = 0; + *(p++) = x; + *(p++) = y; + } + + return data; +} + +static void +validate_result (TestState *state) +{ + int i, division_num, x, y; + CoglTexture2D *test_tex; + CoglSubTexture *sub_texture; + uint8_t *texture_data, *p; + int tex_width, tex_height; + + /* Sub texture of the bottom right corner of the texture */ + validate_part (0, 0, DIVISION_WIDTH, DIVISION_HEIGHT, + corner_colors[ + (SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X + + SOURCE_DIVISIONS_X - 1]); + + /* Sub texture of the top half repeated horizontally */ + for (i = 0; i < 2; i++) + for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++) + validate_part (i * SOURCE_SIZE + division_num * DIVISION_WIDTH, + SOURCE_SIZE, + DIVISION_WIDTH, DIVISION_HEIGHT, + corner_colors[division_num]); + + /* Sub sub texture */ + p = texture_data = g_malloc (10 * 10 * 4); + cogl_flush (); + cogl_framebuffer_read_pixels (test_fb, + 0, SOURCE_SIZE * 2, 10, 10, + COGL_PIXEL_FORMAT_RGBA_8888, + p); + for (y = 0; y < 10; y++) + for (x = 0; x < 10; x++) + { + g_assert (*(p++) == x + 40); + g_assert (*(p++) == y + 20); + p += 2; + } + g_free (texture_data); + + /* Try reading back the texture data */ + sub_texture = cogl_sub_texture_new (test_ctx, + state->tex, + SOURCE_SIZE / 4, + SOURCE_SIZE / 4, + SOURCE_SIZE / 2, + SOURCE_SIZE / 2); + tex_width = cogl_texture_get_width (sub_texture); + tex_height = cogl_texture_get_height (sub_texture); + p = texture_data = g_malloc (tex_width * tex_height * 4); + cogl_texture_get_data (sub_texture, + COGL_PIXEL_FORMAT_RGBA_8888, + tex_width * 4, + texture_data); + for (y = 0; y < tex_height; y++) + for (x = 0; x < tex_width; x++) + { + int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) / + DIVISION_WIDTH); + int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) / + DIVISION_HEIGHT); + uint32_t reference = corner_colors[div_x + div_y * SOURCE_DIVISIONS_X] >> 8; + uint32_t color = GUINT32_FROM_BE (*((uint32_t *)p)) >> 8; + g_assert (color == reference); + p += 4; + } + g_free (texture_data); + cogl_object_unref (sub_texture); + + /* Create a 256x256 test texture */ + test_tex = create_test_texture (state); + /* Create a sub texture the views the center half of the texture */ + sub_texture = cogl_sub_texture_new (test_ctx, + test_tex, + 64, 64, 128, 128); + /* Update the center half of the sub texture */ + texture_data = create_update_data (); + cogl_texture_set_region (sub_texture, + 0, 0, 32, 32, 64, 64, 256, 256, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4, + texture_data); + g_free (texture_data); + cogl_object_unref (sub_texture); + /* Get the texture data */ + p = texture_data = g_malloc (256 * 256 * 4); + cogl_texture_get_data (test_tex, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 256 * 4, texture_data); + + /* Verify the texture data */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + /* If we're in the center quarter */ + if (x >= 96 && x < 160 && y >= 96 && y < 160) + { + g_assert ((*p++) == 0); + g_assert ((*p++) == 0); + g_assert ((*p++) == x - 96); + g_assert ((*p++) == y - 96); + } + else + { + g_assert ((*p++) == x); + g_assert ((*p++) == y); + g_assert ((*p++) == 255); + g_assert ((*p++) == 255); + } + } + g_free (texture_data); + cogl_object_unref (test_tex); +} + +void +test_sub_texture (void) +{ + TestState state; + + state.tex = create_source (&state); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + cogl_framebuffer_get_width (test_fb), + cogl_framebuffer_get_height (test_fb), + -1, + 100); + + paint (&state); + validate_result (&state); + + cogl_object_unref (state.tex); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-texture-3d.c b/cogl/tests/conform/test-texture-3d.c new file mode 100644 index 000000000..8cc5bb595 --- /dev/null +++ b/cogl/tests/conform/test-texture-3d.c @@ -0,0 +1,274 @@ +#include +#include + +#include "test-utils.h" + +#define TEX_WIDTH 4 +#define TEX_HEIGHT 8 +#define TEX_DEPTH 16 +/* Leave four bytes of padding between each row */ +#define TEX_ROWSTRIDE (TEX_WIDTH * 4 + 4) +/* Leave four rows of padding between each image */ +#define TEX_IMAGE_STRIDE ((TEX_HEIGHT + 4) * TEX_ROWSTRIDE) + +typedef struct _TestState +{ + int fb_width; + int fb_height; +} TestState; + +static CoglTexture3D * +create_texture_3d (CoglContext *context) +{ + int x, y, z; + uint8_t *data = g_malloc (TEX_IMAGE_STRIDE * TEX_DEPTH); + uint8_t *p = data; + CoglTexture3D *tex; + CoglError *error = NULL; + + for (z = 0; z < TEX_DEPTH; z++) + { + for (y = 0; y < TEX_HEIGHT; y++) + { + for (x = 0; x < TEX_WIDTH; x++) + { + /* Set red, green, blue to values based on x, y, z */ + *(p++) = 255 - x * 8; + *(p++) = y * 8; + *(p++) = 255 - z * 8; + /* Fully opaque */ + *(p++) = 0xff; + } + + /* Set the padding between rows to 0xde */ + memset (p, 0xde, TEX_ROWSTRIDE - (TEX_WIDTH * 4)); + p += TEX_ROWSTRIDE - (TEX_WIDTH * 4); + } + /* Set the padding between images to 0xad */ + memset (p, 0xba, TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE)); + p += TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE); + } + + tex = cogl_texture_3d_new_from_data (context, + TEX_WIDTH, TEX_HEIGHT, TEX_DEPTH, + COGL_PIXEL_FORMAT_RGBA_8888, + TEX_ROWSTRIDE, + TEX_IMAGE_STRIDE, + data, + &error); + + if (tex == NULL) + { + g_assert (error != NULL); + g_warning ("Failed to create 3D texture: %s", error->message); + g_assert_not_reached (); + } + + g_free (data); + + return tex; +} + +static void +draw_frame (TestState *state) +{ + CoglTexture *tex = create_texture_3d (test_ctx); + CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); + typedef struct { float x, y, s, t, r; } Vert; + CoglPrimitive *primitive; + CoglAttributeBuffer *attribute_buffer; + CoglAttribute *attributes[2]; + Vert *verts, *v; + int i; + + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_object_unref (tex); + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + /* Render the texture repeated horizontally twice using a regular + cogl rectangle. This should end up with the r texture coordinates + as zero */ + cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, + 0.0f, 0.0f, TEX_WIDTH * 2, TEX_HEIGHT, + 0.0f, 0.0f, 2.0f, 1.0f); + + /* Render all of the images in the texture using coordinates from a + CoglPrimitive */ + v = verts = g_new (Vert, 4 * TEX_DEPTH); + for (i = 0; i < TEX_DEPTH; i++) + { + float r = (i + 0.5f) / TEX_DEPTH; + + v->x = i * TEX_WIDTH; + v->y = TEX_HEIGHT; + v->s = 0; + v->t = 0; + v->r = r; + v++; + + v->x = i * TEX_WIDTH; + v->y = TEX_HEIGHT * 2; + v->s = 0; + v->t = 1; + v->r = r; + v++; + + v->x = i * TEX_WIDTH + TEX_WIDTH; + v->y = TEX_HEIGHT * 2; + v->s = 1; + v->t = 1; + v->r = r; + v++; + + v->x = i * TEX_WIDTH + TEX_WIDTH; + v->y = TEX_HEIGHT; + v->s = 1; + v->t = 0; + v->r = r; + v++; + } + + attribute_buffer = cogl_attribute_buffer_new (test_ctx, + 4 * TEX_DEPTH * sizeof (Vert), + verts); + attributes[0] = cogl_attribute_new (attribute_buffer, + "cogl_position_in", + sizeof (Vert), + G_STRUCT_OFFSET (Vert, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (attribute_buffer, + "cogl_tex_coord_in", + sizeof (Vert), + G_STRUCT_OFFSET (Vert, s), + 3, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + 6 * TEX_DEPTH, + attributes, + 2 /* n_attributes */); + + cogl_primitive_set_indices (primitive, + cogl_get_rectangle_indices (test_ctx, + TEX_DEPTH), + 6 * TEX_DEPTH); + + cogl_primitive_draw (primitive, test_fb, pipeline); + + g_free (verts); + + cogl_object_unref (primitive); + cogl_object_unref (attributes[0]); + cogl_object_unref (attributes[1]); + cogl_object_unref (attribute_buffer); + cogl_object_unref (pipeline); +} + +static void +validate_block (int block_x, int block_y, int z) +{ + int x, y; + + for (y = 0; y < TEX_HEIGHT; y++) + for (x = 0; x < TEX_WIDTH; x++) + test_utils_check_pixel_rgb (test_fb, + block_x * TEX_WIDTH + x, + block_y * TEX_HEIGHT + y, + 255 - x * 8, + y * 8, + 255 - z * 8); +} + +static void +validate_result (void) +{ + int i; + + validate_block (0, 0, 0); + + for (i = 0; i < TEX_DEPTH; i++) + validate_block (i, 1, i); +} + +static void +test_multi_texture (TestState *state) +{ + CoglPipeline *pipeline; + CoglTexture3D *tex_3d; + CoglTexture2D *tex_2d; + uint8_t tex_data[4]; + + cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + /* Tests a pipeline that is using multi-texturing to combine a 3D + texture with a 2D texture. The texture from another layer is + sampled with TEXTURE_? just to pick up a specific bug that was + happening with the ARBfp fragend */ + + pipeline = cogl_pipeline_new (test_ctx); + + tex_data[0] = 0xff; + tex_data[1] = 0x00; + tex_data[2] = 0x00; + tex_data[3] = 0xff; + tex_2d = cogl_texture_2d_new_from_data (test_ctx, + 1, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + tex_data, + NULL); + cogl_pipeline_set_layer_texture (pipeline, 0, tex_2d); + + tex_data[0] = 0x00; + tex_data[1] = 0xff; + tex_data[2] = 0x00; + tex_data[3] = 0xff; + tex_3d = cogl_texture_3d_new_from_data (test_ctx, + 1, 1, 1, /* width/height/depth */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + 4, /* image_stride */ + tex_data, + NULL); + cogl_pipeline_set_layer_texture (pipeline, 1, tex_3d); + + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGBA = REPLACE(PREVIOUS)", + NULL); + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA = ADD(TEXTURE_0, TEXTURE_1)", + NULL); + + cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); + + test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); + + cogl_object_unref (tex_2d); + cogl_object_unref (tex_3d); + cogl_object_unref (pipeline); +} + +void +test_texture_3d (void) +{ + TestState state; + + state.fb_width = cogl_framebuffer_get_width (test_fb); + state.fb_height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + state.fb_width, /* x_2 */ + state.fb_height /* y_2 */, + -1, 100 /* near/far */); + + draw_frame (&state); + validate_result (); + + test_multi_texture (&state); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-texture-get-set-data.c b/cogl/tests/conform/test-texture-get-set-data.c new file mode 100644 index 000000000..59bd0f635 --- /dev/null +++ b/cogl/tests/conform/test-texture-get-set-data.c @@ -0,0 +1,144 @@ +#include + +#include + +#include "test-utils.h" + +static void +check_texture (int width, int height, TestUtilsTextureFlags flags) +{ + CoglTexture *tex; + uint8_t *data, *p; + int y, x; + int rowstride; + CoglBitmap *bmp; + + p = data = g_malloc (width * height * 4); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + *(p++) = x; + *(p++) = y; + *(p++) = 128; + *(p++) = (x ^ y); + } + + bmp = cogl_bitmap_new_for_data (test_ctx, + width, height, + COGL_PIXEL_FORMAT_RGBA_8888, + width * 4, + data); + + tex = test_utils_texture_new_from_bitmap (bmp, flags, + FALSE); + + /* Replace the bottom right quarter of the data with negated data to + test set_region */ + rowstride = width * 4; + p = data + (height / 2) * rowstride + rowstride / 2; + for (y = 0; y < height / 2; y++) + { + for (x = 0; x < width / 2; x++) + { + p[0] = ~p[0]; + p[1] = ~p[1]; + p[2] = ~p[2]; + p[3] = ~p[3]; + p += 4; + } + p += width * 2; + } + cogl_texture_set_region (tex, + width / 2, + height / 2, + width / 2, /* dest x */ + height / 2, /* dest y */ + width / 2, /* region width */ + height / 2, /* region height */ + width, /* src width */ + height, /* src height */ + COGL_PIXEL_FORMAT_RGBA_8888, + rowstride, + data); + + /* Check passing a NULL pointer and a zero rowstride. The texture + should calculate the needed data size and return it */ + g_assert_cmpint (cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_ANY, 0, NULL), + ==, + width * height * 4); + + /* Try first receiving the data as RGB. This should cause a + * conversion */ + memset (data, 0, width * height * 4); + + cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGB_888, + width * 3, data); + + p = data; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + if (x >= width / 2 && y >= height / 2) + { + g_assert_cmpint (p[0], ==, ~x & 0xff); + g_assert_cmpint (p[1], ==, ~y & 0xff); + g_assert_cmpint (p[2], ==, ~128 & 0xff); + } + else + { + g_assert_cmpint (p[0], ==, x & 0xff); + g_assert_cmpint (p[1], ==, y & 0xff); + g_assert_cmpint (p[2], ==, 128); + } + p += 3; + } + + /* Now try receiving the data as RGBA. This should not cause a + * conversion and no unpremultiplication because we explicitly set + * the internal format when we created the texture */ + memset (data, 0, width * height * 4); + + cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGBA_8888, + width * 4, data); + + p = data; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + if (x >= width / 2 && y >= height / 2) + { + g_assert_cmpint (p[0], ==, ~x & 0xff); + g_assert_cmpint (p[1], ==, ~y & 0xff); + g_assert_cmpint (p[2], ==, ~128 & 0xff); + g_assert_cmpint (p[3], ==, ~(x ^ y) & 0xff); + } + else + { + g_assert_cmpint (p[0], ==, x & 0xff); + g_assert_cmpint (p[1], ==, y & 0xff); + g_assert_cmpint (p[2], ==, 128); + g_assert_cmpint (p[3], ==, (x ^ y) & 0xff); + } + p += 4; + } + + cogl_object_unref (tex); + g_free (data); +} + +void +test_texture_get_set_data (void) +{ + /* First try without atlasing */ + check_texture (256, 256, TEST_UTILS_TEXTURE_NO_ATLAS); + /* Try again with atlasing. This should end up testing the atlas + backend and the sub texture backend */ + check_texture (256, 256, 0); + /* Try with a really big texture in the hope that it will end up + sliced. */ + check_texture (4, 5128, TEST_UTILS_TEXTURE_NO_ATLAS); + /* And in the other direction. */ + check_texture (5128, 4, TEST_UTILS_TEXTURE_NO_ATLAS); +} diff --git a/cogl/tests/conform/test-texture-mipmaps.c b/cogl/tests/conform/test-texture-mipmaps.c new file mode 100644 index 000000000..3118ba86d --- /dev/null +++ b/cogl/tests/conform/test-texture-mipmaps.c @@ -0,0 +1,136 @@ +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; + +#define TEX_SIZE 64 + +typedef struct _TestState +{ + unsigned int padding; +} TestState; + +/* Creates a texture where the pixels are evenly divided between + selecting just one of the R,G and B components */ +static CoglHandle +make_texture (void) +{ + guchar *tex_data = g_malloc (TEX_SIZE * TEX_SIZE * 3), *p = tex_data; + CoglHandle tex; + int x, y; + + for (y = 0; y < TEX_SIZE; y++) + for (x = 0; x < TEX_SIZE; x++) + { + memset (p, 0, 3); + /* Set one of the components to full. The components should be + evenly represented so that each gets a third of the + texture */ + p[(p - tex_data) / (TEX_SIZE * TEX_SIZE * 3 / 3)] = 255; + p += 3; + } + + tex = test_utils_texture_new_from_data (TEX_SIZE, TEX_SIZE, TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGB_888, + COGL_PIXEL_FORMAT_ANY, + TEX_SIZE * 3, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglHandle tex; + CoglHandle material; + uint8_t pixels[8]; + + tex = make_texture (); + material = cogl_material_new (); + cogl_material_set_layer (material, 0, tex); + cogl_handle_unref (tex); + + /* Render a 1x1 pixel quad without mipmaps */ + cogl_set_source (material); + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_rectangle (0, 0, 1, 1); + /* Then with mipmaps */ + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_rectangle (1, 0, 2, 1); + + cogl_handle_unref (material); + + /* Read back the two pixels we rendered */ + cogl_read_pixels (0, 0, 2, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixels); + + /* The first pixel should be just one of the colors from the + texture. It doesn't matter which one */ + g_assert ((pixels[0] == 255 && pixels[1] == 0 && pixels[2] == 0) || + (pixels[0] == 0 && pixels[1] == 255 && pixels[2] == 0) || + (pixels[0] == 0 && pixels[1] == 0 && pixels[2] == 255)); + /* The second pixel should be more or less the average of all of the + pixels in the texture. Each component gets a third of the image + so each component should be approximately 255/3 */ + g_assert (ABS (pixels[4] - 255 / 3) <= 3 && + ABS (pixels[5] - 255 / 3) <= 3 && + ABS (pixels[6] - 255 / 3) <= 3); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + clutter_main_quit (); +#endif +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_texture_mipmaps (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + unsigned int idle_source; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-texture-no-allocate.c b/cogl/tests/conform/test-texture-no-allocate.c new file mode 100644 index 000000000..b0199a988 --- /dev/null +++ b/cogl/tests/conform/test-texture-no-allocate.c @@ -0,0 +1,80 @@ +#include + +#include "test-utils.h" + +/* Tests that the various texture types can be freed without being + * allocated */ + +/* Texture size that is probably to big to fit within the texture + * limits */ +#define BIG_TEX_WIDTH 16384 +#define BIG_TEX_HEIGHT 128 + +void +test_texture_no_allocate (void) +{ + uint8_t *tex_data; + CoglTexture *texture; + CoglTexture2D *texture_2d; + GError *error = NULL; + + tex_data = g_malloc (BIG_TEX_WIDTH * BIG_TEX_HEIGHT * 4); + + /* NB: if we make the atlas and sliced texture APIs public then this + * could changed to explicitly use that instead of the magic texture + * API */ + + /* Try to create an atlas texture that is too big so it will + * internally be freed without allocating */ + texture = + cogl_atlas_texture_new_from_data (test_ctx, + BIG_TEX_WIDTH, + BIG_TEX_HEIGHT, + /* format */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + /* rowstride */ + BIG_TEX_WIDTH * 4, + tex_data, + &error); + + g_free (tex_data); + + /* It's ok if this causes an error, we just don't want it to + * crash */ + + if (texture == NULL) + cogl_error_free (error); + else + cogl_object_unref (texture); + + /* Try to create a sliced texture without allocating it */ + texture = + cogl_texture_2d_sliced_new_with_size (test_ctx, + BIG_TEX_WIDTH, + BIG_TEX_HEIGHT, + COGL_TEXTURE_MAX_WASTE); + cogl_object_unref (texture); + + /* 2D texture */ + texture_2d = cogl_texture_2d_new_with_size (test_ctx, + 64, 64); + cogl_object_unref (texture_2d); + + /* 3D texture */ + if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_3D)) + { + CoglTexture3D *texture_3d = + cogl_texture_3d_new_with_size (test_ctx, + 64, 64, 64); + cogl_object_unref (texture_3d); + } + + /* Rectangle texture */ + if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE)) + { + CoglTextureRectangle *texture_rect = + cogl_texture_rectangle_new_with_size (test_ctx, + 64, 64); + cogl_object_unref (texture_rect); + } +} diff --git a/cogl/tests/conform/test-texture-pixmap-x11.c b/cogl/tests/conform/test-texture-pixmap-x11.c new file mode 100644 index 000000000..4e8d55059 --- /dev/null +++ b/cogl/tests/conform/test-texture-pixmap-x11.c @@ -0,0 +1,245 @@ +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; + +#ifdef COGL_HAS_XLIB + +#include +#include + +#define PIXMAP_WIDTH 512 +#define PIXMAP_HEIGHT 256 +#define GRID_SQUARE_SIZE 16 + +/* Coordinates of a square that we'll update */ +#define PIXMAP_CHANGE_X 1 +#define PIXMAP_CHANGE_Y 1 + +typedef struct _TestState +{ + ClutterActor *stage; + CoglHandle tfp; + Pixmap pixmap; + unsigned int frame_count; + Display *display; +} TestState; + +static Pixmap +create_pixmap (TestState *state) +{ + Pixmap pixmap; + XGCValues gc_values = { 0, }; + GC black_gc, white_gc; + int screen = DefaultScreen (state->display); + int x, y; + + pixmap = XCreatePixmap (state->display, + DefaultRootWindow (state->display), + PIXMAP_WIDTH, PIXMAP_HEIGHT, + DefaultDepth (state->display, screen)); + + gc_values.foreground = BlackPixel (state->display, screen); + black_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values); + gc_values.foreground = WhitePixel (state->display, screen); + white_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values); + + /* Draw a grid of alternative black and white rectangles to the + pixmap */ + for (y = 0; y < PIXMAP_HEIGHT / GRID_SQUARE_SIZE; y++) + for (x = 0; x < PIXMAP_WIDTH / GRID_SQUARE_SIZE; x++) + XFillRectangle (state->display, pixmap, + ((x ^ y) & 1) ? black_gc : white_gc, + x * GRID_SQUARE_SIZE, + y * GRID_SQUARE_SIZE, + GRID_SQUARE_SIZE, + GRID_SQUARE_SIZE); + + XFreeGC (state->display, black_gc); + XFreeGC (state->display, white_gc); + + return pixmap; +} + +static void +update_pixmap (TestState *state) +{ + XGCValues gc_values = { 0, }; + GC black_gc; + int screen = DefaultScreen (state->display); + + gc_values.foreground = BlackPixel (state->display, screen); + black_gc = XCreateGC (state->display, state->pixmap, + GCForeground, &gc_values); + + /* Fill in one the rectangles with black */ + XFillRectangle (state->display, state->pixmap, + black_gc, + PIXMAP_CHANGE_X * GRID_SQUARE_SIZE, + PIXMAP_CHANGE_Y * GRID_SQUARE_SIZE, + GRID_SQUARE_SIZE, GRID_SQUARE_SIZE); + + XFreeGC (state->display, black_gc); +} + +static CoglBool +check_paint (TestState *state, int x, int y, int scale) +{ + uint8_t *data, *p, update_value = 0; + + p = data = g_malloc (PIXMAP_WIDTH * PIXMAP_HEIGHT * 4); + + cogl_read_pixels (x, y, PIXMAP_WIDTH / scale, PIXMAP_HEIGHT / scale, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + data); + + for (y = 0; y < PIXMAP_HEIGHT / scale; y++) + for (x = 0; x < PIXMAP_WIDTH / scale; x++) + { + int grid_x = x * scale / GRID_SQUARE_SIZE; + int grid_y = y * scale / GRID_SQUARE_SIZE; + + /* If this is the updatable square then we'll let it be either + color but we'll return which one it was */ + if (grid_x == PIXMAP_CHANGE_X && grid_y == PIXMAP_CHANGE_Y) + { + if (x % (GRID_SQUARE_SIZE / scale) == 0 && + y % (GRID_SQUARE_SIZE / scale) == 0) + update_value = *p; + else + g_assert_cmpint (p[0], ==, update_value); + + g_assert (p[1] == update_value); + g_assert (p[2] == update_value); + p += 4; + } + else + { + uint8_t value = ((grid_x ^ grid_y) & 1) ? 0x00 : 0xff; + g_assert_cmpint (*(p++), ==, value); + g_assert_cmpint (*(p++), ==, value); + g_assert_cmpint (*(p++), ==, value); + p++; + } + } + + g_free (data); + + return update_value == 0x00; +} + +/* We skip these frames first */ +#define FRAME_COUNT_BASE 5 +/* First paint the tfp with no mipmaps */ +#define FRAME_COUNT_NORMAL 6 +/* Then use mipmaps */ +#define FRAME_COUNT_MIPMAP 7 +/* After this frame will start waiting for the pixmap to change */ +#define FRAME_COUNT_UPDATED 8 + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglHandle material; + + material = cogl_material_new (); + cogl_material_set_layer (material, 0, state->tfp); + if (state->frame_count == FRAME_COUNT_MIPMAP) + { + const CoglMaterialFilter min_filter = + COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST; + cogl_material_set_layer_filters (material, 0, + min_filter, + COGL_MATERIAL_FILTER_NEAREST); + } + else + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_set_source (material); + + cogl_rectangle (0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT); + + cogl_rectangle (0, PIXMAP_HEIGHT, + PIXMAP_WIDTH / 4, PIXMAP_HEIGHT * 5 / 4); + + if (state->frame_count >= 5) + { + CoglBool big_updated, small_updated; + + big_updated = check_paint (state, 0, 0, 1); + small_updated = check_paint (state, 0, PIXMAP_HEIGHT, 4); + + g_assert (big_updated == small_updated); + + if (state->frame_count < FRAME_COUNT_UPDATED) + g_assert (big_updated == FALSE); + else if (state->frame_count == FRAME_COUNT_UPDATED) + /* Change the pixmap and keep drawing until it updates */ + update_pixmap (state); + else if (big_updated) + /* If we successfully got the update then the test is over */ + clutter_main_quit (); + } + + state->frame_count++; +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +#endif /* COGL_HAS_XLIB */ + +void +test_texture_pixmap_x11 (TestUtilsGTestFixture *fixture, + void *data) +{ +#ifdef COGL_HAS_XLIB + + TestState state; + unsigned int idle_handler; + unsigned int paint_handler; + + state.frame_count = 0; + state.stage = clutter_stage_get_default (); + + state.display = clutter_x11_get_default_display (); + + state.pixmap = create_pixmap (&state); + state.tfp = cogl_texture_pixmap_x11_new (state.pixmap, TRUE); + + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + paint_handler = g_signal_connect_after (state.stage, "paint", + G_CALLBACK (on_paint), &state); + + idle_handler = g_idle_add (queue_redraw, state.stage); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_signal_handler_disconnect (state.stage, paint_handler); + + g_source_remove (idle_handler); + + XFreePixmap (state.display, state.pixmap); + + if (cogl_test_verbose ()) + g_print ("OK\n"); + +#else /* COGL_HAS_XLIB */ + + if (cogl_test_verbose ()) + g_print ("Skipping\n"); + +#endif /* COGL_HAS_XLIB */ +} + diff --git a/cogl/tests/conform/test-texture-rectangle.c b/cogl/tests/conform/test-texture-rectangle.c new file mode 100644 index 000000000..3784cdcfe --- /dev/null +++ b/cogl/tests/conform/test-texture-rectangle.c @@ -0,0 +1,276 @@ +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; +} TestState; + +static CoglHandle +create_source_rect (void) +{ +#ifdef GL_TEXTURE_RECTANGLE_ARB + + int x, y; + GLint prev_unpack_row_length; + GLint prev_unpack_alignment; + GLint prev_unpack_skip_rows; + GLint prev_unpack_skip_pixles; + GLint prev_rectangle_binding; + uint8_t *data = g_malloc (256 * 256 * 4), *p = data; + CoglHandle tex; + GLuint gl_tex; + + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = x; + *(p++) = y; + *(p++) = 0; + *(p++) = 255; + } + + /* We are about to use OpenGL directly to create a TEXTURE_RECTANGLE + * texture so we need to save the state that we modify so we can + * restore it afterwards and be sure not to interfere with any state + * caching that Cogl may do internally. + */ + glGetIntegerv (GL_UNPACK_ROW_LENGTH, &prev_unpack_row_length); + glGetIntegerv (GL_UNPACK_ALIGNMENT, &prev_unpack_alignment); + glGetIntegerv (GL_UNPACK_SKIP_ROWS, &prev_unpack_skip_rows); + glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &prev_unpack_skip_pixles); + glGetIntegerv (GL_TEXTURE_BINDING_RECTANGLE_ARB, &prev_rectangle_binding); + + glPixelStorei (GL_UNPACK_ROW_LENGTH, 256); + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); + + glGenTextures (1, &gl_tex); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, gl_tex); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, + GL_RGBA, 256, 256, 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + data); + + /* Now restore the original GL state as Cogl had left it */ + glPixelStorei (GL_UNPACK_ROW_LENGTH, prev_unpack_row_length); + glPixelStorei (GL_UNPACK_ALIGNMENT, prev_unpack_alignment); + glPixelStorei (GL_UNPACK_SKIP_ROWS, prev_unpack_skip_rows); + glPixelStorei (GL_UNPACK_SKIP_PIXELS, prev_unpack_skip_pixles); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, prev_rectangle_binding); + + g_assert (glGetError () == GL_NO_ERROR); + + g_free (data); + + tex = test_utils_texture_new_from_foreign (gl_tex, + GL_TEXTURE_RECTANGLE_ARB, + 256, 256, 0, 0, + COGL_PIXEL_FORMAT_RGBA_8888); + + return tex; + +#else /* GL_TEXTURE_RECTANGLE_ARB */ + + return NULL; + +#endif /* GL_TEXTURE_RECTANGLE_ARB */ +} + +static CoglHandle +create_source_2d (void) +{ + int x, y; + uint8_t *data = g_malloc (256 * 256 * 4), *p = data; + CoglHandle tex; + + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = 0; + *(p++) = x; + *(p++) = y; + *(p++) = 255; + } + + tex = test_utils_texture_new_from_data (256, 256, TEST_UTILS_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + 256 * 4, + data); + + g_free (data); + + return tex; +} + +static void +draw_frame (TestState *state) +{ + GLuint gl_tex; + CoglHandle tex_rect = create_source_rect (); + CoglHandle material_rect = cogl_material_new (); + CoglHandle tex_2d = create_source_2d (); + CoglHandle material_2d = cogl_material_new (); + + g_assert (tex_rect != NULL); + + cogl_material_set_layer (material_rect, 0, tex_rect); + cogl_material_set_layer_filters (material_rect, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + + cogl_material_set_layer (material_2d, 0, tex_2d); + cogl_material_set_layer_filters (material_2d, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + + cogl_set_source (material_rect); + + /* Render the texture repeated horizontally twice */ + cogl_rectangle_with_texture_coords (0.0f, 0.0f, 512.0f, 256.0f, + 0.0f, 0.0f, 2.0f, 1.0f); + /* Render the top half of the texture to test without repeating */ + cogl_rectangle_with_texture_coords (0.0f, 256.0f, 256.0f, 384.0f, + 0.0f, 0.0f, 1.0f, 0.5f); + + cogl_set_source (material_2d); + + /* Render the top half of a regular 2D texture */ + cogl_rectangle_with_texture_coords (256.0f, 256.0f, 512.0f, 384.0f, + 0.0f, 0.0f, 1.0f, 0.5f); + + /* Flush the rendering now so we can safely delete the texture */ + cogl_flush (); + + cogl_handle_unref (material_rect); + + /* Cogl doesn't destroy foreign textures so we have to do it manually */ + cogl_texture_get_gl_texture (tex_rect, &gl_tex, NULL); + glDeleteTextures (1, &gl_tex); + cogl_handle_unref (tex_rect); +} + +static void +validate_result (TestState *state) +{ + uint8_t *data, *p; + int x, y; + + p = data = g_malloc (512 * 384 * 4); + + cogl_read_pixels (0, 0, 512, 384, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); + + for (y = 0; y < 384; y++) + for (x = 0; x < 512; x++) + { + if (x >= 256 && y >= 256) + { + g_assert_cmpint (p[0], ==, 0); + g_assert_cmpint (p[1], ==, x & 0xff); + g_assert_cmpint (p[2], ==, y & 0xff); + } + else + { + g_assert_cmpint (p[0], ==, x & 0xff); + g_assert_cmpint (p[1], ==, y & 0xff); + g_assert_cmpint (p[2], ==, 0); + } + p += 4; + } + + g_free (data); + + /* Comment this out to see what the test paints */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + draw_frame (state); + + validate_result (state); +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +static CoglBool +check_rectangle_extension (void) +{ + static const char rect_extension[] = "GL_ARB_texture_rectangle"; + const char *extensions = (const char *) glGetString (GL_EXTENSIONS); + const char *extensions_end; + + extensions_end = extensions + strlen (extensions); + + while (extensions < extensions_end) + { + const char *end = strchr (extensions, ' '); + + if (end == NULL) + end = extensions_end; + + if (end - extensions == sizeof (rect_extension) - 1 && + !memcmp (extensions, rect_extension, sizeof (rect_extension) - 1)) + return TRUE; + + extensions = end + 1; + } + + return FALSE; +} + +void +test_texture_rectangle (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + unsigned int idle_source; + unsigned int paint_handler; + + state.stage = clutter_stage_get_default (); + + /* Check whether GL supports the rectangle extension. If not we'll + just assume the test passes */ + if (check_rectangle_extension ()) + { + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, state.stage); + + paint_handler = g_signal_connect_after (state.stage, "paint", + G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_source_remove (idle_source); + g_signal_handler_disconnect (state.stage, paint_handler); + + if (cogl_test_verbose ()) + g_print ("OK\n"); + } + else if (cogl_test_verbose ()) + g_print ("Skipping\n"); +} + diff --git a/cogl/tests/conform/test-texture-rg.c b/cogl/tests/conform/test-texture-rg.c new file mode 100644 index 000000000..72a5ae930 --- /dev/null +++ b/cogl/tests/conform/test-texture-rg.c @@ -0,0 +1,74 @@ +#include + +#include + +#include "test-utils.h" + +#define TEX_WIDTH 8 +#define TEX_HEIGHT 8 + +static CoglTexture2D * +make_texture (void) +{ + uint8_t tex_data[TEX_WIDTH * TEX_HEIGHT * 2], *p = tex_data; + int x, y; + + for (y = 0; y < TEX_HEIGHT; y++) + for (x = 0; x < TEX_WIDTH; x++) + { + *(p++) = x * 256 / TEX_WIDTH; + *(p++) = y * 256 / TEX_HEIGHT; + } + + return cogl_texture_2d_new_from_data (test_ctx, + TEX_WIDTH, TEX_HEIGHT, + COGL_PIXEL_FORMAT_RG_88, + TEX_WIDTH * 2, + tex_data, + NULL); +} + +void +test_texture_rg (void) +{ + CoglPipeline *pipeline; + CoglTexture2D *tex; + int fb_width, fb_height; + int x, y; + + fb_width = cogl_framebuffer_get_width (test_fb); + fb_height = cogl_framebuffer_get_height (test_fb); + + tex = make_texture (); + + g_assert (cogl_texture_get_components (tex) == COGL_TEXTURE_COMPONENTS_RG); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_texture (pipeline, 0, tex); + cogl_pipeline_set_layer_filters (pipeline, + 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + cogl_framebuffer_draw_rectangle (test_fb, + pipeline, + -1.0f, 1.0f, + 1.0f, -1.0f); + + for (y = 0; y < TEX_HEIGHT; y++) + for (x = 0; x < TEX_WIDTH; x++) + { + test_utils_check_pixel_rgb (test_fb, + x * fb_width / TEX_WIDTH + + fb_width / (TEX_WIDTH * 2), + y * fb_height / TEX_HEIGHT + + fb_height / (TEX_HEIGHT * 2), + x * 256 / TEX_WIDTH, + y * 256 / TEX_HEIGHT, + 0); + } + + cogl_object_unref (pipeline); + cogl_object_unref (tex); +} diff --git a/cogl/tests/conform/test-version.c b/cogl/tests/conform/test-version.c new file mode 100644 index 000000000..b651165e8 --- /dev/null +++ b/cogl/tests/conform/test-version.c @@ -0,0 +1,85 @@ +#include + +/* These will be redefined in config.h */ +#undef COGL_ENABLE_EXPERIMENTAL_2_0_API +#undef COGL_ENABLE_EXPERIMENTAL_API + +#include "test-utils.h" +#include "config.h" + +/* So we can use _COGL_STATIC_ASSERT we include the internal + * cogl-util.h header. Since internal headers explicitly guard against + * applications including them directly instead of including + * we define __COGL_H_INSIDE__ here to subvert those + * guards in this case... */ +#define __COGL_H_INSIDE__ +#include +#undef __COGL_H_INSIDE__ + +_COGL_STATIC_ASSERT (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO) == + COGL_VERSION, + "The pre-encoded Cogl version does not match the version " + "encoding macro"); + +_COGL_STATIC_ASSERT (COGL_VERSION_GET_MAJOR (COGL_VERSION_ENCODE (100, + 200, + 300)) == + 100, + "Getting the major component out of a encoded version " + "does not work"); +_COGL_STATIC_ASSERT (COGL_VERSION_GET_MINOR (COGL_VERSION_ENCODE (100, + 200, + 300)) == + 200, + "Getting the minor component out of a encoded version " + "does not work"); +_COGL_STATIC_ASSERT (COGL_VERSION_GET_MICRO (COGL_VERSION_ENCODE (100, + 200, + 300)) == + 300, + "Getting the micro component out of a encoded version " + "does not work"); + +_COGL_STATIC_ASSERT (COGL_VERSION_CHECK (COGL_VERSION_MAJOR, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO), + "Checking the Cogl version against the current version " + "does not pass"); +_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO + 1), + "Checking the Cogl version against a later micro version " + "should not pass"); +_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR, + COGL_VERSION_MINOR + 1, + COGL_VERSION_MICRO), + "Checking the Cogl version against a later minor version " + "should not pass"); +_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR + 1, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO), + "Checking the Cogl version against a later major version " + "should not pass"); + +_COGL_STATIC_ASSERT (COGL_VERSION_CHECK (COGL_VERSION_MAJOR - 1, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO), + "Checking the Cogl version against a older major version " + "should pass"); + +void +test_version (void) +{ + const char *version = g_strdup_printf ("version = %i.%i.%i", + COGL_VERSION_MAJOR, + COGL_VERSION_MINOR, + COGL_VERSION_MICRO); + + g_assert_cmpstr (version, ==, "version = " COGL_VERSION_STRING); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-vertex-buffer-contiguous.c b/cogl/tests/conform/test-vertex-buffer-contiguous.c new file mode 100644 index 000000000..1cd7b456b --- /dev/null +++ b/cogl/tests/conform/test-vertex-buffer-contiguous.c @@ -0,0 +1,257 @@ + +#include +#include + +#include "test-conform-common.h" + +/* This test verifies that the simplest usage of the vertex buffer API, + * where we add contiguous (x,y) GLfloat vertices, and RGBA GLubyte color + * attributes to a buffer, submit, and draw. + * + * It also tries to verify that the enable/disable attribute APIs are working + * too. + * + * If you want visual feedback of what this test paints for debugging purposes, + * then remove the call to clutter_main_quit() in validate_result. + */ + +typedef struct _TestState +{ + CoglHandle buffer; + CoglHandle texture; + CoglHandle material; + ClutterGeometry stage_geom; +} TestState; + +static void +validate_result (TestState *state) +{ + GLubyte pixel[4]; + GLint y_off = 90; + + if (cogl_test_verbose ()) + g_print ("y_off = %d\n", y_off); + + /* NB: We ignore the alpha, since we don't know if our render target is + * RGB or RGBA */ + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + + /* Should see a blue pixel */ + cogl_read_pixels (10, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0); + + /* Should see a red pixel */ + cogl_read_pixels (110, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0); + + /* Should see a blue pixel */ + cogl_read_pixels (210, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 2 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0); + + /* Should see a green pixel, at bottom of 4th triangle */ + cogl_read_pixels (310, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 3 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[GREEN] > pixel[RED] && pixel[GREEN] > pixel[BLUE]); + + /* Should see a red pixel, at top of 4th triangle */ + cogl_read_pixels (310, y_off - 70, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 4 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] > pixel[GREEN] && pixel[RED] > pixel[BLUE]); + + +#undef RED +#undef GREEN +#undef BLUE + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + /* Draw a faded blue triangle */ + cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue"); + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + /* Draw a red triangle */ + /* Here we are testing that the disable attribute works; if it doesn't + * the triangle will remain faded blue */ + cogl_translate (100, 0, 0); + cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue"); + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + /* Draw a faded blue triangle */ + /* Here we are testing that the re-enable works; if it doesn't + * the triangle will remain red */ + cogl_translate (100, 0, 0); + cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue"); + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + /* Draw a textured triangle */ + cogl_translate (100, 0, 0); + cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue"); + cogl_set_source (state->material); + cogl_material_set_color4ub (state->material, 0xff, 0xff, 0xff, 0xff); + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + validate_result (state); +} + +static CoglBool +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + + + +void +test_vertex_buffer_contiguous (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; + ClutterActor *group; + unsigned int idle_source; + guchar tex_data[] = { + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff + }; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr); + clutter_actor_get_geometry (stage, &state.stage_geom); + + group = clutter_group_new (); + clutter_actor_set_size (group, + state.stage_geom.width, + state.stage_geom.height); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing incase someone comments out the + * clutter_main_quit and wants visual feedback for the test since we + * wont be doing anything else that will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + state.texture = cogl_texture_new_from_data (2, 2, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_ANY, + 0, /* auto calc row stride */ + tex_data); + + state.material = cogl_material_new (); + cogl_material_set_color4ub (state.material, 0x00, 0xff, 0x00, 0xff); + cogl_material_set_layer (state.material, 0, state.texture); + + { + GLfloat triangle_verts[3][2] = + { + {0.0, 0.0}, + {100.0, 100.0}, + {0.0, 100.0} + }; + GLbyte triangle_colors[3][4] = + { + {0x00, 0x00, 0xff, 0xff}, /* blue */ + {0x00, 0x00, 0xff, 0x00}, /* transparent blue */ + {0x00, 0x00, 0xff, 0x00} /* transparent blue */ + }; + GLfloat triangle_tex_coords[3][2] = + { + {0.0, 0.0}, + {1.0, 1.0}, + {0.0, 1.0} + }; + state.buffer = cogl_vertex_buffer_new (3 /* n vertices */); + cogl_vertex_buffer_add (state.buffer, + "gl_Vertex", + 2, /* n components */ + GL_FLOAT, + FALSE, /* normalized */ + 0, /* stride */ + triangle_verts); + cogl_vertex_buffer_add (state.buffer, + "gl_Color::blue", + 4, /* n components */ + GL_UNSIGNED_BYTE, + FALSE, /* normalized */ + 0, /* stride */ + triangle_colors); + cogl_vertex_buffer_add (state.buffer, + "gl_MultiTexCoord0", + 2, /* n components */ + GL_FLOAT, + FALSE, /* normalized */ + 0, /* stride */ + triangle_tex_coords); + + cogl_vertex_buffer_submit (state.buffer); + } + + clutter_actor_show_all (stage); + + clutter_main (); + + cogl_handle_unref (state.buffer); + cogl_handle_unref (state.material); + cogl_handle_unref (state.texture); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-vertex-buffer-interleved.c b/cogl/tests/conform/test-vertex-buffer-interleved.c new file mode 100644 index 000000000..2d31e364a --- /dev/null +++ b/cogl/tests/conform/test-vertex-buffer-interleved.c @@ -0,0 +1,162 @@ + +#include +#include + +#include "test-conform-common.h" + +/* This test verifies that interleved attributes work with the vertex buffer + * API. We add (x,y) GLfloat vertices, interleved with RGBA GLubyte color + * attributes to a buffer, submit and draw. + * + * If you want visual feedback of what this test paints for debugging purposes, + * then remove the call to clutter_main_quit() in validate_result. + */ + +typedef struct _TestState +{ + CoglHandle buffer; + ClutterGeometry stage_geom; +} TestState; + +typedef struct _InterlevedVertex +{ + GLfloat x; + GLfloat y; + + GLubyte r; + GLubyte g; + GLubyte b; + GLubyte a; +} InterlevedVertex; + + +static void +validate_result (TestState *state) +{ + GLubyte pixel[4]; + GLint y_off = 90; + + /* NB: We ignore the alpha, since we don't know if our render target is + * RGB or RGBA */ + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + + /* Should see a blue pixel */ + cogl_read_pixels (10, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0); + +#undef RED +#undef GREEN +#undef BLUE + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + /* Draw a faded blue triangle */ + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + validate_result (state); +} + +static CoglBool +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_vertex_buffer_interleved (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; + ClutterActor *group; + unsigned int idle_source; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr); + clutter_actor_get_geometry (stage, &state.stage_geom); + + group = clutter_group_new (); + clutter_actor_set_size (group, + state.stage_geom.width, + state.stage_geom.height); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing incase someone comments out the + * clutter_main_quit and wants visual feedback for the test since we + * wont be doing anything else that will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + { + InterlevedVertex verts[3] = + { + { /* .x = */ 0.0, /* .y = */ 0.0, + /* blue */ + /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0xff }, + + { /* .x = */ 100.0, /* .y = */ 100.0, + /* transparent blue */ + /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 }, + + { /* .x = */ 0.0, /* .y = */ 100.0, + /* transparent blue */ + /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 }, + }; + + /* We assume the compiler is doing no funny struct padding for this test: + */ + g_assert (sizeof (InterlevedVertex) == 12); + + state.buffer = cogl_vertex_buffer_new (3 /* n vertices */); + cogl_vertex_buffer_add (state.buffer, + "gl_Vertex", + 2, /* n components */ + GL_FLOAT, + FALSE, /* normalized */ + 12, /* stride */ + &verts[0].x); + cogl_vertex_buffer_add (state.buffer, + "gl_Color", + 4, /* n components */ + GL_UNSIGNED_BYTE, + FALSE, /* normalized */ + 12, /* stride */ + &verts[0].r); + cogl_vertex_buffer_submit (state.buffer); + } + + clutter_actor_show_all (stage); + + clutter_main (); + + cogl_handle_unref (state.buffer); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-vertex-buffer-mutability.c b/cogl/tests/conform/test-vertex-buffer-mutability.c new file mode 100644 index 000000000..9c8d5da8c --- /dev/null +++ b/cogl/tests/conform/test-vertex-buffer-mutability.c @@ -0,0 +1,198 @@ + +#include +#include + +#include "test-conform-common.h" + +/* This test verifies that modifying a vertex buffer works, by updating + * vertex positions, and deleting and re-adding different color attributes. + * + * If you want visual feedback of what this test paints for debugging purposes, + * then remove the call to clutter_main_quit() in validate_result. + */ + +typedef struct _TestState +{ + CoglHandle buffer; + ClutterGeometry stage_geom; +} TestState; + +static void +validate_result (TestState *state) +{ + GLubyte pixel[4]; + GLint y_off = 90; + + /* NB: We ignore the alpha, since we don't know if our render target is + * RGB or RGBA */ + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + + /* Should see a red pixel */ + cogl_read_pixels (110, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0); + + /* Should see a green pixel */ + cogl_read_pixels (210, y_off, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + if (cogl_test_verbose ()) + g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]); + g_assert (pixel[RED] == 0 && pixel[GREEN] != 0 && pixel[BLUE] == 0); + +#undef RED +#undef GREEN +#undef BLUE + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + GLfloat triangle_verts[3][2] = + { + {100.0, 0.0}, + {200.0, 100.0}, + {100.0, 100.0} + }; + GLbyte triangle_colors[3][4] = + { + {0x00, 0xff, 0x00, 0xff}, /* blue */ + {0x00, 0xff, 0x00, 0x00}, /* transparent blue */ + {0x00, 0xff, 0x00, 0x00} /* transparent blue */ + }; + + /* + * Draw a red triangle + */ + + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + + cogl_vertex_buffer_add (state->buffer, + "gl_Vertex", + 2, /* n components */ + GL_FLOAT, + FALSE, /* normalized */ + 0, /* stride */ + triangle_verts); + cogl_vertex_buffer_delete (state->buffer, "gl_Color"); + cogl_vertex_buffer_submit (state->buffer); + + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + /* + * Draw a faded green triangle + */ + + cogl_vertex_buffer_add (state->buffer, + "gl_Color", + 4, /* n components */ + GL_UNSIGNED_BYTE, + FALSE, /* normalized */ + 0, /* stride */ + triangle_colors); + cogl_vertex_buffer_submit (state->buffer); + + cogl_translate (100, 0, 0); + cogl_vertex_buffer_draw (state->buffer, + GL_TRIANGLE_STRIP, /* mode */ + 0, /* first */ + 3); /* count */ + + validate_result (state); +} + +static CoglBool +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_vertex_buffer_mutability (TestUtilsGTestFixture *fixture, + void *data) +{ + TestState state; + ClutterActor *stage; + ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; + ClutterActor *group; + unsigned int idle_source; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr); + clutter_actor_get_geometry (stage, &state.stage_geom); + + group = clutter_group_new (); + clutter_actor_set_size (group, + state.stage_geom.width, + state.stage_geom.height); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing incase someone comments out the + * clutter_main_quit and wants visual feedback for the test since we + * wont be doing anything else that will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + { + GLfloat triangle_verts[3][2] = + { + {0.0, 0.0}, + {100.0, 100.0}, + {0.0, 100.0} + }; + GLbyte triangle_colors[3][4] = + { + {0x00, 0x00, 0xff, 0xff}, /* blue */ + {0x00, 0x00, 0xff, 0x00}, /* transparent blue */ + {0x00, 0x00, 0xff, 0x00} /* transparent blue */ + }; + state.buffer = cogl_vertex_buffer_new (3 /* n vertices */); + cogl_vertex_buffer_add (state.buffer, + "gl_Vertex", + 2, /* n components */ + GL_FLOAT, + FALSE, /* normalized */ + 0, /* stride */ + triangle_verts); + cogl_vertex_buffer_add (state.buffer, + "gl_Color", + 4, /* n components */ + GL_UNSIGNED_BYTE, + FALSE, /* normalized */ + 0, /* stride */ + triangle_colors); + cogl_vertex_buffer_submit (state.buffer); + } + + clutter_actor_show_all (stage); + + clutter_main (); + + cogl_handle_unref (state.buffer); + + g_source_remove (idle_source); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-viewport.c b/cogl/tests/conform/test-viewport.c new file mode 100644 index 000000000..a1937c42a --- /dev/null +++ b/cogl/tests/conform/test-viewport.c @@ -0,0 +1,416 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define FRAMEBUFFER_WIDTH 640 +#define FRAMEBUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +static void +assert_region_color (int x, + int y, + int width, + int height, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) +{ + uint8_t *data = g_malloc0 (width * height * 4); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + data); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + uint8_t *pixel = &data[y*width*4 + x*4]; +#if 1 + g_assert (pixel[RED] == red && + pixel[GREEN] == green && + pixel[BLUE] == blue && + pixel[ALPHA] == alpha); +#endif + } + g_free (data); +} + +static void +assert_rectangle_color_and_black_border (int x, + int y, + int width, + int height, + uint8_t red, + uint8_t green, + uint8_t blue) +{ + /* check the rectangle itself... */ + assert_region_color (x, y, width, height, red, green, blue, 0xff); + /* black to left of the rectangle */ + assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black to right of the rectangle */ + assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black above the rectangle */ + assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff); + /* and black below the rectangle */ + assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff); +} + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + CoglColor black; + float x0; + float y0; + float width; + float height; + + /* for clearing the offscreen framebuffer to black... */ + cogl_color_init_from_4ub (&black, 0x00, 0x00, 0x00, 0xff); + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* - Create a 100x200 viewport (i.e. smaller than the onscreen framebuffer) + * and position it a (20, 10) inside the framebuffer. + * - Fill the whole viewport with a purple rectangle + * - Verify that the framebuffer is black with a 100x200 purple rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with purple.. */ + cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0xff); + + + /* - Create a viewport twice the size of the onscreen framebuffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which + * is (20, 10) within the framebuffer) + * - Verify that the framebuffer is black with a 100x200 green rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + FRAMEBUFFER_WIDTH * 2, /* width */ + FRAMEBUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 green rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f; + width = (1.0f / FRAMEBUFFER_WIDTH) * 100; + height = (1.0f / FRAMEBUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0xff, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the framebuffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rectangle (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the framebuffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the framebuffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push_rectangle (-(width/2.0), -(height/2.0), + width/2.0, height/2.0); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something specific so we can verify that it gets + * restored after we are done testing with an offscreen framebuffer... */ + cogl_set_viewport (20, 10, 100, 200); + + /* + * Next test offscreen drawing... + */ + data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); + tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + TEST_UTILS_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + FRAMEBUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_with_texture (tex); + + cogl_push_framebuffer (offscreen); + + + /* - Create a 100x200 viewport (i.e. smaller than the offscreen framebuffer) + * and position it a (20, 10) inside the framebuffer. + * - Fill the whole viewport with a blue rectangle + * - Verify that the framebuffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a viewport twice the size of the offscreen framebuffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which + * is (20, 10) within the framebuffer) + * - Verify that the framebuffer is black with a 100x200 red rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + FRAMEBUFFER_WIDTH * 2, /* width */ + FRAMEBUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 red rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f; + width = (1.0f / FRAMEBUFFER_WIDTH) * 100; + height = (1.0f / FRAMEBUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the framebuffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rectangle (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the framebuffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the framebuffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push_rectangle (-(width/2.0), -(height/2.0), + width/2, height/2); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something obscure to verify that it gets + * replace when we switch back to the onscreen framebuffer... */ + cogl_set_viewport (0, 0, 10, 10); + + cogl_pop_framebuffer (); + cogl_handle_unref (offscreen); + + /* + * Verify that the previous onscreen framebuffer's viewport was restored + * by drawing a white rectangle across the whole viewport. This should + * draw a 100x200 rectangle at (20,10) relative to the onscreen draw + * buffer... + */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0xff, 0xff); + + + /* Uncomment to display the last contents of the offscreen framebuffer */ +#if 1 + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + cogl_set_viewport (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); +#endif + + cogl_handle_unref (tex); + + /* Finally restore the stage's original state... */ + cogl_pop_matrix (); + cogl_set_projection_matrix (&saved_projection); + cogl_set_viewport (saved_viewport[0], saved_viewport[1], + saved_viewport[2], saved_viewport[3]); + + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static CoglBool +queue_redraw (void *stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_viewport (TestUtilsGTestFixture *fixture, + void *data) +{ + unsigned int idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/cogl/tests/conform/test-wrap-modes.c b/cogl/tests/conform/test-wrap-modes.c new file mode 100644 index 000000000..e5a2a1a61 --- /dev/null +++ b/cogl/tests/conform/test-wrap-modes.c @@ -0,0 +1,296 @@ +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 + +#include +#include + +#include "test-utils.h" + +#define TEX_SIZE 4 + +typedef struct _TestState +{ + int width; + int height; + CoglTexture *texture; +} TestState; + +static CoglTexture * +create_texture (TestUtilsTextureFlags flags) +{ + uint8_t *data = g_malloc (TEX_SIZE * TEX_SIZE * 4), *p = data; + CoglTexture *tex; + int x, y; + + for (y = 0; y < TEX_SIZE; y++) + for (x = 0; x < TEX_SIZE; x++) + { + *(p++) = 0; + *(p++) = (x & 1) * 255; + *(p++) = (y & 1) * 255; + *(p++) = 255; + } + + tex = test_utils_texture_new_from_data (test_ctx, + TEX_SIZE, TEX_SIZE, flags, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + TEX_SIZE * 4, + data); + g_free (data); + + return tex; +} + +static CoglPipeline * +create_pipeline (TestState *state, + CoglPipelineWrapMode wrap_mode_s, + CoglPipelineWrapMode wrap_mode_t) +{ + CoglPipeline *pipeline; + + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_layer_texture (pipeline, 0, state->texture); + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0, wrap_mode_s); + cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0, wrap_mode_t); + + return pipeline; +} + +static CoglPipelineWrapMode +wrap_modes[] = + { + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_REPEAT, + + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + + COGL_PIPELINE_WRAP_MODE_REPEAT, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_REPEAT, + + COGL_PIPELINE_WRAP_MODE_AUTOMATIC, + COGL_PIPELINE_WRAP_MODE_AUTOMATIC, + + COGL_PIPELINE_WRAP_MODE_AUTOMATIC, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE + }; + +static void +draw_tests (TestState *state) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) + { + CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; + CoglPipeline *pipeline; + + /* Create a separate pipeline for each pair of wrap modes so + that we can verify whether the batch splitting works */ + wrap_mode_s = wrap_modes[i]; + wrap_mode_t = wrap_modes[i + 1]; + pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t); + /* Render the pipeline at four times the size of the texture */ + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + i * TEX_SIZE, + 0, + (i + 2) * TEX_SIZE, + TEX_SIZE * 2, + 0, 0, 2, 2); + cogl_object_unref (pipeline); + } +} + +static const CoglTextureVertex vertices[4] = + { + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, TEX_SIZE * 2, 0.0f, 0.0f, 2.0f }, + { TEX_SIZE * 2, TEX_SIZE * 2, 0.0f, 2.0f, 2.0f }, + { TEX_SIZE * 2, 0.0f, 0.0f, 2.0f, 0.0f } + }; + +static void +draw_tests_polygon (TestState *state) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) + { + CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; + CoglPipeline *pipeline; + + wrap_mode_s = wrap_modes[i]; + wrap_mode_t = wrap_modes[i + 1]; + pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t); + cogl_set_source (pipeline); + cogl_object_unref (pipeline); + cogl_push_matrix (); + cogl_translate (TEX_SIZE * i, 0.0f, 0.0f); + /* Render the pipeline at four times the size of the texture */ + cogl_polygon (vertices, G_N_ELEMENTS (vertices), FALSE); + cogl_pop_matrix (); + } +} + +static void +draw_tests_vbo (TestState *state) +{ + CoglHandle vbo; + int i; + + vbo = cogl_vertex_buffer_new (4); + cogl_vertex_buffer_add (vbo, "gl_Vertex", 3, + COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, + sizeof (vertices[0]), + &vertices[0].x); + cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0", 2, + COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, + sizeof (vertices[0]), + &vertices[0].tx); + cogl_vertex_buffer_submit (vbo); + + for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) + { + CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; + CoglPipeline *pipeline; + + wrap_mode_s = wrap_modes[i]; + wrap_mode_t = wrap_modes[i + 1]; + pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t); + cogl_set_source (pipeline); + cogl_object_unref (pipeline); + cogl_push_matrix (); + cogl_translate (TEX_SIZE * i, 0.0f, 0.0f); + /* Render the pipeline at four times the size of the texture */ + cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_TRIANGLE_FAN, 0, 4); + cogl_pop_matrix (); + } + + cogl_handle_unref (vbo); +} + +static void +validate_set (TestState *state, int offset) +{ + uint8_t data[TEX_SIZE * 2 * TEX_SIZE * 2 * 4], *p; + int x, y, i; + + for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) + { + CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; + + wrap_mode_s = wrap_modes[i]; + wrap_mode_t = wrap_modes[i + 1]; + + cogl_framebuffer_read_pixels (test_fb, i * TEX_SIZE, offset * TEX_SIZE * 2, + TEX_SIZE * 2, TEX_SIZE * 2, + COGL_PIXEL_FORMAT_RGBA_8888, + data); + + p = data; + + for (y = 0; y < TEX_SIZE * 2; y++) + for (x = 0; x < TEX_SIZE * 2; x++) + { + uint8_t green, blue; + + if (x < TEX_SIZE || + wrap_mode_s == COGL_PIPELINE_WRAP_MODE_REPEAT || + wrap_mode_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + green = (x & 1) * 255; + else + green = ((TEX_SIZE - 1) & 1) * 255; + + if (y < TEX_SIZE || + wrap_mode_t == COGL_PIPELINE_WRAP_MODE_REPEAT || + wrap_mode_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + blue = (y & 1) * 255; + else + blue = ((TEX_SIZE - 1) & 1) * 255; + + g_assert_cmpint (p[0], ==, 0); + g_assert_cmpint (p[1], ==, green); + g_assert_cmpint (p[2], ==, blue); + + p += 4; + } + } +} + +static void +validate_result (TestState *state) +{ + validate_set (state, 0); /* non-atlased rectangle */ +#if 0 /* this doesn't currently work */ + validate_set (state, 1); /* atlased rectangle */ +#endif + validate_set (state, 2); /* cogl_polygon */ + validate_set (state, 3); /* vertex buffer */ +} + +static void +paint (TestState *state) +{ + /* Draw the tests first with a non atlased texture */ + state->texture = create_texture (TEST_UTILS_TEXTURE_NO_ATLAS); + draw_tests (state); + cogl_object_unref (state->texture); + + /* Draw the tests again with a possible atlased texture. This should + end up testing software repeats */ + state->texture = create_texture (TEST_UTILS_TEXTURE_NONE); + cogl_framebuffer_push_matrix (test_fb); + cogl_framebuffer_translate (test_fb, 0.0f, TEX_SIZE * 2.0f, 0.0f); + draw_tests (state); + cogl_pop_matrix (); + cogl_object_unref (state->texture); + + /* Draw the tests using cogl_polygon */ + state->texture = create_texture (COGL_TEXTURE_NO_ATLAS); + cogl_push_matrix (); + cogl_translate (0.0f, TEX_SIZE * 4.0f, 0.0f); + draw_tests_polygon (state); + cogl_pop_matrix (); + cogl_object_unref (state->texture); + + /* Draw the tests using a vertex buffer */ + state->texture = create_texture (COGL_TEXTURE_NO_ATLAS); + cogl_push_matrix (); + cogl_translate (0.0f, TEX_SIZE * 6.0f, 0.0f); + draw_tests_vbo (state); + cogl_pop_matrix (); + cogl_object_unref (state->texture); + + validate_result (state); +} + +void +test_wrap_modes (void) +{ + TestState state; + + state.width = cogl_framebuffer_get_width (test_fb); + state.height = cogl_framebuffer_get_height (test_fb); + + cogl_framebuffer_orthographic (test_fb, + 0, 0, + state.width, + state.height, + -1, + 100); + + /* XXX: we have to push/pop a framebuffer since this test currently + * uses the legacy cogl_vertex_buffer_draw() api. */ + cogl_push_framebuffer (test_fb); + paint (&state); + cogl_pop_framebuffer (); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/conform/test-wrap-rectangle-textures.c b/cogl/tests/conform/test-wrap-rectangle-textures.c new file mode 100644 index 000000000..73b357536 --- /dev/null +++ b/cogl/tests/conform/test-wrap-rectangle-textures.c @@ -0,0 +1,175 @@ +#include + +#include + +#include "test-utils.h" + +#define DRAW_SIZE 64 + +static CoglPipeline * +create_base_pipeline (void) +{ + CoglBitmap *bmp; + CoglTextureRectangle *tex; + CoglPipeline *pipeline; + uint8_t tex_data[] = + { + 0x44, 0x44, 0x44, 0x88, 0x88, 0x88, + 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff + }; + + bmp = cogl_bitmap_new_for_data (test_ctx, + 2, 2, /* width/height */ + COGL_PIXEL_FORMAT_RGB_888, + 2 * 3, /* rowstride */ + tex_data); + + tex = cogl_texture_rectangle_new_from_bitmap (bmp); + + cogl_object_unref (bmp); + + pipeline = cogl_pipeline_new (test_ctx); + + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + cogl_pipeline_set_layer_texture (pipeline, + 0, /* layer */ + tex); + + cogl_object_unref (tex); + + return pipeline; +} + +static void +check_colors (int x_offset, + int y_offset, + const uint8_t expected_colors[9]) +{ + int x, y; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + { + uint32_t color = expected_colors[x + y * 4]; + test_utils_check_region (test_fb, + x * DRAW_SIZE / 4 + 1 + x_offset, + y * DRAW_SIZE / 4 + 1 + y_offset, + DRAW_SIZE / 4 - 2, + DRAW_SIZE / 4 - 2, + 0xff | + (color << 8) | + (color << 16) | + (color << 24)); + } +} + +static void +test_pipeline (CoglPipeline *pipeline, + int x_offset, + int y_offset, + const uint8_t expected_colors[9]) +{ + float x1 = x_offset; + float y1 = y_offset; + float x2 = x1 + DRAW_SIZE; + float y2 = y1 + DRAW_SIZE; + int y, x; + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + x1, y1, + x2, y2, + -0.5f, /* s1 */ + -0.5f, /* t1 */ + 1.5f, /* s2 */ + 1.5f /* t2 */); + + check_colors (x_offset, y_offset, expected_colors); + + /* Also try drawing each quadrant of the rectangle with a small + * rectangle */ + + for (y = -1; y < 3; y++) + for (x = -1; x < 3; x++) + { + x1 = x_offset + (x + 1) * DRAW_SIZE / 4 + DRAW_SIZE; + y1 = y_offset + (y + 1) * DRAW_SIZE / 4; + x2 = x1 + DRAW_SIZE / 4; + y2 = y1 + DRAW_SIZE / 4; + + cogl_framebuffer_draw_textured_rectangle (test_fb, + pipeline, + x1, y1, + x2, y2, + x / 2.0f, /* s1 */ + y / 2.0f, /* t1 */ + (x + 1) / 2.0f, /* s2 */ + (y + 1) / 2.0f /* t2 */); + } + + check_colors (x_offset + DRAW_SIZE, y_offset, expected_colors); +} + +void +test_wrap_rectangle_textures (void) +{ + float fb_width = cogl_framebuffer_get_width (test_fb); + float fb_height = cogl_framebuffer_get_height (test_fb); + CoglPipeline *base_pipeline; + CoglPipeline *clamp_pipeline; + CoglPipeline *repeat_pipeline; + /* The textures are drawn with the texture coordinates from + * -0.5→1.5. That means we get one complete copy of the texture and + * an extra half of the texture surrounding it. The drawing is + * tested against a 4x4 grid of colors. The center 2x2 colours + * specify the normal texture colors and the other colours specify + * what the wrap mode should generate */ + static const uint8_t clamp_colors[] = + { + 0x44, 0x44, 0x88, 0x88, + 0x44, 0x44, 0x88, 0x88, + 0xcc, 0xcc, 0xff, 0xff, + 0xcc, 0xcc, 0xff, 0xff + }; + static const uint8_t repeat_colors[] = + { + 0xff, 0xcc, 0xff, 0xcc, + 0x88, 0x44, 0x88, 0x44, + 0xff, 0xcc, 0xff, 0xcc, + 0x88, 0x44, 0x88, 0x44 + }; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + fb_width, /* x_2 */ + fb_height /* y_2 */, + -1, 100 /* near/far */); + + base_pipeline = create_base_pipeline (); + + clamp_pipeline = cogl_pipeline_copy (base_pipeline); + cogl_pipeline_set_layer_wrap_mode (clamp_pipeline, + 0, /* layer */ + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); + + repeat_pipeline = cogl_pipeline_copy (base_pipeline); + cogl_pipeline_set_layer_wrap_mode (repeat_pipeline, + 0, /* layer */ + COGL_PIPELINE_WRAP_MODE_REPEAT); + + test_pipeline (clamp_pipeline, + 0, 0, /* x/y offset */ + clamp_colors); + + test_pipeline (repeat_pipeline, + 0, DRAW_SIZE * 2, /* x/y offset */ + repeat_colors); + + cogl_object_unref (repeat_pipeline); + cogl_object_unref (clamp_pipeline); + cogl_object_unref (base_pipeline); +} diff --git a/cogl/tests/conform/test-write-texture-formats.c b/cogl/tests/conform/test-write-texture-formats.c new file mode 100644 index 000000000..d415df0a7 --- /dev/null +++ b/cogl/tests/conform/test-write-texture-formats.c @@ -0,0 +1,184 @@ +#include +#include + +#include "test-utils.h" + +/* + * This tests writing data to an RGBA texture in all of the available + * pixel formats + */ + +static void +test_color (CoglTexture *texture, + uint32_t expected_pixel) +{ + uint8_t received_pixel[4]; + + cogl_texture_get_data (texture, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + received_pixel); + + test_utils_compare_pixel_and_alpha (received_pixel, expected_pixel); +} + +static void +test_write_byte (CoglContext *context, + CoglPixelFormat format, + uint8_t byte, + uint32_t expected_pixel) +{ + CoglTexture *texture = test_utils_create_color_texture (context, 0); + + cogl_texture_set_region (texture, + 0, 0, /* src_x / src_y */ + 0, 0, /* dst_x / dst_y */ + 1, 1, /* dst_w / dst_h */ + 1, 1, /* width / height */ + format, + 1, /* rowstride */ + &byte); + + test_color (texture, expected_pixel); + + cogl_object_unref (texture); +} + +static void +test_write_short (CoglContext *context, + CoglPixelFormat format, + uint16_t value, + uint32_t expected_pixel) +{ + CoglTexture *texture = test_utils_create_color_texture (context, 0); + + cogl_texture_set_region (texture, + 0, 0, /* src_x / src_y */ + 0, 0, /* dst_x / dst_y */ + 1, 1, /* dst_w / dst_h */ + 1, 1, /* width / height */ + format, + 2, /* rowstride */ + (uint8_t *) &value); + + test_color (texture, expected_pixel); + + cogl_object_unref (texture); +} + +static void +test_write_bytes (CoglContext *context, + CoglPixelFormat format, + uint32_t value, + uint32_t expected_pixel) +{ + CoglTexture *texture = test_utils_create_color_texture (context, 0); + + value = GUINT32_TO_BE (value); + + cogl_texture_set_region (texture, + 0, 0, /* src_x / src_y */ + 0, 0, /* dst_x / dst_y */ + 1, 1, /* dst_w / dst_h */ + 1, 1, /* width / height */ + format, + 4, /* rowstride */ + (uint8_t *) &value); + + test_color (texture, expected_pixel); + + cogl_object_unref (texture); +} + +static void +test_write_int (CoglContext *context, + CoglPixelFormat format, + uint32_t expected_pixel, + ...) +{ + va_list ap; + int bits; + uint32_t tex_data = 0; + int bits_sum = 0; + CoglTexture *texture = test_utils_create_color_texture (context, 0); + + va_start (ap, expected_pixel); + + /* Convert the va args into a single 32-bit value */ + while ((bits = va_arg (ap, int)) != -1) + { + uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 127) / 255; + + bits_sum += bits; + + tex_data |= value << (32 - bits_sum); + } + + va_end (ap); + + cogl_texture_set_region (texture, + 0, 0, /* src_x / src_y */ + 0, 0, /* dst_x / dst_y */ + 1, 1, /* dst_w / dst_h */ + 1, 1, /* width / height */ + format, + 4, /* rowstride */ + (uint8_t *) &tex_data); + + test_color (texture, expected_pixel); + + cogl_object_unref (texture); +} + +void +test_write_texture_formats (void) +{ + test_write_byte (test_ctx, COGL_PIXEL_FORMAT_A_8, 0x34, 0x00000034); +#if 0 + /* I'm not sure what's the right value to put here because Nvidia + and Mesa seem to behave differently so one of them must be + wrong. */ + test_write_byte (test_ctx, COGL_PIXEL_FORMAT_G_8, 0x34, 0x340000ff); +#endif + + /* We should always be able to read from an RG buffer regardless of + * whether RG textures are supported because Cogl will do the + * conversion for us */ + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RG_88, 0x123456ff, 0x123400ff); + + test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGB_565, 0x0843, 0x080819ff); + test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_4444_PRE, 0x1234, 0x11223344); + test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_5551_PRE, 0x0887, 0x081019ff); + + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff, 0x123456ff); + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff, 0x123456ff); + + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 0x12345678, 0x12345678); + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGRA_8888_PRE, + 0x56341278, 0x12345678); + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ARGB_8888_PRE, + 0x78123456, 0x12345678); + test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ABGR_8888_PRE, + 0x78563412, 0x12345678); + + test_write_int (test_ctx, COGL_PIXEL_FORMAT_RGBA_1010102_PRE, + 0x123456ff, + 10, 0x12, 10, 0x34, 10, 0x56, 2, 0xff, + -1); + test_write_int (test_ctx, COGL_PIXEL_FORMAT_BGRA_1010102_PRE, + 0x123456ff, + 10, 0x56, 10, 0x34, 10, 0x12, 2, 0xff, + -1); + test_write_int (test_ctx, COGL_PIXEL_FORMAT_ARGB_2101010_PRE, + 0x123456ff, + 2, 0xff, 10, 0x12, 10, 0x34, 10, 0x56, + -1); + test_write_int (test_ctx, COGL_PIXEL_FORMAT_ABGR_2101010_PRE, + 0x123456ff, + 2, 0xff, 10, 0x56, 10, 0x34, 10, 0x12, + -1); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} diff --git a/cogl/tests/data/Makefile.am b/cogl/tests/data/Makefile.am new file mode 100644 index 000000000..3a2030a77 --- /dev/null +++ b/cogl/tests/data/Makefile.am @@ -0,0 +1,3 @@ +NULL = + +EXTRA_DIST = valgrind.suppressions diff --git a/cogl/tests/data/valgrind.suppressions b/cogl/tests/data/valgrind.suppressions new file mode 100644 index 000000000..f47498d16 --- /dev/null +++ b/cogl/tests/data/valgrind.suppressions @@ -0,0 +1,173 @@ +{ + ioctl_1 + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:driDrawableInitVBlank + fun:intelMakeCurrent + fun:glXMakeContextCurrent +} + +{ + ioctl_2 + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:driDrawableGetMSC32 + fun:clutter_backend_glx_redraw +} + +{ + ioctl_3 + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:driWaitForMSC32 + fun:clutter_backend_glx_redraw +} + +{ + mesa_init_context + Memcheck:Leak + fun:*alloc + ... + fun:glXCreateNewContext +} + +{ + type_register + Memcheck:Leak + fun:*alloc + ... + fun:g_type_register_* +} + +{ + type_ref + Memcheck:Leak + fun:*alloc + ... + fun:g_type_class_ref +} + +{ + type_interface_prereq + Memcheck:Leak + fun:*alloc + ... + fun:g_type_interface_add_prerequisite +} + +{ + get_charset + Memcheck:Leak + fun:*alloc + ... + fun:g_get_charset +} + +{ + cogl_features + Memcheck:Leak + fun:*alloc + ... + fun:cogl_get_features +} + +{ + glx_query_version + Memcheck:Leak + fun:*alloc + ... + fun:glXQueryVersion +} + +{ + glx_create_context + Memcheck:Leak + fun:*alloc + ... + fun:glXCreateNewContext +} + +{ + glx_make_current + Memcheck:Leak + fun:*alloc + ... + fun:glXMakeContextCurrent +} + +{ + gl_draw_arrays + Memcheck:Leak + fun:*malloc + ... + fun:glDrawArrays +} + +{ + cogl_clear + Memcheck:Leak + fun:*alloc + ... + fun:cogl_clear +} + +{ + default_font + Memcheck:Leak + fun:*alloc + ... + fun:clutter_backend_get_font_name +} + +{ + id_pool + Memcheck:Leak + fun:*alloc + ... + fun:clutter_id_pool_new +} + +{ + x_open_display + Memcheck:Leak + fun:*alloc + ... + fun:XOpenDisplay +} + +# ... and font descriptions from every "sans 12" type string +{ + pango_font_description_from_string + Memcheck:Leak + fun:*alloc + ... + fun:pango_font_description_from_string +} + +# other lib init +{ + fontconfig_init + Memcheck:Leak + fun:*alloc + ... + fun:FcConfigParseAndLoad +} + +{ + freetype_init + Memcheck:Leak + fun:*alloc + ... + fun:FT_Open_Face +} + +{ + x_init_ext + Memcheck:Leak + fun:*alloc + ... + fun:XInitExtension +} diff --git a/cogl/tests/micro-perf/Makefile.am b/cogl/tests/micro-perf/Makefile.am new file mode 100644 index 000000000..75d02b2ce --- /dev/null +++ b/cogl/tests/micro-perf/Makefile.am @@ -0,0 +1,24 @@ +NULL = + +AM_CPPFLAGS = \ + -I$(top_srcdir) + +test_conformance_CPPFLAGS = \ + -DCOGL_ENABLE_EXPERIMENTAL_API \ + -DCOGL_DISABLE_DEPRECATED \ + -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" + + +noinst_PROGRAMS = + +noinst_PROGRAMS += test-journal + +AM_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) + +common_ldadd = \ + $(COGL_DEP_LIBS) \ + $(top_builddir)/cogl/libmutter-cogl.la \ + $(LIBM) + +test_journal_SOURCES = test-journal.c +test_journal_LDADD = $(common_ldadd) diff --git a/cogl/tests/micro-perf/test-journal.c b/cogl/tests/micro-perf/test-journal.c new file mode 100644 index 000000000..35e3cd5a3 --- /dev/null +++ b/cogl/tests/micro-perf/test-journal.c @@ -0,0 +1,190 @@ +#include +#include +#include + +#include "cogl/cogl-profile.h" + +#define FRAMEBUFFER_WIDTH 800 +#define FRAMEBUFFER_HEIGHT 600 + +CoglBool run_all = FALSE; + +typedef struct _Data +{ + CoglContext *ctx; + CoglFramebuffer *fb; + CoglPipeline *pipeline; + CoglPipeline *alpha_pipeline; + GTimer *timer; + int frame; +} Data; + +static void +test_rectangles (Data *data) +{ +#define RECT_WIDTH 5 +#define RECT_HEIGHT 5 + int x; + int y; + + cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1); + + cogl_framebuffer_push_rectangle_clip (data->fb, + 10, + 10, + FRAMEBUFFER_WIDTH - 10, + FRAMEBUFFER_HEIGHT - 10); + + /* Should the rectangles be randomly positioned/colored/rotated? + * + * It could be good to develop equivalent GL and Cairo tests so we can + * have a sanity check for our Cogl performance. + * + * The color should vary to check that we correctly batch color changes + * The use of alpha should vary so we have a variation of which rectangles + * require blending. + * Should this be a random variation? + * It could be good to experiment with focibly enabling blending for + * rectangles that don't technically need it for the sake of extending + * batching. E.g. if you a long run of interleved rectangles with every + * other rectangle needing blending then it may be worth enabling blending + * for all the rectangles to avoid the state changes. + * The modelview should change between rectangles to check the software + * transform codepath. + * Should we group some rectangles under the same modelview? Potentially + * we could avoid software transform for long runs of rectangles with the + * same modelview. + * + */ + for (y = 0; y < FRAMEBUFFER_HEIGHT; y += RECT_HEIGHT) + { + for (x = 0; x < FRAMEBUFFER_WIDTH; x += RECT_WIDTH) + { + cogl_framebuffer_push_matrix (data->fb); + cogl_framebuffer_translate (data->fb, x, y, 0); + cogl_framebuffer_rotate (data->fb, 45, 0, 0, 1); + + cogl_pipeline_set_color4f (data->pipeline, + 1, + (1.0f/FRAMEBUFFER_WIDTH)*y, + (1.0f/FRAMEBUFFER_HEIGHT)*x, + 1); + cogl_framebuffer_draw_rectangle (data->fb, + data->pipeline, + 0, 0, RECT_WIDTH, RECT_HEIGHT); + + cogl_framebuffer_pop_matrix (data->fb); + } + } + + for (y = 0; y < FRAMEBUFFER_HEIGHT; y += RECT_HEIGHT) + { + for (x = 0; x < FRAMEBUFFER_WIDTH; x += RECT_WIDTH) + { + cogl_framebuffer_push_matrix (data->fb); + cogl_framebuffer_translate (data->fb, x, y, 0); + + cogl_pipeline_set_color4f (data->alpha_pipeline, + 1, + (1.0f/FRAMEBUFFER_WIDTH)*x, + (1.0f/FRAMEBUFFER_HEIGHT)*y, + (1.0f/FRAMEBUFFER_WIDTH)*x); + cogl_framebuffer_draw_rectangle (data->fb, + data->alpha_pipeline, + 0, 0, RECT_WIDTH, RECT_HEIGHT); + + cogl_framebuffer_pop_matrix (data->fb); + } + } + + cogl_framebuffer_pop_clip (data->fb); +} + +static CoglBool +paint_cb (void *user_data) +{ + Data *data = user_data; + double elapsed; + + data->frame++; + + test_rectangles (data); + + cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb)); + + elapsed = g_timer_elapsed (data->timer, NULL); + if (elapsed > 1.0) + { + g_print ("fps = %f\n", data->frame / elapsed); + g_timer_start (data->timer); + data->frame = 0; + } + + return FALSE; /* remove the callback */ +} + +static void +frame_event_cb (CoglOnscreen *onscreen, + CoglFrameEvent event, + CoglFrameInfo *info, + void *user_data) +{ + if (event == COGL_FRAME_EVENT_SYNC) + paint_cb (user_data); +} + +int +main (int argc, char **argv) +{ + Data data; + CoglOnscreen *onscreen; + GSource *cogl_source; + GMainLoop *loop; + COGL_STATIC_TIMER (mainloop_timer, + NULL, //no parent + "Mainloop", + "The time spent in the glib mainloop", + 0); // no application private data + + data.ctx = cogl_context_new (NULL, NULL); + + onscreen = cogl_onscreen_new (data.ctx, + FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); + cogl_onscreen_set_swap_throttled (onscreen, FALSE); + cogl_onscreen_show (onscreen); + + data.fb = onscreen; + cogl_framebuffer_orthographic (data.fb, + 0, 0, + FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, + -1, + 100); + + data.pipeline = cogl_pipeline_new (data.ctx); + cogl_pipeline_set_color4f (data.pipeline, 1, 1, 1, 1); + data.alpha_pipeline = cogl_pipeline_new (data.ctx); + cogl_pipeline_set_color4f (data.alpha_pipeline, 1, 1, 1, 0.5); + + cogl_source = cogl_glib_source_new (data.ctx, G_PRIORITY_DEFAULT); + + g_source_attach (cogl_source, NULL); + + cogl_onscreen_add_frame_callback (COGL_ONSCREEN (data.fb), + frame_event_cb, + &data, + NULL); /* destroy notify */ + + g_idle_add (paint_cb, &data); + + data.frame = 0; + data.timer = g_timer_new (); + g_timer_start (data.timer); + + loop = g_main_loop_new (NULL, TRUE); + COGL_TIMER_START (uprof_get_mainloop_context (), mainloop_timer); + g_main_loop_run (loop); + COGL_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer); + + return 0; +} + diff --git a/cogl/tests/run-tests.sh b/cogl/tests/run-tests.sh new file mode 100755 index 000000000..7e62bf0f6 --- /dev/null +++ b/cogl/tests/run-tests.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +if test -z "$G_DEBUG"; then + G_DEBUG=fatal-warnings +else + G_DEBUG="$G_DEBUG,fatal-warnings" +fi + +export G_DEBUG + +ENVIRONMENT_CONFIG=$1 +shift + +TEST_BINARY=$1 +shift + +. $ENVIRONMENT_CONFIG + +set +m + +trap "" ERR +trap "" SIGABRT +trap "" SIGFPE +trap "" SIGSEGV + +EXIT=0 +MISSING_FEATURE="WARNING: Missing required feature"; +KNOWN_FAILURE="WARNING: Test is known to fail"; + +echo "Key:" +echo "ok = Test passed" +echo "n/a = Driver is missing a feature required for the test" +echo "FAIL = Unexpected failure" +echo "FIXME = Test failed, but it was an expected failure" +echo "PASS! = Unexpected pass" +echo "" + +get_status() +{ + case $1 in + # Special value we use to indicate that the test failed + # but it was an expected failure so don't fail the + # overall test run as a result... + 300) + echo -n "FIXME";; + # Special value we use to indicate that the test passed + # but we weren't expecting it to pass‽ + 400) + echo -n 'PASS!';; + + # Special value to indicate the test is missing a required feature + 500) + echo -n "n/a";; + + 0) + echo -n "ok";; + + *) + echo -n "FAIL";; + esac +} + +run_test() +{ + $($TEST_BINARY $1 &>.log) + TMP=$? + var_name=$2_result + eval $var_name=$TMP + if grep -q "$MISSING_FEATURE" .log; then + if test $TMP -ne 0; then + eval $var_name=500 + else + eval $var_name=400 + fi + elif grep -q "$KNOWN_FAILURE" .log; then + if test $TMP -ne 0; then + eval $var_name=300 + else + eval $var_name=400 + fi + else + if test $TMP -ne 0; then EXIT=$TMP; fi + fi +} + +TITLE_FORMAT="%35s" +printf $TITLE_FORMAT "Test" + +if test $HAVE_GL -eq 1; then + GL_FORMAT=" %6s %8s %7s %6s %6s" + printf "$GL_FORMAT" "GL+FF" "GL+ARBFP" "GL+GLSL" "GL-NPT" "GL3" +fi +if test $HAVE_GLES2 -eq 1; then + GLES2_FORMAT=" %6s %7s" + printf "$GLES2_FORMAT" "ES2" "ES2-NPT" +fi + +echo "" +echo "" + +for test in `cat unit-tests` +do + export COGL_DEBUG= + + if test $HAVE_GL -eq 1; then + export COGL_DRIVER=gl + export COGL_DEBUG=disable-glsl,disable-arbfp + run_test $test gl_ff + + export COGL_DRIVER=gl + # NB: we can't explicitly disable fixed + glsl in this case since + # the arbfp code only supports fragment processing so we need either + # the fixed or glsl vertends + export COGL_DEBUG= + run_test $test gl_arbfp + + export COGL_DRIVER=gl + export COGL_DEBUG=disable-fixed,disable-arbfp + run_test $test gl_glsl + + export COGL_DRIVER=gl + export COGL_DEBUG=disable-npot-textures + run_test $test gl_npot + + export COGL_DRIVER=gl3 + export COGL_DEBUG= + run_test $test gl3 + fi + + if test $HAVE_GLES2 -eq 1; then + export COGL_DRIVER=gles2 + export COGL_DEBUG= + run_test $test gles2 + + export COGL_DRIVER=gles2 + export COGL_DEBUG=disable-npot-textures + run_test $test gles2_npot + fi + + printf $TITLE_FORMAT "$test:" + if test $HAVE_GL -eq 1; then + printf "$GL_FORMAT" \ + "`get_status $gl_ff_result`" \ + "`get_status $gl_arbfp_result`" \ + "`get_status $gl_glsl_result`" \ + "`get_status $gl_npot_result`" \ + "`get_status $gl3_result`" + fi + if test $HAVE_GLES2 -eq 1; then + printf "$GLES2_FORMAT" \ + "`get_status $gles2_result`" \ + "`get_status $gles2_npot_result`" + fi + echo "" +done + +exit $EXIT diff --git a/cogl/tests/test-launcher.sh b/cogl/tests/test-launcher.sh new file mode 100755 index 000000000..e159e2e49 --- /dev/null +++ b/cogl/tests/test-launcher.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +TEST_BINARY=$1 +shift + +SYMBOL_PREFIX=$1 +shift + +UNIT_TEST=$1 +shift + +test -z ${UNIT_TEST} && { + echo "Usage: $0 UNIT_TEST" + exit 1 +} + +BINARY_NAME=`basename $TEST_BINARY` +UNIT_TEST=`echo $UNIT_TEST|sed 's/-/_/g'` + +echo "Running: ./$BINARY_NAME ${UNIT_TEST} $@" +echo "" +COGL_TEST_VERBOSE=1 $TEST_BINARY ${UNIT_TEST} "$@" +exit_val=$? + +if test $exit_val -eq 0; then + echo "OK" +fi + +echo "" +echo "NOTE: For debugging purposes, you can run this single test as follows:" +echo "$ libtool --mode=execute \\" +echo " gdb --eval-command=\"start\" --eval-command=\"b ${UNIT_TEST#${SYMBOL_PREFIX}}\" \\" +echo " --args ./$BINARY_NAME ${UNIT_TEST}" +echo "or:" +echo "$ env G_SLICE=always-malloc \\" +echo " libtool --mode=execute \\" +echo " valgrind ./$BINARY_NAME ${UNIT_TEST}" + +exit $exit_val diff --git a/cogl/tests/unit/Makefile.am b/cogl/tests/unit/Makefile.am new file mode 100644 index 000000000..5c1c08100 --- /dev/null +++ b/cogl/tests/unit/Makefile.am @@ -0,0 +1,83 @@ +NULL = + +noinst_PROGRAMS = test-unit + +test_unit_SOURCES = test-unit-main.c + +SHEXT = $(EXEEXT) + +# For convenience, this provides a way to easily run individual unit tests: +.PHONY: wrappers clean-wrappers + +wrappers: stamp-test-unit + @true +stamp-test-unit: Makefile test-unit$(EXEEXT) + @mkdir -p wrappers + . $(top_builddir)/cogl/libmutter-cogl.la ; \ + $(NM) $(top_builddir)/cogl/.libs/"$$dlname"| \ + grep '[DR] _\?unit_test_'|sed 's/.\+ [DR] _\?//' > unit-tests + @chmod +x $(top_srcdir)/tests/test-launcher.sh + @( echo "/stamp-test-unit" ; \ + echo "/test-unit$(EXEEXT)" ; \ + echo "*.o" ; \ + echo ".gitignore" ; \ + echo "unit-tests" ; ) > .gitignore + @for i in `cat unit-tests`; \ + do \ + unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \ + echo " GEN $$unit"; \ + ( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-unit$(EXEEXT) 'unit_test_' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \ + chmod +x $$unit$(SHEXT); \ + echo "/$$unit$(SHEXT)" >> .gitignore; \ + done \ + && echo timestamp > $(@F) + +clean-wrappers: + @for i in `cat unit-tests`; \ + do \ + unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \ + echo " RM $$unit"; \ + rm -f $$unit$(SHEXT) ; \ + done \ + && rm -f unit-tests \ + && rm -f stamp-test-unit + +# NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting +# a phony rule that will generate symlink scripts for running individual tests +BUILT_SOURCES = wrappers + +# The include of the $(buildir)/cogl directory here is to make it so +# that tests that directly include Cogl source code for whitebox +# testing (such as test-bitmask) will still compile +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/test-fixtures \ + -I$(top_builddir)/cogl + +AM_CPPFLAGS += \ + -DCOGL_DISABLE_DEPRECATED \ + -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" \ + -DCOGL_COMPILATION + +test_unit_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) +test_unit_LDADD = \ + $(COGL_DEP_LIBS) \ + $(top_builddir)/cogl/libmutter-cogl.la \ + $(LIBM) +test_unit_LDFLAGS = -export-dynamic + +test: wrappers + @$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-unit$(EXEEXT) + +# XXX: we could prevent the unit test suite from running +# by simply defining this variable conditionally +TEST_PROGS = test-unit + +.PHONY: test + +DISTCLEANFILES = .gitignore + +# we override the clean-generic target to clean up the wrappers so +# we cannot use CLEANFILES +clean-generic: clean-wrappers + $(QUIET_RM)rm -f .log diff --git a/cogl/tests/unit/test-unit-main.c b/cogl/tests/unit/test-unit-main.c new file mode 100644 index 000000000..b1f78645e --- /dev/null +++ b/cogl/tests/unit/test-unit-main.c @@ -0,0 +1,45 @@ +#include + +#include + +#include +#include + +int +main (int argc, char **argv) +{ + GModule *main_module; + const CoglUnitTest *unit_test; + int i; + + if (argc != 2) + { + g_printerr ("usage %s UNIT_TEST\n", argv[0]); + exit (1); + } + + /* Just for convenience in case people try passing the wrapper + * filenames for the UNIT_TEST argument we normalize '-' characters + * to '_' characters... */ + for (i = 0; argv[1][i]; i++) + { + if (argv[1][i] == '-') + argv[1][i] = '_'; + } + + main_module = g_module_open (NULL, /* use main module */ + 0 /* flags */); + + if (!g_module_symbol (main_module, argv[1], (void **) &unit_test)) + { + g_printerr ("Unknown test name \"%s\"\n", argv[1]); + return 1; + } + + test_utils_init (unit_test->requirement_flags, + unit_test->known_failure_flags); + unit_test->run (); + test_utils_fini (); + + return 0; +}