3d graphics float "benchmarks"

bearophile bearophileHUGS at lycos.com
Tue Mar 24 11:07:23 PDT 2009


redditor:
> There is another similar "benchmark" here:
> http://lucille.atso-net.jp/aobench/

The D#2 version is translated from C, the D#3 comes mostly from C#.
(D#1 was a translation from Python).
I may send this D2 version to the original author...

WIDTH = 256
HEIGHT = 256
NSUBSAMPLES = 2
NAO_SAMPLES = 8

Timings, seconds:
  Python:     193.6
  Psyco:       49.80
  D#2:          6.30
  D#3:          4.22  
  C gcc:        3.98
  C llvm-gcc:   3.84

Psyco timings may be improved using classes.
I have tried to create a ShedSkin version too.
Timings for the D#2 are quite good. I'd like to know how LDC goes here (I presume the timings aren't far from the llvm-gcc ones).

D code compiled with:
  DMD v1.041
  -O -release -inline

C code compiled with:
  gcc: V. 4.3.3-dw2-tdm-1 (GCC)
  LLVM: gcc version 4.2.1 (Based on Apple Inc. build 5636) (LLVM build)
  For both:
  -Wall -O3 -s -fomit-frame-pointer -msse3 -march=core2

Python:
  ActivePython 2.6.1.1
  (r261:67515, Dec  5 2008, 13:58:38)
  [MSC v.1500 32 bit (Intel)] on win32

Psyco for Python 2.6, V.1.6.0 final 0


> I'm too much of a D noobie myself to attempt this.

It's not too much difficult to translate the C/C# version, and it's a good training to learn D1, for you.

-----------------------

// ao3_d.d, D#3 version
import std.c.stdio: fopen, fprintf, fwrite, fclose, FILE;
import std.c.stdlib: rand;
import std.math: sqrt, cos, sin, PI;


const int RAND_MAX = short.max;

const int WIDTH = 256;
const int HEIGHT = 256;
const int NSUBSAMPLES = 2;
const int NAO_SAMPLES = 8;


double fpRand() { // if not available
    return rand() / cast(double)RAND_MAX;
}


struct Vec3 {
    double x, y, z;

    static double dot(ref Vec3 v0, ref Vec3 v1) {
        return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z;
    }

    static void cross(ref Vec3 v0, ref Vec3 v1, out Vec3 c) {
        c.x = v0.y * v1.z - v0.z * v1.y;
        c.y = v0.z * v1.x - v0.x * v1.z;
        c.z = v0.x * v1.y - v0.y * v1.x;
    }

    static void normalize(ref Vec3 c) {
        double length = sqrt(dot(c, c));

        if (length > 1e-17) {
            c.x /= length;
            c.y /= length;
            c.z /= length;
        }
    }
}


struct RayIntersection {
    Vec3 rayPosition, rayDirection;
    double distance;
    Vec3 hitPosition, normal;
    bool isHit;
}


struct Sphere {
    Vec3 center;
    double radius;

    void intersects(ref RayIntersection isect) {
        Vec3 rs = Vec3(isect.rayPosition.x - center.x,
                             isect.rayPosition.y - center.y,
                             isect.rayPosition.z - center.z);

        double B = Vec3.dot(rs, isect.rayDirection);
        double C = Vec3.dot(rs, rs) - radius * radius;
        double D = B * B - C;

        if (D > 0.0) {
            double t = -B - sqrt(D);

            if (t > 0.0 && t < isect.distance) {
                isect.distance = t;
                isect.isHit = true;

                isect.hitPosition.x = isect.rayPosition.x + isect.rayDirection.x * t;
                isect.hitPosition.y = isect.rayPosition.y + isect.rayDirection.y * t;
                isect.hitPosition.z = isect.rayPosition.z + isect.rayDirection.z * t;

                isect.normal.x = isect.hitPosition.x - center.x;
                isect.normal.y = isect.hitPosition.y - center.y;
                isect.normal.z = isect.hitPosition.z - center.z;

                Vec3.normalize(isect.normal);
            }
        }
    }
}


struct Plane {
    Vec3 position, normal;

    void intersects(ref RayIntersection isect) {
        double d = -Vec3.dot(position, normal);
        double v = Vec3.dot(isect.rayDirection, normal);
        if (-1e-17 < v && v < 1e-17)
            return;
        double t = -(Vec3.dot(isect.rayPosition, normal) + d) / v;

        if (t > 0.0 && t < isect.distance) {
            isect.distance = t;
            isect.isHit = true;

            isect.hitPosition.x = isect.rayPosition.x + isect.rayDirection.x * t;
            isect.hitPosition.y = isect.rayPosition.y + isect.rayDirection.y * t;
            isect.hitPosition.z = isect.rayPosition.z + isect.rayDirection.z * t;

            isect.normal = normal;
        }
    }
}


Sphere[3] spheres;
Plane plane;


Vec3[] getOrthoBasis(ref Vec3 normal) {
    auto orthoBasis = new Vec3[3];
    orthoBasis[2] = normal;
    orthoBasis[1].x = 0.0;
    orthoBasis[1].y = 0.0;
    orthoBasis[1].z = 0.0;

    if (normal.x < 0.6 && normal.x > -0.6) {
        orthoBasis[1].x = 1.0;
    } else if (normal.y < 0.6 && normal.y > -0.6) {
        orthoBasis[1].y = 1.0;
    } else if (normal.z < 0.6 && normal.z > -0.6) {
        orthoBasis[1].z = 1.0;
    } else {
        orthoBasis[1].x = 1.0;
    }

    Vec3.cross(orthoBasis[1], orthoBasis[2], orthoBasis[0]);
    Vec3.normalize(orthoBasis[0]);

    Vec3.cross(orthoBasis[2], orthoBasis[0], orthoBasis[1]);
    Vec3.normalize(orthoBasis[1]);

    return orthoBasis;
}


void getAmbientOcclusion(ref RayIntersection isect, out Vec3 ambientOcclusion) {
    int ntheta = NAO_SAMPLES;
    int nphi = NAO_SAMPLES;
    const double eps = 0.0001;

    RayIntersection occIsect;
    occIsect.rayPosition.x = isect.hitPosition.x + eps * isect.normal.x;
    occIsect.rayPosition.y = isect.hitPosition.y + eps * isect.normal.y;
    occIsect.rayPosition.z = isect.hitPosition.z + eps * isect.normal.z;
    auto basis = getOrthoBasis(isect.normal);

    int hitCount;
    for (int j = 0; j < ntheta; j++) {
        for (int i = 0; i < nphi; i++) {
            double theta = sqrt(fpRand());
            double phi = 2.0 * PI * fpRand();
            double x = cos(phi) * theta;
            double y = sin(phi) * theta;
            double z = sqrt(1.0 - theta * theta);

            occIsect.rayDirection.x = x * basis[0].x + y * basis[1].x + z * basis[2].x;
            occIsect.rayDirection.y = x * basis[0].y + y * basis[1].y + z * basis[2].y;
            occIsect.rayDirection.z = x * basis[0].z + y * basis[1].z + z * basis[2].z;
            occIsect.distance = 1.0e+17;
            occIsect.isHit = false;

            spheres[0].intersects(occIsect);
            spheres[1].intersects(occIsect);
            spheres[2].intersects(occIsect);
            plane.intersects(occIsect);

            if (occIsect.isHit)
                hitCount++;
        }
    }

    double occlusionRatio = cast(double)(ntheta * nphi - hitCount) / cast(double)(ntheta * nphi);
    ambientOcclusion.x = occlusionRatio;
    ambientOcclusion.y = occlusionRatio;
    ambientOcclusion.z = occlusionRatio;
}


ubyte clamp(double value) {
    int i = cast(int)(value * 255.5);
    if (i > 255)
        i = 255;
    else if (i < 0)
        i = 0;
    return cast(ubyte)i;
}


void render(ubyte[] byteImage, int width, int height, int numberOfSubSamples) {
    auto fimg = new double[width * height * 3];
    fimg[] = 0.0;

    RayIntersection isect;
    isect.rayPosition.x = 0.0;
    isect.rayPosition.y = 0.0;
    isect.rayPosition.z = 0.0;

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            for (int v = 0; v < numberOfSubSamples; v++) {
                for (int u = 0; u < numberOfSubSamples; u++) {
                    isect.rayDirection.x = (x + (u / cast(double)numberOfSubSamples) - (width / 2.0)) / (width / 2.0);
                    isect.rayDirection.y = -(y + (v / cast(double)numberOfSubSamples) - (height / 2.0)) / (height / 2.0);
                    isect.rayDirection.z = -1.0;
                    isect.distance = 1.0e+17;
                    isect.isHit = false;
                    Vec3.normalize(isect.rayDirection);

                    spheres[0].intersects(isect);
                    spheres[1].intersects(isect);
                    spheres[2].intersects(isect);
                    plane.intersects(isect);

                    if (isect.isHit) {
                        Vec3 ambientOcclusion;
                        getAmbientOcclusion(isect, ambientOcclusion);
                        fimg[3 * (y * width + x) + 0] += ambientOcclusion.x;
                        fimg[3 * (y * width + x) + 1] += ambientOcclusion.y;
                        fimg[3 * (y * width + x) + 2] += ambientOcclusion.z;
                    }
                }
            }

            fimg[3 * (y * width + x) + 0] /= cast(double)(numberOfSubSamples * numberOfSubSamples);
            fimg[3 * (y * width + x) + 1] /= cast(double)(numberOfSubSamples * numberOfSubSamples);
            fimg[3 * (y * width + x) + 2] /= cast(double)(numberOfSubSamples * numberOfSubSamples);
            byteImage[3 * (y * width + x) + 0] = clamp(fimg[3 * (y * width + x) + 0]);
            byteImage[3 * (y * width + x) + 1] = clamp(fimg[3 * (y * width + x) + 1]);
            byteImage[3 * (y * width + x) + 2] = clamp(fimg[3 * (y * width + x) + 2]);
        }
    }
}


void setupScene() {
    spheres[0].center.x = -2.0;
    spheres[0].center.y = 0.0;
    spheres[0].center.z = -3.5;
    spheres[0].radius = 0.5;

    spheres[1].center.x = -0.5;
    spheres[1].center.y = 0.0;
    spheres[1].center.z = -3.0;
    spheres[1].radius = 0.5;

    spheres[2].center.x = 1.0;
    spheres[2].center.y = 0.0;
    spheres[2].center.z = -2.2;
    spheres[2].radius = 0.5;

    plane.position.x = 0.0;
    plane.position.y = -0.5;
    plane.position.z = 0.0;

    plane.normal.x = 0.0;
    plane.normal.y = 1.0;
    plane.normal.z = 0.0;
}


void savePPM(char* fname, int w, int h, ubyte* img) {
    FILE *fp;

    fp = fopen(fname, "wb");
    assert(fp);

    fprintf(fp, "P6\n");
    fprintf(fp, "%d %d\n", w, h);
    fprintf(fp, "255\n");
    fwrite(img, w * h * 3, 1, fp);
    fclose(fp);
}


void main() {
    auto img = new ubyte[WIDTH * HEIGHT * 3];
    setupScene();
    render(img, WIDTH, HEIGHT, NSUBSAMPLES);
    savePPM("ao3_d.ppm".ptr, WIDTH, HEIGHT, img.ptr);
}





More information about the Digitalmars-d mailing list