1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library
2*bf2c3715SXin Li // for linear algebra.
3*bf2c3715SXin Li //
4*bf2c3715SXin Li // Copyright (C) 2008 Benoit Jacob <[email protected]>
5*bf2c3715SXin Li //
6*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla
7*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed
8*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9*bf2c3715SXin Li
10*bf2c3715SXin Li #include "mandelbrot.h"
11*bf2c3715SXin Li #include <iostream>
12*bf2c3715SXin Li #include<QtGui/QPainter>
13*bf2c3715SXin Li #include<QtGui/QImage>
14*bf2c3715SXin Li #include<QtGui/QMouseEvent>
15*bf2c3715SXin Li #include<QtCore/QTime>
16*bf2c3715SXin Li
resizeEvent(QResizeEvent *)17*bf2c3715SXin Li void MandelbrotWidget::resizeEvent(QResizeEvent *)
18*bf2c3715SXin Li {
19*bf2c3715SXin Li if(size < width() * height())
20*bf2c3715SXin Li {
21*bf2c3715SXin Li std::cout << "reallocate buffer" << std::endl;
22*bf2c3715SXin Li size = width() * height();
23*bf2c3715SXin Li if(buffer) delete[]buffer;
24*bf2c3715SXin Li buffer = new unsigned char[4*size];
25*bf2c3715SXin Li }
26*bf2c3715SXin Li }
27*bf2c3715SXin Li
28*bf2c3715SXin Li template<typename T> struct iters_before_test { enum { ret = 8 }; };
29*bf2c3715SXin Li template<> struct iters_before_test<double> { enum { ret = 16 }; };
30*bf2c3715SXin Li
render(int img_width,int img_height)31*bf2c3715SXin Li template<typename Real> void MandelbrotThread::render(int img_width, int img_height)
32*bf2c3715SXin Li {
33*bf2c3715SXin Li enum { packetSize = Eigen::internal::packet_traits<Real>::size }; // number of reals in a Packet
34*bf2c3715SXin Li typedef Eigen::Array<Real, packetSize, 1> Packet; // wrap a Packet as a vector
35*bf2c3715SXin Li
36*bf2c3715SXin Li enum { iters_before_test = iters_before_test<Real>::ret };
37*bf2c3715SXin Li max_iter = (max_iter / iters_before_test) * iters_before_test;
38*bf2c3715SXin Li const int alignedWidth = (img_width/packetSize)*packetSize;
39*bf2c3715SXin Li unsigned char *const buffer = widget->buffer;
40*bf2c3715SXin Li const double xradius = widget->xradius;
41*bf2c3715SXin Li const double yradius = xradius * img_height / img_width;
42*bf2c3715SXin Li const int threadcount = widget->threadcount;
43*bf2c3715SXin Li typedef Eigen::Array<Real, 2, 1> Vector2;
44*bf2c3715SXin Li Vector2 start(widget->center.x() - widget->xradius, widget->center.y() - yradius);
45*bf2c3715SXin Li Vector2 step(2*widget->xradius/img_width, 2*yradius/img_height);
46*bf2c3715SXin Li total_iter = 0;
47*bf2c3715SXin Li
48*bf2c3715SXin Li for(int y = id; y < img_height; y += threadcount)
49*bf2c3715SXin Li {
50*bf2c3715SXin Li int pix = y * img_width;
51*bf2c3715SXin Li
52*bf2c3715SXin Li // for each pixel, we're going to do the iteration z := z^2 + c where z and c are complex numbers,
53*bf2c3715SXin Li // starting with z = c = complex coord of the pixel. pzi and pzr denote the real and imaginary parts of z.
54*bf2c3715SXin Li // pci and pcr denote the real and imaginary parts of c.
55*bf2c3715SXin Li
56*bf2c3715SXin Li Packet pzi_start, pci_start;
57*bf2c3715SXin Li for(int i = 0; i < packetSize; i++) pzi_start[i] = pci_start[i] = start.y() + y * step.y();
58*bf2c3715SXin Li
59*bf2c3715SXin Li for(int x = 0; x < alignedWidth; x += packetSize, pix += packetSize)
60*bf2c3715SXin Li {
61*bf2c3715SXin Li Packet pcr, pci = pci_start, pzr, pzi = pzi_start, pzr_buf;
62*bf2c3715SXin Li for(int i = 0; i < packetSize; i++) pzr[i] = pcr[i] = start.x() + (x+i) * step.x();
63*bf2c3715SXin Li
64*bf2c3715SXin Li // do the iterations. Every iters_before_test iterations we check for divergence,
65*bf2c3715SXin Li // in which case we can stop iterating.
66*bf2c3715SXin Li int j = 0;
67*bf2c3715SXin Li typedef Eigen::Matrix<int, packetSize, 1> Packeti;
68*bf2c3715SXin Li Packeti pix_iter = Packeti::Zero(), // number of iteration per pixel in the packet
69*bf2c3715SXin Li pix_dont_diverge; // whether or not each pixel has already diverged
70*bf2c3715SXin Li do
71*bf2c3715SXin Li {
72*bf2c3715SXin Li for(int i = 0; i < iters_before_test/4; i++) // peel the inner loop by 4
73*bf2c3715SXin Li {
74*bf2c3715SXin Li # define ITERATE \
75*bf2c3715SXin Li pzr_buf = pzr; \
76*bf2c3715SXin Li pzr = pzr.square(); \
77*bf2c3715SXin Li pzr -= pzi.square(); \
78*bf2c3715SXin Li pzr += pcr; \
79*bf2c3715SXin Li pzi = (2*pzr_buf)*pzi; \
80*bf2c3715SXin Li pzi += pci;
81*bf2c3715SXin Li ITERATE ITERATE ITERATE ITERATE
82*bf2c3715SXin Li }
83*bf2c3715SXin Li pix_dont_diverge = ((pzr.square() + pzi.square())
84*bf2c3715SXin Li .eval() // temporary fix as what follows is not yet vectorized by Eigen
85*bf2c3715SXin Li <= Packet::Constant(4))
86*bf2c3715SXin Li // the 4 here is not a magic value, it's a math fact that if
87*bf2c3715SXin Li // the square modulus is >4 then divergence is inevitable.
88*bf2c3715SXin Li .template cast<int>();
89*bf2c3715SXin Li pix_iter += iters_before_test * pix_dont_diverge;
90*bf2c3715SXin Li j++;
91*bf2c3715SXin Li total_iter += iters_before_test * packetSize;
92*bf2c3715SXin Li }
93*bf2c3715SXin Li while(j < max_iter/iters_before_test && pix_dont_diverge.any()); // any() is not yet vectorized by Eigen
94*bf2c3715SXin Li
95*bf2c3715SXin Li // compute pixel colors
96*bf2c3715SXin Li for(int i = 0; i < packetSize; i++)
97*bf2c3715SXin Li {
98*bf2c3715SXin Li buffer[4*(pix+i)] = 255*pix_iter[i]/max_iter;
99*bf2c3715SXin Li buffer[4*(pix+i)+1] = 0;
100*bf2c3715SXin Li buffer[4*(pix+i)+2] = 0;
101*bf2c3715SXin Li }
102*bf2c3715SXin Li }
103*bf2c3715SXin Li
104*bf2c3715SXin Li // if the width is not a multiple of packetSize, fill the remainder in black
105*bf2c3715SXin Li for(int x = alignedWidth; x < img_width; x++, pix++)
106*bf2c3715SXin Li buffer[4*pix] = buffer[4*pix+1] = buffer[4*pix+2] = 0;
107*bf2c3715SXin Li }
108*bf2c3715SXin Li return;
109*bf2c3715SXin Li }
110*bf2c3715SXin Li
run()111*bf2c3715SXin Li void MandelbrotThread::run()
112*bf2c3715SXin Li {
113*bf2c3715SXin Li setTerminationEnabled(true);
114*bf2c3715SXin Li double resolution = widget->xradius*2/widget->width();
115*bf2c3715SXin Li max_iter = 128;
116*bf2c3715SXin Li if(resolution < 1e-4f) max_iter += 128 * ( - 4 - std::log10(resolution));
117*bf2c3715SXin Li int img_width = widget->width()/widget->draft;
118*bf2c3715SXin Li int img_height = widget->height()/widget->draft;
119*bf2c3715SXin Li single_precision = resolution > 1e-7f;
120*bf2c3715SXin Li
121*bf2c3715SXin Li if(single_precision)
122*bf2c3715SXin Li render<float>(img_width, img_height);
123*bf2c3715SXin Li else
124*bf2c3715SXin Li render<double>(img_width, img_height);
125*bf2c3715SXin Li }
126*bf2c3715SXin Li
paintEvent(QPaintEvent *)127*bf2c3715SXin Li void MandelbrotWidget::paintEvent(QPaintEvent *)
128*bf2c3715SXin Li {
129*bf2c3715SXin Li static float max_speed = 0;
130*bf2c3715SXin Li long long total_iter = 0;
131*bf2c3715SXin Li
132*bf2c3715SXin Li QTime time;
133*bf2c3715SXin Li time.start();
134*bf2c3715SXin Li for(int th = 0; th < threadcount; th++)
135*bf2c3715SXin Li threads[th]->start(QThread::LowPriority);
136*bf2c3715SXin Li for(int th = 0; th < threadcount; th++)
137*bf2c3715SXin Li {
138*bf2c3715SXin Li threads[th]->wait();
139*bf2c3715SXin Li total_iter += threads[th]->total_iter;
140*bf2c3715SXin Li }
141*bf2c3715SXin Li int elapsed = time.elapsed();
142*bf2c3715SXin Li
143*bf2c3715SXin Li if(draft == 1)
144*bf2c3715SXin Li {
145*bf2c3715SXin Li float speed = elapsed ? float(total_iter)*1000/elapsed : 0;
146*bf2c3715SXin Li max_speed = std::max(max_speed, speed);
147*bf2c3715SXin Li std::cout << threadcount << " threads, "
148*bf2c3715SXin Li << elapsed << " ms, "
149*bf2c3715SXin Li << speed << " iters/s (max " << max_speed << ")" << std::endl;
150*bf2c3715SXin Li int packetSize = threads[0]->single_precision
151*bf2c3715SXin Li ? int(Eigen::internal::packet_traits<float>::size)
152*bf2c3715SXin Li : int(Eigen::internal::packet_traits<double>::size);
153*bf2c3715SXin Li setWindowTitle(QString("resolution ")+QString::number(xradius*2/width(), 'e', 2)
154*bf2c3715SXin Li +QString(", %1 iterations per pixel, ").arg(threads[0]->max_iter)
155*bf2c3715SXin Li +(threads[0]->single_precision ? QString("single ") : QString("double "))
156*bf2c3715SXin Li +QString("precision, ")
157*bf2c3715SXin Li +(packetSize==1 ? QString("no vectorization")
158*bf2c3715SXin Li : QString("vectorized (%1 per packet)").arg(packetSize)));
159*bf2c3715SXin Li }
160*bf2c3715SXin Li
161*bf2c3715SXin Li QImage image(buffer, width()/draft, height()/draft, QImage::Format_RGB32);
162*bf2c3715SXin Li QPainter painter(this);
163*bf2c3715SXin Li painter.drawImage(QPoint(0, 0), image.scaled(width(), height()));
164*bf2c3715SXin Li
165*bf2c3715SXin Li if(draft>1)
166*bf2c3715SXin Li {
167*bf2c3715SXin Li draft /= 2;
168*bf2c3715SXin Li setWindowTitle(QString("recomputing at 1/%1 resolution...").arg(draft));
169*bf2c3715SXin Li update();
170*bf2c3715SXin Li }
171*bf2c3715SXin Li }
172*bf2c3715SXin Li
mousePressEvent(QMouseEvent * event)173*bf2c3715SXin Li void MandelbrotWidget::mousePressEvent(QMouseEvent *event)
174*bf2c3715SXin Li {
175*bf2c3715SXin Li if( event->buttons() & Qt::LeftButton )
176*bf2c3715SXin Li {
177*bf2c3715SXin Li lastpos = event->pos();
178*bf2c3715SXin Li double yradius = xradius * height() / width();
179*bf2c3715SXin Li center = Eigen::Vector2d(center.x() + (event->pos().x() - width()/2) * xradius * 2 / width(),
180*bf2c3715SXin Li center.y() + (event->pos().y() - height()/2) * yradius * 2 / height());
181*bf2c3715SXin Li draft = 16;
182*bf2c3715SXin Li for(int th = 0; th < threadcount; th++)
183*bf2c3715SXin Li threads[th]->terminate();
184*bf2c3715SXin Li update();
185*bf2c3715SXin Li }
186*bf2c3715SXin Li }
187*bf2c3715SXin Li
mouseMoveEvent(QMouseEvent * event)188*bf2c3715SXin Li void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event)
189*bf2c3715SXin Li {
190*bf2c3715SXin Li QPoint delta = event->pos() - lastpos;
191*bf2c3715SXin Li lastpos = event->pos();
192*bf2c3715SXin Li if( event->buttons() & Qt::LeftButton )
193*bf2c3715SXin Li {
194*bf2c3715SXin Li double t = 1 + 5 * double(delta.y()) / height();
195*bf2c3715SXin Li if(t < 0.5) t = 0.5;
196*bf2c3715SXin Li if(t > 2) t = 2;
197*bf2c3715SXin Li xradius *= t;
198*bf2c3715SXin Li draft = 16;
199*bf2c3715SXin Li for(int th = 0; th < threadcount; th++)
200*bf2c3715SXin Li threads[th]->terminate();
201*bf2c3715SXin Li update();
202*bf2c3715SXin Li }
203*bf2c3715SXin Li }
204*bf2c3715SXin Li
main(int argc,char * argv[])205*bf2c3715SXin Li int main(int argc, char *argv[])
206*bf2c3715SXin Li {
207*bf2c3715SXin Li QApplication app(argc, argv);
208*bf2c3715SXin Li MandelbrotWidget w;
209*bf2c3715SXin Li w.show();
210*bf2c3715SXin Li return app.exec();
211*bf2c3715SXin Li }
212*bf2c3715SXin Li
213*bf2c3715SXin Li #include "mandelbrot.moc"
214