E
Eric Peden
First post. Hi.
I'm writing a ray-tracer in Ruby (I know, I know...) Perhaps some other
time I'll wax ecstatic over the advantages Ruby brings to such a
project, but at the moment I have less pleasant issues I'm hoping to
get some help with.
A pure-Ruby ray tracer is just a tad slow. Clearly, some core
functionality needs to be implemented in C for speed. The system is
broken into classes similar to the following:
Camera
Engine
Vector
Point
Ray
Shape
Sphere
Polygon
Of those classes, only a handful are performance critical, and of each
of those, only one or two methods are true bottlenecks. I'm trying to
keep as much of the code as possible in Ruby, while breaking out only
the real work horses into C. Vector, for example, is almost pure C,
whereas only the intersection-testing method in the Shape subclasses is
in C. Unfortunately, I keep running into dependency problems when
trying to build the C functions. My C code is organized like this:
ext/
extconf.rb
ext.c
vector.c
sphere.c
When built, this generates a single library that includes the code for
all of the optimized classes. 'ext.c' has an Init_ function that calls
initialization routines in the other files. Here's an example from
sphere.c:
Init_Sphere() {
VALUE cSphere = rb_const_get(rb_cObject, rb_intern("Sphere"));
}
sphere.rb would look something like this:
require 'vector'
require 'ext/optimized'
class Sphere < Shape
...
end
When I run this, I get a "undefined constant" error from the C code,
which makes sense: when the C library is included, Sphere hasn't been
defined yet. So I just move the "require 'ext/optimized'" to the bottom
of sphere.rb, right? Sadly, no. There's only one library entry point,
so I have to initialize all of my optimized classes at the same time.
'vector.rb' also requires 'ext/optimized', so Init_Sphere() would still
get called before Sphere is actually defined, and I'll still get my
error. Furthermore, since Vector gets used in several other files, I
have to worry about the order in which those files are parsed, as well.
I can see a few solutions: one is to break out every C file so that it
compiles into its own library. This would leave me with an ugly file
structure, since I'd need either a new extconf.rb for each C file, or a
sub-directory for each source file. I've tried doing something like
this:
VALUE cSphere = rb_eval_string("class Shape; end; class Sphere <
Shape; end");
but that feels extremely naughty, not to mention requires me to
duplicate the heritage of each subclass I want to provide C extensions
for. I suppose I could also create a "Optimizer" object in my C
library's Init() method, and attach methods to that class that would
call the class-specific Init_...() methods:
# a .rb file
require 'ext/optimized'
class Sphere < Shape
..
end
Optimizer.init_sphere
But that also seems rather distasteful.
I apologize for my wordiness... I'm trying to make my situation clear.
Am I making this more difficult than it really is? If the classes
involved where pure-C, or if there wasn't any subclassing, I'd be fine.
I just can't see how to manage the class dependencies while dealing
with a single library entry point, and so I've come to seek the
guidance of the wizards. Any help--or even architecture bashing, if
it's constructive--is appreciated.
I'm writing a ray-tracer in Ruby (I know, I know...) Perhaps some other
time I'll wax ecstatic over the advantages Ruby brings to such a
project, but at the moment I have less pleasant issues I'm hoping to
get some help with.
A pure-Ruby ray tracer is just a tad slow. Clearly, some core
functionality needs to be implemented in C for speed. The system is
broken into classes similar to the following:
Camera
Engine
Vector
Point
Ray
Shape
Sphere
Polygon
Of those classes, only a handful are performance critical, and of each
of those, only one or two methods are true bottlenecks. I'm trying to
keep as much of the code as possible in Ruby, while breaking out only
the real work horses into C. Vector, for example, is almost pure C,
whereas only the intersection-testing method in the Shape subclasses is
in C. Unfortunately, I keep running into dependency problems when
trying to build the C functions. My C code is organized like this:
ext/
extconf.rb
ext.c
vector.c
sphere.c
When built, this generates a single library that includes the code for
all of the optimized classes. 'ext.c' has an Init_ function that calls
initialization routines in the other files. Here's an example from
sphere.c:
Init_Sphere() {
VALUE cSphere = rb_const_get(rb_cObject, rb_intern("Sphere"));
}
sphere.rb would look something like this:
require 'vector'
require 'ext/optimized'
class Sphere < Shape
...
end
When I run this, I get a "undefined constant" error from the C code,
which makes sense: when the C library is included, Sphere hasn't been
defined yet. So I just move the "require 'ext/optimized'" to the bottom
of sphere.rb, right? Sadly, no. There's only one library entry point,
so I have to initialize all of my optimized classes at the same time.
'vector.rb' also requires 'ext/optimized', so Init_Sphere() would still
get called before Sphere is actually defined, and I'll still get my
error. Furthermore, since Vector gets used in several other files, I
have to worry about the order in which those files are parsed, as well.
I can see a few solutions: one is to break out every C file so that it
compiles into its own library. This would leave me with an ugly file
structure, since I'd need either a new extconf.rb for each C file, or a
sub-directory for each source file. I've tried doing something like
this:
VALUE cSphere = rb_eval_string("class Shape; end; class Sphere <
Shape; end");
but that feels extremely naughty, not to mention requires me to
duplicate the heritage of each subclass I want to provide C extensions
for. I suppose I could also create a "Optimizer" object in my C
library's Init() method, and attach methods to that class that would
call the class-specific Init_...() methods:
# a .rb file
require 'ext/optimized'
class Sphere < Shape
..
end
Optimizer.init_sphere
But that also seems rather distasteful.
I apologize for my wordiness... I'm trying to make my situation clear.
Am I making this more difficult than it really is? If the classes
involved where pure-C, or if there wasn't any subclassing, I'd be fine.
I just can't see how to manage the class dependencies while dealing
with a single library entry point, and so I've come to seek the
guidance of the wizards. Any help--or even architecture bashing, if
it's constructive--is appreciated.