(**************************************************************************) (* Sample use of ParMap, a simple library to perform Map computations on *) (* a multi-core *) (* *) (* Author(s): Marco Danelutto and Roberto Di Cosmo *) (* *) (* This program is free software: you can redistribute it and/or modify *) (* it under the terms of the GNU General Public License as *) (* published by the Free Software Foundation, either version 2 of the *) (* License, or (at your option) any later version. *) (**************************************************************************) (* for the toplevel #use "topfind";; #require "graphics";; #require "parmap";; #require "sdl";; *) let ncores = ref 4;; (* how many core we use *) let chunksize = ref 1;; (* granularity *) let n = ref 1000;; (* the size of the square screen windows in pixels *) let res = ref 1000;; (* the resolution: maximum number of iterations allowed *) (* scale factor and offset of the picture *) let scale = ref 1.;; let ofx = ref 0.;; let ofy = ref 0.;; (* convert an integer in the range 0..res into a screen color *) let color_of c res = Pervasives.truncate (((float c)/.(float res))*.(float Graphics.white));; (* compute the color of a pixel by iterating z_n+1=z_n^2+c *) (* j,k are the pixel coordinates *) let pixel (j,k,n) = let zr = ref 0.0 in let zi = ref 0.0 in let cr = ref 0.0 in let ci = ref 0.0 in let zrs = ref 0.0 in let zis = ref 0.0 in let d = ref (2.0 /. ((float n) -. 1.0)) in let colour = Array.create n (Graphics.black) in for s = 0 to (n-1) do let j1 = ref (((float j.(s)) +. !ofx) /. !scale) in let k1 = ref (((float k) +. !ofy) /. !scale) in begin zr := !j1 *. !d -. 1.0; zi := !k1 *. !d -. 1.0; cr := !zr; ci := !zi; zrs := 0.0; zis := 0.0; for i=0 to (!res-1) do begin if(not((!zrs +. !zis) > 4.0)) then begin zrs := !zr *. !zr; zis := !zi *. !zi; zi := 2.0 *. !zr *. !zi +. !ci; zr := !zrs -. !zis +. !cr; Array.set colour s (color_of i !res); end; end done end done; (colour,k);; (* generate the initial configuration *) let initsegm n = let rec aux acc = function 0 -> acc | n -> aux (n::acc) (n-1) in aux [] n ;; let tasks = let ini = Array.create !n 0 in let iniv = for i=0 to (!n-1) do Array.set ini i i done; ini in List.map (fun seed -> (iniv,seed,!n)) (initsegm !n) ;; (* draw a line on the screen using fast image functions *) let unpack_color n = let r = (n land 0xff0000) lsr 16 and g = (n land 0x00ff00) lsr 8 and b = n land 0xff in (r,g,b) ;; let draw_line screen (col,j) = Array.iteri (fun i c -> Sdlvideo.put_pixel_color screen i j (unpack_color c)) col;; let draw screen res = List.iter (fun c -> draw_line screen c) res;; (* compute *) let compute () = let d = Unix.gettimeofday() in let res = if !ncores > 1 then Parmap.parmap ~ncores: !ncores ~chunksize: !chunksize pixel (Parmap.L tasks) else List.map pixel tasks in Printf.eprintf " [time: %f] %!" (Unix.gettimeofday() -. d); res ;; (*** Open the main graphics window and run the event loop *) open Sdlevent;; open Sdlkey;; open Sdlvideo;; Sdl.init [`VIDEO];; let (bpp, w, h) = (24, !n, !n);; let screen = Sdlvideo.set_video_mode ~w ~h ~bpp [];; (* two pixel deep surfaces for drawing and saving area borders *) (* one pixel for each border: two horizontal and two vertical *) let shadowh = Sdlvideo.create_RGB_surface_format screen [`SWSURFACE] ~w ~h:2;; let shadowv = Sdlvideo.create_RGB_surface_format screen [`SWSURFACE] ~w:2 ~h;; (* one pixel deep white surfaces for drawing area borders *) let whiteh = let surf = Sdlvideo.create_RGB_surface_format screen [`SWSURFACE] ~w ~h:1 in for i = 0 to w-1 do Sdlvideo.put_pixel_color ~x:i ~y:0 surf Sdlvideo.white done; surf;; let whitev = let surf = Sdlvideo.create_RGB_surface_format screen [`SWSURFACE] ~w:1 ~h in for i = 0 to h-1 do Sdlvideo.put_pixel_color ~x:0 ~y:i surf Sdlvideo.white done; surf;; (* blit a rectangle border *) type action = Draw | Save | Restore | Update;; let border action x y w h = let hrect1 = {r_x=x;r_y=y;r_w=w;r_h=1} and vrect1 = {r_x=x;r_y=y;r_w=1;r_h=h} and hrect2 = {r_x=x;r_y=y+h;r_w=w;r_h=1} and vrect2 = {r_x=x+w;r_y=y;r_w=1;r_h=h} in match action with Draw -> blit_surface ~src:whiteh ~src_rect:{hrect1 with r_y=0;r_h=1} ~dst:screen ~dst_rect:hrect1 (); blit_surface ~src:whiteh ~src_rect:{hrect2 with r_y=0;r_h=1} ~dst:screen ~dst_rect:hrect2 (); blit_surface ~src:whitev ~src_rect:{vrect1 with r_x=0;r_w=1} ~dst:screen ~dst_rect:vrect1 (); blit_surface ~src:whitev ~src_rect:{vrect2 with r_x=0;r_w=1} ~dst:screen ~dst_rect:vrect2 () | Save -> blit_surface ~dst:shadowh ~dst_rect:{hrect1 with r_y=0;r_h=1} ~src:screen ~src_rect:hrect1 (); blit_surface ~dst:shadowh ~dst_rect:{hrect2 with r_y=1;r_h=1} ~src:screen ~src_rect:hrect2 (); blit_surface ~dst:shadowv ~dst_rect:{vrect1 with r_x=0;r_w=1} ~src:screen ~src_rect:vrect1 (); blit_surface ~dst:shadowv ~dst_rect:{vrect2 with r_x=1;r_w=1} ~src:screen ~src_rect:vrect2 () | Restore -> blit_surface ~src:shadowh ~src_rect:{hrect1 with r_y=0;r_h=1} ~dst:screen ~dst_rect:hrect1 (); blit_surface ~src:shadowh ~src_rect:{hrect2 with r_y=1;r_h=1} ~dst:screen ~dst_rect:hrect2 (); blit_surface ~src:shadowv ~src_rect:{vrect1 with r_x=0;r_w=1} ~dst:screen ~dst_rect:vrect1 (); blit_surface ~src:shadowv ~src_rect:{vrect2 with r_x=1;r_w=1} ~dst:screen ~dst_rect:vrect2 () | Update -> List.iter (fun r -> Sdlvideo.update_rect ~rect:r screen) [hrect1;hrect2;vrect1;vrect2] ;; (* draw *) let redraw () = Printf.eprintf "Computing...%!"; draw screen (compute()); Sdlvideo.update_rect screen; Printf.eprintf "done.\n%!";; (* event loop for zooming into the picture *) let rezoom x y w = let deltas = ((float !n)/.(float w)) in ofx := (!ofx +. (float x)) *. deltas; ofy := (!ofy +. (float y)) *. deltas; scale := !scale *. deltas; redraw();; let reset () = scale:=1.; ofx:=0.; ofy:=0.;redraw();; let refine () = res:=!res*2; redraw ();; let unrefine () = res:=!res/2; redraw ();; let zoom_in () = rezoom (!n/4) (!n/4) (!n/2);; let zoom_out () = rezoom (-(!n/2)) (-(!n/2)) (!n*2);; let dump () = Printf.eprintf "Dumping image taken at ofx: %f ofy: %f scale: %f\n%!" !ofx !ofy !scale; Sdlvideo.save_BMP screen (Printf.sprintf "mandels-ofx-%f-ofy-%f-scale-%f.bmp" !ofx !ofy !scale);; (* encode state machine here *) let rec init () = match wait_event () with | KEYDOWN {keysym=k} -> (match k with KEY_PLUS -> let _ = zoom_in() in init () | KEY_MINUS -> let _ = zoom_out() in init () | KEY_r -> let _ = refine () in init () | KEY_u -> let _ = unrefine () in init () | KEY_c -> let _ = reset() in init () | KEY_d -> let _ = dump() in init () | KEY_q -> Sdl.quit | _ -> init () ) | MOUSEBUTTONDOWN {mbe_x=x;mbe_y=y} -> track_rect x y (x,y,0,0) | _ -> init () and track_rect x y (ox,oy,ow,oh) = match wait_event () with | MOUSEBUTTONUP {mbe_x=x';mbe_y=y'} -> let bx,by,w,h = (min x x'), (min y y'), (abs (x'-x)), (abs (y'-y)) in (rezoom bx by (min w h); init()) | MOUSEMOTION {mme_x=x';mme_y=y'} -> let bx,by,w,h = (min x x'), (min y y'), (abs (x'-x)), (abs (y'-y)) in (* restore old image if necessary *) if ow>0 & oh>0 then begin border Restore ox oy ow oh; border Update ox oy ow oh end; (* save image if necessary *) if w>0 & h>0 then begin border Save bx by w h; (* draw the border *) border Draw bx by w h; border Update bx by w h; end; track_rect x y (bx,by,w,h) | _ -> track_rect x y (ox,oy,ow,oh) ;; let _ = let getarg i = max 1 (int_of_string Sys.argv.(i)) in try ncores := getarg 1;Printf.eprintf "Setting nproc = %d \n%!" !ncores; chunksize := getarg 2;Printf.eprintf "Setting chunksize = %d \n%!" !chunksize with _ -> () ;; redraw();; init()