summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cogl/.gitignore107
-rw-r--r--cogl/Makefile.am37
-rw-r--r--cogl/build/autotools/Makefile.am.enums52
-rw-r--r--cogl/build/autotools/as-compiler-flag.m462
-rw-r--r--cogl/build/autotools/introspection.m494
-rw-r--r--cogl/cogl-gles2/GLES2/gl2.h169
-rw-r--r--cogl/cogl-gles2/GLES2/gl2ext.h1498
-rw-r--r--cogl/cogl-gles2/GLES2/gl2platform.h28
-rw-r--r--cogl/cogl-gles2/Makefile.am31
-rw-r--r--cogl/cogl-gles2/cogl-gles2-api.c1048
-rw-r--r--cogl/cogl-gles2/mutter-cogl-gles2-1.0.pc.in13
-rw-r--r--cogl/cogl-pango/Makefile.am109
-rw-r--r--cogl/cogl-pango/cogl-pango-display-list.c499
-rw-r--r--cogl/cogl-pango/cogl-pango-display-list.h84
-rw-r--r--cogl/cogl-pango/cogl-pango-fontmap.c184
-rw-r--r--cogl/cogl-pango/cogl-pango-glyph-cache.c433
-rw-r--r--cogl/cogl-pango/cogl-pango-glyph-cache.h100
-rw-r--r--cogl/cogl-pango/cogl-pango-pipeline-cache.c242
-rw-r--r--cogl/cogl-pango/cogl-pango-pipeline-cache.h72
-rw-r--r--cogl/cogl-pango/cogl-pango-private.h65
-rw-r--r--cogl/cogl-pango/cogl-pango-render.c929
-rw-r--r--cogl/cogl-pango/cogl-pango.h298
-rw-r--r--cogl/cogl-pango/cogl-pango.symbols12
-rw-r--r--cogl/cogl-pango/mutter-cogl-pango-1.0.pc.in13
-rw-r--r--cogl/cogl-path/Makefile.am104
-rw-r--r--cogl/cogl-path/cogl-path-enum-types.c.in50
-rw-r--r--cogl/cogl-path/cogl-path-enum-types.h.in25
-rw-r--r--cogl/cogl-path/cogl-path-private.h126
-rw-r--r--cogl/cogl-path/cogl-path-types.h85
-rw-r--r--cogl/cogl-path/cogl-path.c1602
-rw-r--r--cogl/cogl-path/cogl-path.h68
-rw-r--r--cogl/cogl-path/cogl-path.symbols59
-rw-r--r--cogl/cogl-path/cogl1-path-functions.h467
-rw-r--r--cogl/cogl-path/cogl1-path.c353
-rw-r--r--cogl/cogl-path/cogl2-path-functions.h545
-rw-r--r--cogl/cogl-path/mutter-cogl-path-1.0.pc.in13
-rw-r--r--cogl/cogl-path/tesselator/GL/glu.h47
-rw-r--r--cogl/cogl-path/tesselator/README446
-rw-r--r--cogl/cogl-path/tesselator/dict-list.h100
-rw-r--r--cogl/cogl-path/tesselator/dict.c111
-rw-r--r--cogl/cogl-path/tesselator/dict.h100
-rw-r--r--cogl/cogl-path/tesselator/geom.c264
-rw-r--r--cogl/cogl-path/tesselator/geom.h84
-rw-r--r--cogl/cogl-path/tesselator/gluos.h1
-rw-r--r--cogl/cogl-path/tesselator/memalloc.h49
-rw-r--r--cogl/cogl-path/tesselator/mesh.c798
-rw-r--r--cogl/cogl-path/tesselator/mesh.h266
-rw-r--r--cogl/cogl-path/tesselator/normal.c257
-rw-r--r--cogl/cogl-path/tesselator/normal.h45
-rw-r--r--cogl/cogl-path/tesselator/priorityq-heap.c256
-rw-r--r--cogl/cogl-path/tesselator/priorityq-heap.h107
-rw-r--r--cogl/cogl-path/tesselator/priorityq-sort.h117
-rw-r--r--cogl/cogl-path/tesselator/priorityq.c261
-rw-r--r--cogl/cogl-path/tesselator/priorityq.h117
-rw-r--r--cogl/cogl-path/tesselator/render.c502
-rw-r--r--cogl/cogl-path/tesselator/render.h52
-rw-r--r--cogl/cogl-path/tesselator/sweep.c1361
-rw-r--r--cogl/cogl-path/tesselator/sweep.h77
-rw-r--r--cogl/cogl-path/tesselator/tess.c632
-rw-r--r--cogl/cogl-path/tesselator/tess.h165
-rw-r--r--cogl/cogl-path/tesselator/tesselator.h122
-rw-r--r--cogl/cogl-path/tesselator/tessmono.c201
-rw-r--r--cogl/cogl-path/tesselator/tessmono.h71
-rw-r--r--cogl/cogl/Makefile.am547
-rw-r--r--cogl/cogl/cogl-atlas-texture-private.h81
-rw-r--r--cogl/cogl/cogl-atlas-texture.c1047
-rw-r--r--cogl/cogl/cogl-atlas-texture.h258
-rw-r--r--cogl/cogl/cogl-atlas.c690
-rw-r--r--cogl/cogl/cogl-atlas.h105
-rw-r--r--cogl/cogl/cogl-attribute-buffer-private.h44
-rw-r--r--cogl/cogl/cogl-attribute-buffer.c104
-rw-r--r--cogl/cogl/cogl-attribute-buffer.h152
-rw-r--r--cogl/cogl/cogl-attribute-private.h140
-rw-r--r--cogl/cogl/cogl-attribute.c687
-rw-r--r--cogl/cogl/cogl-attribute.h558
-rw-r--r--cogl/cogl/cogl-bitmap-conversion.c748
-rw-r--r--cogl/cogl/cogl-bitmap-packing.h767
-rw-r--r--cogl/cogl/cogl-bitmap-pixbuf.c136
-rw-r--r--cogl/cogl/cogl-bitmap-private.h201
-rw-r--r--cogl/cogl/cogl-bitmap.c522
-rw-r--r--cogl/cogl/cogl-bitmap.h310
-rw-r--r--cogl/cogl/cogl-bitmask.c489
-rw-r--r--cogl/cogl/cogl-bitmask.h312
-rw-r--r--cogl/cogl/cogl-blend-string.c1003
-rw-r--r--cogl/cogl/cogl-blend-string.h144
-rw-r--r--cogl/cogl/cogl-blit.c438
-rw-r--r--cogl/cogl/cogl-blit.h101
-rw-r--r--cogl/cogl/cogl-boxed-value.c377
-rw-r--r--cogl/cogl/cogl-boxed-value.h117
-rw-r--r--cogl/cogl/cogl-buffer-private.h180
-rw-r--r--cogl/cogl/cogl-buffer.c411
-rw-r--r--cogl/cogl/cogl-buffer.h324
-rw-r--r--cogl/cogl/cogl-clip-stack.c412
-rw-r--r--cogl/cogl/cogl-clip-stack.h213
-rw-r--r--cogl/cogl/cogl-closure-list-private.h118
-rw-r--r--cogl/cogl/cogl-closure-list.c71
-rw-r--r--cogl/cogl/cogl-color-private.h51
-rw-r--r--cogl/cogl/cogl-color.c449
-rw-r--r--cogl/cogl/cogl-color.h604
-rw-r--r--cogl/cogl/cogl-config-private.h45
-rw-r--r--cogl/cogl/cogl-config.c147
-rw-r--r--cogl/cogl/cogl-context-private.h407
-rw-r--r--cogl/cogl/cogl-context.c786
-rw-r--r--cogl/cogl/cogl-context.h381
-rw-r--r--cogl/cogl/cogl-debug-options.h199
-rw-r--r--cogl/cogl/cogl-debug.c302
-rw-r--r--cogl/cogl/cogl-debug.h126
-rw-r--r--cogl/cogl/cogl-defines.h.in45
-rw-r--r--cogl/cogl/cogl-deprecated.h43
-rw-r--r--cogl/cogl/cogl-depth-state-private.h38
-rw-r--r--cogl/cogl/cogl-depth-state.c116
-rw-r--r--cogl/cogl/cogl-depth-state.h270
-rw-r--r--cogl/cogl/cogl-display-private.h54
-rw-r--r--cogl/cogl/cogl-display.c167
-rw-r--r--cogl/cogl/cogl-display.h214
-rw-r--r--cogl/cogl/cogl-driver.h268
-rw-r--r--cogl/cogl/cogl-egl-defines.h.in40
-rw-r--r--cogl/cogl/cogl-egl-private.h40
-rw-r--r--cogl/cogl/cogl-egl.h118
-rw-r--r--cogl/cogl/cogl-enum-types.c.in50
-rw-r--r--cogl/cogl/cogl-enum-types.h.in25
-rw-r--r--cogl/cogl/cogl-error-private.h59
-rw-r--r--cogl/cogl/cogl-error.c135
-rw-r--r--cogl/cogl/cogl-error.h185
-rw-r--r--cogl/cogl/cogl-euler.c198
-rw-r--r--cogl/cogl/cogl-euler.h269
-rw-r--r--cogl/cogl/cogl-feature-private.c234
-rw-r--r--cogl/cogl/cogl-feature-private.h106
-rw-r--r--cogl/cogl/cogl-fence-private.h66
-rw-r--r--cogl/cogl/cogl-fence.c236
-rw-r--r--cogl/cogl/cogl-fence.h143
-rw-r--r--cogl/cogl/cogl-flags.h130
-rw-r--r--cogl/cogl/cogl-frame-info-private.h50
-rw-r--r--cogl/cogl/cogl-frame-info.c81
-rw-r--r--cogl/cogl/cogl-frame-info.h148
-rw-r--r--cogl/cogl/cogl-framebuffer-private.h514
-rw-r--r--cogl/cogl/cogl-framebuffer.c2544
-rw-r--r--cogl/cogl/cogl-framebuffer.h1866
-rw-r--r--cogl/cogl/cogl-gl-header.h.in46
-rw-r--r--cogl/cogl/cogl-gles2-context-private.h201
-rw-r--r--cogl/cogl/cogl-gles2-context.c1966
-rw-r--r--cogl/cogl/cogl-gles2-types.h474
-rw-r--r--cogl/cogl/cogl-gles2.h420
-rw-r--r--cogl/cogl/cogl-glib-source.c195
-rw-r--r--cogl/cogl/cogl-glib-source.h97
-rw-r--r--cogl/cogl/cogl-glsl-shader-boilerplate.h86
-rw-r--r--cogl/cogl/cogl-glsl-shader-private.h41
-rw-r--r--cogl/cogl/cogl-glsl-shader.c192
-rw-r--r--cogl/cogl/cogl-glx-display-private.h62
-rw-r--r--cogl/cogl/cogl-glx-renderer-private.h108
-rw-r--r--cogl/cogl/cogl-glx.h95
-rw-r--r--cogl/cogl/cogl-gpu-info-private.h117
-rw-r--r--cogl/cogl/cogl-gpu-info.c581
-rw-r--r--cogl/cogl/cogl-gtype-private.h278
-rw-r--r--cogl/cogl/cogl-gtype.c153
-rw-r--r--cogl/cogl/cogl-i18n-private.h39
-rw-r--r--cogl/cogl/cogl-index-buffer-private.h44
-rw-r--r--cogl/cogl/cogl-index-buffer.c112
-rw-r--r--cogl/cogl/cogl-index-buffer.h107
-rw-r--r--cogl/cogl/cogl-indices-private.h60
-rw-r--r--cogl/cogl/cogl-indices.c271
-rw-r--r--cogl/cogl/cogl-indices.h165
-rw-r--r--cogl/cogl/cogl-journal-private.h121
-rw-r--r--cogl/cogl/cogl-journal.c1853
-rw-r--r--cogl/cogl/cogl-kms-display.h119
-rw-r--r--cogl/cogl/cogl-kms-renderer.h74
-rw-r--r--cogl/cogl/cogl-list.c94
-rw-r--r--cogl/cogl/cogl-list.h129
-rw-r--r--cogl/cogl/cogl-macros.h287
-rw-r--r--cogl/cogl/cogl-magazine-private.h81
-rw-r--r--cogl/cogl/cogl-magazine.c84
-rw-r--r--cogl/cogl/cogl-matrix-private.h58
-rw-r--r--cogl/cogl/cogl-matrix-stack-private.h200
-rw-r--r--cogl/cogl/cogl-matrix-stack.c1211
-rw-r--r--cogl/cogl/cogl-matrix-stack.h645
-rw-r--r--cogl/cogl/cogl-matrix.c2313
-rw-r--r--cogl/cogl/cogl-matrix.h821
-rw-r--r--cogl/cogl/cogl-memory-stack-private.h50
-rw-r--r--cogl/cogl/cogl-memory-stack.c196
-rw-r--r--cogl/cogl/cogl-meta-texture.c642
-rw-r--r--cogl/cogl/cogl-meta-texture.h194
-rw-r--r--cogl/cogl/cogl-node-private.h89
-rw-r--r--cogl/cogl/cogl-node.c110
-rw-r--r--cogl/cogl/cogl-object-private.h323
-rw-r--r--cogl/cogl/cogl-object.c304
-rw-r--r--cogl/cogl/cogl-object.h251
-rw-r--r--cogl/cogl/cogl-offscreen.h172
-rw-r--r--cogl/cogl/cogl-onscreen-private.h115
-rw-r--r--cogl/cogl/cogl-onscreen-template-private.h45
-rw-r--r--cogl/cogl/cogl-onscreen-template.c105
-rw-r--r--cogl/cogl/cogl-onscreen-template.h143
-rw-r--r--cogl/cogl/cogl-onscreen.c720
-rw-r--r--cogl/cogl/cogl-onscreen.h911
-rw-r--r--cogl/cogl/cogl-output-private.h57
-rw-r--r--cogl/cogl/cogl-output.c119
-rw-r--r--cogl/cogl/cogl-output.h261
-rw-r--r--cogl/cogl/cogl-pango.h40
-rw-r--r--cogl/cogl/cogl-pipeline-cache.c216
-rw-r--r--cogl/cogl/cogl-pipeline-cache.h93
-rw-r--r--cogl/cogl/cogl-pipeline-debug.c301
-rw-r--r--cogl/cogl/cogl-pipeline-hash-table.c233
-rw-r--r--cogl/cogl/cogl-pipeline-hash-table.h81
-rw-r--r--cogl/cogl/cogl-pipeline-layer-private.h390
-rw-r--r--cogl/cogl/cogl-pipeline-layer-state-private.h141
-rw-r--r--cogl/cogl/cogl-pipeline-layer-state.c1814
-rw-r--r--cogl/cogl/cogl-pipeline-layer-state.h620
-rw-r--r--cogl/cogl/cogl-pipeline-layer.c942
-rw-r--r--cogl/cogl/cogl-pipeline-private.h998
-rw-r--r--cogl/cogl/cogl-pipeline-snippet-private.h116
-rw-r--r--cogl/cogl/cogl-pipeline-snippet.c286
-rw-r--r--cogl/cogl/cogl-pipeline-state-private.h196
-rw-r--r--cogl/cogl/cogl-pipeline-state.c2171
-rw-r--r--cogl/cogl/cogl-pipeline-state.h980
-rw-r--r--cogl/cogl/cogl-pipeline.c3174
-rw-r--r--cogl/cogl/cogl-pipeline.h194
-rw-r--r--cogl/cogl/cogl-pixel-buffer-private.h52
-rw-r--r--cogl/cogl/cogl-pixel-buffer.c134
-rw-r--r--cogl/cogl/cogl-pixel-buffer.h138
-rw-r--r--cogl/cogl/cogl-point-in-poly-private.h46
-rw-r--r--cogl/cogl/cogl-point-in-poly.c101
-rw-r--r--cogl/cogl/cogl-poll-private.h77
-rw-r--r--cogl/cogl/cogl-poll.c267
-rw-r--r--cogl/cogl/cogl-poll.h195
-rw-r--r--cogl/cogl/cogl-primitive-private.h73
-rw-r--r--cogl/cogl/cogl-primitive-texture.c60
-rw-r--r--cogl/cogl/cogl-primitive-texture.h111
-rw-r--r--cogl/cogl/cogl-primitive.c645
-rw-r--r--cogl/cogl/cogl-primitive.h942
-rw-r--r--cogl/cogl/cogl-primitives-private.h67
-rw-r--r--cogl/cogl/cogl-primitives.c1148
-rw-r--r--cogl/cogl/cogl-primitives.h197
-rw-r--r--cogl/cogl/cogl-private.h170
-rw-r--r--cogl/cogl/cogl-profile.c124
-rw-r--r--cogl/cogl/cogl-profile.h68
-rw-r--r--cogl/cogl/cogl-quaternion-private.h44
-rw-r--r--cogl/cogl/cogl-quaternion.c673
-rw-r--r--cogl/cogl/cogl-quaternion.h564
-rw-r--r--cogl/cogl/cogl-rectangle-map.c764
-rw-r--r--cogl/cogl/cogl-rectangle-map.h84
-rw-r--r--cogl/cogl/cogl-renderer-private.h112
-rw-r--r--cogl/cogl/cogl-renderer.c804
-rw-r--r--cogl/cogl/cogl-renderer.h432
-rw-r--r--cogl/cogl/cogl-sampler-cache-private.h96
-rw-r--r--cogl/cogl/cogl-sampler-cache.c371
-rw-r--r--cogl/cogl/cogl-snippet-private.h77
-rw-r--r--cogl/cogl/cogl-snippet.c187
-rw-r--r--cogl/cogl/cogl-snippet.h866
-rw-r--r--cogl/cogl/cogl-spans.c183
-rw-r--r--cogl/cogl/cogl-spans.h81
-rw-r--r--cogl/cogl/cogl-sub-texture-private.h62
-rw-r--r--cogl/cogl/cogl-sub-texture.c480
-rw-r--r--cogl/cogl/cogl-sub-texture.h136
-rw-r--r--cogl/cogl/cogl-swap-chain-private.h45
-rw-r--r--cogl/cogl/cogl-swap-chain.c76
-rw-r--r--cogl/cogl/cogl-swap-chain.h71
-rw-r--r--cogl/cogl/cogl-texture-2d-gl.h78
-rw-r--r--cogl/cogl/cogl-texture-2d-private.h134
-rw-r--r--cogl/cogl/cogl-texture-2d-sliced-private.h67
-rw-r--r--cogl/cogl/cogl-texture-2d-sliced.c1546
-rw-r--r--cogl/cogl/cogl-texture-2d-sliced.h301
-rw-r--r--cogl/cogl/cogl-texture-2d.c695
-rw-r--r--cogl/cogl/cogl-texture-2d.h234
-rw-r--r--cogl/cogl/cogl-texture-3d-private.h66
-rw-r--r--cogl/cogl/cogl-texture-3d.c761
-rw-r--r--cogl/cogl/cogl-texture-3d.h204
-rw-r--r--cogl/cogl/cogl-texture-driver.h206
-rw-r--r--cogl/cogl/cogl-texture-private.h409
-rw-r--r--cogl/cogl/cogl-texture-rectangle-private.h66
-rw-r--r--cogl/cogl/cogl-texture-rectangle.c781
-rw-r--r--cogl/cogl/cogl-texture-rectangle.h218
-rw-r--r--cogl/cogl/cogl-texture.c1540
-rw-r--r--cogl/cogl/cogl-texture.h524
-rw-r--r--cogl/cogl/cogl-types.h940
-rw-r--r--cogl/cogl/cogl-util.c286
-rw-r--r--cogl/cogl/cogl-util.h305
-rw-r--r--cogl/cogl/cogl-vector.c300
-rw-r--r--cogl/cogl/cogl-vector.h356
-rw-r--r--cogl/cogl/cogl-version.h358
-rw-r--r--cogl/cogl/cogl-wayland-server.h163
-rw-r--r--cogl/cogl/cogl-x11-renderer-private.h40
-rw-r--r--cogl/cogl/cogl-xlib-private.h54
-rw-r--r--cogl/cogl/cogl-xlib-renderer-private.h103
-rw-r--r--cogl/cogl/cogl-xlib-renderer.c677
-rw-r--r--cogl/cogl/cogl-xlib-renderer.h191
-rw-r--r--cogl/cogl/cogl-xlib.c112
-rw-r--r--cogl/cogl/cogl-xlib.h132
-rw-r--r--cogl/cogl/cogl.c820
-rw-r--r--cogl/cogl/cogl.h191
-rw-r--r--cogl/cogl/cogl.symbols1085
-rw-r--r--cogl/cogl/cogl1-context.h862
-rw-r--r--cogl/cogl/cogl2-experimental.h37
-rw-r--r--cogl/cogl/deprecated/cogl-auto-texture.c419
-rw-r--r--cogl/cogl/deprecated/cogl-auto-texture.h221
-rw-r--r--cogl/cogl/deprecated/cogl-clip-state.c138
-rw-r--r--cogl/cogl/deprecated/cogl-clip-state.h266
-rw-r--r--cogl/cogl/deprecated/cogl-clutter-xlib.h46
-rw-r--r--cogl/cogl/deprecated/cogl-clutter.c114
-rw-r--r--cogl/cogl/deprecated/cogl-clutter.h54
-rw-r--r--cogl/cogl/deprecated/cogl-fixed.c1112
-rw-r--r--cogl/cogl/deprecated/cogl-fixed.h811
-rw-r--r--cogl/cogl/deprecated/cogl-framebuffer-deprecated.c295
-rw-r--r--cogl/cogl/deprecated/cogl-framebuffer-deprecated.h264
-rw-r--r--cogl/cogl/deprecated/cogl-material-compat.c461
-rw-r--r--cogl/cogl/deprecated/cogl-material-compat.h1391
-rw-r--r--cogl/cogl/deprecated/cogl-program-private.h88
-rw-r--r--cogl/cogl/deprecated/cogl-program.c503
-rw-r--r--cogl/cogl/deprecated/cogl-shader-private.h72
-rw-r--r--cogl/cogl/deprecated/cogl-shader.c377
-rw-r--r--cogl/cogl/deprecated/cogl-shader.h704
-rw-r--r--cogl/cogl/deprecated/cogl-texture-deprecated.c85
-rw-r--r--cogl/cogl/deprecated/cogl-texture-deprecated.h105
-rw-r--r--cogl/cogl/deprecated/cogl-type-casts.h53
-rw-r--r--cogl/cogl/deprecated/cogl-vertex-buffer-private.h165
-rw-r--r--cogl/cogl/deprecated/cogl-vertex-buffer.c1795
-rw-r--r--cogl/cogl/deprecated/cogl-vertex-buffer.h451
-rw-r--r--cogl/cogl/driver/gl/cogl-attribute-gl-private.h53
-rw-r--r--cogl/cogl/driver/gl/cogl-attribute-gl.c541
-rw-r--r--cogl/cogl/driver/gl/cogl-buffer-gl-private.h74
-rw-r--r--cogl/cogl/driver/gl/cogl-buffer-gl.c442
-rw-r--r--cogl/cogl/driver/gl/cogl-clip-stack-gl-private.h45
-rw-r--r--cogl/cogl/driver/gl/cogl-clip-stack-gl.c627
-rw-r--r--cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h102
-rw-r--r--cogl/cogl/driver/gl/cogl-framebuffer-gl.c1624
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed-private.h42
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-fragend-fixed.c435
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl-private.h45
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl.c1149
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-opengl-private.h158
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-opengl.c1484
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-progend-fixed-private.h42
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-progend-fixed.c112
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-progend-glsl-private.h47
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-progend-glsl.c1074
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed-private.h42
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-vertend-fixed.c121
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl-private.h45
-rw-r--r--cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl.c680
-rw-r--r--cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h119
-rw-r--r--cogl/cogl/driver/gl/cogl-texture-2d-gl.c756
-rw-r--r--cogl/cogl/driver/gl/cogl-texture-gl-private.h66
-rw-r--r--cogl/cogl/driver/gl/cogl-texture-gl.c158
-rw-r--r--cogl/cogl/driver/gl/cogl-util-gl-private.h93
-rw-r--r--cogl/cogl/driver/gl/cogl-util-gl.c186
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-driver-gl.c699
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp-private.h42
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-pipeline-fragend-arbfp.c990
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp-private.h42
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c121
-rw-r--r--cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c555
-rw-r--r--cogl/cogl/driver/gl/gles/cogl-driver-gles.c487
-rw-r--r--cogl/cogl/driver/gl/gles/cogl-texture-driver-gles.c652
-rw-r--r--cogl/cogl/driver/nop/cogl-attribute-nop-private.h48
-rw-r--r--cogl/cogl/driver/nop/cogl-attribute-nop.c49
-rw-r--r--cogl/cogl/driver/nop/cogl-clip-stack-nop-private.h44
-rw-r--r--cogl/cogl/driver/nop/cogl-clip-stack-nop.c43
-rw-r--r--cogl/cogl/driver/nop/cogl-driver-nop.c86
-rw-r--r--cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h100
-rw-r--r--cogl/cogl/driver/nop/cogl-framebuffer-nop.c121
-rw-r--r--cogl/cogl/driver/nop/cogl-texture-2d-nop-private.h103
-rw-r--r--cogl/cogl/driver/nop/cogl-texture-2d-nop.c132
-rw-r--r--cogl/cogl/gl-prototypes/cogl-all-functions.h328
-rw-r--r--cogl/cogl/gl-prototypes/cogl-core-functions.h198
-rw-r--r--cogl/cogl/gl-prototypes/cogl-fixed-functions.h119
-rw-r--r--cogl/cogl/gl-prototypes/cogl-gles1-functions.h43
-rw-r--r--cogl/cogl/gl-prototypes/cogl-gles2-functions.h43
-rw-r--r--cogl/cogl/gl-prototypes/cogl-glsl-functions.h286
-rw-r--r--cogl/cogl/gl-prototypes/cogl-in-gles-core-functions.h148
-rw-r--r--cogl/cogl/gl-prototypes/cogl-in-gles1-core-functions.h78
-rw-r--r--cogl/cogl/gl-prototypes/cogl-in-gles2-core-functions.h186
-rw-r--r--cogl/cogl/mutter-cogl-1.0.pc.in13
-rw-r--r--cogl/cogl/winsys/cogl-texture-pixmap-x11-private.h103
-rw-r--r--cogl/cogl/winsys/cogl-texture-pixmap-x11.c1184
-rw-r--r--cogl/cogl/winsys/cogl-texture-pixmap-x11.h294
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h149
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-kms-private.h41
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-kms.c1335
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-private.h203
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-x11-private.h39
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl-x11.c856
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl.c1085
-rw-r--r--cogl/cogl/winsys/cogl-winsys-glx-feature-functions.h210
-rw-r--r--cogl/cogl/winsys/cogl-winsys-glx-private.h37
-rw-r--r--cogl/cogl/winsys/cogl-winsys-glx.c2748
-rw-r--r--cogl/cogl/winsys/cogl-winsys-private.h196
-rw-r--r--cogl/cogl/winsys/cogl-winsys-stub-private.h37
-rw-r--r--cogl/cogl/winsys/cogl-winsys-stub.c197
-rw-r--r--cogl/cogl/winsys/cogl-winsys.c52
-rw-r--r--cogl/config-custom.h32
-rw-r--r--cogl/configure.ac1036
-rw-r--r--cogl/test-fixtures/Makefile.am21
-rw-r--r--cogl/test-fixtures/test-unit.h31
-rw-r--r--cogl/test-fixtures/test-utils.c535
-rw-r--r--cogl/test-fixtures/test-utils.h287
-rw-r--r--cogl/tests/Makefile.am31
-rw-r--r--cogl/tests/README63
-rw-r--r--cogl/tests/config.env.in3
-rw-r--r--cogl/tests/conform/Makefile.am176
-rw-r--r--cogl/tests/conform/test-alpha-test.c73
-rw-r--r--cogl/tests/conform/test-alpha-textures.c123
-rw-r--r--cogl/tests/conform/test-atlas-migration.c145
-rw-r--r--cogl/tests/conform/test-backface-culling.c311
-rw-r--r--cogl/tests/conform/test-blend-strings.c430
-rw-r--r--cogl/tests/conform/test-blend.c64
-rw-r--r--cogl/tests/conform/test-color-hsl.c45
-rw-r--r--cogl/tests/conform/test-color-mask.c110
-rw-r--r--cogl/tests/conform/test-conform-main.c157
-rw-r--r--cogl/tests/conform/test-copy-replace-texture.c120
-rw-r--r--cogl/tests/conform/test-custom-attributes.c301
-rw-r--r--cogl/tests/conform/test-depth-test.c301
-rw-r--r--cogl/tests/conform/test-euler-quaternion.c81
-rw-r--r--cogl/tests/conform/test-fence.c63
-rw-r--r--cogl/tests/conform/test-fixed.c18
-rw-r--r--cogl/tests/conform/test-fixtures.c12
-rw-r--r--cogl/tests/conform/test-framebuffer-get-bits.c40
-rw-r--r--cogl/tests/conform/test-gles2-context.c962
-rw-r--r--cogl/tests/conform/test-just-vertex-shader.c205
-rw-r--r--cogl/tests/conform/test-layer-remove.c145
-rw-r--r--cogl/tests/conform/test-map-buffer-range.c123
-rw-r--r--cogl/tests/conform/test-materials.c253
-rw-r--r--cogl/tests/conform/test-multitexture.c206
-rw-r--r--cogl/tests/conform/test-no-gl-header.c16
-rw-r--r--cogl/tests/conform/test-npot-texture.c170
-rw-r--r--cogl/tests/conform/test-object.c86
-rw-r--r--cogl/tests/conform/test-offscreen.c199
-rw-r--r--cogl/tests/conform/test-path-clip.c68
-rw-r--r--cogl/tests/conform/test-path.c215
-rw-r--r--cogl/tests/conform/test-pipeline-cache-unrefs-texture.c91
-rw-r--r--cogl/tests/conform/test-pipeline-shader-state.c93
-rw-r--r--cogl/tests/conform/test-pipeline-uniforms.c415
-rw-r--r--cogl/tests/conform/test-pipeline-user-matrix.c140
-rw-r--r--cogl/tests/conform/test-pixel-buffer.c269
-rw-r--r--cogl/tests/conform/test-point-size-attribute.c166
-rw-r--r--cogl/tests/conform/test-point-size.c99
-rw-r--r--cogl/tests/conform/test-point-sprite.c194
-rw-r--r--cogl/tests/conform/test-premult.c301
-rw-r--r--cogl/tests/conform/test-primitive-and-journal.c122
-rw-r--r--cogl/tests/conform/test-primitive.c334
-rw-r--r--cogl/tests/conform/test-read-texture-formats.c222
-rw-r--r--cogl/tests/conform/test-readpixels.c178
-rw-r--r--cogl/tests/conform/test-snippets.c815
-rw-r--r--cogl/tests/conform/test-sparse-pipeline.c62
-rw-r--r--cogl/tests/conform/test-sub-texture.c325
-rw-r--r--cogl/tests/conform/test-texture-3d.c274
-rw-r--r--cogl/tests/conform/test-texture-get-set-data.c144
-rw-r--r--cogl/tests/conform/test-texture-mipmaps.c136
-rw-r--r--cogl/tests/conform/test-texture-no-allocate.c80
-rw-r--r--cogl/tests/conform/test-texture-pixmap-x11.c245
-rw-r--r--cogl/tests/conform/test-texture-rectangle.c276
-rw-r--r--cogl/tests/conform/test-texture-rg.c74
-rw-r--r--cogl/tests/conform/test-version.c85
-rw-r--r--cogl/tests/conform/test-vertex-buffer-contiguous.c257
-rw-r--r--cogl/tests/conform/test-vertex-buffer-interleved.c162
-rw-r--r--cogl/tests/conform/test-vertex-buffer-mutability.c198
-rw-r--r--cogl/tests/conform/test-viewport.c416
-rw-r--r--cogl/tests/conform/test-wrap-modes.c296
-rw-r--r--cogl/tests/conform/test-wrap-rectangle-textures.c175
-rw-r--r--cogl/tests/conform/test-write-texture-formats.c184
-rw-r--r--cogl/tests/data/Makefile.am3
-rw-r--r--cogl/tests/data/valgrind.suppressions173
-rw-r--r--cogl/tests/micro-perf/Makefile.am24
-rw-r--r--cogl/tests/micro-perf/test-journal.c190
-rwxr-xr-xcogl/tests/run-tests.sh157
-rwxr-xr-xcogl/tests/test-launcher.sh39
-rw-r--r--cogl/tests/unit/Makefile.am83
-rw-r--r--cogl/tests/unit/test-unit-main.c45
465 files changed, 147172 insertions, 0 deletions
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 <ebassi@linux.intel.com>
+
+# 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 <ds@schleef.org>
+
+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 <GLES2/gl2platform.h>
+#include <cogl/cogl-gles2-types.h>
+
+#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 <GLES2/gl2.h>
+
+#include <cogl/cogl-gles2.h>
+
+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 <glib.h>
+#include <string.h>
+
+#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 <glib.h>
+#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 <pango/pango-fontmap.h>
+#include <pango/pangocairo.h>
+#include <pango/pango-renderer.h>
+
+#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 <glib.h>
+
+#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 <glib.h>
+#include <pango/pango-font.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#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 <neil@linux.intel.com>
+ */
+
+#ifndef __COGL_PANGO_PIPELINE_CACHE_H__
+#define __COGL_PANGO_PIPELINE_CACHE_H__
+
+#include <glib.h>
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ * Matthew Allum <mallum@openedhand.com>
+ */
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ * Matthew Allum <mallum@openedhand.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_BACKEND 1
+#endif
+
+#include <pango/pango-fontmap.h>
+#include <pango/pangocairo.h>
+#include <pango/pango-renderer.h>
+#include <cairo.h>
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ * Matthew Allum <mallum@openedhand.com>
+ */
+
+#ifndef __COGL_PANGO_H__
+#define __COGL_PANGO_H__
+
+#include <glib-object.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+
+/* XXX: Currently this header may be included both as an internal
+ * header (within the cogl-pango implementation) and as a public
+ * header.
+ *
+ * Since <cogl/cogl.h> 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 <cogl/cogl.h>
+#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 <glib-object.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_PATH_TYPES_H__
+#define __COGL_PATH_TYPES_H__
+
+#include <cogl/cogl-types.h>
+
+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 <xref
+ * linkend="fill-rule-non-zero"/>.
+ * @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 <xref linkend="fill-rule-even-odd"/>.
+ *
+ * #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.
+ *
+ * <figure id="fill-rule-non-zero">
+ * <title>Example of filling various paths using the non-zero rule</title>
+ * <graphic fileref="fill-rule-non-zero.png" format="PNG"/>
+ * </figure>
+ *
+ * <figure id="fill-rule-even-odd">
+ * <title>Example of filling various paths using the even-odd rule</title>
+ * <graphic fileref="fill-rule-even-odd.png" format="PNG"/>
+ * </figure>
+ *
+ * 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 <ivan@openedhand.com>
+ * Øyvind Kolås <pippin@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+#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 <cogl/cogl-defines.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <cogl-path/cogl-path-enum-types.h>
+#endif
+
+#include <cogl-path/cogl-path-types.h>
+
+#ifdef COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <cogl-path/cogl2-path-functions.h>
+#else
+#include <cogl-path/cogl1-path-functions.h>
+#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 <cogl/cogl.h> 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/cogl-types.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL2_PATH_FUNCTIONS_H__
+#define __COGL2_PATH_FUNCTIONS_H__
+
+#include <cogl/cogl-types.h>
+#ifdef COGL_COMPILATION
+#include "cogl-context.h"
+#else
+#include <cogl/cogl.h>
+#endif
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <GL/glu.h> */
+
+#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 <stddef.h>
+#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 <assert.h>
+#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 <stdlib.h>
+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 <glib.h>
+
+#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 <stddef.h>
+#include <assert.h>
+#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 <GL/glu.h>
+
+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 <math.h>
+#include <assert.h>
+
+#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 <stdlib.h>
+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 <stddef.h>
+#include <assert.h>
+#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 <stddef.h>
+#include <assert.h>
+#include <limits.h> /* 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 <assert.h>
+#include <stddef.h>
+#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 <assert.h>
+#include <stddef.h>
+#include <setjmp.h> /* longjmp */
+#include <limits.h> /* 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 <stddef.h>
+#include <assert.h>
+#include <setjmp.h>
+#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 <GL/glu.h>
+#include <setjmp.h>
+#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 <stdlib.h>
+#include "geom.h"
+#include "mesh.h"
+#include "tessmono.h"
+#include <assert.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <stdlib.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef _COGL_ATLAS_TEXTURE_H_
+#define _COGL_ATLAS_TEXTURE_H_
+
+#include <cogl/cogl-context.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ */
+
+
+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().
+ *
+ * <note>Allocate call can fail if Cogl considers the internal
+ * format to be incompatible with the format of its internal
+ * atlases.</note>
+ *
+ * <note>The returned #CoglAtlasTexture is a high-level meta-texture
+ * with some limitations. See the documentation for #CoglMetaTexture
+ * for more details.</note>
+ *
+ * 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().
+ *
+ * <note>Allocate call can fail if Cogl considers the internal
+ * format to be incompatible with the format of its internal
+ * atlases.</note>
+ *
+ * <note>The returned #CoglAtlasTexture is a high-level meta-texture
+ * with some limitations. See the documentation for #CoglMetaTexture
+ * for more details.</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * <note>Allocate call can fail if Cogl considers the internal
+ * format to be incompatible with the format of its internal
+ * atlases.</note>
+ *
+ * <note>The returned #CoglAtlasTexture is a high-level
+ * meta-texture with some limitations. See the documentation for
+ * #CoglMetaTexture for more details.</note>
+ *
+ * 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().
+ *
+ * <note>Allocate call can fail if Cogl considers the internal
+ * format to be incompatible with the format of its internal
+ * atlases.</note>
+ *
+ * <note>The returned #CoglAtlasTexture is a high-level meta-texture
+ * with some limitations. See the documentation for #CoglMetaTexture
+ * for more details.</note>
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <stdlib.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-context.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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().</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* 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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-attribute-buffer.h>
+#include <cogl/cogl-indices.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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
+ * <literal>sizeof (MyVertex)</literal>)
+ * @offset: The byte offset from the start of @attribute_buffer for
+ * the first attribute value. (Usually
+ * <literal>offsetof (MyVertex, component0)</literal>
+ * @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:
+ * <itemizedlist>
+ * <listitem>"cogl_position_in" (used for vertex positions)</listitem>
+ * <listitem>"cogl_color_in" (used for vertex colors)</listitem>
+ * <listitem>"cogl_tex_coord0_in", "cogl_tex_coord1", ...
+ * (used for vertex texture coordinates)</listitem>
+ * <listitem>"cogl_normal_in" (used for vertex normals)</listitem>
+ * <listitem>"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.
+ * </listitem>
+ * </itemizedlist>
+ *
+ * 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 <literal>sizeof (MyVertex)</literal> 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 <literal>(2 * sizeof (float))</literal>. 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 <literal>MyVertex</literal> example above the @offset is the
+ * offset inside the <literal>MyVertex</literal> structure to the first
+ * component of the attribute. For the texture coordinate attribute
+ * the offset would be <literal>offsetof (MyVertex, s)</literal> or instead of
+ * using the offsetof macro you could use <literal>sizeof (float) *
+ * 3</literal>. 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 <string.h>
+
+#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 <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+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 <glib.h>
+
+#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 <string.h>
+
+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 <cogl/cogl.h> 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 <cogl/cogl-types.h>
+#include <cogl/cogl-buffer.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-pixel-buffer.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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().</note>
+ *
+ * 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 #GdkPixbufError<!-- -->s
+ * 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 <neil@linux.intel.com>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+
+#include <test-fixtures/test-unit.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifndef __COGL_BITMASK_H
+#define __COGL_BITMASK_H
+
+#include <glib.h>
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifndef COGL_BLEND_STRING_H
+#define COGL_BLEND_STRING_H
+
+#include <stdlib.h>
+#include <glib.h>
+
+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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <glib.h>
+#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 <string.h>
+
+#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 <glib.h>
+
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_BUFFER_PRIVATE_H__
+#define __COGL_BUFFER_PRIVATE_H__
+
+#include <glib.h>
+
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+/* 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 <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_BUFFER_H__
+#define __COGL_BUFFER_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-error.h>
+
+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 #CoglBufferMapHint<!-- -->s 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 #CoglBufferMapHint<!-- -->s 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 <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#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 <config.h>
+
+#include <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_COLOR_PRIVATE_PRIVATE_H
+#define __COGL_COLOR_PRIVATE_PRIVATE_H
+
+#include "cogl-color.h"
+
+#include <glib.h>
+
+/* 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 <string.h>
+
+#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 <cogl/cogl.h> 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 <cogl/cogl-types.h>
+#include <cogl/cogl-macros.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 #CoglColor<!-- -->s 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 #CoglColor<!-- -->s 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-debug.h"
+#include "cogl-config-private.h"
+
+#include <glib.h>
+
+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 <string.h>
+#include <stdlib.h>
+
+#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 <robert@linux.intel.com>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-defines.h>
+#include <cogl/cogl-display.h>
+#include <cogl/cogl-primitive.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note><para>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.</para></note>
+ */
+
+#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 CoglFeatureID<!-- -->s
+ *
+ * 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 <stdlib.h>
+
+#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 <glib.h>
+
+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 <cogl/cogl.h> 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_DISPLAY_H__
+#define __COGL_DISPLAY_H__
+
+#include <cogl/cogl-renderer.h>
+#include <cogl/cogl-onscreen-template.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <cogl/cogl-egl-defines.h>
+#include <cogl/cogl-types.h>
+
+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 <glib-object.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-types.h"
+#include "cogl-util.h"
+#include "cogl-error-private.h"
+
+#include <glib.h>
+
+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 <cogl/cogl.h> 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl-util.h>
+#include <cogl-euler.h>
+#include <cogl-matrix.h>
+#include "cogl-gtype-private.h"
+
+#include <math.h>
+#include <string.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_EULER_H
+#define __COGL_EULER_H
+
+#include <cogl/cogl-types.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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:
+ * <itemizedlist>
+ * <listitem>
+ * Easy to understand and use, compared to quaternions and matrices,
+ * so may be a good choice for a user interface.
+ * </listitem>
+ * <listitem>
+ * Efficient storage, needing only 3 components any rotation can be
+ * represented.
+ * <note>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.</note>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Disadvantages:
+ * <itemizedlist>
+ * <listitem>
+ * Aliasing: it's possible to represent some rotations with multiple
+ * different heading, pitch and roll rotations.
+ * </listitem>
+ * <listitem>
+ * 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.
+ * </listitem>
+ * <listitem>
+ * 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.
+ * </listitem>
+ * <listitem>
+ * 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".
+ * </listitem>
+ * </itemizedlist>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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</note>
+ *
+ * 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 <string.h>
+
+#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 <glib.h>
+
+
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_FENCE_H__
+#define __COGL_FENCE_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-framebuffer.h>
+
+/**
+ * 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 <neil@linux.intel.com>
+ */
+
+#ifndef __COGL_FLAGS_H
+#define __COGL_FLAGS_H
+
+#include <glib.h>
+
+#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 <otaylor@redhat.com>
+ */
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_FRAME_INFO_H
+#define __COGL_FRAME_INFO_H
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-output.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#endif
+#include <glib.h>
+
+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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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().</note>
+ *
+ * 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 <X11/Xlib.h>
+#endif
+
+#ifdef COGL_HAS_GLX_SUPPORT
+#include <GL/glx.h>
+#include <GL/glxext.h>
+#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<<COGL_FRAMEBUFFER_STATE_INDEX_MAX) - 1)
+
+/* Private flags that can internally be added to CoglReadPixelsFlags */
+typedef enum
+{
+ /* If this is set then the data will not be flipped to compensate
+ for GL's upside-down coordinate system but instead will be left
+ in whatever order GL gives us (which will depend on whether the
+ framebuffer is offscreen or not) */
+ COGL_READ_PIXELS_NO_FLIP = 1L << 30
+} CoglPrivateReadPixelsFlags;
+
+typedef struct
+{
+ int red;
+ int blue;
+ int green;
+ int alpha;
+ int depth;
+ int stencil;
+} CoglFramebufferBits;
+
+struct _CoglFramebuffer
+{
+ CoglObject _parent;
+ CoglContext *context;
+ CoglFramebufferType type;
+
+ /* The user configuration before allocation... */
+ CoglFramebufferConfig config;
+
+ int width;
+ int height;
+ /* Format of the pixels in the framebuffer (including the expected
+ premult state) */
+ CoglPixelFormat internal_format;
+ CoglBool allocated;
+
+ CoglMatrixStack *modelview_stack;
+ CoglMatrixStack *projection_stack;
+ float viewport_x;
+ float viewport_y;
+ float viewport_width;
+ float viewport_height;
+ int viewport_age;
+ int viewport_age_for_scissor_workaround;
+
+ CoglClipStack *clip_stack;
+
+ CoglBool dither_enabled;
+ CoglBool depth_writing_enabled;
+ CoglColorMask color_mask;
+ CoglStereoMode stereo_mode;
+
+ /* We journal the textured rectangles we want to submit to OpenGL so
+ * we have an oppertunity to batch them together into less draw
+ * calls. */
+ CoglJournal *journal;
+
+ /* The scene of a given framebuffer may depend on images in other
+ * framebuffers... */
+ GList *deps;
+
+ /* As part of an optimization for reading-back single pixels from a
+ * framebuffer in some simple cases where the geometry is still
+ * available in the journal we need to track the bounds of the last
+ * region cleared, its color and we need to track when something
+ * does in fact draw to that region so it is no longer clear.
+ */
+ float clear_color_red;
+ float clear_color_green;
+ float clear_color_blue;
+ float clear_color_alpha;
+ int clear_clip_x0;
+ int clear_clip_y0;
+ int clear_clip_x1;
+ int clear_clip_y1;
+ CoglBool clear_clip_dirty;
+
+ /* Whether something has been drawn to the buffer since the last
+ * swap buffers or swap region. */
+ CoglBool mid_scene;
+
+ /* driver specific */
+ CoglBool dirty_bitmasks;
+ CoglFramebufferBits bits;
+
+ int samples_per_pixel;
+};
+
+typedef enum {
+ COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL = 1L<<0,
+ COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH = 1L<<1,
+ COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL = 1L<<2
+} CoglOffscreenAllocateFlags;
+
+typedef struct _CoglGLFramebuffer
+{
+ GLuint fbo_handle;
+ GList *renderbuffers;
+ int samples_per_pixel;
+} CoglGLFramebuffer;
+
+struct _CoglOffscreen
+{
+ CoglFramebuffer _parent;
+
+ CoglGLFramebuffer gl_framebuffer;
+
+ CoglTexture *texture;
+ int texture_level;
+
+ CoglTexture *depth_texture;
+
+ CoglOffscreenAllocateFlags allocation_flags;
+
+ /* FIXME: _cogl_offscreen_new_with_texture_full should be made to use
+ * fb->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 <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <cogl/cogl-pipeline.h>
+#include <cogl/cogl-indices.h>
+#include <cogl/cogl-bitmap.h>
+#ifdef COGL_ENABLE_EXPERIMENTAL_API
+#include <cogl/cogl-quaternion.h>
+#include <cogl/cogl-euler.h>
+#include <cogl/cogl-texture.h>
+#endif
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <emphasis>distance</emphasis> to the near clipping
+ * plane (will be <emphasis>negative</emphasis> if the plane is
+ * behind the viewer)
+ * @far: The <emphasis>distance</emphasis> to the far clipping
+ * plane (will be <emphasis>negative</emphasis> 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.
+ *
+ * <note>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.</note>
+ *
+ * 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().
+ *
+ * <note>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.</note>
+ * <note>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.
+ * </note>
+ *
+ * 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.
+ *
+ * <note>Calling this function implicitely allocates the framebuffer.</note>
+ * <note>The texture returned stays valid as long as the framebuffer stays
+ * valid.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * Stability: unstable
+ * Since: 1.10
+ * Deprecated: 1.16: Use #CoglPrimitive<!-- -->s 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 #CoglAttribute<!-- -->s 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 #CoglAttribute<!-- -->s 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 #CoglPrimitive<!-- -->s 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 #CoglAttribute<!-- -->s 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.
+ *
+ * <note>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()</note>
+ *
+ * Stability: unstable
+ * Since: 1.10
+ * Deprecated: 1.16: Use #CoglPrimitive<!-- -->s 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 #CoglAttribute<!-- -->s 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * Stability: unstable
+ * Since: 1.10
+ * Deprecated: 1.16: Use #CoglPrimitive<!-- -->s 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * Stability: unstable
+ * Since: 1.10
+ * Deprecated: 1.16: Use #CoglPrimitive<!-- -->s 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).
+ *
+ * <note>The position is the position before the rectangle has been
+ * transformed by the model-view matrix and the projection
+ * matrix.</note>
+ *
+ * <note>If you want to describe a rectangle with a texture mapped on
+ * it then you can use
+ * cogl_framebuffer_draw_textured_rectangle().</note>
+ *
+ * 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).
+ *
+ * <note>The position is the position before the rectangle has been
+ * transformed by the model-view matrix and the projection
+ * matrix.</note>
+ *
+ * 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.
+ *
+ * <note>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 </note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>The position is the position before the rectangle has been
+ * transformed by the model-view matrix and the projection
+ * matrix.</note>
+ *
+ * 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.
+ *
+ * <note>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).</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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...
+ *
+ * <note>The position is the position before the rectangle has been
+ * transformed by the model-view matrix and the projection
+ * matrix.</note>
+ *
+ * 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.
+ *
+ * <note>The position is the position before the rectangle has been
+ * transformed by the model-view matrix and the projection
+ * matrix.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <tomeu.vizoso@collabora.com>
+ * Robert Bragg <robert@linux.intel.com>
+ *
+ */
+
+#ifndef __COGL_GLES2_CONTEXT_PRIVATE_H
+#define __COGL_GLES2_CONTEXT_PRIVATE_H
+
+#include <glib.h>
+
+#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 <tomeu.vizoso@collabora.com>
+ * Robert Bragg <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <stdint.h>
+
+#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 <tomeu.vizoso@collabora.com>
+ * Robert Bragg <robert@linux.intel.com>
+ *
+ */
+
+#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 <cogl/cogl-defines.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-texture.h>
+#include <cogl/cogl-texture-2d.h>
+
+/* 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 <cogl/cogl-gles2-types.h>
+#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 <cogl/gl-prototypes/cogl-gles2-functions.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>You should not try to directly link to and use the symbols
+ * provided by any system OpenGLES 2.0 driver.</note>
+ *
+ * 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.
+ *
+ * <note>This interface is only intended for sharing textures to read
+ * from. The behaviour is undefined if the texture is modified using
+ * the Cogl api.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>This interface is only intended for sharing textures to read
+ * from. The behaviour is undefined if the texture is modified by a
+ * GLES2 context.</note>
+ *
+ * <note>This function will only return %TRUE for low-level
+ * #CoglTexture<!-- -->s such as #CoglTexture2D or #CoglTexture3D but
+ * not for high level meta textures such as
+ * #CoglTexture2DSliced</note>
+ *
+ * <note>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.</note>
+ *
+ * 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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_GSOURCE_H__
+#define __COGL_GSOURCE_H__
+
+#include <glib.h>
+#include <cogl/cogl-context.h>
+
+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.
+ *
+ * <note>This api is actually just a thin convenience wrapper around
+ * cogl_glib_renderer_source_new()</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#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 <string.h>
+
+#include <glib.h>
+
+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 <gmodule.h>
+#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 <GL/glx.h>
+#include <cogl/cogl-types.h>
+
+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 <string.h>
+#include <errno.h>
+
+#include <test-fixtures/test-unit.h>
+
+#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<git hash>" 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 <glib.h>
+#include <glib-object.h>
+
+#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 <gobject/gvaluecollector.h>
+
+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 <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_INDEX_BUFFER_H__
+#define __COGL_INDEX_BUFFER_H__
+
+#include <cogl/cogl-context.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#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 <stdarg.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-index-buffer.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <string.h>
+#include <gmodule.h>
+#include <math.h>
+
+/* 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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_KMS_DISPLAY_H__
+#define __COGL_KMS_DISPLAY_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-display.h>
+
+#include <xf86drmMode.h>
+
+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.
+ *
+ * <note>The @display must have been either explicitly setup via
+ * cogl_display_setup() or implicitily setup by having created a
+ * context using the @display</note>
+ *
+ * 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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_KMS_RENDERER_H__
+#define __COGL_KMS_RENDERER_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-renderer.h>
+
+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 <stdlib.h>
+#include <string.h>
+
+#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 <stddef.h>
+
+/**
+ * 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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_MACROS_H__
+#define __COGL_MACROS_H__
+
+#include <cogl/cogl-version.h>
+
+/* 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 <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-memory-stack-private.h"
+#include "cogl-magazine-private.h"
+#include <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_MATRIX_PRIVATE_H
+#define __COGL_MATRIX_PRIVATE_H
+
+#include <glib.h>
+
+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 <hp@pobox.com> for litl
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <hp@pobox.com> for litl
+ * Robert Bragg <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#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 <hp@pobox.com> for litl
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef _COGL_MATRIX_STACK_H_
+#define _COGL_MATRIX_STACK_H_
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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:
+ * <itemizedlist>
+ * <listitem>Faster equality comparisons of transformations</listitem>
+ * <listitem>Efficient comparisons of the differences between arbitrary
+ * transformations</listitem>
+ * <listitem>Avoid redundant arithmetic related to common transforms
+ * </listitem>
+ * <listitem>Can be more space efficient (not always though)</listitem>
+ * </itemizedlist>
+ *
+ * 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.
+ *
+ * <note>#CoglMatrixEntry<!-- -->s are reference counted using
+ * cogl_matrix_entry_ref() and cogl_matrix_entry_unref() not with
+ * cogl_object_ref() and cogl_object_unref().</note>
+ */
+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.
+ *
+ * <note>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.</note>
+ */
+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 <emphasis>distance</emphasis> to the near clipping
+ * plane (will be <emphasis>negative</emphasis> if the plane is
+ * behind the viewer)
+ * @far: The <emphasis>distance</emphasis> to the far clipping
+ * plane (will be <emphasis>negative</emphasis> 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.
+ *
+ * <note>The transform represented by a #CoglMatrixEntry is
+ * immutable.</note>
+ *
+ * <note>#CoglMatrixEntry<!-- -->s 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().</note>
+ *
+ * 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.
+ *
+ * <note>@matrix will be left untouched if a direct pointer is
+ * returned.</note>
+ *
+ * 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.
+ *
+ * <note>@matrix will be left untouched if a direct pointer is
+ * returned.</note>
+ *
+ * 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.
+ *
+ * <note>In many cases it is unnecessary to use this api and instead
+ * direct pointer comparisons of entries are good enough and much
+ * cheaper too.</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+/*
+ * 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 <cogl-util.h>
+#include <cogl-debug.h>
+#include <cogl-quaternion.h>
+#include <cogl-quaternion-private.h>
+#include <cogl-matrix.h>
+#include <cogl-matrix-private.h>
+#include <cogl-quaternion-private.h>
+
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+
+#include <cogl-gtype-private.h>
+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.
+ *
+ * <note>It's assumed that @result != @b. @product == @a is allowed.</note>
+ *
+ * <note>KW: 4*16 = 64 multiplications</note>
+ */
+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<<x)
+#define ONE(x) (1<<(x+16))
+
+#define MASK_NO_TRX (ZERO(12) | ZERO(13) | ZERO(14))
+#define MASK_NO_2D_SCALE ( ONE(0) | ONE(5))
+
+#define MASK_IDENTITY ( ONE(0) | ZERO(4) | ZERO(8) | ZERO(12) |\
+ ZERO(1) | ONE(5) | ZERO(9) | ZERO(13) |\
+ ZERO(2) | ZERO(6) | ONE(10) | ZERO(14) |\
+ ZERO(3) | ZERO(7) | ZERO(11) | ONE(15) )
+
+#define MASK_2D_NO_ROT ( ZERO(4) | ZERO(8) | \
+ ZERO(1) | ZERO(9) | \
+ ZERO(2) | ZERO(6) | ONE(10) | ZERO(14) |\
+ ZERO(3) | ZERO(7) | ZERO(11) | ONE(15) )
+
+#define MASK_2D ( ZERO(8) | \
+ ZERO(9) | \
+ ZERO(2) | ZERO(6) | ONE(10) | ZERO(14) |\
+ ZERO(3) | ZERO(7) | ZERO(11) | ONE(15) )
+
+
+#define MASK_3D_NO_ROT ( ZERO(4) | ZERO(8) | \
+ ZERO(1) | ZERO(9) | \
+ ZERO(2) | ZERO(6) | \
+ ZERO(3) | ZERO(7) | ZERO(11) | ONE(15) )
+
+#define MASK_3D ( \
+ \
+ \
+ ZERO(3) | ZERO(7) | ZERO(11) | ONE(15) )
+
+
+#define MASK_PERSPECTIVE ( ZERO(4) | ZERO(12) |\
+ ZERO(1) | ZERO(13) |\
+ ZERO(2) | ZERO(6) | \
+ ZERO(3) | ZERO(7) | ZERO(15) )
+
+#define SQ(x) ((x)*(x))
+
+/*
+ * Determine type and flags from scratch.
+ *
+ * This is expensive enough to only want to do it once.
+ */
+static void
+analyse_from_scratch (CoglMatrix *matrix)
+{
+ const float *m = (float *)matrix;
+ unsigned int mask = 0;
+ unsigned int i;
+
+ for (i = 0 ; i < 16 ; i++)
+ {
+ if (m[i] == 0.0) mask |= (1<<i);
+ }
+
+ if (m[0] == 1.0f) mask |= (1<<16);
+ if (m[5] == 1.0f) mask |= (1<<21);
+ if (m[10] == 1.0f) mask |= (1<<26);
+ if (m[15] == 1.0f) mask |= (1<<31);
+
+ matrix->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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_MATRIX_H
+#define __COGL_MATRIX_H
+
+#include <cogl/cogl-defines.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#endif /* COGL_HAS_GTYPE_SUPPORT */
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-macros.h>
+
+#ifdef COGL_ENABLE_EXPERIMENTAL_API
+#include <cogl/cogl-quaternion.h>
+#endif
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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
+ *
+ * <note>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().</note>
+ */
+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.
+ *
+ * <note>It is possible to multiply the @a matrix in-place, so
+ * @result can be equal to @a but can't be equal to @b.</note>
+ */
+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.
+ *
+ * <note>You should never look directly along the world-up
+ * vector.</note>
+ *
+ * <note>It is assumed you are using a typical projection matrix where
+ * your origin maps to the center of your viewport.</note>
+ *
+ * <note>Almost always when you use this function it should be the first
+ * transform applied to a new modelview transform</note>
+ *
+ * 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
+ *
+ * <note>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.</note>
+ */
+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 <emphasis>distance</emphasis> to the near clipping
+ * plane (will be <emphasis>negative</emphasis> if the plane is
+ * behind the viewer)
+ * @far: The <emphasis>distance</emphasis> to the far clipping
+ * plane (will be <emphasis>negative</emphasis> 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 <emphasis>distance</emphasis> to the near clipping
+ * plane (will be <emphasis>negative</emphasis> if the plane is
+ * behind the viewer)
+ * @far: The <emphasis>distance</emphasis> to the far clipping
+ * plane (will be <emphasis>negative</emphasis> 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <glib.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-memory-stack-private.h"
+#include "cogl-list.h"
+
+#include <stdint.h>
+
+#include <glib.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_META_TEXTURE_H__
+#define __COGL_META_TEXTURE_H__
+
+#include <cogl/cogl-pipeline-layer-state.h>
+
+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 #CoglAtlasTexture<!-- -->s, #CoglSubTexture<!-- -->s,
+ * #CoglTexturePixmapX11 textures and #CoglTexture2DSliced textures
+ * consistently.
+ *
+ * A #CoglMetaTexture is a texture that might internally be
+ * represented by one or more low-level #CoglTexture<!-- -->s
+ * 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 #CoglAtlasTexture<!-- -->s or #CoglSubTexture<!-- -->s 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().
+ *
+ * <note>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.</note>
+ */
+
+#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 #CoglTexture<!-- -->s 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_OBJECT_PRIVATE_H
+#define __COGL_OBJECT_PRIVATE_H
+
+#include <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+
+#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 <cogl/cogl-types.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_OFFSCREEN_H__
+#define __COGL_OFFSCREEN_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-texture.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>This api only works with low-level #CoglTexture types such as
+ * #CoglTexture2D, #CoglTexture3D and #CoglTextureRectangle, and not
+ * with meta-texture types such as #CoglTexture2DSliced.</note>
+ *
+ * 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.
+ *
+ * <note>This only works with low-level #CoglTexture types such as
+ * #CoglTexture2D, #CoglTexture3D and #CoglTextureRectangle, and not
+ * with meta-texture types such as #CoglTexture2DSliced.</note>
+ *
+ * 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 <glib.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <stdlib.h>
+
+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 <robert@linux.intel.com>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_ONSCREEN_TEMPLATE_H__
+#define __COGL_ONSCREEN_TEMPLATE_H__
+
+#include <cogl/cogl-swap-chain.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_ONSCREEN_H
+#define __COGL_ONSCREEN_H
+
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-frame-info.h>
+#include <cogl/cogl-object.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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().
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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 <string.h>
+
+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 <otaylor@redhat.com>
+ */
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_OUTPUT_H
+#define __COGL_OUTPUT_H
+
+#include <cogl/cogl-types.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <cogl/cogl-pango.h> is unsupported; please #include <cogl-pango/cogl-pango.h>"
+#else
+#warning "#include <cogl/cogl-pango.h> is deprecated; please #include <cogl-pango/cogl-pango.h>"
+#include <cogl-pango/cogl-pango.h>
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <test-fixtures/test-unit.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+
+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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-context-private.h"
+#include "cogl-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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+
+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<<COGL_PIPELINE_LAYER_STATE_UNIT_INDEX,
+ COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE =
+ 1L<<COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX,
+ COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA =
+ 1L<<COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX,
+ COGL_PIPELINE_LAYER_STATE_SAMPLER =
+ 1L<<COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX,
+
+ COGL_PIPELINE_LAYER_STATE_COMBINE =
+ 1L<<COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX,
+ COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT =
+ 1L<<COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX,
+ COGL_PIPELINE_LAYER_STATE_USER_MATRIX =
+ 1L<<COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX,
+
+ COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS =
+ 1L<<COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX,
+
+ COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS =
+ 1L<<COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX,
+ COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS =
+ 1L<<COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX,
+
+ /* COGL_PIPELINE_LAYER_STATE_TEXTURE_INTERN = 1L<<8, */
+
+} CoglPipelineLayerState;
+
+/*
+ * Various special masks that tag state-groups in different ways...
+ */
+
+#define COGL_PIPELINE_LAYER_STATE_ALL \
+ ((1L<<COGL_PIPELINE_LAYER_STATE_COUNT) - 1)
+
+#define COGL_PIPELINE_LAYER_STATE_ALL_SPARSE \
+ COGL_PIPELINE_LAYER_STATE_ALL
+
+#define COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE \
+ (COGL_PIPELINE_LAYER_STATE_COMBINE | \
+ COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT | \
+ COGL_PIPELINE_LAYER_STATE_USER_MATRIX | \
+ COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS | \
+ COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS | \
+ COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS)
+
+#define COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY \
+ (COGL_PIPELINE_LAYER_STATE_COMBINE | \
+ COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS | \
+ COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS)
+
+#define COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN \
+ COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS
+
+typedef enum
+{
+ /* These are the same values as GL */
+ COGL_PIPELINE_COMBINE_FUNC_ADD = 0x0104,
+ COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED = 0x8574,
+ COGL_PIPELINE_COMBINE_FUNC_SUBTRACT = 0x84E7,
+ COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE = 0x8575,
+ COGL_PIPELINE_COMBINE_FUNC_REPLACE = 0x1E01,
+ COGL_PIPELINE_COMBINE_FUNC_MODULATE = 0x2100,
+ COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB = 0x86AE,
+ COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA = 0x86AF
+} CoglPipelineCombineFunc;
+
+typedef enum
+{
+ /* Note that these numbers are deliberately not the same as the GL
+ numbers so that we can reserve all numbers > 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_PIPELINE_LAYER_STATE_H__
+#define __COGL_PIPELINE_LAYER_STATE_H__
+
+#include <cogl/cogl-pipeline.h>
+#include <cogl/cogl-color.h>
+#include <cogl/cogl-matrix.h>
+#include <cogl/cogl-texture.h>
+
+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.
+ *
+ * <note>In the future, we may define other types of pipeline layers, such
+ * as purely GLSL based layers.</note>
+ *
+ * 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 <link linkend="cogl-Blend-Strings">Cogl blend string</link>
+ * 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
+ * <link linkend="cogl-Blend-Strings">here</link> for an overview of what blend
+ * strings are and there syntax.
+ *
+ * These are all the functions available for texture combining:
+ * <itemizedlist>
+ * <listitem>REPLACE(arg0) = arg0</listitem>
+ * <listitem>MODULATE(arg0, arg1) = arg0 x arg1</listitem>
+ * <listitem>ADD(arg0, arg1) = arg0 + arg1</listitem>
+ * <listitem>ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5</listitem>
+ * <listitem>INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2)</listitem>
+ * <listitem>SUBTRACT(arg0, arg1) = arg0 - arg1</listitem>
+ * <listitem>
+ * <programlisting>
+ * 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))
+ * </programlisting>
+ * </listitem>
+ * <listitem>
+ * <programlisting>
+ * 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))
+ * </programlisting>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Refer to the
+ * <link linkend="cogl-Blend-String-syntax">color-source syntax</link> for
+ * describing the arguments. The valid source names for texture combining
+ * are:
+ * <variablelist>
+ * <varlistentry>
+ * <term>TEXTURE</term>
+ * <listitem>Use the color from the current texture layer</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>TEXTURE_0, TEXTURE_1, etc</term>
+ * <listitem>Use the color from the specified texture layer</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>CONSTANT</term>
+ * <listitem>Use the color from the constant given with
+ * cogl_pipeline_set_layer_combine_constant()</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>PRIMARY</term>
+ * <listitem>Use the color of the pipeline as set with
+ * cogl_pipeline_set_color()</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>PREVIOUS</term>
+ * <listitem>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()</listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * <refsect2 id="cogl-Layer-Combine-Examples">
+ * <title>Layer Combine Examples</title>
+ * <para>This is effectively what the default blending is:</para>
+ * <informalexample><programlisting>
+ * RGBA = MODULATE (PREVIOUS, TEXTURE)
+ * </programlisting></informalexample>
+ * <para>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().</para>
+ * <informalexample><programlisting>
+ * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A])
+ * </programlisting></informalexample>
+ * </refsect2>
+ *
+ * <note>You can't give a multiplication factor for arguments as you can
+ * with blending.</note>
+ *
+ * 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%.
+ *
+ * <note>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.</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+
+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<<i);
+
+ if (state & found)
+ authorities[i] = authority;
+ else if (state > 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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+
+#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<<COGL_PIPELINE_STATE_COLOR_INDEX,
+ COGL_PIPELINE_STATE_BLEND_ENABLE =
+ 1L<<COGL_PIPELINE_STATE_BLEND_ENABLE_INDEX,
+ COGL_PIPELINE_STATE_LAYERS =
+ 1L<<COGL_PIPELINE_STATE_LAYERS_INDEX,
+
+ COGL_PIPELINE_STATE_LIGHTING =
+ 1L<<COGL_PIPELINE_STATE_LIGHTING_INDEX,
+ COGL_PIPELINE_STATE_ALPHA_FUNC =
+ 1L<<COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX,
+ COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE =
+ 1L<<COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX,
+ COGL_PIPELINE_STATE_BLEND =
+ 1L<<COGL_PIPELINE_STATE_BLEND_INDEX,
+ COGL_PIPELINE_STATE_USER_SHADER =
+ 1L<<COGL_PIPELINE_STATE_USER_SHADER_INDEX,
+ COGL_PIPELINE_STATE_DEPTH =
+ 1L<<COGL_PIPELINE_STATE_DEPTH_INDEX,
+ COGL_PIPELINE_STATE_FOG =
+ 1L<<COGL_PIPELINE_STATE_FOG_INDEX,
+ COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE =
+ 1L<<COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX,
+ COGL_PIPELINE_STATE_POINT_SIZE =
+ 1L<<COGL_PIPELINE_STATE_POINT_SIZE_INDEX,
+ COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE =
+ 1L<<COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX,
+ COGL_PIPELINE_STATE_LOGIC_OPS =
+ 1L<<COGL_PIPELINE_STATE_LOGIC_OPS_INDEX,
+ COGL_PIPELINE_STATE_CULL_FACE =
+ 1L<<COGL_PIPELINE_STATE_CULL_FACE_INDEX,
+ COGL_PIPELINE_STATE_UNIFORMS =
+ 1L<<COGL_PIPELINE_STATE_UNIFORMS_INDEX,
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS =
+ 1L<<COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX,
+ COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS =
+ 1L<<COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX,
+
+ COGL_PIPELINE_STATE_REAL_BLEND_ENABLE =
+ 1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
+
+} CoglPipelineState;
+
+/*
+ * Various special masks that tag state-groups in different ways...
+ */
+
+#define COGL_PIPELINE_STATE_ALL \
+ ((1L<<COGL_PIPELINE_STATE_COUNT) - 1)
+
+#define COGL_PIPELINE_STATE_ALL_SPARSE \
+ (COGL_PIPELINE_STATE_ALL \
+ & ~COGL_PIPELINE_STATE_REAL_BLEND_ENABLE)
+
+#define COGL_PIPELINE_STATE_AFFECTS_BLENDING \
+ (COGL_PIPELINE_STATE_COLOR | \
+ COGL_PIPELINE_STATE_BLEND_ENABLE | \
+ COGL_PIPELINE_STATE_LAYERS | \
+ COGL_PIPELINE_STATE_LIGHTING | \
+ COGL_PIPELINE_STATE_BLEND | \
+ COGL_PIPELINE_STATE_USER_SHADER | \
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
+ COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
+
+#define COGL_PIPELINE_STATE_NEEDS_BIG_STATE \
+ (COGL_PIPELINE_STATE_LIGHTING | \
+ COGL_PIPELINE_STATE_ALPHA_FUNC | \
+ COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE | \
+ COGL_PIPELINE_STATE_BLEND | \
+ COGL_PIPELINE_STATE_USER_SHADER | \
+ COGL_PIPELINE_STATE_DEPTH | \
+ COGL_PIPELINE_STATE_FOG | \
+ COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE | \
+ COGL_PIPELINE_STATE_POINT_SIZE | \
+ COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE | \
+ COGL_PIPELINE_STATE_LOGIC_OPS | \
+ COGL_PIPELINE_STATE_CULL_FACE | \
+ COGL_PIPELINE_STATE_UNIFORMS | \
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
+ COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
+
+#define COGL_PIPELINE_STATE_MULTI_PROPERTY \
+ (COGL_PIPELINE_STATE_LAYERS | \
+ COGL_PIPELINE_STATE_LIGHTING | \
+ COGL_PIPELINE_STATE_BLEND | \
+ COGL_PIPELINE_STATE_DEPTH | \
+ COGL_PIPELINE_STATE_FOG | \
+ COGL_PIPELINE_STATE_LOGIC_OPS | \
+ COGL_PIPELINE_STATE_CULL_FACE | \
+ COGL_PIPELINE_STATE_UNIFORMS | \
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
+ COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
+
+typedef enum
+{
+ COGL_PIPELINE_LIGHTING_STATE_PROPERTY_AMBIENT = 1,
+ COGL_PIPELINE_LIGHTING_STATE_PROPERTY_DIFFUSE,
+ COGL_PIPELINE_LIGHTING_STATE_PROPERTY_SPECULAR,
+ COGL_PIPELINE_LIGHTING_STATE_PROPERTY_EMISSION,
+ COGL_PIPELINE_LIGHTING_STATE_PROPERTY_SHININESS
+} CoglPipelineLightingStateProperty;
+
+typedef struct
+{
+ /* Standard OpenGL lighting model attributes */
+ float ambient[4];
+ float diffuse[4];
+ float specular[4];
+ float emission[4];
+ float shininess;
+} CoglPipelineLightingState;
+
+typedef struct
+{
+ /* Determines what fragments are discarded based on their alpha */
+ CoglPipelineAlphaFunc alpha_func;
+ float alpha_func_reference;
+} CoglPipelineAlphaFuncState;
+
+typedef enum _CoglPipelineBlendEnable
+{
+ /* XXX: we want to detect users mistakenly using TRUE or FALSE
+ * so start the enum at 2. */
+ COGL_PIPELINE_BLEND_ENABLE_ENABLED = 2,
+ COGL_PIPELINE_BLEND_ENABLE_DISABLED,
+ COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC
+} CoglPipelineBlendEnable;
+
+typedef struct
+{
+ /* Determines how this pipeline is blended with other primitives */
+#if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL)
+ GLenum blend_equation_rgb;
+ GLenum blend_equation_alpha;
+ GLint blend_src_factor_alpha;
+ GLint blend_dst_factor_alpha;
+ CoglColor blend_constant;
+#endif
+ GLint blend_src_factor_rgb;
+ GLint blend_dst_factor_rgb;
+} CoglPipelineBlendState;
+
+typedef struct
+{
+ CoglBool enabled;
+ CoglColor color;
+ CoglFogMode mode;
+ float density;
+ float z_near;
+ float z_far;
+} CoglPipelineFogState;
+
+typedef struct
+{
+ CoglColorMask color_mask;
+} CoglPipelineLogicOpsState;
+
+typedef struct
+{
+ CoglPipelineCullFaceMode mode;
+ CoglWinding front_winding;
+} CoglPipelineCullFaceState;
+
+typedef struct
+{
+ CoglBitmask override_mask;
+
+ /* This is an array of values. Only the uniforms that have a bit set
+ in override_mask have a corresponding value here. The uniform's
+ location is implicit from the order in this array */
+ CoglBoxedValue *override_values;
+
+ /* Uniforms that have been modified since this pipeline was last
+ flushed */
+ CoglBitmask changed_mask;
+} CoglPipelineUniformsState;
+
+typedef struct
+{
+ CoglPipelineLightingState lighting_state;
+ CoglPipelineAlphaFuncState alpha_state;
+ CoglPipelineBlendState blend_state;
+ CoglHandle user_program;
+ CoglDepthState depth_state;
+ CoglPipelineFogState fog_state;
+ float point_size;
+ unsigned int non_zero_point_size : 1;
+ unsigned int per_vertex_point_size : 1;
+ CoglPipelineLogicOpsState logic_ops_state;
+ CoglPipelineCullFaceState cull_face_state;
+ CoglPipelineUniformsState uniforms_state;
+ CoglPipelineSnippetList vertex_snippets;
+ CoglPipelineSnippetList fragment_snippets;
+} CoglPipelineBigState;
+
+typedef struct
+{
+ CoglPipeline *owner;
+ CoglPipelineLayer *layer;
+} CoglPipelineLayerCacheEntry;
+
+typedef struct _CoglPipelineHashState
+{
+ unsigned long layer_differences;
+ CoglPipelineEvalFlags flags;
+ unsigned int hash;
+} CoglPipelineHashState;
+
+/*
+ * CoglPipelineDestroyCallback
+ * @pipeline: The #CoglPipeline that has been destroyed
+ * @user_data: The private data associated with the callback
+ *
+ * Notifies when a weak pipeline has been destroyed because one
+ * of its ancestors has been freed or modified.
+ */
+typedef void (*CoglPipelineDestroyCallback)(CoglPipeline *pipeline,
+ void *user_data);
+
+struct _CoglPipeline
+{
+ /* XXX: Please think twice about adding members that *have* be
+ * initialized during a cogl_pipeline_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*
+ * pipelines 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;
+
+ /* 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 #CoglPipeline<!-- -->s 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;
+ *
+ * /&nbsp;* Start validating source... *&nbsp;/
+ *
+ * /&nbsp;* If you find you need to change something... *&nbsp;/
+ * if (cache->validated_source == source)
+ * cache->validated_source =
+ * cogl_pipeline_weak_copy (source,
+ * invalidate_cache_cb,
+ * cache);
+ *
+ * /&nbsp;* Modify cache->validated_source *&nbsp;/
+ * }
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#ifndef __COGL_PIPELINE_SNIPPET_PRIVATE_H
+#define __COGL_PIPELINE_SNIPPET_PRIVATE_H
+
+#include <glib.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <test-fixtures/test-unit.h>
+
+#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 (&current_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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_PIPELINE_STATE_H__
+#define __COGL_PIPELINE_STATE_H__
+
+#include <cogl/cogl-pipeline.h>
+#include <cogl/cogl-color.h>
+#include <cogl/cogl-depth-state.h>
+
+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 <link linkend="cogl-Blend-Strings">Cogl blend string</link>
+ * 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 <link linkend="cogl-Blend-Strings">here</link>
+ * 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:
+ *
+ * |[
+ * &lt;channel-mask&gt;=ADD(SRC_COLOR*(&lt;factor&gt;), DST_COLOR*(&lt;factor&gt;))
+ * ]|
+ *
+ * This is the list of source-names usable as blend factors:
+ * <itemizedlist>
+ * <listitem><para>SRC_COLOR: The color of the in comming fragment</para></listitem>
+ * <listitem><para>DST_COLOR: The color of the framebuffer</para></listitem>
+ * <listitem><para>CONSTANT: The constant set via cogl_pipeline_set_blend_constant()</para></listitem>
+ * </itemizedlist>
+ *
+ * The source names can be used according to the
+ * <link linkend="cogl-Blend-String-syntax">color-source and factor syntax</link>,
+ * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would
+ * "(CONSTANT[RGB])"
+ *
+ * These can also be used as factors:
+ * <itemizedlist>
+ * <listitem>0: (0, 0, 0, 0)</listitem>
+ * <listitem>1: (1, 1, 1, 1)</listitem>
+ * <listitem>SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A])</listitem>
+ * </itemizedlist>
+ *
+ * <note>Remember; all color components are normalized to the range [0, 1]
+ * before computing the result of blending.</note>
+ *
+ * <example id="cogl-Blend-Strings-blend-unpremul">
+ * <title>Blend Strings/1</title>
+ * <para>Blend a non-premultiplied source over a destination with
+ * premultiplied alpha:</para>
+ * <programlisting>
+ * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))"
+ * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))"
+ * </programlisting>
+ * </example>
+ *
+ * <example id="cogl-Blend-Strings-blend-premul">
+ * <title>Blend Strings/2</title>
+ * <para>Blend a premultiplied source over a destination with
+ * premultiplied alpha</para>
+ * <programlisting>
+ * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))"
+ * </programlisting>
+ * </example>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+#include <glib/gprintf.h>
+#include <string.h>
+
+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<<state->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<<i); i++)
+ ;
+
+ _cogl_pipeline_prune_to_n_layers (pipeline, i);
+ }
+
+ if (options->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<<i);
+
+ if (state & found)
+ authorities[i] = authority;
+ else if (state > 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<<i);
+
+ /* XXX: we are hashing the un-mixed hash values of all the
+ * individual state groups; we should provide a means to test
+ * the quality of the final hash values we are getting with this
+ * approach... */
+ if (differences & current_state)
+ {
+ CoglPipelineLayer *authority = authorities[i];
+ layer_state_hash_functions[i] (authority, authorities, state);
+ }
+
+ if (current_state > 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<<i);
+
+ /* XXX: we are hashing the un-mixed hash values of all the
+ * individual state groups; we should provide a means to test
+ * the quality of the final hash values we are getting with this
+ * approach... */
+ if (differences & current_state)
+ {
+ CoglPipeline *authority = authorities[i];
+ state_hash_functions[i] (authority, &state);
+ final_hash = _cogl_util_one_at_a_time_hash (final_hash, &state.hash,
+ sizeof (state.hash));
+ }
+
+ if (current_state > 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 <cogl/cogl.h> 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 <cogl/cogl-types.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-snippet.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_PIXEL_BUFFER_PRIVATE_H__
+#define __COGL_PIXEL_BUFFER_PRIVATE_H__
+
+#include "cogl-object-private.h"
+#include "cogl-buffer-private.h"
+
+#include <glib.h>
+
+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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+/* 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 <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-types.h>
+#include <cogl/cogl-context.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <glib.h>
+
+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 <glib.h>
+
+/* 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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_POLL_H__
+#define __COGL_POLL_H__
+
+#include <cogl/cogl-defines.h>
+#include <cogl/cogl-context.h>
+
+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
+ * #CoglPollFDEvent<!-- -->s that should cause the application to wake
+ * up. After the application is woken up from idle it should pass back
+ * an array of #CoglPollFD<!-- -->s 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 #CoglPollFD<!-- -->s
+ * @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.
+ *
+ * <note>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.</note>
+ *
+ * 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 CoglPollFD<!-- -->s 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 #CoglPollFD<!-- -->s 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.
+ *
+ * <note>If your application didn't originally create a #CoglRenderer
+ * manually then you can easily get a #CoglRenderer pointer by calling
+ * cogl_get_renderer().</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <cogl/cogl.h> 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.
+ *
+ * <note>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.</note>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <stdarg.h>
+#include <string.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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 <cogl/cogl-types.h> /* for CoglVerticesMode */
+#include <cogl/cogl-attribute.h>
+#include <cogl/cogl-framebuffer.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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 #CoglAttribute<!-- -->s 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 <structfield>n_vertices</structfield> property as if
+ * cogl_primitive_set_n_vertices() were called. This property defines
+ * the number of vertices to read when drawing.
+
+ * <note>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)</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <structfield>n_vertices</structfield> 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.
+ *
+ * <note>The #CoglPrimitive <structfield>first_vertex</structfield> property
+ * also affects drawing with indices by defining the first entry of the
+ * indices to start drawing from.</note>
+ *
+ * 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 <glib.h>
+
+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 <string.h>
+#include <math.h>
+
+#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 <glib.h>
+
+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 #float<!-- -->s 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 #float<!-- -->s 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 <cogl/cogl-pipeline.h>
+
+#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 <stdlib.h>
+
+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 <uprof.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_QUATERNION_PRIVATE_H__
+#define __COGL_QUATERNION_PRIVATE_H__
+
+#include <glib.h>
+
+/* 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 <robert@linux.intel.com>
+ *
+ * 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 <cogl-util.h>
+#include <cogl-quaternion.h>
+#include <cogl-quaternion-private.h>
+#include <cogl-matrix.h>
+#include <cogl-vector.h>
+#include <cogl-euler.h>
+#include "cogl-gtype-private.h"
+
+#include <string.h>
+#include <math.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_QUATERNION_H__
+#define __COGL_QUATERNION_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-vector.h>
+
+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
+ * <ulink url="http://en.wikipedia.org/wiki/Gimbal_lock">"Gimbal lock"</ulink>
+ * where two of the axis of rotation may become aligned and you loose a
+ * degree of freedom.
+ * .
+ */
+#include <cogl/cogl-vector.h>
+#include <cogl/cogl-euler.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <literal>[w (x, y, z)]</literal>.
+ *
+ * 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:
+ *
+ * <itemizedlist>
+ * <listitem>
+ * <ulink url="http://mathworld.wolfram.com/Quaternion.html"/>
+ * </listitem>
+ * <listitem>
+ * <ulink url="http://www.gamedev.net/reference/articles/article1095.asp"/>
+ * </listitem>
+ * <listitem>
+ * <ulink url="http://www.cprogramming.com/tutorial/3d/quaternions.html"/>
+ * </listitem>
+ * <listitem>
+ * <ulink url="http://www.isner.com/tutorials/quatSpells/quaternion_spells_12.htm"/>
+ * </listitem>
+ * <listitem>
+ * 3D Maths Primer for Graphics and Game Development ISBN-10: 1556229119
+ * </listitem>
+ * <listitem>
+ * <ulink url="http://www.cs.caltech.edu/courses/cs171/quatut.pdf"/>
+ * </listitem>
+ * <listitem>
+ * <ulink url="http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56"/>
+ * </listitem>
+ * </itemizedlist>
+ *
+ */
+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.
+ *
+ * <note>It is possible to multiply the @a quaternion in-place, so
+ * @result can be equal to @a but can't be equal to @b.</note>
+ *
+ * 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:
+ * <itemizedlist>
+ * <listitem>
+ * commutative: No
+ * </listitem>
+ * <listitem>
+ * constant velocity: Yes
+ * </listitem>
+ * <listitem>
+ * torque minimal (travels along the surface of the 4-sphere): Yes
+ * </listitem>
+ * <listitem>
+ * more expensive than cogl_quaternion_nlerp()
+ * </listitem>
+ * </itemizedlist>
+ */
+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:
+ * <itemizedlist>
+ * <listitem>
+ * commutative: Yes
+ * </listitem>
+ * <listitem>
+ * constant velocity: No
+ * </listitem>
+ * <listitem>
+ * torque minimal (travels along the surface of the 4-sphere): Yes
+ * </listitem>
+ * <listitem>
+ * faster than cogl_quaternion_slerp()
+ * </listitem>
+ * </itemizedlist>
+ */
+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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#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 <cairo.h>
+
+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 <glib.h>
+#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 <gmodule.h>
+
+#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 <X11/Xlib.h>
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_RENDERER_H__
+#define __COGL_RENDERER_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-onscreen-template.h>
+#include <cogl/cogl-error.h>
+#include <cogl/cogl-output.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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().
+ *
+ * <note>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.</note>
+ */
+
+
+/**
+ * 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().
+ *
+ * <note>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.</note>
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifndef __COGL_SNIPPET_PRIVATE_H
+#define __COGL_SNIPPET_PRIVATE_H
+
+#include <glib.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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
+ *
+ * #CoglSnippet<!-- -->s 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):
+ *
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>declarations</glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>pre</glossterm>
+ * <glossdef><para>
+ * The code in this string will be inserted before the hook point.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>post</glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>replace</glossterm>
+ * <glossdef><para>
+ * If present the code in this string will replace the generated code
+ * for the hook point.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ *
+ * 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:
+ *
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_modelview_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * The current modelview matrix. This is equivalent to
+ * #gl_ModelViewMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_projection_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * The current projection matrix. This is equivalent to
+ * #gl_ProjectionMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_modelview_projection_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_texture_matrix</emphasis>[]</glossterm>
+ * <glossdef><para>
+ * An array of matrices for transforming the texture
+ * coordinates. This is equivalent to #gl_TextureMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ *
+ * In a vertex shader, the following are also available:
+ *
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_position_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The incoming vertex position. This is equivalent to #gl_Vertex.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_color_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The incoming vertex color. This is equivalent to #gl_Color.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_tex_coord_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The texture coordinate for layer 0. This is an alternative name
+ * for #cogl_tex_coord0_in.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_tex_coord0_in</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec3
+ * <emphasis>cogl_normal_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The normal of the vertex. This is equivalent to #gl_Normal.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>vec4
+ * <emphasis>cogl_position_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated position of the vertex. This must be written to
+ * in all vertex shaders. This is equivalent to #gl_Position.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>float
+ * <emphasis>cogl_point_size_in</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>float
+ * <emphasis>cogl_point_size_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated size of a point. This is equivalent to #gl_PointSize.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_color_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated color of a vertex. This is equivalent to #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_tex_coord0_out</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ *
+ * In a fragment shader, the following are also available:
+ *
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>varying vec4 <emphasis>cogl_color_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated color of a vertex. This is equivalent to #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_tex_coord0_in</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>vec4 <emphasis>cogl_color_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The final calculated color of the fragment. All fragment shaders
+ * must write to this variable. This is equivalent to
+ * #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>float <emphasis>cogl_depth_out</emphasis></glossterm>
+ * <glossdef><para>
+ * An optional output variable specifying the depth value to use
+ * for this fragment. This is equivalent to #gl_FragDepth.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>bool <emphasis>cogl_front_facing</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>vec2 <emphasis>cogl_point_coord</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ *
+ * Here is an example of using a snippet to add a desaturate effect to the
+ * generated color on a pipeline.
+ *
+ * <programlisting>
+ * 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 ();
+ * </programlisting>
+ */
+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.
+ *
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_VERTEX_GLOBALS</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_VERTEX</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * The ‘pre’ string in @snippet will be inserted at the top of the
+ * main() function before any vertex processing is done.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_VERTEX_TRANSFORM</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * The ‘pre’ string in @snippet will be inserted at the top of the
+ * main() function before the vertex transform is done.
+ * </para>
+ * <para>
+ * The ‘replace’ string in @snippet will be used instead of the
+ * generated vertex transform if it is present.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_POINT_SIZE</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * The ‘pre’ string in @snippet will be inserted just before
+ * calculating the point size.
+ * </para>
+ * <para>
+ * The ‘replace’ string in @snippet will be used instead of the
+ * generated point size calculation if it is present.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_FRAGMENT</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * The ‘pre’ string in @snippet will be inserted at the top of the
+ * main() function before any fragment processing is done.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_LAYER_FRAGMENT</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * The ‘pre’ string in @snippet will be inserted just before the
+ * fragment processing for this layer.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>%COGL_SNIPPET_HOOK_TEXTURE_LOOKUP</glossterm>
+ * <glossdef>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * </glossdef>
+ * </glossentry>
+ * </glosslist>
+ *
+ * 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 <glib.h>
+
+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 <neil@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+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 <neil@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_SWAP_CHAIN_H__
+#define __COGL_SWAP_CHAIN_H__
+
+#include <cogl/cogl-types.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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().
+ *
+ * <note>The results are undefined for passing an invalid @gl_handle
+ * or if @width or @height don't have the correct texture
+ * geometry.</note>
+ *
+ * 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 <glib.h>
+
+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 <mallum@openedhand.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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().
+ *
+ * <note>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().</note>
+ *
+ * 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().
+ *
+ * <note>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().</note>
+ *
+ * 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.
+ *
+ * <note>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()</note>
+ *
+ * <note>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().</note>
+ *
+ * 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().
+ *
+ * <note>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().</note>
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+/* 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 <neil@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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().
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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().</note>
+ *
+ * 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().
+ *
+ * <note>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.</note>
+ *
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <math.h>
+
+/* 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 <robert@linux.intel.com>
+ */
+
+#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.
+ *
+ * <note>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.</note>
+ *
+ * <note>If you want to sample from a rectangle texture from GLSL you
+ * should use the sampler2DRect sampler type.</note>
+ *
+ * <note>Applications wanting to use #CoglTextureRectangle should
+ * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature
+ * using cogl_has_feature().</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>If you want to sample from a rectangle texture from GLSL you
+ * should use the sampler2DRect sampler type.</note>
+ *
+ * <note>Applications wanting to use #CoglTextureRectangle should
+ * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature
+ * using cogl_has_feature().</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>The results are undefined for passing an invalid @gl_handle
+ * or if @width or @height don't have the correct texture
+ * geometry.</note>
+ *
+ * <note>If you want to sample from a rectangle texture from GLSL you
+ * should use the sampler2DRect sampler type.</note>
+ *
+ * <note>Applications wanting to use #CoglTextureRectangle should
+ * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature
+ * using cogl_has_feature().</note>
+ *
+ * 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 <mallum@openedhand.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* 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; y<target_height; ++y)
+ {
+ for (x=0; x<target_width; ++x)
+ {
+ srcpixel = srcdata + x*bpp;
+ dstpixel = dstdata + x*bpp;
+ dstpixel[3] = srcpixel[0];
+ }
+ srcdata += alpha_rowstride;
+ dstdata += target_rowstride;
+ }
+
+ _cogl_bitmap_unmap (alpha_bmp);
+
+ _cogl_bitmap_unmap (target_bmp);
+
+ cogl_object_unref (alpha_bmp);
+ }
+
+ status = TRUE;
+
+EXIT:
+ /* Restore old state */
+ cogl_framebuffer_pop_matrix (framebuffer);
+ _cogl_framebuffer_pop_projection (framebuffer);
+ cogl_framebuffer_set_viewport (framebuffer,
+ save_viewport[0],
+ save_viewport[1],
+ save_viewport[2],
+ save_viewport[3]);
+
+ return status;
+}
+
+static CoglBool
+get_texture_bits_via_offscreen (CoglTexture *meta_texture,
+ CoglTexture *sub_texture,
+ int x,
+ int y,
+ int width,
+ int height,
+ uint8_t *dst_bits,
+ unsigned int dst_rowstride,
+ CoglPixelFormat closest_format)
+{
+ CoglContext *ctx = sub_texture->context;
+ 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 <cogl/cogl.h> 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 <cogl/cogl-types.h>
+#include <cogl/cogl-macros.h>
+#include <cogl/cogl-defines.h>
+#if defined (COGL_ENABLE_EXPERIMENTAL_API)
+#include <cogl/cogl-pixel-buffer.h>
+#endif
+#include <cogl/cogl-bitmap.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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.
+ *
+ * <note>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</note>
+ *
+ * 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.
+ *
+ * <note>The region set can't be larger than the source @data</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>The region updated can't be larger than the source
+ * bitmap</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_TYPES_H__
+#define __COGL_TYPES_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <cogl/cogl-defines.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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:
+ *
+ * <itemizedlist>
+ * <listitem><para>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.</para></listitem>
+ * <listitem><para>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</para></listitem>
+ * <listitem><para>The driver does not support some
+ * configuration.</para></listiem>
+ * </itemizedlist>
+ *
+ * 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
+ * <constant>GL_POINTS</constant>
+ * @COGL_VERTICES_MODE_LINES: FIXME, equivalent to <constant>GL_LINES</constant>
+ * @COGL_VERTICES_MODE_LINE_LOOP: FIXME, equivalent to
+ * <constant>GL_LINE_LOOP</constant>
+ * @COGL_VERTICES_MODE_LINE_STRIP: FIXME, equivalent to
+ * <constant>GL_LINE_STRIP</constant>
+ * @COGL_VERTICES_MODE_TRIANGLES: FIXME, equivalent to
+ * <constant>GL_TRIANGLES</constant>
+ * @COGL_VERTICES_MODE_TRIANGLE_STRIP: FIXME, equivalent to
+ * <constant>GL_TRIANGLE_STRIP</constant>
+ * @COGL_VERTICES_MODE_TRIANGLE_FAN: FIXME, equivalent to <constant>GL_TRIANGLE_FAN</constant>
+ *
+ * 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 <string.h>
+
+#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 <glib.h>
+#include <math.h>
+
+#include <cogl/cogl-defines.h>
+#include "cogl-types.h"
+
+#ifndef COGL_HAS_GLIB_SUPPORT
+#include <stdio.h>
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl-util.h>
+#include <cogl-vector.h>
+
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> 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
+ * @vector<!-- -->s magnitude or length is equal to 1.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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</note>
+ *
+ * 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 <cogl/cogl-defines.h>
+
+/**
+ * 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 <wayland-server.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 <cogl/cogl-context.h>
+#include <cogl/cogl-texture-2d.h>
+
+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.
+ *
+ * <note>The results are undefined for passing an invalid @buffer
+ * pointer</note>
+ * <note>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)</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <X11/Xlib.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+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 <cogl/cogl-xlib.h> can be included directly."
+#endif
+
+#ifndef __COGL_XLIB_RENDERER_H__
+#define __COGL_XLIB_RENDERER_H__
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.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_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE__
+#endif
+
+#endif /* COGL_COMPILATION */
+
+#include <cogl/cogl-renderer.h>
+
+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 <robert@linux.intel.com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl-xlib.h>
+
+#include <cogl-object-private.h>
+#include <cogl-context-private.h>
+#include <cogl-framebuffer-private.h>
+#include <cogl-display-private.h>
+#include <cogl-renderer-private.h>
+#include <cogl-xlib-renderer-private.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+
+#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 <X11/Xlib.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_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 <cogl/cogl-types.h>
+#include <cogl/deprecated/cogl-clutter-xlib.h>
+#include <cogl/cogl-xlib-renderer.h>
+#include <cogl/cogl-macros.h>
+
+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 <config.h>
+
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#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 "<cogl/cogl.h> 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 <cogl/cogl-defines.h>
+#include <cogl/cogl-macros.h>
+
+#include <cogl/cogl-error.h>
+
+#include <cogl/cogl-object.h>
+#include <cogl/cogl1-context.h>
+#include <cogl/cogl-bitmap.h>
+#include <cogl/cogl-color.h>
+#include <cogl/cogl-matrix.h>
+#include <cogl/cogl-matrix-stack.h>
+#include <cogl/cogl-offscreen.h>
+#include <cogl/cogl-primitives.h>
+#include <cogl/cogl-texture.h>
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-version.h>
+
+/*
+ * 1.x only api...
+ */
+#ifndef COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <cogl/cogl-enum-types.h>
+#include <cogl/deprecated/cogl-clip-state.h>
+#include <cogl/deprecated/cogl-vertex-buffer.h>
+#include <cogl/deprecated/cogl-fixed.h>
+#include <cogl/deprecated/cogl-material-compat.h>
+#include <cogl/deprecated/cogl-shader.h>
+#include <cogl/deprecated/cogl-texture-deprecated.h>
+#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 <cogl/deprecated/cogl-type-casts.h>
+
+#include <cogl/deprecated/cogl-framebuffer-deprecated.h>
+#include <cogl/deprecated/cogl-auto-texture.h>
+
+/*
+ * 2.0 api that's compatible with the 1.x api...
+ */
+#if defined (COGL_ENABLE_EXPERIMENTAL_API)
+#include <cogl/cogl-swap-chain.h>
+#include <cogl/cogl-renderer.h>
+#include <cogl/cogl-output.h>
+#include <cogl/cogl-display.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-buffer.h>
+#include <cogl/cogl-pixel-buffer.h>
+#include <cogl/cogl-vector.h>
+#include <cogl/cogl-euler.h>
+#include <cogl/cogl-quaternion.h>
+#include <cogl/cogl-texture-2d.h>
+#include <cogl/cogl-texture-2d-gl.h>
+#include <cogl/cogl-texture-rectangle.h>
+#include <cogl/cogl-texture-3d.h>
+#include <cogl/cogl-texture-2d-sliced.h>
+#include <cogl/cogl-sub-texture.h>
+#include <cogl/cogl-atlas-texture.h>
+#include <cogl/cogl-meta-texture.h>
+#include <cogl/cogl-primitive-texture.h>
+#include <cogl/cogl-index-buffer.h>
+#include <cogl/cogl-attribute-buffer.h>
+#include <cogl/cogl-indices.h>
+#include <cogl/cogl-attribute.h>
+#include <cogl/cogl-primitive.h>
+#include <cogl/cogl-depth-state.h>
+#include <cogl/cogl-pipeline.h>
+#include <cogl/cogl-pipeline-state.h>
+#include <cogl/cogl-pipeline-layer-state.h>
+#include <cogl/cogl-snippet.h>
+#include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-onscreen.h>
+#include <cogl/cogl-frame-info.h>
+#include <cogl/cogl-poll.h>
+#include <cogl/cogl-fence.h>
+#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
+#include <cogl/cogl-kms-renderer.h>
+#include <cogl/cogl-kms-display.h>
+#endif
+#ifdef COGL_HAS_GLIB_SUPPORT
+#include <cogl/cogl-glib-source.h>
+#endif
+/* XXX: This will definitly go away once all the Clutter winsys
+ * code has been migrated down into Cogl! */
+#include <cogl/deprecated/cogl-clutter.h>
+#endif
+
+/*
+ * API deprecations
+ */
+#include <cogl/cogl-deprecated.h>
+
+/*
+ * 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 <cogl-path/cogl-path.h>
+#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 <robert@linux.intel.com>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_1_CONTEXT_H__
+#define __COGL_1_CONTEXT_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-texture.h>
+#include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-macros.h>
+
+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.
+ *
+ * <note>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.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <emphasis>distance</emphasis> to the near clipping
+ * plane (negative if the plane is behind the viewer)
+ * @far: The <emphasis>distance</emphasis> for the far clipping
+ * plane (negative if the plane is behind the viewer)
+ *
+ * Replaces the current projection matrix with an orthographic projection
+ * matrix. See <xref linkend="cogl-ortho-matrix"/> to see how the matrix is
+ * calculated.
+ *
+ * <figure id="cogl-ortho-matrix">
+ * <title></title>
+ * <graphic fileref="cogl_ortho.png" format="PNG"/>
+ * </figure>
+ *
+ * <note>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.</note>
+ *
+ * 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 #float<!-- -->s 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().
+ *
+ * <note>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.</note>
+ *
+ * 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
+ * #CoglMaterial<!-- -->s 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().
+ *
+ * <note>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.</note>
+ *
+ * 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
+ * #CoglMaterial<!-- -->s 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
+ * #CoglMaterial<!-- -->s 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
+ * #CoglMaterial<!-- -->s 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
+ * #CoglMaterial<!-- -->s 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
+ * #CoglMaterial<!-- -->s 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
+ * #CoglMaterial<!-- -->s 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:
+ * <programlisting>
+ * 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);
+ * </programlisting>
+ *
+ * Since: 1.0
+ * Deprecated: 1.16: Latest drawing apis all take an explicit
+ * #CoglPipeline argument so this stack of
+ * #CoglMaterial<!-- -->s 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.
+ *
+ * <note>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().</note>
+ *
+ * <note>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.</note>
+ *
+ * <note>You can not nest begin/end blocks.</note>
+ *
+ * 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 <cogl/cogl.h>
+
+#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 <mallum@openedhand.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#include <config.h>
+
+#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 <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_CLIP_STATE_H__
+#define __COGL_CLIP_STATE_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-macros.h>
+#include <cogl/cogl-primitive.h>
+
+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 <cogl/cogl-xlib.h> can be included directly."
+#endif
+
+#ifndef __COGL_CLUTTER_XLIB_H__
+#define __COGL_CLUTTER_XLIB_H__
+
+#include <X11/Xutil.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+
+#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 <cogl/cogl.h> 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 <glib-object.h>
+#include <gobject/gvaluecollector.h>
+
+#ifdef HAVE_FLOAT_WORD_ORDER
+#include <endian.h>
+#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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_FIXED_H__
+#define __COGL_FIXED_H__
+
+#include <cogl/cogl-types.h>
+
+/**
+ * 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:
+ * <itemizedlist>
+ * <listitem>
+ * <para>Two fixed point numbers can be directly added, subtracted and
+ * have their modulus taken.</para>
+ * </listitem>
+ * <listitem>
+ * <para>To add other numerical type to a fixed point number it has to
+ * be first converted to fixed point.</para>
+ * </listitem>
+ * <listitem>
+ * <para>A fixed point number can be directly multiplied or divided by
+ * an integer.</para>
+ * </listitem>
+ * <listitem>
+ * <para>Two fixed point numbers can only be multiplied and divided by
+ * the provided %COGL_FIXED_MUL and %COGL_FIXED_DIV macros.</para>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * 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.
+ *
+ * <note>This macro might lose precision. If the precision of the result
+ * is important use %COGL_FIXED_MUL instead.</note>
+ *
+ * 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.
+ *
+ * <note>This macro might lose precision. If the precision of the result
+ * is important use %COGL_FIXED_DIV instead.</note>
+ *
+ * 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 <config.h>
+
+#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/cogl-macros.h>
+
+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:
+ * <itemizedlist>
+ * <listitem><simpara>The projection matrix</simpara></listitem>
+ * <listitem><simpara>The modelview matrix stack</simpara></listitem>
+ * <listitem><simpara>The viewport</simpara></listitem>
+ * <listitem><simpara>The clip stack</simpara></listitem>
+ * </itemizedlist>
+ * 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 (&amp;projection_matrix);
+ * z_camera = 0.5 * projection_matrix.xx;
+ *
+ * cogl_matrix_init_identity (&amp;mv_matrix);
+ * cogl_matrix_translate (&amp;mv_matrix, -0.5f, -0.5f, -z_camera);
+ * cogl_matrix_scale (&amp;mv_matrix, 1.0f / width, -1.0f / height, 1.0f / width);
+ * cogl_matrix_translate (&amp;mv_matrix, 0.0f, -1.0 * height, 0.0f);
+ * cogl_set_modelview_matrix (&amp;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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl-material-compat.h>
+#include <cogl-pipeline.h>
+#include <cogl-pipeline-private.h>
+#include <cogl-types.h>
+#include <cogl-matrix.h>
+#include <cogl-context-private.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_MATERIAL_H__
+#define __COGL_MATERIAL_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-matrix.h>
+#include <cogl/cogl-depth-state.h>
+#include <cogl/cogl-error.h>
+#include <cogl/cogl-macros.h>
+
+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 <link linkend="cogl-Blend-Strings">Cogl blend string</link>
+ * 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 <link linkend="cogl-Blend-Strings">here</link>
+ * 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:
+ *
+ * |[
+ * &lt;channel-mask&gt;=ADD(SRC_COLOR*(&lt;factor&gt;), DST_COLOR*(&lt;factor&gt;))
+ * ]|
+ *
+ * <warning>The brackets around blend factors are currently not
+ * optional!</warning>
+ *
+ * This is the list of source-names usable as blend factors:
+ * <itemizedlist>
+ * <listitem><para>SRC_COLOR: The color of the in comming fragment</para></listitem>
+ * <listitem><para>DST_COLOR: The color of the framebuffer</para></listitem>
+ * <listitem><para>CONSTANT: The constant set via cogl_material_set_blend_constant()</para></listitem>
+ * </itemizedlist>
+ *
+ * The source names can be used according to the
+ * <link linkend="cogl-Blend-String-syntax">color-source and factor syntax</link>,
+ * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would
+ * "(CONSTANT[RGB])"
+ *
+ * These can also be used as factors:
+ * <itemizedlist>
+ * <listitem>0: (0, 0, 0, 0)</listitem>
+ * <listitem>1: (1, 1, 1, 1)</listitem>
+ * <listitem>SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A])</listitem>
+ * </itemizedlist>
+ *
+ * <note>Remember; all color components are normalized to the range [0, 1]
+ * before computing the result of blending.</note>
+ *
+ * <example id="cogl-Blend-Strings-blend-unpremul">
+ * <title>Blend Strings/1</title>
+ * <para>Blend a non-premultiplied source over a destination with
+ * premultiplied alpha:</para>
+ * <programlisting>
+ * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))"
+ * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))"
+ * </programlisting>
+ * </example>
+ *
+ * <example id="cogl-Blend-Strings-blend-premul">
+ * <title>Blend Strings/2</title>
+ * <para>Blend a premultiplied source over a destination with
+ * premultiplied alpha</para>
+ * <programlisting>
+ * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))"
+ * </programlisting>
+ * </example>
+ *
+ * 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.
+ *
+ * <note>In the future, we may define other types of material layers, such
+ * as purely GLSL based layers.</note>
+ *
+ * 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 <link linkend="cogl-Blend-Strings">Cogl blend string</link>
+ * 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
+ * <link linkend="cogl-Blend-Strings">here</link> for an overview of what blend
+ * strings are and there syntax.
+ *
+ * These are all the functions available for texture combining:
+ * <itemizedlist>
+ * <listitem>REPLACE(arg0) = arg0</listitem>
+ * <listitem>MODULATE(arg0, arg1) = arg0 x arg1</listitem>
+ * <listitem>ADD(arg0, arg1) = arg0 + arg1</listitem>
+ * <listitem>ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5</listitem>
+ * <listitem>INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2)</listitem>
+ * <listitem>SUBTRACT(arg0, arg1) = arg0 - arg1</listitem>
+ * <listitem>
+ * <programlisting>
+ * 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))
+ * </programlisting>
+ * </listitem>
+ * <listitem>
+ * <programlisting>
+ * 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))
+ * </programlisting>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Refer to the
+ * <link linkend="cogl-Blend-String-syntax">color-source syntax</link> for
+ * describing the arguments. The valid source names for texture combining
+ * are:
+ * <variablelist>
+ * <varlistentry>
+ * <term>TEXTURE</term>
+ * <listitem>Use the color from the current texture layer</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>TEXTURE_0, TEXTURE_1, etc</term>
+ * <listitem>Use the color from the specified texture layer</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>CONSTANT</term>
+ * <listitem>Use the color from the constant given with
+ * cogl_material_set_layer_constant()</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>PRIMARY</term>
+ * <listitem>Use the color of the material as set with
+ * cogl_material_set_color()</listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>PREVIOUS</term>
+ * <listitem>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()</listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * <refsect2 id="cogl-Layer-Combine-Examples">
+ * <title>Layer Combine Examples</title>
+ * <para>This is effectively what the default blending is:</para>
+ * <informalexample><programlisting>
+ * RGBA = MODULATE (PREVIOUS, TEXTURE)
+ * </programlisting></informalexample>
+ * <para>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.</para>
+ * <informalexample><programlisting>
+ * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A])
+ * </programlisting></informalexample>
+ * </refsect2>
+ *
+ * <note>You can't give a multiplication factor for arguments as you can
+ * with blending.</note>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * <note>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.</note>
+ *
+ * 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
+ * <link linkend="cogl-Textures">texture</link>
+ *
+ * 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.
+ *
+ * <note>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.</note>
+ *
+ * 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 <string.h>
+
+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 <glib.h>
+
+#include <string.h>
+
+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 <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_SHADER_H__
+#define __COGL_SHADER_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-defines.h>
+#include <cogl/cogl-macros.h>
+
+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:
+ *
+ * <tip>
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_modelview_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * The current modelview matrix. This is equivalent to
+ * #gl_ModelViewMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_projection_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * The current projection matrix. This is equivalent to
+ * #gl_ProjectionMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_modelview_projection_matrix</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>uniform mat4
+ * <emphasis>cogl_texture_matrix</emphasis>[]</glossterm>
+ * <glossdef><para>
+ * An array of matrices for transforming the texture
+ * coordinates. This is equivalent to #gl_TextureMatrix.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ * </tip>
+ *
+ * In a vertex shader, the following are also available:
+ *
+ * <tip>
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_position_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The incoming vertex position. This is equivalent to #gl_Vertex.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_color_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The incoming vertex color. This is equivalent to #gl_Color.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_tex_coord_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The texture coordinate for the first texture unit. This is
+ * equivalent to #gl_MultiTexCoord0.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec4
+ * <emphasis>cogl_tex_coord0_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The texture coordinate for the first texture unit. This is
+ * equivalent to #gl_MultiTexCoord0. There is also
+ * #cogl_tex_coord1_in and so on.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>attribute vec3
+ * <emphasis>cogl_normal_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The normal of the vertex. This is equivalent to #gl_Normal.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>vec4
+ * <emphasis>cogl_position_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated position of the vertex. This must be written to
+ * in all vertex shaders. This is equivalent to #gl_Position.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>float
+ * <emphasis>cogl_point_size_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated size of a point. This is equivalent to #gl_PointSize.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_color_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated color of a vertex. This is equivalent to #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_tex_coord_out</emphasis>[]</glossterm>
+ * <glossdef><para>
+ * An array of calculated texture coordinates for a vertex. This is
+ * equivalent to #gl_TexCoord.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ * </tip>
+ *
+ * In a fragment shader, the following are also available:
+ *
+ * <tip>
+ * <glosslist>
+ * <glossentry>
+ * <glossterm>varying vec4 <emphasis>cogl_color_in</emphasis></glossterm>
+ * <glossdef><para>
+ * The calculated color of a vertex. This is equivalent to #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>varying vec4
+ * <emphasis>cogl_tex_coord_in</emphasis>[]</glossterm>
+ * <glossdef><para>
+ * An array of calculated texture coordinates for a vertex. This is
+ * equivalent to #gl_TexCoord.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>vec4 <emphasis>cogl_color_out</emphasis></glossterm>
+ * <glossdef><para>
+ * The final calculated color of the fragment. All fragment shaders
+ * must write to this variable. This is equivalent to
+ * #gl_FrontColor.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>float <emphasis>cogl_depth_out</emphasis></glossterm>
+ * <glossdef><para>
+ * An optional output variable specifying the depth value to use
+ * for this fragment. This is equivalent to #gl_FragDepth.
+ * </para></glossdef>
+ * </glossentry>
+ * <glossentry>
+ * <glossterm>bool <emphasis>cogl_front_facing</emphasis></glossterm>
+ * <glossdef><para>
+ * 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.
+ * </para></glossdef>
+ * </glossentry>
+ * </glosslist>
+ * </tip>
+ *
+ * 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 <link
+ * linkend="cogl-Shaders-and-Programmable-Pipeline.description">above</link>
+ * 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 <config.h>
+
+#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.
+ *
+ * <note>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</note>
+ *
+ * <note>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.</note>
+ *
+ * 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 <cogl/cogl.h> 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 <robert@linux.intel.com>
+ */
+
+#ifndef __COGL_VERTEX_BUFFER_H
+#define __COGL_VERTEX_BUFFER_H
+
+#include "cogl-object-private.h"
+
+#include "cogl-primitive.h"
+
+#include <glib.h>
+
+/* 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 <robert@linux.intel.com>
+ */
+
+/* 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 <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_VERTEX_BUFFER_H__
+#define __COGL_VERTEX_BUFFER_H__
+
+#include <glib.h>
+#include <cogl/cogl-defines.h>
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-macros.h>
+
+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:
+ * <orderedlist>
+ * <listitem>
+ * a "gl_Position" describing the (x,y,z) position for each vertex.
+ * </listitem>
+ * <listitem>
+ * a "gl_MultiTexCoord0" describing the (tx,ty) texture coordinates for each
+ * vertex.
+ * </listitem>
+ * </orderedlist>
+ *
+ * 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)
+ * <itemizedlist>
+ * <listitem>"gl_Color"</listitem>
+ * <listitem>"gl_Normal"</listitem>
+ * <listitem>"gl_MultiTexCoord0, gl_MultiTexCoord1, ..."</listitem>
+ * <listitem>"gl_Vertex"</listitem>
+ * </itemizedlist>
+ * 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.
+ *
+ * <note>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:
+ * <programlisting>
+ * |-0-0-0-0-0-0-0-0-0-0|
+ * </programlisting>
+ * This is not ok:
+ * <programlisting>
+ * |- - - - -0-0-0-0-0-0 0 0 0 0|
+ * </programlisting>
+ * (Though you can have multiple groups of interleved attributes)</note>
+ *
+ * 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 <xref linkend="quad-indices-order"/> then you can request 6
+ * indices to render two triangles like those shown in <xref
+ * linkend="quad-indices-triangles"/>.
+ *
+ * <figure id="quad-indices-order">
+ * <title>Example of vertices submitted to form a quad</title>
+ * <graphic fileref="quad-indices-order.png" format="PNG"/>
+ * </figure>
+ *
+ * <figure id="quad-indices-triangles">
+ * <title>Illustration of the triangle indices that will be generated</title>
+ * <graphic fileref="quad-indices-triangles.png" format="PNG"/>
+ * </figure>
+ *
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <damien.lespiau@intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifndef _COGL_CLIP_STACK_GL_PRIVATE_H_
+#define _COGL_CLIP_STACK_GL_PRIVATE_H_
+
+#include "cogl-types.h"
+#include "cogl-framebuffer.h"
+#include "cogl-clip-stack.h"
+
+void
+_cogl_clip_stack_gl_flush (CoglClipStack *stack,
+ CoglFramebuffer *framebuffer);
+
+#endif /* _COGL_CLIP_STACK_GL_PRIVATE_H_ */
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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-context-private.h"
+#include "cogl-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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+#include <glib/gprintf.h>
+#include <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <glib.h>
+
+/*
+ * 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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <test-fixtures/test-unit.h>
+
+#include <glib.h>
+#include <string.h>
+
+/*
+ * 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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <test-fixtures/test-unit.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#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 <strings.h>
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+#include <glib/gprintf.h>
+#include <string.h>
+
+/* 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 <neil@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <mallum@openedhand.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#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 <string.h>
+
+#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 <mallum@openedhand.com>
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <string.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <glib.h>
+#include <string.h>
+
+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 <robert@linux.intel.com>
+ */
+
+#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 <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xdamage.h>
+
+#include <sys/shm.h>
+
+#ifdef COGL_HAS_GLX_SUPPORT
+#include <GL/glx.h>
+#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 <neil@linux.intel.com>
+ * Johan Bilien <johan.bilien@nokia.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+
+#include <string.h>
+#include <math.h>
+
+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 <cogl/cogl-context.h>
+
+#ifdef COGL_HAS_GTYPE_SUPPORT
+#include <glib-object.h>
+#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 <neil@linux.intel.com>
+ */
+
+#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 <rob@linux.intel.com>
+ * Kristian Høgsberg (from eglkms.c)
+ * Benjamin Franzke (from eglkms.c)
+ * Robert Bragg <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <errno.h>
+#include <stddef.h>
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <gbm.h>
+#include <glib.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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 <robert@linux.intel.com>
+ * Neil Roberts <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xlib.h>
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#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 <robert@linux.intel.com>
+ */
+
+#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 <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#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 (&current_count);
+ glx_renderer->glXWaitVideoSync (2,
+ (current_count + 1) % 2,
+ &current_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 <X11/Xutil.h>
+#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 <robert@linux.intel.com>
+ *
+ */
+
+#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 <string.h>
+
+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 <gmodule.h>
+
+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 <GLES/gl.h>])
+
+ # 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 <GLES/egl.h>"],
+ [test "x$ac_cv_header_EGL_egl_h" = "xyes"],
+ [
+ COGL_EGL_INCLUDES="#include <EGL/egl.h>"
+ ],
+ [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 <EGL/eglext.h>"],
+ [],
+ [$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 <GLES2/gl2.h>])
+
+ 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 <EGL/egl.h>])
+
+ 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 <EGL/egl.h>
+#include <EGL/eglext.h>"
+ 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 <poll.h>],
+ AC_MSG_ERROR([Unable to get value of POLLIN]))
+ AC_COMPUTE_INT(COGL_SYSDEF_POLLPRI, POLLPRI, [#include <poll.h>],
+ AC_MSG_ERROR([Unable to get value of POLLPRI]))
+ AC_COMPUTE_INT(COGL_SYSDEF_POLLOUT, POLLOUT, [#include <poll.h>],
+ AC_MSG_ERROR([Unable to get value of POLLOUT]))
+ AC_COMPUTE_INT(COGL_SYSDEF_POLLERR, POLLERR, [#include <poll.h>],
+ AC_MSG_ERROR([Unable to get value of POLLERR]))
+ AC_COMPUTE_INT(COGL_SYSDEF_POLLHUP, POLLHUP, [#include <poll.h>],
+ AC_MSG_ERROR([Unable to get value of POLLHUP]))
+ AC_COMPUTE_INT(COGL_SYSDEF_POLLNVAL, POLLNVAL, [#include <poll.h>],
+ 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 <test-fixtures/test-utils.h>
+
+#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 <config.h>
+
+#include <stdlib.h>
+
+#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 <cogl/cogl.h> but since that will only provide
+ * opaque typedefs we need to include the specific internal headers
+ * for testing private apis...
+ */
+#ifdef COGL_COMPILATION
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-onscreen.h>
+#include <cogl/cogl-offscreen.h>
+#include <cogl/cogl-texture-2d.h>
+#include <cogl/cogl-primitive-texture.h>
+#include <cogl/cogl-texture-2d-sliced.h>
+#include <cogl/cogl-meta-texture.h>
+#include <cogl/cogl-atlas-texture.h>
+#else
+#include <cogl/cogl.h>
+#endif
+
+#include <glib.h>
+
+/* 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 <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <math.h>
+#include <string.h>
+
+#include <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#include <glib.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+#include <math.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+/* 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 <stdio.h>
+#include <clutter/clutter.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+#include <cogl/cogl-gles2.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+/* 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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+#include <cogl-path/cogl-path.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+#include <cogl-path/cogl-path.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl2-experimental.h>
+
+#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 <cogl/cogl2-experimental.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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 <cogl/cogl2-experimental.h>
+#include <stdarg.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl2-experimental.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl2-experimental.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#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 <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+#ifdef COGL_HAS_XLIB
+
+#include <clutter/x11/clutter-x11.h>
+#include <cogl/cogl-texture-pixmap-x11.h>
+
+#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 <clutter/clutter.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+/* 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
+ * <cogl/cogl.h> we define __COGL_H_INSIDE__ here to subvert those
+ * guards in this case... */
+#define __COGL_H_INSIDE__
+#include <cogl/cogl-util.h>
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#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 <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#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 <cogl/cogl.h>
+#include <string.h>
+
+#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 <cogl/cogl.h>
+
+#include <string.h>
+
+#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 <cogl/cogl2-experimental.h>
+#include <stdarg.h>
+
+#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 <glib.h>
+#include <cogl/cogl2-experimental.h>
+#include <math.h>
+
+#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 <config.h>
+
+#include <gmodule.h>
+
+#include <test-fixtures/test-unit.h>
+#include <stdlib.h>
+
+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;
+}