1 | #include "ZydecoCommon.hpp" |
2 | #include "Mandelbrot.hpp" |
3 | #include "IEventHandler.hpp" |
4 | #include "IWindow.hpp" |
5 | #include "IEventMouseSubscriber.hpp" |
6 | #include "GLRenderObjectFractal.hpp" |
7 | #include "GLProgram.hpp" |
8 | #include "GLUniformUploader.hpp" |
9 | #include "GLTexture.hpp" |
10 |
|
11 | static Logger LOGGER("Mandelbrot"); |
12 |
|
13 |
|
14 | ldvec3 ComplexSquare(ldvec3 in) |
15 | { |
16 | long double real_part = in.x*in.x - in.y*in.y; |
17 | long double complex_part = 2.*in.x*in.y; |
18 | return ldvec3{real_part, complex_part, in.z}; |
19 | } |
20 |
|
21 |
|
22 | Mandelbrot::Mandelbrot(IEventHandler& r_event_handler, IWindow& r_window, MandelbrotSettings *p_settings): |
23 | m_rEventHandler(r_event_handler), |
24 | m_rWindow(r_window), |
25 | m_pSettings(p_settings) |
26 | { |
27 | r_event_handler.RegisterMouseEventSubscriber(this); |
28 |
|
29 | m_windowWidth = r_window.GetWidth(); |
30 | m_windowHeight = r_window.GetHeight(); |
31 |
|
32 |
|
33 | m_pTexture = new GLTexture(GL_RGBA32F, nullptr, m_windowWidth, m_windowHeight); |
34 |
|
35 | m_pComputeUniformUploader = new GLUniformUploader(); |
36 | m_pComputeUniformUploader->AssignUniformPointer<1, int>("texture0", {new int {0}}); |
37 | m_pComputeUniformUploader->AssignUniformPointer<2, int>("screensize", {&m_windowWidth, &m_windowHeight}); |
38 | m_pComputeUniformUploader->AssignUniformPointer<2, double>("offset", {&m_pSettings->pos_x, &m_pSettings->pos_y}); |
39 | m_pComputeUniformUploader->AssignUniformPointer<1, double>("zoom", {&m_pSettings->zoom}); |
40 | m_pComputeUniformUploader->AssignUniformPointer<1, float>("z", {&m_pSettings->param_z}); |
41 | m_pComputeUniformUploader->AssignUniformPointer<1, float>("discard_threshold", {&m_pSettings->discard_threshold}); |
42 | m_pComputeUniformUploader->AssignUniformPointer<1, int>("current_iteration", {&m_pSettings->current_iteration}); |
43 | m_pComputeUniformUploader->AssignUniformPointer<1, int>("it_steps", {&m_pSettings->adjusted_iteration_step}); |
44 | m_pComputeUniformUploader->AssignUniformPointer<1, int>("enable_interlacing", {&m_pSettings->do_interlacing}); |
45 | m_pComputeUniformUploader->AssignUniformPointer<1, int>("interlace_layer", {&m_pSettings->interlace_layer}); |
46 |
|
47 | // m_pComputeUniformUploader->AssignUniformPointer<1, int>("first_interlace", {&m_pSettings->first_interlace}); |
48 |
|
49 | m_pRenderObject = new GLRenderObjectFractal {}; |
50 | m_pRenderObject->AddTexture(0, m_pTexture); |
51 | // m_pRenderObject->AssignUniformPointer<1, int>("texture0", {new int {0}}); |
52 | m_pRenderObject->AssignUniformPointer<1, int>("it_count", {&m_pSettings->adjusted_iteration_step}); |
53 | // m_pRenderObject->AssignUniformPointer<2, int>("screensize", {&m_windowWidth, &m_windowHeight}); |
54 | // m_pRenderObject->AssignUniformPointer<1, double>("zoom", {&m_pSettings->zoom}); |
55 | m_pRenderObject->AssignUniformPointer<1, float>("brightness", {&m_pSettings->brightness}); |
56 | m_pRenderObject->AssignUniformPointer<1, int>("enable_interlacing", {&m_pSettings->do_interlacing}); |
57 | m_pRenderObject->AssignUniformPointer<1, int>("interlace_layer", {&m_pSettings->interlace_layer}); |
58 | m_pRenderObject->AssignUniformPointer<1, int>("first_interlace", {&m_pSettings->first_interlace}); |
59 | } |
60 |
|
61 | Mandelbrot::~Mandelbrot() |
62 | { |
63 | delete m_pRenderObject; |
64 | delete m_pTexture; |
65 | } |
66 |
|
67 | bool Mandelbrot::Update() |
68 | { |
69 | uint64_t updated_window_width = m_rWindow.GetWidth(); |
70 | uint64_t updated_window_height = m_rWindow.GetHeight(); |
71 | if (updated_window_width != m_windowWidth || updated_window_height != m_windowHeight) |
72 | { |
73 | m_windowWidth = updated_window_width; |
74 | m_windowHeight = updated_window_height; |
75 | m_pSettings->current_iteration = 0; |
76 | m_pSettings->interlace_layer = 0; |
77 | m_pSettings->first_interlace = 1; |
78 | m_pSettings->restart = false; |
79 | // using nullptr causes openGL to zero-reallocate texture with new width/height |
80 | m_pTexture->SetDataSourceAndReload(nullptr, m_windowWidth, m_windowHeight); |
81 | } |
82 | else if (m_pSettings->restart) |
83 | { |
84 | m_pSettings->current_iteration = 0; |
85 | m_pSettings->interlace_layer = 0; |
86 | m_pSettings->first_interlace = 1; |
87 | } |
88 |
|
89 | if (m_pSettings->current_iteration >= m_pSettings->adjusted_iteration_step) { return false; } |
90 |
|
91 | m_pSettings->adjusted_iteration_step = m_pSettings->iteration_step * log2(2.0 + 1./m_pSettings->zoom); |
92 |
|
93 |
|
94 | bool waiting = false; |
95 | if (m_glSyncObject != nullptr) |
96 | { |
97 | int previous_invocation_finished = 0; |
98 | glGetSynciv(m_glSyncObject, GL_SYNC_STATUS, 1, nullptr, &previous_invocation_finished); |
99 | if (previous_invocation_finished == GL_SIGNALED) |
100 | { |
101 | waiting = false; |
102 | glDeleteSync(m_glSyncObject); |
103 | m_glSyncObject = nullptr; |
104 | } |
105 | else |
106 | { |
107 | waiting = true; |
108 | } |
109 | } |
110 |
|
111 |
|
112 | if (!waiting) |
113 | { |
114 | if (m_pSettings->do_interlacing) |
115 | { |
116 | if (m_pSettings->interlace_layer == 7) |
117 | { |
118 | m_pSettings->current_iteration += m_pSettings->adjusted_iteration_step; |
119 | m_pSettings->interlace_layer = 1; |
120 | m_pSettings->first_interlace = 0; |
121 | //if (m_pSettings->current_iteration >= m_pSettings->iteration_count) { return false; } |
122 | } |
123 | else |
124 | { |
125 | m_pSettings->interlace_layer++; |
126 | } |
127 | } |
128 | else |
129 | { |
130 | m_pSettings->interlace_layer = 1; |
131 | m_pSettings->first_interlace = 1; |
132 | } |
133 |
|
134 | if (m_pSettings->restart) |
135 | { |
136 | m_pSettings->restart = false; |
137 | // m_pTexture->ReloadFromDataSource(); |
138 | m_pTexture->SetDataSourceAndReload(nullptr, m_windowWidth, m_windowHeight); |
139 | } |
140 |
|
141 | LOGGER.Log(Logger::INFO, "Dispatching compute (interlace layer {}, {}x{})", m_pSettings->interlace_layer, m_windowWidth, m_windowHeight); |
142 |
|
143 | uint64_t program = GLProgram::GetGLProgram("FractalCompute")->GetGLProgramID(); |
144 |
|
145 | glUseProgram(program); |
146 | assert(glIsProgram(program)); |
147 |
|
148 | m_pTexture->BindAsImage(0); |
149 | m_pComputeUniformUploader->UploadUniforms(program); |
150 |
|
151 | glDispatchCompute((m_windowWidth+31)/32, (m_windowHeight+31)/32, 1); |
152 |
|
153 | if (!m_pSettings->do_interlacing) |
154 | { |
155 | m_pSettings->current_iteration += m_pSettings->adjusted_iteration_step; |
156 | } |
157 |
|
158 | glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
159 | } |
160 |
|
161 | return false; |
162 | } |
163 |
|
164 | void Mandelbrot::OnMouseLeftDownEvent(uint64_t x, uint64_t y) |
165 | { |
166 | m_mouseDown = true; |
167 | } |
168 |
|
169 | void Mandelbrot::OnMouseLeftUpEvent(uint64_t x, uint64_t y) |
170 | { |
171 | m_mouseDown = false; |
172 | } |
173 |
|
174 | void Mandelbrot::OnMouseMiddleDownEvent(uint64_t x, uint64_t y) |
175 | { |
176 |
|
177 | } |
178 |
|
179 | void Mandelbrot::OnMouseMiddleUpEvent(uint64_t x, uint64_t y) |
180 | { |
181 |
|
182 | } |
183 |
|
184 | void Mandelbrot::OnMouseRightDownEvent(uint64_t x, uint64_t y) |
185 | { |
186 |
|
187 | } |
188 |
|
189 | void Mandelbrot::OnMouseRightUpEvent(uint64_t x, uint64_t y) |
190 | { |
191 |
|
192 | } |
193 |
|
194 | void Mandelbrot::OnMouseMoveEvent(uint64_t x, uint64_t y, int64_t dx, int64_t dy) |
195 | { |
196 | m_mousex = x; |
197 | m_mousey = y; |
198 |
|
199 | if (m_mouseDown) { |
200 | m_pSettings->pos_x += static_cast<long double>(dx) * m_pSettings->zoom; |
201 | m_pSettings->pos_y -= static_cast<long double>(dy) * m_pSettings->zoom; |
202 |
|
203 | m_pSettings->restart = true; |
204 |
|
205 | LOGGER.Log(Logger::INFO, "X, Y: <{}, {}>", m_pSettings->pos_x, m_pSettings->pos_y); |
206 | } |
207 | } |
208 |
|
209 | void Mandelbrot::OnMouseWheelScrollEvent(int64_t dx, int64_t dy) |
210 | { |
211 | float factor = static_cast<long double>(dy) / 1'000'000'000.; |
212 |
|
213 | double old_view_width = (long double)m_windowWidth * m_pSettings->zoom; |
214 | double old_view_height = (long double)m_windowHeight * m_pSettings->zoom; |
215 |
|
216 | if (dy > 0) |
217 | { |
218 | m_pSettings->zoom /= factor; |
219 | } |
220 | else if (dy < 0) |
221 | { |
222 | m_pSettings->zoom *= -factor; |
223 | } |
224 |
|
225 | long double xrel = ((long double)m_mousex / (long double)m_windowWidth) - 0.5; |
226 | long double yrel = ((long double)m_mousey / (long double)m_windowHeight) - 0.5; |
227 |
|
228 | long double new_view_width = (long double)m_windowWidth * m_pSettings->zoom; |
229 | long double new_view_height = (long double)m_windowHeight * m_pSettings->zoom; |
230 |
|
231 | long double xdelta = xrel*(new_view_width - old_view_width); |
232 | long double ydelta = yrel*(new_view_height - old_view_height); |
233 |
|
234 | m_pSettings->pos_x += xdelta; |
235 | m_pSettings->pos_y -= ydelta; |
236 |
|
237 | m_pSettings->restart = true; |
238 |
|
239 | LOGGER.Log(Logger::INFO, "Zoom level: {}", m_pSettings->zoom); |
240 | } |
241 |
|