first steps in openCL

No Comments »

I’m going to post something about my trip to Italy in a bit, after I get my pics uploaded properly to Flickr. But I wanted to put this up so I don’t forget about it later.

A conversation at work got me interested enough in GPGPU calculations to start doing a bit of research into the topic. Seems that at a base level both the ATI and NVidia APIs behave similarly, and that OpenCL is a nice, understandable alternative if you don’t want to be locked in to a single vendor. (Not that I use ATI cards.) I tried getting CUDA working on my Mac, but between weirdness caused by the drivers and the fact that it doesn’t support 64-bit on OSX, I’ve decided to go with OpenCL instead for my tests. Functionally everything will be the same, and after a nice tutorial on ATI’s site detailing some introductory points I have code that compiles and does what I want it to do, which is more than I can say for CUDA.

Anyway, here’s my first test. I wanted something simple for getting device information off my laptop.

#include <iostream>
using namespace std;

#include <stdio.h>
#include <stdlib.h>

#include <OpenGL/gl.h>
#include <OpenGL/CGLDevice.h>
#include <OpenCL/cl.h>
#include <OpenCL/cl_gl.h>
#include <OpenCL/cl_gl_ext.h>
#include <OpenCL/cl_ext.h>
 
#define MSG_SIZE 4096
#define MAX_DEVICE_IDS 16

#define RT_STRING       0
#define RT_UINT         1
#define RT_BOOL         2
#define RT_PLATFORMID   3
#define RT_SIZET        5
#define RT_SIZET_ARR    6

void printDeviceInfo(cl_device_id deviceID) {
   
    cl_device_info param_list[] = {
        CL_DEVICE_NAME, CL_DEVICE_VENDOR, CL_DRIVER_VERSION, CL_DEVICE_VERSION,
        CL_DEVICE_PROFILE,
        CL_DEVICE_PLATFORM, CL_DEVICE_VENDOR_ID,
        CL_DEVICE_MAX_COMPUTE_UNITS, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS,
        CL_DEVICE_MAX_WORK_ITEM_SIZES, CL_DEVICE_MAX_WORK_GROUP_SIZE,
       
        NULL
    };
   
    int param_return_types[] = {
        RT_STRING, RT_STRING, RT_STRING, RT_STRING,
        RT_STRING, RT_PLATFORMID, RT_UINT,
        RT_UINT, RT_UINT, RT_SIZET_ARR, RT_SIZET,
       
        NULL
    };
   
    char *param_strings[] = {
        "Device Name", "Vendor", "Driver Version", "Device Version",
        "Device Profile", "Device Platform", "Device Vendor ID",
        "Max Compute Units", "Max Work Item Dimensions",
        "Max Work Item Sizes", "Max Work Group Size",
   
        NULL
    };
   
    int i = 0;
    int maxWorkItemDimensions = 3;
   
    while(param_strings[i] != NULL) {
        char    msg[MSG_SIZE];
        size_t  param_sizeT;
        size_t  param_sizeT_array[MSG_SIZE];
        cl_uint param_uint;
        cl_bool param_bool;
        size_t  param_value_ret;
        cl_platform_id param_platformID;
       
        cl_int error;
       
        switch (param_return_types[i]) {
            case RT_STRING:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        MSG_SIZE,
                                        msg,
                                        &param_value_ret
                                        );

                cout << param_strings[i] << ": " << msg << "\n";
                break;

            case RT_UINT:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        sizeof(cl_uint),
                                        &param_uint,
                                        NULL
                                        );

                cout << param_strings[i] << ": " << param_uint << "\n";
                break;
               
               
            case RT_BOOL:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        sizeof(cl_bool),
                                        &param_bool,
                                        NULL
                                        );

                cout << param_strings[i] << ": " << (param_bool ? "True" : "False") << "\n";
                break;
               
            case RT_PLATFORMID:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        sizeof(cl_platform_id),
                                        param_platformID,
                                        NULL
                                        );

                cout << param_strings[i] << ": " << param_platformID << "\n";
                break;
           
            case RT_SIZET:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        sizeof(size_t),
                                        &param_sizeT,
                                        NULL
                                        );

                cout << param_strings[i] << ": " << param_sizeT << "\n";
                break;
               
            case RT_SIZET_ARR:
                error = clGetDeviceInfo(deviceID,
                                        param_list[i],
                                        sizeof(size_t) * maxWorkItemDimensions,
                                        param_sizeT_array,
                                        &param_value_ret
                                        );

                cout << param_strings[i] << ": " << param_sizeT_array[0] << ", "
                    << param_sizeT_array[1] << ", "
                    << param_sizeT_array[2] << "\n";
                break;
               
               
               
            default:
                break;
        }
       
        i++;
    }
}

int main(int argc, char **argv) {
    cl_device_id deviceID;
    cl_uint numDevices;

    cl_uint err = clGetDeviceIDs(
                NULL,                  
                CL_DEVICE_TYPE_CPU,    
                MAX_DEVICE_IDS,        
                &deviceID,             
                &numDevices
                );
   
    cout << "Number of CPU devices: " << numDevices << "\n";
   
    for (int i = 0; i < numDevices; i++) {
        printDeviceInfo(deviceID);
    }

    cout << "\n\n";

   
   

    err = clGetDeviceIDs(
                NULL,
                CL_DEVICE_TYPE_GPU,
                MAX_DEVICE_IDS,
                &deviceID,
                &numDevices
                );
   
    cout << "Number of GPU devices: " << numDevices << "\n";
   
    for (int i = 0; i < numDevices; i++) {
        printDeviceInfo(deviceID);
    }
   
    cout << "\n\n";
   
}
 

(Hope that copy-and-pasted correctly.) To compile it on your Mac:

g++ test2.cpp -o test2 -framework OpenCL

Here are the results on my laptop, a 2010 Core i7 MacBook Pro 15″:

Number of CPU devices: 1
Device Name: Intel(R) Core(TM) i7 CPU       M 620  @ 2.67GHz
Vendor: Intel
Driver Version: 1.0
Device Version: OpenCL 1.0
Device Profile: FULL_PROFILE
Device Platform: 0
Device Vendor ID: 16909312
Max Compute Units: 4
Max Work Item Dimensions: 3
Max Work Item Sizes: 1, 1, 1
Max Work Group Size: 1

Number of GPU devices: 1
Device Name: GeForce GT 330M
Vendor: NVIDIA
Driver Version: CLH 1.0
Device Version: OpenCL 1.0
Device Profile: FULL_PROFILE
Device Platform: 0
Device Vendor ID: 16918016
Max Compute Units: 6
Max Work Item Dimensions: 3
Max Work Item Sizes: 512, 512, 64
Max Work Group Size: 512

Next step: getting the GPU to do some calculations.

Posted on July 31st 2010 in programming

funhouse and the big top

No Comments »

As I type this I’m over at FUNHouse central with Dimos. It’s been a while since we were able to work on things together, in person. Usually we work a lot over email and lunchtime talks; it’ll be good to sit and jam together.

It’s been an interesting week. I’ve been tasked with doing research on cloth simming, or rather not simming, which is about all I can say. However, I can say that it’s been an interesting challenge and has helped me to better understand some of the more esoteric corners of Maya Dynamics.

Work aside, the G20 meet up has the entire city by the balls. The trains are shutting down due to bomb threats, and a number of people at the office have been ID’d on the way in by the some of the hundreds of cops patrolling the downtown core. The whole thing is a mess. Lots of downtown businesses are losing money because of forced shutdowns. Even my office is shutting for friday because the building is getting boarded up.

It’s strange to me… I can’t quite follow the logic on violent protests for something like this. Regardless of what’s being discussed, I can’t imagine any topics are worthy of that kind of response.

Anyway, to work with us!

Posted on June 11th 2010 in programming

new yaaaawk, baybee!

No Comments »

Dimos and I headed out over the weekend to see Mike in New York. The title of this post is what Dimos screamed, cheerleader-on-prom-night-in-the-back-of-a-volkswagen style, every five minutes. Somehow it never got old.

There are amazing pictures taken by Mike of both Dimos and myself on Facebook; hopefully I’ll find some time this weekend to get the pics of my own camera and Flickrize them.

I might talk a bit more about the trip in a few days, but what I actually wanted to write is this:

To project a worldspace point into camera space, you need to multiply the camera’s view matrix by its projection matrix. The projection matrix is grabbable through the OpenMaya API (there’s a function for it on MFnCamera). The view matrix is simply the inverse of the worldspace transform matrix.

Needed to get that out of my head and into a place I wouldn’t lose it. If that makes no sense to you, just pretend it wasn’t there. :)

Posted on April 2nd 2010 in 3d, math, maya, programming

playing in coffee

No Comments »

I’m slowly coming out of the fog of Maya, now that I’m done with using it for Animation Mentor, and I’ve been trying to get back into Cinema 4D. I just wanted to post this short COFFEE script snippet. This looks through all the animation curves on an object and spits out information on them.

var op = doc->GetActiveObject();
var curTime = doc->GetTime();

println("\nKey dump for ", op->GetName(), ":\n");

var t = op->GetFirstCTrack();
if (t == NULL)
	println("Error.");

var i = 0;
var j = 0;
var k;
var c;

while (t != NULL) {
	i++;
	c = t->GetCurve();
	println("\tTrack ", i, ": ", t->GetName(), " -- ",
	c->GetKeyCount(), " keys.");

	for (j = 0; j < c->GetKeyCount(); j++) {
		k = c->GetKey(j);
		var bt = k->GetTime();
		println("\t\t", j, ": v", k->GetValue(), ", t:", bt->GetFrame(30));
	}

	t = t->GetNext();
}

println("Number of tracks: ", i);

The sad part is this: you can’t add new keys to curves in COFFEE; you can only read what’s there. (EPIC FAIL, Maxon.) At least now I know how to get keys and how the BaseTime class works.

Posted on September 12th 2009 in 3d, cinema4d, coffee, programming

Weak References

No Comments »

Last year while I was working at Red Rover, I heard the term “weak reference” in reference to a technique for referencing objects in 3DS Max. The Max TD used them for a variety of things. I didn’t quite understand what he was talking about at the time, since the last version of Max I used was 2.5 and I never did rigging or coding for it then.

More recently I’ve come to the same technique on my own both in Maya and in Cinema 4D, and was reminded of the name for it by that same TD over beers a few weeks back.

Essentially, weak references are variables on objects that contain a pointer to an object, and not a reference to it’s name. In Maya, for example, you may see rigging scripts written by TDs referencing specific objects by name or saving names of objects as they’re created and using those names to connect attributes or add constraints. In a clean scene this works fine, as long as the rigger is meticulous in their naming scheme and runs different stages of the rigging script in proper order.

But what happens when Maya namespaces become involved? As soon as you reference in an asset, every object that makes up that asset gets a namespace prefixed onto it’s name. If you’ve written scripts that require specific names, they break. If your layout files aren’t perfect and the namespace is different between two or more shots (as Maya is wont to append a number, regardless of what you specify), useful tools like character GUIs and the like break and you’re left doing namespace surgery in a text editor.

Weak references sidestep all this by giving you a permanent connection to an object regardless of name changes or namespace prefixes.

A good example is how I’m currently handling cameras in scenes. A decision was made early on, on the current project at work, to name cameras in layout files by the name of the shot / sequence. Normally this isnt a problem, but we’re using a renderer that’s not linked into Maya directly and therefore needs a command line batch exporter written. If all the cameras are named differently, and the camera’s animation has to be baked and exported as well, how do you go about picking the right object?

Using weak references, the problem becomes trivial. You create them as follows:

addAttr -ln "cameraObj" -at "message";

You’ve probably seen attribute connections of type message into IK handles and other things. The message attribute carries no data– that is, it never changes and causes no DAG recalculations. (This is doubly useful because you can connect the message attributes of two objects to message-typed user attributes in a cycle without causing a cycle error– more on that later.) However, the attribute can be used to get the name of the connected object like so:

listConnections object.messageAttribute;

It will return an array of strings. If you rename an object, you can get the object’s current name through the above command.

So where do you store these attributes? For the moment I’m using a trick I saw on the Siggraph ’08 talk by Blue Sky on procedural rigging: I create non-executing script nodes and store connections on them. In the camera example above, every scene has a master script node. On that node are a few attributes, including its “type” and a .message connection to the render camera. It’s them trivial to find the camera’s name:

string $sel[] = `ls -type "script"`;
for ($s in $sel) {
	if (`attributeQuery -node $s -ex "snCamera"`) {
		// this should be the one you need
		// normally I search for the type, but this is an example
		string $conn[] = `listConnections ($s + ".snCamera")`;
		// if it's only one connection incoming, then you're done.
		print("Camera is named " + ($conn[0]) + "\n");
	}
}

This technique can be extended to include all kinds of objects. It can also be very helpful for scripts like character GUIs that need to know what characters are present in a scene, and be able to change the positions of all those controls.

One final note on this for now: In Cinema 4D, every object and tag in a scene can be named the same. Searches for objects or tags by name are often fruitless because of this; if two objects or tags have the same name there’s really no easy way to tell which is which in a COFFEE script. What you can do, however, is create a user data variable that is of the Link type. This allows you to drag and drop an object into that variable’s edit field, and provides a permanent pointer to that object regardless of name. This is very useful in rigging; for example, you can always tell which joints in a leg are control joints, and which are bind joints, by creating links. You can also expose the links in XPresso and use the pointers as if you’d dragged an object onto the XPresso node window.

Posted on August 19th 2009 in 3d, cinema4d, maya, programming, rigging

pure unadulterated actionscript geekery

No Comments »

Man oh man. One link clicked led to another, and suddenly I’m obsessed with PaperVision3D.

It all started with this link (which you’ve already seen if you follow me on Twitter): the The Eco Zoo. If you haven’t seen it, please clicky! It’s fantastic.

PaperVision’s been used for a number of neat projects, but the Eco Zoo is a work of art. So I started searching for stuff on the train in the morning (thank you, iPhone) and turned up a bunch of great links:

For those who didn’t know, you can either use the open source Flex SDK or, if you’re a student, you can get a free copy of Flex Builder 3 by sending them proof of enrollment. I did it; I got a serial in about an hour later, although YMMV.

Not that I have time to sit down and actually play with any of this… But it’s fun to learn about. Oh, and I like PaperVision3D because the API is very well thought out and very Flash AS3-like; if you don’t like it there are other options. There’s a nice comparsion on the four major 3D engines for Flash here (warning: it’s in French). Lots of talk about speed of object creation, memory requirements, and so on. It’s always good to be informed of options before starting a project.

Posted on February 28th 2009 in 3d, flash, programming

stuff and things

No Comments »

I’ve seen a bit of complaining about Dollhouse. I think people are expecting Joss to play by regular Joss rules, while Joss is going in a new direction. I’m totally enjoying the show. It’s not Firefly or Buffy, and I think that’s a good thing. While Joss’ fans have always been rabid, myself included, I think Dollhouse is more accessible to the general public. Also, the second episode opened a lot of plot doors. I’m excited I see where this show goes, and that’s not something I can often say.

I’m back on Twitter with a bit more consistency now, thanks to Tweetdeck. If you haven’t tried it you should– it’s the only client I’ve tried so far that does everything I want. I’m not into paying for Twitterific since most free AIR-based clients have similar or better features, but even free clients had me looking around for something better. Just wish AIR could publish iPhone apps.

This week I move from a “finished” storyboard into layout on my short film at Animation Mentor. This is currently frightening because I don’t have one of my characters modeled and neither ares fully rigged. :) I have a feeling it’s going to be a long weekend, and for me lately that’s saying something.

I did buy that book on doing short films in Blender, so I’ve been reading it as time allows for info specific to Blender and managing data flow. The first shock I had was that “library data” (Blender’s name for references / XRefs) is not editable. You can easily set up library data for animation, but there are different ways of doing so and which way you use depends on the asset. I like the system, though– it means that unlike Maya, nothing I do in my animation scene files can corrupt what I’ve set up in my library assets. Shibby!

Okay. Today my list says: write emails (check!), blog (check!), research that thing Cory Doctorow uses for backups with Git, prototype some face rig ideas, and do some Python scripting research. I need to think through some workflow issues and whether or not scripting solutions will save me time in the long run. (Surprisingly, for the rigs it will not.) Speaking of which, if you’re not a usual follower of Jason Schleifer’s Justin Barrett’s blog (Jason, Justin, you can understand why I was confused), I highly recommend his articles on Lambda functions. Maya UI coders, take note!

Posted on February 25th 2009 in 3d, Uncategorized, programming

cinema 4d scripting, part 2

No Comments »

Three posts in three days! I don’t actually have as much time as It appears I do; the iPhone WordPress app is very useful.

I’m finally at a good place with my rigging library of COFFEE functions that now when I code out a set or rig steps, things are behaving as I expect. I’ve recreated the “Wipix Leg” in code (if you’re not familiar with it, it’s a method for making a stable IK solution without the 90 degree pole vector flip trick in used in Maya), and I’m now at a point where I have to do some rig redesigning. In other words, I’m no longer fighting the code misbehaving– now I’m back to working on conceptual stuff.

I do plan on releasing some of what I’ve learned either for free here or on a DVD at some point down the line, with my function library.

One thing I found out that really put a thorn in my side is that my constraint code was made useless by changes to the Constraint tag in R11. By cleaning up how the tag works, Maxon has rendered the tag completely useless when working from COFFEE. I’m not sure whether Py4D will be able to do what’s needed; I won’t be able to begin porting my rig to Python until the whole thing is finished. However, because you can get selected objects as arrays through Py4D (and not through COFFEE) maybe there will be better constraint code workarounds in my future.

Also nice to discover was that the Wipix Leg works just fine in Blender. Stable leg IK was the one thing for which I didn’t have a setup I liked in Blender, but that problem is now sorted. Still thinking I’ll be using Blender for my short film because of it’s speed of use. I can build that leg from scratch in Blender in twenty minutes; it takes significantly longer in both C4D and Maya.

Posted on January 19th 2009 in programming, short film blog

qt 4.5 now lgpl

No Comments »

Some excellent news: Qt is soon to be released under the LGPL. Long story short, this means you can now use Qt for commercial projects (provided you’re careful and understand how the LGPL 2.1 and provided you don’t modify the Qt libraries) without paying the old $4995US fee.

They’re still providing the “commercial” release for people who want to pay for it. I suppose in some circumstances companies might need it, depending on what other code they need to interface with, but for the majority of new projects the LGPL license is perfect.

I’d always shied away from Qt in the past because as good as it is, the licensing was too restrictive for some of the things I wanted to do. But combine this news with the fact that Qt 4.5 will support 64-bit Cocoa for app creation and I’m already planning on using it for my next personal project.

Posted on January 18th 2009 in programming