@@ -7,6 +7,8 @@ const pwidth = 800
7
7
8
8
const pheight = 600
9
9
10
+ const chunk_height = 2 // the image is recalculated in chunks, each chunk processed in a separate thread
11
+
10
12
const zoom_factor = 1.1
11
13
12
14
struct ViewRect {
@@ -17,62 +19,104 @@ mut:
17
19
y_max f64
18
20
}
19
21
22
+ fn (v &ViewRect) width () f64 {
23
+ return v.x_max - v.x_min
24
+ }
25
+
26
+ fn (v &ViewRect) height () f64 {
27
+ return v.y_max - v.y_min
28
+ }
29
+
20
30
struct AppState {
21
31
mut :
22
32
gg & gg.Context = 0
23
33
iidx int
24
- pixels [] u32 = [] u32 {len: pwidth * pheight}
25
- npixels [] u32 = [] u32 {len: pwidth * pheight} // all drawing happens here, results are copied at the end
34
+ pixels & u32 = unsafe { vcalloc ( pwidth * pheight * sizeof ( u32 )) }
35
+ npixels & u32 = unsafe { vcalloc ( pwidth * pheight * sizeof ( u32 )) } // all drawing happens here, results are swapped at the end
26
36
view ViewRect = ViewRect{- 2.7610033817025625 , 1.1788897130338223 , - 1.824584023871934 , 2.1153096311072788 }
27
37
ntasks int = runtime.nr_jobs ()
28
38
}
29
39
30
40
const colors = [gx.black, gx.blue, gx.red, gx.green, gx.yellow, gx.orange, gx.purple, gx.white,
31
- gx.indigo, gx.violet, gx.black]
41
+ gx.indigo, gx.violet, gx.black].map (u32 (it .abgr8 ()))
42
+
43
+ struct MandelChunk {
44
+ cview ViewRect
45
+ ymin f64
46
+ ymax f64
47
+ }
32
48
33
49
fn (mut state AppState) update () {
34
- mut sw := time.new_stopwatch ()
50
+ mut chunk_channel := chan MandelChunk{cap: state.ntasks}
51
+ mut chunk_ready_channel := chan bool {cap: 1000 }
52
+ mut threads := []thread{cap: state.ntasks}
53
+ defer {
54
+ chunk_channel.close ()
55
+ threads.wait ()
56
+ }
57
+ for t in 0 .. state.ntasks {
58
+ threads << go state.worker (t, chunk_channel, chunk_ready_channel)
59
+ }
60
+ //
35
61
mut oview := ViewRect{}
62
+ mut sw := time.new_stopwatch ()
36
63
for {
37
64
sw.restart ()
38
65
cview := state.view
39
66
if oview == cview {
40
67
time.sleep (5 * time.millisecond)
41
68
continue
42
69
}
43
- sheight := pheight / state.ntasks
44
- mut threads := []thread{}
45
- for start := 0 ; start < pheight; start + = sheight {
46
- threads << go state.recalc_lines (cview, start, start + sheight)
70
+ // schedule chunks, describing the work:
71
+ mut nchunks := 0
72
+ for start := 0 ; start < pheight; start + = chunk_height {
73
+ chunk_channel < - MandelChunk{
74
+ cview: cview
75
+ ymin: start
76
+ ymax: start + chunk_height
77
+ }
78
+ nchunks++
79
+ }
80
+ // wait for all chunks to be processed:
81
+ for _ in 0 .. nchunks {
82
+ _ := < - chunk_ready_channel
47
83
}
48
- threads. wait ()
49
- state.pixels = state.npixels
84
+ // everything is done, swap the buffer pointers
85
+ state.pixels, state. npixels = state.npixels, state.pixels
50
86
println ('$state.ntasks threads; $sw.elapsed ().milliseconds() ms / frame' )
51
87
oview = cview
52
88
}
53
89
}
54
90
55
- fn (mut state AppState) recalc_lines (cview ViewRect, ymin f64 , ymax f64 ) {
56
- for y_pixel := ymin; y_pixel < ymax && y_pixel < pheight; y_pixel++ {
57
- y0 := (y_pixel / pheight) * (cview.y_max - cview.y_min) + cview.y_min
58
- for x_pixel := 0.0 ; x_pixel < pwidth; x_pixel++ {
59
- x0 := (x_pixel / pwidth) * (cview.x_max - cview.x_min) + cview.x_min
60
- mut x, mut y := x0 , y0
61
- mut iter := 0
62
- for ; iter < 80 ; iter++ {
63
- x , y = x * x - y * y + x0 , 2 * x * y + y0
64
- if x * x + y * y > 4 {
65
- break
91
+ fn (mut state AppState) worker (id int , input chan MandelChunk, ready chan bool ) {
92
+ for {
93
+ chunk := < - input or { break }
94
+ yscale := chunk.cview.height () / pheight
95
+ xscale := chunk.cview.width () / pwidth
96
+ for y_pixel := chunk.ymin; y_pixel < chunk.ymax && y_pixel < pheight; y_pixel++ {
97
+ y0 := y_pixel * yscale + chunk.cview.y_min
98
+ for x_pixel := 0.0 ; x_pixel < pwidth; x_pixel++ {
99
+ x0 := x_pixel * xscale + chunk.cview.x_min
100
+ mut x, mut y := x0 , y0
101
+ mut iter := 0
102
+ for ; iter < 80 ; iter++ {
103
+ x , y = x * x - y * y + x0 , 2 * x * y + y0
104
+ if x * x + y * y > 4 {
105
+ break
106
+ }
107
+ }
108
+ unsafe {
109
+ state.npixels[int (y_pixel * pwidth) + int (x_pixel)] = colors[iter & 7 ]
66
110
}
67
111
}
68
- state.npixels[int (y_pixel) * pwidth + int (x_pixel)] = u32 (colors[iter % 8 ].abgr8 ())
69
112
}
113
+ ready < - true
70
114
}
71
115
}
72
116
73
117
fn (mut state AppState) draw () {
74
118
mut istream_image := state.gg.get_cached_image_by_idx (state.iidx)
75
- istream_image.update_pixel_data (& state.pixels[ 0 ] )
119
+ istream_image.update_pixel_data (state.pixels)
76
120
size := gg.window_size ()
77
121
state.gg.draw_image (0 , 0 , size.width, size.height, istream_image)
78
122
}
@@ -110,17 +154,17 @@ fn graphics_frame(mut state AppState) {
110
154
fn graphics_click (x f32 , y f32 , btn gg.MouseButton, mut state AppState) {
111
155
if btn == .right {
112
156
size := gg.window_size ()
113
- m_x := (x / size.width) * ( state.view.x_max - state.view.x_min ) + state.view.x_min
114
- m_y := (y / size.height) * ( state.view.y_max - state.view.y_min ) + state.view.y_min
157
+ m_x := (x / size.width) * state.view.width ( ) + state.view.x_min
158
+ m_y := (y / size.height) * state.view.height ( ) + state.view.y_min
115
159
state.center (m_x, m_y)
116
160
}
117
161
}
118
162
119
163
fn graphics_move (x f32 , y f32 , mut state AppState) {
120
164
if state.gg.mouse_buttons.has (.left) {
121
165
size := gg.window_size ()
122
- d_x := (f64 (state.gg.mouse_dx) / size.width) * ( state.view.x_max - state.view.x_min )
123
- d_y := (f64 (state.gg.mouse_dy) / size.height) * ( state.view.y_max - state.view.y_min )
166
+ d_x := (f64 (state.gg.mouse_dx) / size.width) * state.view.width ( )
167
+ d_y := (f64 (state.gg.mouse_dy) / size.height) * state.view.height ( )
124
168
state.view.x_min - = d_x
125
169
state.view.x_max - = d_x
126
170
state.view.y_min - = d_y
@@ -133,8 +177,8 @@ fn graphics_scroll(e &gg.Event, mut state AppState) {
133
177
}
134
178
135
179
fn graphics_keydown (code gg.KeyCode, mod gg.Modifier, mut state AppState) {
136
- s_x := ( state.view.x_max - state.view.x_min ) / 5
137
- s_y := ( state.view.y_max - state.view.y_min ) / 5
180
+ s_x := state.view.width ( ) / 5
181
+ s_y := state.view.height ( ) / 5
138
182
// movement
139
183
mut d_x, mut d_y := 0.0 , 0.0
140
184
if code == .enter {
0 commit comments