Raytracing: FPC vs Java lazarus / 39-Graphics ---------------------------------------------------------------------- argb32 · 2017-02-04 16:30 I took raytracing demo from this topic and tried to make a compiler comparison. Initial results on my PC (64bit Linux target): FPC 11 fps, Java 24 fps. Seems too bad for FPC. First of all I modified the demos and now they output identical picture. Java version: Code: Java  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i import java.applet.Applet;import java.awt.Frame;import java.awt.image.BufferedImage;import java.awt.image.DataBufferInt; public class Minecraft4k extends Applet implements Runnable {     private static final long serialVersionUID = 1L;    private static final String version = "0.0.1";     private static final int WIDTH = 640;    private static final int HEIGHT = 400;    private static final int SCALE = 1;     // the block world map is stored in a cube of sides 64 in length;    // each map entry determines the colour of the corresponding block    private static final int MAPDIM = 64; // map dimensions    private static final int MAPCUBE = MAPDIM * MAPDIM * MAPDIM; // value 262144    private static final long TIME_SCALE = 100000;    private int[] map = new int[MAPCUBE];     public static void main(String[] args) {        Frame f = new Frame("Minecraft4k Java Demo " + version);        f.addWindowListener(new java.awt.event.WindowAdapter() {            @Override            public void windowClosing(java.awt.event.WindowEvent e) {                System.exit(0);            };        });         Minecraft4k mc = new Minecraft4k();        mc.setSize(WIDTH * SCALE + 8, HEIGHT * SCALE + 24);        f.add(mc);        f.pack();        mc.start();        f.setSize(WIDTH * SCALE + 8, HEIGHT * SCALE + 24);        f.setVisible(true);    }     @Override    public void start() {      new Thread(this).start(); // triggers the run() method    }         /*         * The following textures are generated         * 0 Air       * 1 Grass + Dirt         * 2 Dirt         * 3 Dirt         * 4 Stone (gray)         * 5 Brick         * 6 Dirt         * 7 Wood       * 8 Tree leaves         * 9 Water         * 10 .. 13 Dirt         * 14 Stone with moss         * 15 Red Brick with moss         */    private int[] generate_textures() {        // Algorithmically generate textures...        int[] texmap = new int[12288]; // 16 * 16 * 16 * 3 = 12288        int j = 1;         while (j < 16) {            int lBrightness = 255 - nextInt(96);            int y = 0;            while (y < (16 * 3)) { // 3 texture images (top, side and bottom),              // stored one below the other                int x = 0;              while (x < 16) {                    int lBrightness2;                  int lColor;                    int color = 0x966C4A; // ground                    if (j == 4) {                        color = 0x7F7F7F; // stone                    }                    if (j != 4 || nextInt(3) == 0) {                        lBrightness = 255 - nextInt(96);                    }                    if (j == 1 && y < (x * x * 3 + x * 81 >> 2 & 3) + 18) {                        color = 0x6AAA40; // grass                    } else if (j == 1 && y < (x * x * 3 + x * 81 >> 2 & 3) + 19) {                        lBrightness = lBrightness * 2 / 3;                    }                    if (j == 7) {                        color = 0x675231; // wood                      if (x > 0 && x < 15 && (y > 0 && y < 15 || y > 32 && y < 47)) {                            color = 0xBC9862; // wood                          // This is WRONG - see the JavaScript version for                          // details                            lBrightness2 = x - 7;                            lColor = (y & 15) - 7;                          if (lBrightness2 < 0) {                                lBrightness2 = 1 - lBrightness2;                            }                          if (lColor < 0) {                                lColor = 1 - lColor;                            }                          if (lColor > lBrightness2) {                                lBrightness2 = lColor;                            }                          lBrightness = 196 - nextInt(32) + lBrightness2 % 3 * 32;                      } else if (nextInt(2) == 0) {                          lBrightness = lBrightness * (150 - (x & 1) * 100) / 100;                      }                    }                    if (j == 5) {                      color = 0xB53A15; // brick                        if ((x + y / 4 * 4) % 8 == 0 || y % 4 == 0) {                          color = 0xBCAFA5;                        }                    }                  if (j == 9) {                        color = 0x4040ff; // water                    }                    if (j == 14) {                      color = 0x7F7F7F; // stone with moss                      if (nextInt(3) == 0) {                            color = 0x6AAA40;                        } else if ((x + y / 4 * 4) % 8 == 0 || y % 4 == 0) {                            color = 0x6AAA60;                      }                    }                    if (j == 15) {                        color = 0xB53A15; // red bricks with moss                      if (nextInt(3) == 0) {                            color = 0x6AAA40;                        } else if ((x + y / 4 * 4) % 8 == 0 || y % 4 == 0) {                            color = 0x6AAA60;                      }                    }                     lBrightness2 = lBrightness;                    if (y >= 32) {                      lBrightness2 /= 2;                    }                  if (j == 8) {                        color = 0x50D937; // leaves                      if (nextInt(2) == 0) {                            color = 0;                            lBrightness2 = 255;                      }                    }                    lColor = (color >> 16 & 0xFF) * lBrightness2 / 0xFF << 16 //                            | (color >> 8 & 0xFF) * lBrightness2 / 0xFF << 8 //                          | (color & 0xFF) * lBrightness2 / 0xFF;                    texmap[x + y * 16 + j * 256 * 3] = lColor;                    ++x;              }                ++y;            }            ++j;         }         return texmap;    }     private int curr = 1254543;    private int rndCnt = 0;    private int nextInt(int bound) {        rndCnt++;        curr = (curr * bound + 476611) % 455345;        return curr % bound;    }     private void generateMap() {        for (int x = 0; x < MAPDIM; x++) {            for (int y = 0; y < MAPDIM; y++) {                for (int z = 0; z < MAPDIM; z++) {                  int i = (z << 12) | (y << 6) | x;                    float yd = (y - 32.5f) * 0.4f;                    float zd = (z - 32.5f) * 0.4f;                    map[i] = nextInt(16);                    float th = nextInt(256) / 256.0f;                    if ((th > (Math.sqrt(Math.sqrt(yd * yd + zd * zd)) - 0.8f)) || (th < 0.5f)) {                      map[i] = 0;                    }                }            }        }    }     @Override    public void run() {        try {            long startTime = System.currentTimeMillis();          long lastTime = startTime;            int frames = 0;             BufferedImage localBufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);            int[] imgBufferData = ((DataBufferInt) localBufferedImage.getRaster().getDataBuffer()).getData();           int[] texmap = generate_textures();            System.out.println("Rnd cnt1: " + rndCnt);            generateMap();            System.out.println("Rnd cnt1: " + rndCnt);             do {              float now = (System.currentTimeMillis() - startTime) % TIME_SCALE / (float)TIME_SCALE;                 float ox = 32.5f + now * 64;              final float oy = 32.5f;                final float oz = 32.5f;                 float xRot = (float) ((float) Math.sin(now * Math.PI * 2f) * 0.4f + Math.PI / 2f);                float yRot = (float) Math.cos(now * Math.PI * 2f) * 0.4f;                 float xSin = (float) Math.sin(xRot);                float xCos = (float) Math.cos(xRot);                float ySin = (float) Math.sin(yRot);              float yCos = (float) Math.cos(yRot);                 int x = 0;                // raycaster: for each column                while (x < WIDTH) {                    float ___xd = (x - WIDTH / 2) / (float) HEIGHT;                    int y = 0;                    while (y < HEIGHT) {                        float __yd = (y - HEIGHT / 2) / (float) HEIGHT;                        float __zd = 1.0f;                      float ___zd = __zd * yCos + __yd * ySin;                      float _yd = __yd * yCos - __zd * ySin;                        float _xd = ___xd * xCos + ___zd * xSin;                        float _zd = ___zd * xCos - ___xd * xSin;                         int col = 0; // black sky colour                        int br = 255; // brightness                      float closest = 32.0f; // how far can we see                       int d = 0;                        while (d < 3) {                          float dimLength = _xd;                          if (d == 1) {                                dimLength = _yd;                          } else if (d == 2) {                              dimLength = _zd;                            }                          float ll = 1.0f / Math.abs(dimLength);                            float xd = _xd * ll;                            float yd = _yd * ll;                          float zd = _zd * ll;                          float initial = ox - ((int) ox);                            if (d == 1) {                                initial = oy - ((int) oy);                          } else if (d == 2) {                              initial = oz - ((int) oz);                            }                          if (dimLength >= 0.0f) {                                initial = 1.0f - initial;                            }                           float dist = ll * initial;                            float xp = ox + xd * initial;                            float yp = oy + yd * initial;                            float zp = oz + zd * initial;                             if (dimLength < 0.0f) {                              if (d == 0) {                                    xp--;                                } else if (d == 1) {                                  yp--;                                } else if (d == 2) {                                    zp--;                              }                             }                           // while we are considering a ray that is still closer then the best so far                            while (dist < closest) {                               // quantize to the map grid                              int mapOffset = (((int) zp & 63) << 12) | (((int) yp & 63) << 6) | ((int) xp & 63);                                int tex = map[mapOffset];                                 // if this voxel has a texture applied                                if (tex > 0) {                                   // find the uv coordinates of the intersection point                                    int u;                                  int v;                                     // fix uv's for alternate directions?                                    if (d == 1) {                                        u = (int) (xp * 16.0f) & 15;                                        v = (int) (zp * 16.0f) & 15;                                        if (yd < 0.0f) {                                          v += 32;                                      }                                    } else {                                      u = (int) ((xp + zp) * 16.0f) & 15;                                      v = ((int) (yp * 16.0f) & 15) + 16;                                    }                                   // find the colour at the intersection point                                  int cc = texmap[u + v * 16 + tex * 256 * 3];                                   // if the colour is not transparent                                  if (cc > 0) {                                      col = cc;                                        br = 255 - (int) (dist * (255.0f / 32.0f));                                      br = br * (255 - (d + 2) % 3 * 50) / 255;                                      // we now have the closest hit point (also terminates this ray)                                        closest = dist;                                  }                                }                              // advance the ray                                xp += xd;                                yp += yd;                              zp += zd;                                dist += ll;                          }                            ++d;                      }                        int r = (col >> 16 & 0xFF) * br / 0xFF;                        int g = (col >> 8 & 0xFF) * br / 0xFF;                      int b = (col & 0xFF) * br / 0xFF;                      imgBufferData[x + y * WIDTH] = r << 16 | g << 8 | b;                      ++y;                    }                    ++x;              }                 frames++;                 this.getGraphics().drawImage(localBufferedImage, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);                 // FPS counter printed every 1 second.                if (System.currentTimeMillis() - lastTime > 1000) {                    System.out.println(frames + " fps");                  lastTime = System.currentTimeMillis();                    frames = 0;                }             } while (true);        } catch (Exception e) {            e.printStackTrace();            return;        }    } } As Java's (int) typecast equivalent in Pascal is Trunc(), not Round() or Floor(). The latter is slow btw. After replacing with Trunc() fps become 18. More 3 fps gave unrolling 3-rd loop. It's weird that optimization settings (via {$OPTIMIZATION}) seems to have no effect. As well as changing Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type map : array[0..MAPDIM * MAPDIM * MAPDIM] of Integer; to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i map : array[0..MAPDIM-1, 0..MAPDIM-1, 0..MAPDIM-1] of Integer; So final results: FPC 21 fps, Java 24 fps. That's much better. ---------------------------------------------------------------------- argb32 · 2017-02-04 16:32 Pascal version (20K characters limit on post  ) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program mc4k;        {$mode objfpc}{$h+}        {$ifdef mswindows}        {$apptype gui}        {$endif}        {$RANGECHECKS OFF}{$FPUTYPE SSE3}{$INLINE ON}{$A 32}{$OPTIMIZATION LOOPUNROLL,LEVEL3,UNCERTAIN,PEE PHOLE,ASMCSE,ORDERFIELDS,FASTMATH,CSE,DFA}uses    cmem,    sdl,    math; type    p32Array = ^u32Array;    u32Array = array[0..$FFFFFF] of int32; const    WIDTH = 640;    HEIGHT = 400;    math_pi = 3.141592;  MAPDIM = 64;    TIME_SCALE = 100000; var    event : TSDL_Event;    done : boolean = false;    screen : PSDL_Surface;    texmap : array[0..12287] of Integer;    map : array[0..MAPDIM * MAPDIM * MAPDIM] of Integer;    tickStart: LongWord; function inttostr(v : integer): ansistring;begin    str(v,result);end; var curr: Integer = 1254543;var rndCnt: Integer = 0;function nextInt(bound: Integer): Integer;begin    rndCnt := rndCnt + 1;    curr := (curr * bound + 476611) mod 455345;    Result := curr mod bound;end; procedure makeTextures();var    j, y, lBrightness, color, lBrightness2, lColor, x : int32;begin    // each texture    for j:=1 to 15 do begin         lBrightness := 255 - nextInt(96);         // each pixel in the texture        for y:=0  to (16 * 3)-1 do            for x :=0 to 15 do begin                 color := $966C4A;                lBrightness2 := 0;                lColor := 0;                 if (j = 4) then                  color := $7F7F7F;                if ((j <> 4) or (nextInt(3) = 0)) then                    lBrightness := 255 - nextInt(96);                if (j = 1) and (y < (((x * x * 3 + x * 81) >> 2) and $3) + 18) then                    color := $6AAA40              else if (j = 1) and (y < (((x * x * 3 + x * 81) >> 2) and $3) + 19) then                    lBrightness := lBrightness * 2 div 3;              if (j = 7) then                begin                    color := $675231;                    if ((x > 0) and (x < 15) and (((y > 0) and (y < 15)) or ((y > 32) and (y < 47)))) then                  begin                        color := $BC9862;                      lBrightness2 := x - 7;                        lColor := (y and $F) - 7;                        if (lBrightness2 < 0) then                          lBrightness2 := 1 - lBrightness2;                         if (lColor < 0.0) then                            lColor := 1 - lColor;                         if (lColor > lBrightness2) then                          lBrightness2 := lColor;                         lBrightness := 196 - nextInt(32) + lBrightness2 mod 3 * 32;                    end                    else if (nextInt(2) = 0) then                      lBrightness := lBrightness * (150 - (x and $1) * 100) div 100;              end;                if (j = 5) then                begin                  color := $B53A15;                    if (((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0)) then                        color := $BCAFA5;                end;                if (j = 9) then                  color := $4040ff; // water                if (j = 14) then begin                    color := $7F7F7F; // stone with moss                  if nextInt(3) = 0 then                        color := $6AAA40                    else if ((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0) then                        color := $6AAA60;              end;                if (j = 15) then begin                    color := $B53A15; // red bricks with moss                    if nextInt(3) = 0 then                        color := $6AAA40                    else if ((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0) then                      color := $6AAA60;                end;                 lBrightness2 := lBrightness;                if (y >= 32) then                  lBrightness2 := lBrightness2 div 2;                if (j = 8) then                begin                    color := $50D937; // leaves                    if (nextInt(2) = 0) then                    begin                        color := 0;                        lBrightness2 := 255;                    end;                end;               // fixed point colour multiply between i1 and i2              lColor :=                ((((color >> 16) and $FF) * lBrightness2 div 255) << 16) or                ((((color >>  8) and $FF) * lBrightness2 div 255) <<  8) or                ((color        and $FF) * lBrightness2 div 255);                // pack the colour away              texmap[ x + y * 16 + j * 256 * 3 ] := lColor;            end;    end;end; procedure makeMap( ) ;var    x, y, z, i : Integer;    yd, zd, th : single;begin    // add random blocks to the map    for x := 0 to MAPDIM-1 do begin        for y := 0 to MAPDIM-1 do begin          for z := 0 to MAPDIM-1 do begin                i := (z shl 12) or (y shl 6) or x;                yd := (y - 32.5) * 0.4;                zd := (z - 32.5) * 0.4;                map[i] := nextInt( 16 );              th := nextInt( 256 ) / 256.0;                if (th > sqrt( sqrt( sqr((y - 32.5) * 0.4) + sqr(zd) ) ) - 0.8) or (th < 0.5) then                  map[i] := 0;            end;        end;    end;end; procedure init( );begin    makeTextures( );    makeMap( );end; procedure render();var    now,xRot,yRot,yCos,    ySin,xCos,xSin,    ox, oy, oz,    __zd,    ___xd,__yd,___zd,_yd,    _xd,_zd,closest,    dimLength,ll,    xd,yd,zd,initial,dist,    xp,yp,zp: single;    x,y ,col,br,d,tex,u,v,cc: Integer;begin    now := ((SDL_GetTicks() - tickStart) mod TIME_SCALE) / TIME_SCALE;     xRot := sin(now * math_pi * 2) * 0.4 + math_pi / 2;    yRot := cos(now * math_pi * 2) * 0.4;    yCos := cos(yRot);    ySin := sin(yRot);    xCos := cos(xRot);    xSin := sin(xRot);     ox := 32.5 + now * 64.0;    oy := 32.5;    oz := 32.5;     // for each column    for x :=0 to WIDTH -1 do begin        // get the x axis delta        ___xd := (x - WIDTH div 2) / HEIGHT;        // for each row        for y :=0 to HEIGHT-1 do begin            // get the y axis delta            __yd := (y - HEIGHT div 2) / HEIGHT;            __zd := 1;            ___zd :=  __zd * yCos +  __yd * ySin;            _yd :=  __yd * yCos -  __zd * ySin;            _xd := ___xd * xCos + ___zd * xSin;            _zd := ___zd * xCos - ___xd * xSin;             col := 0;            br := 255;            closest := 32.0;             // for each principle axis  x,y,z            for d :=0 to 2 do begin                dimLength := _xd;                if (d = 1) then                    dimLength := _yd                else if (d = 2) then                  dimLength := _zd;                ll := 1.0 / abs(dimLength);              xd := _xd * ll;                yd := _yd * ll;                zd := _zd * ll;                 initial := ox - Trunc(ox);              if (d = 1) then initial := oy - Trunc(oy);                if (d = 2) then initial := oz - Trunc(oz);                 if (dimLength > 0.0) then initial := 1 - initial;                 dist := ll * initial;               xp := ox + xd * initial;                yp := oy + yd * initial;                zp := oz + zd * initial;                 if (dimLength < 0.0) then begin                    if (d = 0)then xp-=1;                    if (d = 1)then yp-=1;                    if (d = 2)then zp-=1;                end;                 // while we are concidering a ray that is still closer then the best so far              while (dist < closest) do begin                    // quantize to the map grid                    tex := map[ ((trunc(zp) and 63) << 12) or ((trunc(yp) and 63) << 6) or (trunc(xp) and 63) ];                   // if this voxel has a texture applied                    if (tex > 0) then begin                        // find the uv coordinates of the intersection point                        u := (trunc((xp + zp) * 16)) and 15;                        v := (trunc(yp        * 16)  and 15) + 16;                         // fix uvs for alternate directions?                      if (d = 1) then begin                            u :=  (trunc(xp * 16)) and 15;                            v := ((trunc(zp * 16)) and 15);                            if (yd < 0.0) then                              v += 32;                        end;                       //find the colour at the intersection point                      cc := texmap[ u + v * 16 + tex * 256 * 3 ];                      // if the colour is not transparent                        if (cc > 0) then begin                            col := cc;                          br := 255 - trunc(dist * (255.0 / 32.0));                          br := br * (255 - ((d + 2) mod 3) * 50) div 255;                          //we now have the closest hit point (also terminates this ray)                            closest := dist;                      end;                    end;                    // advance the ray                  xp += xd;                    yp += yd;                  zp += zd;                    dist += ll;                end;          end;             p32Array(screen^.pixels)^[y * WIDTH + x] := ((br * ((col shr 16) and 255) div 255) shl 16                                                     + (br * ((col shr 8 ) and 255) div 255) shl 8                                                       +  br * (col and 255) div 255);        end;    end;end; var    tickf: longword;    frames : longword = 0;begin    SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);    SDL_putenv('SDL_VIDEO_CENTERED=1');    SDL_Init(SDL_INIT_VIDEO);   screen := SDL_SetVideoMode( WIDTH, HEIGHT, 32 , SDL_SWSURFACE );    init();    tickStart := SDL_GetTicks();    tickf := tickStart;    while not done  do    begin        while  SDL_PollEvent( @event ) > 0  do            case event.type_  of                SDL_KEYDOWN:                  if ( event.key.keysym.sym = SDLK_ESCAPE ) then                  begin                        done := true;                    end;                SDL_QUITEV:                begin                  done := true;                end;            end;        render( );      inc(frames);        SDL_Flip(screen);        if SDL_GetTicks() - tickf > 1000  then begin            tickf := SDL_GetTicks();          SDL_WM_SetCaption(pchar('FPS='+inttostr(frames)), nil);            frames := 0;        end;    end;end. ---------------------------------------------------------------------- argb32 · 2017-02-04 16:32 Version with unrolled loop: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program mc4k;        {$mode objfpc}{$h+}        {$ifdef mswindows}        {$apptype gui}        {$endif}        {$RANGECHECKS OFF}{$FPUTYPE SSE3}{$INLINE ON}{$A 32}{$OPTIMIZATION LOOPUNROLL,LEVEL3,UNCERTAIN,PEE PHOLE,ASMCSE,ORDERFIELDS,FASTMATH,CSE,DFA}uses    cmem,    sdl,    math; type    p32Array = ^u32Array;    u32Array = array[0..$FFFFFF] of int32; const    WIDTH = 640;    HEIGHT = 400;    math_pi = 3.141592;  MAPDIM = 64;    TIME_SCALE = 100000; var    event : TSDL_Event;    done : boolean = false;    screen : PSDL_Surface;    texmap : array[0..12287] of Integer;    map : array[0..MAPDIM * MAPDIM * MAPDIM-1] of Integer;    tickStart: LongWord; function inttostr(v : integer): ansistring;begin    str(v,result);end; var curr: Integer = 1254543;var rndCnt: Integer = 0;function nextInt(bound: Integer): Integer;begin    rndCnt := rndCnt + 1;    curr := (curr * bound + 476611) mod 455345;    Result := curr mod bound;end; procedure makeTextures();var    j, y, lBrightness, color, lBrightness2, lColor, x : int32;begin    // each texture    for j:=1 to 15 do begin         lBrightness := 255 - nextInt(96);         // each pixel in the texture        for y:=0  to (16 * 3)-1 do            for x :=0 to 15 do begin                 color := $966C4A;                lBrightness2 := 0;                lColor := 0;                 if (j = 4) then                  color := $7F7F7F;                if ((j <> 4) or (nextInt(3) = 0)) then                    lBrightness := 255 - nextInt(96);                if (j = 1) and (y < (((x * x * 3 + x * 81) >> 2) and $3) + 18) then                    color := $6AAA40              else if (j = 1) and (y < (((x * x * 3 + x * 81) >> 2) and $3) + 19) then                    lBrightness := lBrightness * 2 div 3;              if (j = 7) then                begin                    color := $675231;                    if ((x > 0) and (x < 15) and (((y > 0) and (y < 15)) or ((y > 32) and (y < 47)))) then                  begin                        color := $BC9862;                      lBrightness2 := x - 7;                        lColor := (y and $F) - 7;                        if (lBrightness2 < 0) then                          lBrightness2 := 1 - lBrightness2;                         if (lColor < 0.0) then                            lColor := 1 - lColor;                         if (lColor > lBrightness2) then                          lBrightness2 := lColor;                         lBrightness := 196 - nextInt(32) + lBrightness2 mod 3 * 32;                    end                    else if (nextInt(2) = 0) then                      lBrightness := lBrightness * (150 - (x and $1) * 100) div 100;              end;                if (j = 5) then                begin                  color := $B53A15;                    if (((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0)) then                        color := $BCAFA5;                end;                if (j = 9) then                  color := $4040ff; // water                if (j = 14) then begin                    color := $7F7F7F; // stone with moss                  if nextInt(3) = 0 then                        color := $6AAA40                    else if ((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0) then                        color := $6AAA60;              end;                if (j = 15) then begin                    color := $B53A15; // red bricks with moss                    if nextInt(3) = 0 then                        color := $6AAA40                    else if ((x + y div 4 * 4) mod 8 = 0) or (y mod 4 = 0) then                      color := $6AAA60;                end;                 lBrightness2 := lBrightness;                if (y >= 32) then                  lBrightness2 := lBrightness2 div 2;                if (j = 8) then                begin                    color := $50D937; // leaves                    if (nextInt(2) = 0) then                    begin                        color := 0;                        lBrightness2 := 255;                    end;                end;               // fixed point colour multiply between i1 and i2              lColor :=                ((((color >> 16) and $FF) * lBrightness2 div 255) << 16) or                ((((color >>  8) and $FF) * lBrightness2 div 255) <<  8) or                ((color        and $FF) * lBrightness2 div 255);                // pack the colour away              texmap[ x + y * 16 + j * 256 * 3 ] := lColor;            end;    end;end; procedure makeMap();var    x, y, z, i : Integer;    yd, zd, th : single;begin    // add random blocks to the map    for x := 0 to MAPDIM-1 do begin        for y := 0 to MAPDIM-1 do begin            for z := 0 to MAPDIM-1 do begin                i := (z shl 12) or (y shl 6) or x;                yd := (y - 32.5) * 0.4;                zd := (z - 32.5) * 0.4;                map[i] := nextInt( 16 );              th := nextInt( 256 ) / 256.0;                if (th > sqrt( sqrt( sqr((y - 32.5) * 0.4) + sqr(zd) ) ) - 0.8) or (th < 0.5) then                  map[i] := 0;            end;        end;    end;end; procedure init( );begin    makeTextures( );    makeMap( );end; procedure render();const    __zd = 1.0;    oy = 32.5;    oz = 32.5;var    now,xRot,yRot,yCos,    ySin,xCos,xSin,    ox,    ___xd,__yd,___zd,_yd,    _xd,_zd,closest,    dimLength,ll,    xd,yd,zd,initial,dist,    xp,yp,zp: single;    x,y ,col,br: Integer; procedure InnerLoop(d: Integer); inline;var    cc,tex,u,v:Integer;begin    // while we are concidering a ray that is still closer then the best so far    while (dist < closest) do begin      // quantize to the map grid        tex := map[ ((trunc(zp) and 63) << 12) or ((trunc(yp) and 63) << 6) or (trunc(xp) and 63) ];       // if this voxel has a texture applied        if (tex > 0) then begin            // find the uv coordinates of the intersection point            if (d <> 0) then begin                // fix uvs for alternate directions?                u := (trunc((xp + zp) * 16)) and 15;                v := (trunc(yp        * 16)  and 15) + 16;          end else begin                u :=  (trunc(xp * 16)) and 15;              v := ((trunc(zp * 16)) and 15);                if (yd < 0.0) then                    v += 32;            end;             //find the colour at the intersection point            cc := texmap[ u + v * 16 + tex * 256 * 3 ];            // if the colour is not transparent          if (cc > 0) then begin                col := cc;              br := 255 - trunc(dist * (255.0 / 32.0));                br := br * (255 - d) div 255;                //we now have the closest hit point (also terminates this ray)                closest := dist;            end;        end;        // advance the ray        xp += xd;        yp += yd;        zp += zd;        dist += ll;    end;end; procedure UnrolledLoop(); inline;var t: Single;begin    // d = 0    if (_xd > 0.0) then    begin        ll := 1.0 / (_xd);        xd := _xd * ll;      yd := _yd * ll;        zd := _zd * ll;        t := 1-(ox - Trunc(ox));        xp := ox + xd * t;    end else begin        ll := 1.0 / (-_xd);        xd := _xd * ll;        yd := _yd * ll;        zd := _zd * ll;        t := ox - Trunc(ox);        xp := ox + xd * t - 1;    end;    dist := ll * t;    yp := oy + yd * t;    zp := oz + zd * t;    InnerLoop(((0 + 2) mod 3) * 50);     // d = 1    if (_yd > 0.0) then    begin        ll := 1.0 / (_yd);        xd := _xd * ll;        yd := _yd * ll;        zd := _zd * ll;        t := 1-(oy - Trunc(oy));        yp := oy + yd * t;    end else begin        ll := 1.0 / (-_yd);        xd := _xd * ll;        yd := _yd * ll;        zd := _zd * ll;        t := oy - Trunc(oy);        yp := oy + yd * t - 1;    end;    dist := ll * t;    xp := ox + xd * t;    zp := oz + zd * t;    InnerLoop(((1 + 2) mod 3) * 50);     // d = 2    if (_zd > 0.0) then    begin        ll := 1.0 / _zd;        xd := _xd * ll;        yd := _yd * ll;        zd := _zd * ll;        t := 1 - oz + Trunc(oz);      zp := oz + zd * t;    end else begin        ll := 1.0 / -_zd;      xd := _xd * ll;        yd := _yd * ll;        zd := _zd * ll;      t := oz - Trunc(oz);        zp := oz + zd * t - 1;    end;    dist := ll * t;    xp := ox + xd * t;    yp := oy + yd * t;    InnerLoop(((2 + 2) mod 3) * 50);end; begin    now := ((SDL_GetTicks() - tickStart) mod TIME_SCALE) / TIME_SCALE;     xRot := sin(now * math_pi * 2) * 0.4 + math_pi / 2;    yRot := cos(now * math_pi * 2) * 0.4;    yCos := cos(yRot);    ySin := sin(yRot);    xCos := cos(xRot);    xSin := sin(xRot);     ox := 32.5 + now * 64.0;     // for each column    for x :=0 to WIDTH -1 do begin        // get the x axis delta        ___xd := (x - WIDTH div 2) / HEIGHT;        // for each row        for y :=0 to HEIGHT-1 do begin            // get the y axis delta            __yd := (y - HEIGHT div 2) / HEIGHT;            ___zd :=  __zd * yCos +  __yd * ySin;            _yd :=  __yd * yCos -  __zd * ySin;          _xd := ___xd * xCos + ___zd * xSin;            _zd := ___zd * xCos - ___xd * xSin;             col := 0;            br := 255;          closest := 32.0;             UnrolledLoop();             p32Array(screen^.pixels)^[y * WIDTH + x] := ((br * ((col shr 16) and 255) div 255) shl 16                                                     + (br * ((col shr 8 ) and 255) div 255) shl 8                                                     +  br * (col and 255) div 255);      end;    end;end; var    tickf: longword;    frames : longword = 0;begin    SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);    SDL_putenv('SDL_VIDEO_CENTERED=1');    SDL_Init(SDL_INIT_VIDEO);     screen := SDL_SetVideoMode( WIDTH, HEIGHT, 32 , SDL_SWSURFACE );    init();    tickStart := SDL_GetTicks();    tickf := tickStart;    while not done  do    begin        while  SDL_PollEvent( @event ) > 0  do            case event.type_  of                SDL_KEYDOWN:                  if ( event.key.keysym.sym = SDLK_ESCAPE ) then                  begin                        done := true;                    end;                SDL_QUITEV:                begin                  done := true;                end;            end;        render( );      inc(frames);        SDL_Flip(screen);        if SDL_GetTicks() - tickf > 1000  then begin            tickf := SDL_GetTicks();          SDL_WM_SetCaption(pchar('FPS='+inttostr(frames)), nil);            frames := 0;        end;    end;end. ---------------------------------------------------------------------- marcov · 2017-02-04 16:42 level3 is a collection of optimizations. Set it first, or it may erase earlier switches? ---------------------------------------------------------------------- Graeme · 2017-02-04 17:30 Quote from: argb32 on February 04, 2017, 05:30:34 pmSo final results: FPC 21 fps, Java 24 fps. That's much better. That's all good and well...   But now start optimising the Java version too. ;-)  The gap between the two compilers increase again. There is simply no way FPC can beat Java. As I mentioned in the original thread... we all know FPC was developed with "ease of maintenance" and "easy of portability" in mind. Performance was never on the table. Contrary to what many may think, in recent years, Java (even with badly written code) does an amazing job with optimisations. It's a damn good language and JVM these days. ---------------------------------------------------------------------- marcov · 2017-02-04 17:50 Quote from: Graeme on February 04, 2017, 06:30:11 pmThere is simply no way FPC can beat Java. As I mentioned in the original thread... we all know FPC was developed with "ease of maintenance" and "easy of portability" in mind. Performance was never on the table. Contrary to what many may think, in recent years, Java (even with badly written code) does an amazing job with optimisations. It's a damn good language and JVM these days. That is a pretty bad generalization. These benchmarks are totally atypical, don't use objects or dynamic memory management in the core loop, and map 1:1 on C. Then it amounts to what you can buy wrt compiler technology, but that doesn't say really that much about Java the language and the performance of larger Java programs. ---------------------------------------------------------------------- Graeme · 2017-02-04 18:37 @Marco: You are forgetting the fact that the original code (JavaScript version) was written based on Java code (original code was never released) which was submitted to a 4K (4000 bytes maximum) competition. So it was by design ugly (as Notch clearly stated) and was all about how much functionality he could achieved with a mere 4K executable. The C, Object Pascal and my Java version are all based on the "ugly code". So all of them are written on the same "level playing field" - you are welcome to disagree. None of them taking real advantage of each language [features] per se. So I personally think it's a good comparison, to see what a compiler can do with "ugly written code". Some compilers are clearly way better at that than others. What I didn't mention in my previous post (and probably should have), is that FPC does have some advantages over Java too. eg: a all-in-one executable that doesn't require a 80MB runtime environment. Saying all this, it is still possible writing games with FPC. If they are graphics intensive, simply make sure the game isn't based on software rendering, but rather hardware accelerated. Then you have nothing to worry about. My FPC+SDL2+OpenGL port of this demo so far reaches 450 fps - way more speed than I really need. ---------------------------------------------------------------------- marcov · 2017-02-04 19:55 Quote from: Graeme on February 04, 2017, 07:37:44 pm@Marco: You are forgetting the fact that the original code (JavaScript version) was written based on Java code (original code was never released) which was submitted to a 4K (4000 bytes maximum) competition. So it was by design ugly (as Notch clearly stated) and was all about how much functionality he could achieved with a mere 4K executable. The C, Object Pascal and my Java version are all based on the "ugly code". So all of them are written on the same "level playing field" - you are welcome to disagree. None of them taking real advantage of each language [features] per se. So I personally think it's a good comparison, to see what a compiler can do with "ugly written code". Some compilers are clearly way better at that than others. All true, and all an open door if you are going to write e.g. codecs. Or vision apps (though even those are quite of more memory bandwidth than CPU limited). But it has absolutely, absolutely nothing to do with the language, and your statement Quote It's a damn good language and JVM these days. is very (and TOO) general. Specially since it has nought to do with language, just compiler technology. ---------------------------------------------------------------------- argb32 · 2017-02-04 20:53 Quote from: marcov on February 04, 2017, 05:42:35 pmlevel3 is a collection of optimizations. Set it first, or it may erase earlier switches? I've tried different combinations. Presence of level3 seems to give about 1 fps. Others (with or without level3) seems to have no effect. Quote from: Graeme on February 04, 2017, 06:30:11 pmThat's all good and well...   But now start optimising the Java version too. ;-)  The gap between the two compilers increase again. I've applied some optimizations but didn't managed to get any performance gain in java version. Although I didn't apply loop unrolling. Quote from: Graeme on February 04, 2017, 06:30:11 pmIt's a damn good language and JVM these days. Agree. JVM (Oracle HotSpot) is very polished platform and JIT compiler does impressive job. Java as language is not as good IMHO but there are many languages which can use JVM. Quote from: Graeme on February 04, 2017, 07:37:44 pmIf they are graphics intensive, simply make sure the game isn't based on software rendering, but rather hardware accelerated. Then you have nothing to worry about. Unfortunately there is still reason to worry as usually algorithms computing which data should be passed to graphics hardware (e.g. which portion is potentially visible) require CPU power. ---------------------------------------------------------------------- Graeme · 2017-02-04 21:47 Quote from: argb32 on February 04, 2017, 09:53:06 pm(e.g. which portion is potentially visible) require CPU power. Nope, that is done by OpenGL (GPU) as well. Depth calculations, perspective views, translation matrix etc.... all done via OpenGL on the GPU. It's actually brilliant! At the moment the only thing Object Pascal does in my SDL2+OpenGL application is setting up the graphics system, input handling and game logic. All graphics stuff is done on the GPU via OpenGL, hence I can get up to 4000+ fps. ---------------------------------------------------------------------- Akira1364 · 2017-02-05 00:52 Quote from: Graeme on February 04, 2017, 10:47:32 pmQuote from: argb32 on February 04, 2017, 09:53:06 pm(e.g. which portion is potentially visible) require CPU power. Nope, that is done by OpenGL (GPU) as well. Depth calculations, perspective views, translation matrix etc.... all done via OpenGL on the GPU. It's actually brilliant! At the moment the only thing Object Pascal does in my SDL2+OpenGL application is setting up the graphics system, input handling and game logic. All graphics stuff is done on the GPU via OpenGL, hence I can get up to 4000+ fps. You've said recently that you're new to OpenGL, so just to be sure you're not starting off on the wrong foot: are you, then, doing all of the things you just described within a GLSL shader? Because if not, you're still using the CPU. All of the fixed-function-pipeline matrix and viewport-related methods (as in glRotate, glScale, glTranslate, gluPerspective, glPushMatrix, glPopMatrix, glLoadMatrix, e.t.c) are executed strictly on the CPU, and upload their results to the GPU afterwards. To oversimplify it: anything that isn't being executed within a compiled shader (or a display list, although they're as deprecated as the fixed-function pipeline) is not using your GPU. Also, doing all of your matrix math inside a shader is not even really a good idea, as it means you'll be repeating the calculations for every single vertex. A better way to do it is to do the calculations once per "draw call" on the CPU, and send them to your shader via a uniform. ---------------------------------------------------------------------- Graeme · 2017-02-05 01:49 Quote from: Akira1364 on February 05, 2017, 01:52:28 amYou've said recently that you're new to OpenGL, Quoteso just to be sure you're not starting off on the wrong foot: are you, then, doing all of the things you just described within a GLSL shader? Correct, I set up a translation matrix (including some other matrix's too) and they are all executed by the shader. I'm not using any deprecated OpenGL calls. So I only support OpenGL 3.3 and up in my application and do things according to the "modern OpenGL way". QuoteAlso, doing all of your matrix math inside a shader is not even really a good idea, as it means you'll be repeating the calculations for every single vertex. I'll see how it goes, but the GPU with its many many cores were design to do such things, and do them 1000's of times faster than the "general" CPU can. I'm just giving them what they were designed to do. ;-) ---------------------------------------------------------------------- skalogryz · 2017-02-05 02:38 did anyone make LLVM version for comparison? ---------------------------------------------------------------------- Akira1364 · 2017-02-05 03:49 Like, Clang, you mean? Would likely be very similar to the GCC results. ---------------------------------------------------------------------- Thaddy · 2017-02-05 12:39 Also try out ppcjvm. FPC is a jvm language too.. With sometimes indeed much faster results compared to true native code. ---------------------------------------------------------------------- Ñuño_Martínez · 2017-02-05 13:40 Quote from: Thaddy on February 05, 2017, 01:39:54 pmAlso try out ppcjvm. FPC is a jvm language too.. With sometimes indeed much faster results compared to true native code. This puzzled me a lot.  How is it possible an interpreted/virtual machine program is faster than native code?  It should be slightly slower.  Even using "Just in time compile" mode. ---------------------------------------------------------------------- Leledumbo · 2017-02-05 14:21 Quote from: Ñuño_Martínez on February 05, 2017, 02:40:30 pmQuote from: Thaddy on February 05, 2017, 01:39:54 pmAlso try out ppcjvm. FPC is a jvm language too.. With sometimes indeed much faster results compared to true native code. This puzzled me a lot.  How is it possible an interpreted/virtual machine program is faster than native code?  It should be slightly slower.  Even using "Just in time compile" mode. JIT could win in tight loops or complex calculations, as the VM can decide the best instruction sequence to use at runtime according to the machine where it runs. ---------------------------------------------------------------------- Thaddy · 2017-02-05 15:39 Quote from: Ñuño_Martínez on February 05, 2017, 02:40:30 pmThis puzzled me a lot.  How is it possible an interpreted/virtual machine program is faster than native code?  It should be slightly slower.  Even using "Just in time compile" mode. http://forum.lazarus.freepascal.org/index.php/topic,35022.msg230871.ht ml#msg230871 Well in this thread I wrote an example that compiled with ppcjvm on a Raspberry pi 3 on Oracle hotspot runs twice as fast as ppcarm compiled armhf-vppv4 code with all proper optimizations. ---------------------------------------------------------------------- lainz · 2017-02-05 16:18 I need to benchmark V8. I'm using Electron to make small desktop web based applications. It's pretty fast even with large DOM. I can use Type Script for the classes but I didn't needed it too much. Java also can have a web browser, also FPC, but I didn't find a way to use them like in Electron. I'm not used to node.js so I'm not using any third party library, pure JS. Edit: sorry is not directly related to raycasting, but with benchmarks is. ---------------------------------------------------------------------- marcov · 2017-02-05 16:34 Quote from: Leledumbo on February 05, 2017, 03:21:52 pmJIT could win in tight loops or complex calculations, as the VM can decide the best instruction sequence to use at runtime according to the machine where it runs. Theoretically yes, in practice that barely happens for x86. The codegenerator of JVM is simply better. If you don't have dynamic allocation, the problem of codegeneration is the same as for C and Pascal. I regularly test against Delphi (which is not really know to be a speedmonster either), and usually FPC lags there, though there are exceptions. ---------------------------------------------------------------------- lainz · 2017-02-05 17:32 According to this tool, https://github.com/mrdoob/stats.js/ The JavaScript version http://jsrun.it/notch/dB1E, runs constantly at 60 fps. I didn't change the resolution, is the original one. If I change that to 640x400 it runs at 31-33fps. (of course you need to save the website, edit the sources, width and height in css, width and height in canvas and width and height in code, then run in a local server with this script in the console: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i javascript:(function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();document.body.appendChild(stats.dom);requestAnimatio nFrame(function loop(){stats.update();requestAnimationFrame(loop)});}; script.src='//rawgit.com/mrdoob/stats.js/master/build/stats.min.js';do cument.head.appendChild(script);})() ). Java version runs at 28-31 fps on my machine. (Latest Java 64 bit). I have 2 theories: - Notch is really a good programmer, even in javascript he did the best code? - JavaScript in Chrome is really faster than FPC and Java? - I need to try with Java 32 bit? ---------------------------------------------------------------------- Leledumbo · 2017-02-05 18:08 Quote from: marcov on February 05, 2017, 05:34:56 pmQuote from: Leledumbo on February 05, 2017, 03:21:52 pmJIT could win in tight loops or complex calculations, as the VM can decide the best instruction sequence to use at runtime according to the machine where it runs. Theoretically yes, in practice that barely happens for x86. The codegenerator of JVM is simply better. If you don't have dynamic allocation, the problem of codegeneration is the same as for C and Pascal. I regularly test against Delphi (which is not really know to be a speedmonster either), and usually FPC lags there, though there are exceptions. Right, as GCC/Clang still wins against Oracle JVM even in microbenchmarks. ---------------------------------------------------------------------- Akira1364 · 2017-02-05 23:57 Quote from: lainz on February 05, 2017, 06:32:29 pmAccording to this tool, https://github.com/mrdoob/stats.js/ The JavaScript version http://jsrun.it/notch/dB1E, runs constantly at 60 fps. I didn't change the resolution, is the original one. If I change that to 640x400 it runs at 31-33fps. (of course you need to save the website, edit the sources, width and height in css, width and height in canvas and width and height in code, then run in a local server with this script in the console: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i javascript:(function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();document.body.appendChild(stats.dom);requestAnimatio nFrame(function loop(){stats.update();requestAnimationFrame(loop)});}; script.src='//rawgit.com/mrdoob/stats.js/master/build/stats.min.js';do cument.head.appendChild(script);})() ). Java version runs at 28-31 fps on my machine. (Latest Java 64 bit). I have 2 theories: - Notch is really a good programmer, even in javascript he did the best code? - JavaScript in Chrome is really faster than FPC and Java? - I need to try with Java 32 bit? I think most people in this thread are forgetting that the HTML5 Canvas element is hardware accelerated, and any JavaScript code passed to it is likely optimized behind the scenes and compiled into some kind of GPU-friendly data structure. It's important to keep in mind that Chrome (and all other major browsers) are in fact "native" desktop applications written in C++. This is in contrast to the SDL- based ports we've seen here, which are explicitly not hardware accelerated (even though they're also native applications and easily could be), and simply blit each individual pixel to the SDL surface one by one. The same applies to the Java version: hardware acceleration is not enabled by default in the Java "Graphics" API. ---------------------------------------------------------------------- argb32 · 2017-02-06 00:48 Quote from: lainz on February 05, 2017, 06:32:29 pmI have 2 theories: - JavaScript in Chrome is really faster than FPC and Java? JavaScript in general should be slower than Java or FPC due to language features. The JS version is not identical to my versions which identical to each other. ---------------------------------------------------------------------- marcov · 2017-02-09 09:26 Quote from: argb32 on February 06, 2017, 01:48:12 amQuote from: lainz on February 05, 2017, 06:32:29 pmI have 2 theories: - JavaScript in Chrome is really faster than FPC and Java? JavaScript in general should be slower than Java or FPC due to language features. What happens here might be the same as the principle for asm.js:  if you only use a certain optimizable subset that is highly optimizable, what ends up at the codegenerator is the same as it would be from a Java, pascal or C/C++ program. Then it is just codegenerator vs codegenerator, and the language doesn't really matter much. ---------------------------------------------------------------------- lainz · 2017-02-09 23:30 Good to know.