View RSS Feed

zabsv

4k Linux fortsätter

Rate this Entry
Jag följer Hildenborgs exempel och försöker rensa bort sånt som är onödigt från koden. Jag lägger in raderna som kommentarer här för den som vill hänga med i vad jag gör. Den som bara vill kompilera och se resultatet hittar hela koden längst ner.

Fönstret behöver inte ha något namn,
//XStoreName(dpy, win, "4k tutorial goes Linux");

Stäng fönstret utan att städa upp först. Man kan ta bort det mesta men XCloseDisplay måste finnas kvar, annars får man ett "spökfönster" som hänger kvar.
//glXMakeCurrent(dpy, None, NULL);
//glXDestroyContext(dpy, glc);
//XDestroyWindow(dpy, win);


XgetWindowsAttributes och glViewPort behövs bara om man vill hantera resize
//XGetWindowAttributes(dpy, win, &gwa);
//glViewport(0, 0, gwa.width, gwa.height);

att slå på depth test har ingen funktion här, det hade kommit med av misstag.
//glEnable(GL_DEPTH_TEST);

Anropen till glClear är lite onödiga här, samma med Matrixmode och LoadIdentity, defaultvärden är grejen när man vill spara bytes.

Efter rensning och komprimering är jag nere på 1345 bytes. Ungefär 400 bytes bara på att rensa i koden med andra ord

Jag har inga djupare kunskaper i varken X, GLX, eller openGL, men vad jag har kunnat hitta så finns det inga uppenbara vinster att göra med att köra fullscreen.

Däremot har jag ett annat trumfkort att plocka fram. Jag har hittills kompilerat för 64bitar, men utan någon större ansträngning kan man göra en 32-bitars binär.

Installera libc6-dev-i386 (den kommer att ta med diverse andra paket som behövs också)
Kod:
$ sudo apt-get install libc6-dev-i386
Kompilera med
Kod:
g++ -m32 -Os -nostdlib 4k.cpp -c
ld -melf_i386 -dynamic-linker /lib/ld-linux.so.2 4k.o /usr/lib/i386-linux-gnu/libX11.so.6 /usr/lib/i386-linux-gnu/mesa/libGL.so.1
Och vips så är vi nere på 1309 bytes. Inte mycket till trumfkort måste medges, men före komprimering är skillnaden nästan 2K, så när koden växer kanske man kan ha större nytta av att skapa 32bitars program.

Raskt vidare till del 3 av tutorialen och här blir det lite problem kan jag avslöja.
Den härliga assemblerkoden som herr H hackat ihop vill inte alls med gcc och Linux. Säkert snabt fixat för den som kan assembler, men en vanlig dödlig som jag får hitta på något annat.

Som tur är har jag roat mig med att plugga gcc idag, så jag kan glatt konstatera att det finns en lösning

Dynamiskt länkat mattebibliotek och fast-math!
gcc verkar dessutom kunna optimera koden betydligt bättre om jag lägger allt i samma fil, och för ett 4k demo känns inte det som något större problem. däremot vore det naturlitvis intressant att veta hur man får samma optimering när koden ligger i flera filer. För går gör det säkert, bara man hittar rätt bland gcc's inställningar.
Kod:
$ g++ -Os -nostdlib -ffast-math -fwhole-program -c 4k.cpp
$ ld -Os -s -dynamic-linker /lib64/ld-linux-x86-64.so.2 4k.o /usr/lib/libGL.so /usr/lib/x86_64-linux-gnu/libX11.so /usr/lib/x86_64-linux-gnu/libm.so
$
$ g++ -m32 -Os -nostdlib -ffast-math -fwhole-program -c 4k.cpp
$ ld -melf_i386 -Os -s -dynamic-linker /lib32/ld-linux.so.2 4k.o /usr/lib/i386-linux-gnu/mesa/libGL.so.1 /usr/lib/i386-linux-gnu/libX11.so.6 /lib32/libm.so.6
Värt att observera är att om jag låter gcc sköta länkningen så länkar den alltid till en statisk libm, men när jag länkar manuellt kan jag välja den dynamiska. Så i det här fallet är det värt att länka separat.
Sedan igår har jag också lärt mig att man kan länka med -s för att få samma effekt som strip -s.
sstrip får jag däremot fortfarande köra.

Efter sstrip och komprimering är jag nere i 2224 bytes för 32 bitars versionen. En ökning på ungefär 900 bytes. Något mer än för den ursprungliga Windowskoden, men ändå acceptabelt.
64-bitars versionen blev 2456 byte, så nu är det i alla fall ett par hundra bytes mellan 32- och 64-bitar.

Och sist men inte minst klistrar jag in den Linuxanpassade källkoden.
För den som eventuellt missat det vill jag passa på att säga att ursprungskoden kommer från Hildenborgs 4k tutorial. Jag har bara anpassat den till Linux.
Kod:
#include 
#include 
#include 
#include 
#include 

#define M_PI            3.1415926535
#define X_NOISE_GEN     1619
#define Y_NOISE_GEN     31337
#define Z_NOISE_GEN     6971
#define SEED_NOISE_GEN  1013
#define SHIFT_NOISE_GEN 8

double SCurve5 (double a)
{
    double a3 = a * a * a;
    double a4 = a3 * a;
    double a5 = a4 * a;
    return (6.0 * a5) - (15.0 * a4) + (10.0 * a3);
}

double NoiseVal(int seed)
{
    int random = (seed * (seed * seed * 60493 + 19990303) + 1376312589);
    return 1.0 - ((double)(random & 0x7fffffff) / 1073741824.0);
}

double LinearInterp (double n0, double n1, double a)
{
    return ((1.0 - a) * n0) + (a * n1);
}

double GradientNoise3D (double fx, double fy, double fz, int ix, int iy, int iz, int seed)
{
    // Randomly generate a gradient vector given the integer coordinates of the
    // input value.  This implementation generates a random number and uses it
    // as an index into a normalized-vector lookup table.
    int vectorIndex = (
        X_NOISE_GEN         * ix
        + Y_NOISE_GEN       * iy
        + Z_NOISE_GEN       * iz
        + SEED_NOISE_GEN    * seed);
    vectorIndex ^= (vectorIndex >> SHIFT_NOISE_GEN);

    // Originally libnoise uses a list of 256 normalized vectors that are uniformly distributed in all directions.
    // To save memory, I introduced this code that randomly calculates normalized vectors.
    // Over an infinite time, the generated vectors will be uniformly distributed, but not so if only a few is generated.
    // Based on information found on: http://mathworld.wolfram.com/SpherePointPicking.html
    // - Micael Hildenborg.
    double zvGradient = NoiseVal(vectorIndex);
    double v = NoiseVal(vectorIndex + SEED_NOISE_GEN);
    double theta = M_PI * (zvGradient + 1.0);
    double invPhi = sqrt(1.0 - (zvGradient * zvGradient));
    double xvGradient = invPhi * cos(theta);
    double yvGradient = invPhi * sin(theta);

    // Set up us another vector equal to the distance between the two vectors
    // passed to this function.
    double xvPoint = (fx - (double)ix);
    double yvPoint = (fy - (double)iy);
    double zvPoint = (fz - (double)iz);

    return ((xvGradient * xvPoint)
        + (yvGradient * yvPoint)
        + (zvGradient * zvPoint)) * 2.12;
}

double GradientCoherentNoise3D (double x, double y, double z, int seed)
{
    // Create a unit-length cube aligned along an integer boundary.  This cube
    // surrounds the input point.
    // The double2int function assures this.
    // - Micael Hildenborg.
    int x0 = (int)(x);
    int x1 = x0 + 1;
    int y0 = (int)(y);
    int y1 = y0 + 1;
    int z0 = (int)(z);
    int z1 = z0 + 1;

    // Map the difference between the coordinates of the input value and the
    // coordinates of the cube's outer-lower-left vertex onto an S-curve.
    double xs = SCurve5(x - (double)x0);
    double ys = SCurve5(y - (double)y0);
    double zs = SCurve5(z - (double)z0);

    // Now calculate the noise values at each vertex of the cube.  To generate
    // the coherent-noise value at the input point, interpolate these eight
    // noise values using the S-curve value as the interpolant (trilinear
    // interpolation.)
    double n0, n1, ix0, ix1, iy0, iy1;
    n0   = GradientNoise3D (x, y, z, x0, y0, z0, seed);
    n1   = GradientNoise3D (x, y, z, x1, y0, z0, seed);
    ix0  = LinearInterp (n0, n1, xs);
    n0   = GradientNoise3D (x, y, z, x0, y1, z0, seed);
    n1   = GradientNoise3D (x, y, z, x1, y1, z0, seed);
    ix1  = LinearInterp (n0, n1, xs);
    iy0  = LinearInterp (ix0, ix1, ys);
    n0   = GradientNoise3D (x, y, z, x0, y0, z1, seed);
    n1   = GradientNoise3D (x, y, z, x1, y0, z1, seed);
    ix0  = LinearInterp (n0, n1, xs);
    n0   = GradientNoise3D (x, y, z, x0, y1, z1, seed);
    n1   = GradientNoise3D (x, y, z, x1, y1, z1, seed);
    ix1  = LinearInterp (n0, n1, xs);
    iy1  = LinearInterp (ix0, ix1, ys);

    return LinearInterp (iy0, iy1, zs);
}

double GetPerlinNoise(double frequency, double lacunarity, double persistence, int octaveCount, double x, double y, double z, int seed, bool billow)
{
    double value = 0.0;
    double signal = 0.0;
    double curPersistence = 1.0;

    x *= frequency;
    y *= frequency;
    z *= frequency;

    // Original code made sure to have coordinates within integer dimensions.
    // I removed that code due to memory constraints, and as it for the purpose of this code is easy for the developer to just limit incomming coordinates himself.
    // - Micael Hildenborg.

    for (int curOctave = 0; curOctave < octaveCount; curOctave++)
    {
        // Get the coherent-noise value from the input value and add it to the
        // final result.
        signal = GradientCoherentNoise3D (x, y, z, seed + curOctave);

        if(billow)
        {
            // Billowing noise only differs from Perlin noise with this line.
            // - Micael Hildenborg.
            signal = 2.0 * std::abs(signal) - 1.0;
        }
        value += signal * curPersistence;

        // Prepare the next octave.
        x *= lacunarity;
        y *= lacunarity;
        z *= lacunarity;
        curPersistence *= persistence;
    }

    return value;
}


asm(".globl _start;"
    "_start:;"
    "call main;"
    "movl $1, %eax;"
    "xorl %ebx, %ebx;"
    "int $0x80;"
   );

Display *dpy;
Window root;
GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win;
GLXContext glc;
XWindowAttributes gwa;
XEvent xev;

enum HANDLE_ENUM
{
	UNBIND=0,
	TEXTURE
};

void GenerateTexture(HANDLE_ENUM handle)
{
    static unsigned char texelArray[256][256];

    for (int y = 0; y < 256; ++y)
    {
        for (int x = 0; x < 256; ++x)
        {
            texelArray[y][x] = (unsigned char)((GetPerlinNoise(10.0, 2.0, 0.5, 6, (double)x / 256.0, (double)y / 256.0, 0.0, 26572, false) * 80.0) + 128.0);
        }
    }

	glBindTexture(GL_TEXTURE_2D, handle);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 256, 256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texelArray);
}

void Draw()
{
    //glClearColor(1.0, 1.0, 1.0, 1.0);
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //glMatrixMode(GL_PROJECTION);
    //glLoadIdentity();

    glBegin(GL_QUADS);
    glColor3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(0.5f, 0.5f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(0.5f, -0.5f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(-0.5f, -0.5f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(-0.5f, 0.5f);
    glEnd();
}

int main()
{
    dpy = XOpenDisplay(NULL);
    root = DefaultRootWindow(dpy);
    vi = glXChooseVisual(dpy, 0, att);
    cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
    swa.colormap = cmap;
    swa.event_mask = ExposureMask | KeyPressMask;

    win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);

    XMapWindow(dpy, win);
    //XStoreName(dpy, win, "4k tutorial goes Linux");

    glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
    glXMakeCurrent(dpy, win, glc);
    //glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);

    GenerateTexture(TEXTURE);

    while(true) {
        XNextEvent(dpy, &xev);
        if(xev.type == Expose) {
            //XGetWindowAttributes(dpy, win, &gwa);
            //glViewport(0, 0, gwa.width, gwa.height);
            Draw();
            glXSwapBuffers(dpy, win);
        }else if(xev.type == KeyPress) {
            //glXMakeCurrent(dpy, None, NULL);
            //glXDestroyContext(dpy, glc);
            //XDestroyWindow(dpy, win);
            XCloseDisplay(dpy);
            return(0);
        }
    }
}

Submit "4k Linux fortsätter" to Digg Submit "4k Linux fortsätter" to del.icio.us Submit "4k Linux fortsätter" to StumbleUpon Submit "4k Linux fortsätter" to Google

Taggar: c++, gcc, linux Lägg till/redigera taggar
Kategorier
Uncategorized

Comments