1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
package # not an official package
OpenGL::Modern::Helpers;
our $VERSION = '0.0401';
use strict;
use Exporter 'import';
use Carp qw(croak);
use Config;
use OpenGL::Modern qw(
GL_NO_ERROR
GL_INVALID_ENUM
GL_INVALID_VALUE
GL_INVALID_OPERATION
GL_STACK_OVERFLOW
GL_STACK_UNDERFLOW
GL_OUT_OF_MEMORY
GL_TABLE_TOO_LARGE
GL_VERSION
glGenTextures_p
glGenFramebuffers_p
glGenVertexArrays_p
glGenBuffers_p
glGetString
glGetError
glGetShaderInfoLog_c
glGetProgramInfoLog_c
glGetProgramiv_c
glGetShaderiv_c
glGetIntegerv_c
glShaderSource_c
glBufferData_c
glUniform2f
glUniform4f
);
=head1 NAME
OpenGL::Modern::Helpers - example usage of raw pointers from perl
=head1 WARNING
This API is an experiment and will change!
=head1 OpenGL::Modern API Implementation
This module exists to support the use of the OpenGL::Modern
package for OpenGL bindings by documenting details of the
implementation and giving example routines showing the
use from perl.
=head2 Implementation
OpenGL::Modern is an XS module providings bindings to the
C OpenGL library for graphics. As such, it needs to handle
conversion of input arguments from perl into the required
datatypes for the C OpenGL API, it then calls the OpenGL
routine, and then converts the return value (if any) from
the C API datatype into an appropriate Perl type.
=head3 Scalar Values
Routines that take scalar values and return scalar
values at the C level, are nicely mapped by the built in
typemap conversions. For example:
GLenum
glCheckNamedFramebufferStatus(GLuint framebuffer, GLenum target);
where the functions takes two values, one an integer and
one an enumeration which is basically an integer value
as well. The return value is another enumeration/integer
value. Since perl scalars can hold integers, the default
XS implementation from perl would be prototyped in perl
as
$status = glCheckNamedFramebufferStatus($framebuffer, $target);
or, taking advantage of the binding of all the OpenGL
enumerations to perl constant functions we could write
$status = glCheckNamedFramebufferStatus($framebuffer, GL_DRAW_FRAMEBUFFER);
The key here is explicit scalar values and types which makes
the XS perl implementation essentially the same at the C one
just with perl scalars in place of C typed values.
Of the 2743 OpenGL API routines, 1092 have scalar input
and return values and can be considered implemented as
is.
=head3 Pointer Values
The remaining OpenGL routines all have one (or more)
pointer argument or return value which are not so
simply mapped into perl because the use of pointers
from C does not fully determine the use of those
values:
=over 4
=item *
Pointers can be used to return values from routines
=item *
Pointers can be used to pass single input values
=item *
Pointers can be used to pass multiple input values
=item *
Pointers can be used to return multiple input values
=back
The current XS implementation now represents non-char
type pointers as the typemap T_PTR and the string and
character pointers are T_PV. The routines will be
renamed with an added _c so as to indicate that the
mapping is the direct C one.
These _c routines closely match the OpenGL C API but
it requires that the perl user hand manage the allocation,
initialization, packing and unpacking, etc for each
function call.
Please see this source file for the implementations of
glGetShaderInfoLog_p
glGetProgramInfoLog_p
glGetVersion_p
croak_on_gl_error
showing the use of some utility routines to interface
to the OpenGL API routines. OpenGL::Modern::Helpers
will be kept up to date with each release to document
the API implementations and usage as the bindings
evolve and improve. Once standardized and stable,
a final version of Helpers.pm will be released.
=cut
our @EXPORT_OK = qw(
pack_GLuint
pack_GLfloat
pack_GLdouble
pack_GLint
pack_GLstrings
pack_ptr
iv_ptr
xs_buffer
glGetShaderInfoLog_p
glGetProgramInfoLog_p
croak_on_gl_error
glGetVersion_p
glGenTextures_p
glGetProgramiv_p
glGetShaderiv_p
glShaderSource_p
glGenFramebuffers_p
glGenVertexArrays_p
glGenBuffers_p
glGetIntegerv_p
glBufferData_p
glUniform2f_p
glUniform4f_p
);
our %glErrorStrings = (
GL_NO_ERROR() => 'No error has been recorded.',
GL_INVALID_ENUM() => 'An unacceptable value is specified for an enumerated argument.',
GL_INVALID_VALUE() => 'A numeric argument is out of range.',
GL_INVALID_OPERATION() => 'The specified operation is not allowed in the current state.',
GL_STACK_OVERFLOW() => 'This command would cause a stack overflow.',
GL_STACK_UNDERFLOW() => 'This command would cause a stack underflow.',
GL_OUT_OF_MEMORY() => 'There is not enough memory left to execute the command.',
GL_TABLE_TOO_LARGE() => 'The specified table exceeds the implementation\'s maximum supported table size.',
);
our $PACK_TYPE = $Config{ptrsize} == 4 ? 'L' : 'Q';
sub pack_GLuint {
my @gluints = @_;
pack 'I*', @gluints;
}
sub pack_GLint {
my @gluints = @_;
pack 'I*', @gluints;
}
sub pack_GLfloat {
my @glfloats = @_;
pack 'f*', @glfloats;
}
sub pack_GLdouble {
my @gldoubles = @_;
pack 'd*', @gldoubles;
}
# No parameter declaration because we don't want copies
sub pack_GLstrings {
pack 'P*', @_;
}
# No parameter declaration because we don't want copies
# This returns a packed string representation of the
# pointer to the perl string data. Not useful as is
# because the scope of the inputs is not maintained so
# the PV data may disappear before the pointer is actually
# accessed by OpenGL routines.
#
sub pack_ptr {
$_[0] = "\0" x $_[1];
return pack 'P', $_[0];
}
sub iv_ptr {
$_[0] = "\0" x $_[1] if $_[1];
return unpack( $PACK_TYPE, pack( 'P', $_[0] ) );
}
# No parameter declaration because we don't want copies
# This makes a packed string buffer of desired length.
# As above, be careful of the variable scopes.
#
sub xs_buffer {
$_[0] = "\0" x $_[1];
$_[0];
}
sub get_info_log_p {
my ( $call, $id ) = @_;
my $bufsize = 1024 * 64;
my $buffer = "\0" x $bufsize;
my $len = "\0" x 4;
# void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
# void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
$call->( $id, $bufsize, unpack( $PACK_TYPE, pack( 'p', $len ) ), $buffer );
$len = unpack 'I', $len;
return substr $buffer, 0, $len;
}
sub glGetShaderInfoLog_p { get_info_log_p \&glGetShaderInfoLog_c, @_ }
sub glGetProgramInfoLog_p { get_info_log_p \&glGetProgramInfoLog_c, @_ }
# This should probably be named glpGetVersion since there is actually
# no glGetVersion() in the OpenGL API.
#
sub glGetVersion_p {
# const GLubyte * GLAPIENTRY glGetString (GLenum name);
my $glVersion = glGetString( GL_VERSION );
( $glVersion ) = ( $glVersion =~ m!^(\d+\.\d+)!g );
$glVersion;
}
sub croak_on_gl_error {
# GLenum glGetError (void);
my $error = glGetError();
if ( $error != GL_NO_ERROR ) {
croak $glErrorStrings{$error} || "Unknown OpenGL error: $error";
}
}
sub gen_thing_p {
my ( $call, $n ) = @_;
xs_buffer my $new_ids, 4 * $n;
$call->( $n, unpack( $PACK_TYPE, pack( 'p', $new_ids ) ) );
my @ids = unpack 'I*', $new_ids;
return wantarray ? @ids : $ids[0];
}
sub get_iv_p {
my ( $call, $id, $pname, $count ) = @_;
$count ||= 1;
xs_buffer my $params, 4 * $count;
$call->( $id, $pname, unpack( "$PACK_TYPE*", pack( 'p*', $params ) ) );
my @params = unpack 'I*', $params;
return wantarray ? @params : $params[0];
}
sub glGetProgramiv_p { get_iv_p \&glGetProgramiv_c, @_ } # TODO: get rid of $count
sub glGetShaderiv_p { get_iv_p \&glGetShaderiv_c, @_ } # TODO: get rid of $count
sub glShaderSource_p {
my ( $shader, @sources ) = @_;
my $count = @sources;
my @lengths = map length, @sources;
glShaderSource_c( $shader, $count, pack( 'P*', @sources ), pack( 'I*', @lengths ) );
return;
}
sub glGetIntegerv_p {
my ( $pname, $count ) = @_; # TODO: get rid of $count
$count ||= 1;
xs_buffer my $data, 4 * $count;
glGetIntegerv_c $pname, unpack( $PACK_TYPE, pack( 'p', $data ) );
my @data = unpack 'I*', $data;
return wantarray ? @data : $data[0];
}
sub glBufferData_p { # NOTE: this might be better named glpBufferDataf_p
my $usage = pop;
my ( $target, $size, @data ) = @_;
my $pdata = pack "f*", @data;
glBufferData_c $target, $size, unpack( $PACK_TYPE, pack( 'p', $pdata ) ), $usage;
}
sub glBufferData_o { # NOTE: this was glBufferData_p in OpenGL
my ( $target, $oga, $usage ) = @_;
glBufferData_c $target, $oga->length, $oga->ptr, $usage;
}
sub glUniform2fv_p { # NOTE: this name is more consistent with OpenGL API
my ( $uniform, $v0, $v1 ) = @_;
glUniform2f $uniform, $v0, $v1;
}
sub glUniform4fv_p { # NOTE: this name is more consistent with OpenGL API
my ( $uniform, $v0, $v1, $v2, $v3 ) = @_;
glUniform4f $uniform, $v0, $v1, $v2, $v3;
}
1;
|