212 lines
5.2 KiB
C++
212 lines
5.2 KiB
C++
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <immintrin.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#pragma comment(lib, "user32.lib")
|
|
#pragma comment(lib, "gdi32.lib")
|
|
|
|
#define WIDTH 1280
|
|
#define HEIGHT 800
|
|
#define MAX_ITER 1000
|
|
|
|
static uint32_t framebuffer[WIDTH * HEIGHT];
|
|
|
|
static double centerX = -0.5;
|
|
static double centerY = 0.0;
|
|
static double scale = 3.0 / WIDTH;
|
|
|
|
static int dragging = 0;
|
|
static int lastX, lastY;
|
|
|
|
static int numThreads = std::thread::hardware_concurrency();
|
|
|
|
static void render_tile(int yStart, int yEnd)
|
|
{
|
|
__m256d two = _mm256_set1_pd(2.0);
|
|
__m256d four = _mm256_set1_pd(4.0);
|
|
|
|
for (int y = yStart; y < yEnd; y++)
|
|
{
|
|
double ci_scalar = centerY + (y - HEIGHT / 2.0) * scale;
|
|
__m256d ci = _mm256_set1_pd(ci_scalar);
|
|
|
|
for (int x = 0; x < WIDTH; x += 4)
|
|
{
|
|
__m256d cr = _mm256_set_pd(
|
|
centerX + (x + 3 - WIDTH / 2.0) * scale,
|
|
centerX + (x + 2 - WIDTH / 2.0) * scale,
|
|
centerX + (x + 1 - WIDTH / 2.0) * scale,
|
|
centerX + (x + 0 - WIDTH / 2.0) * scale
|
|
);
|
|
|
|
__m256d zr = _mm256_setzero_pd();
|
|
__m256d zi = _mm256_setzero_pd();
|
|
|
|
int iter[4] = { 0,0,0,0 };
|
|
|
|
for (int i = 0; i < MAX_ITER; i++)
|
|
{
|
|
__m256d zr2 = _mm256_mul_pd(zr, zr);
|
|
__m256d zi2 = _mm256_mul_pd(zi, zi);
|
|
__m256d mag = _mm256_add_pd(zr2, zi2);
|
|
|
|
__m256d mask = _mm256_cmp_pd(mag, four, _CMP_LT_OS);
|
|
if (_mm256_movemask_pd(mask) == 0)
|
|
break;
|
|
|
|
__m256d zrzi = _mm256_mul_pd(zr, zi);
|
|
|
|
zi = _mm256_add_pd(_mm256_mul_pd(two, zrzi), ci);
|
|
zr = _mm256_add_pd(_mm256_sub_pd(zr2, zi2), cr);
|
|
|
|
for (int k = 0; k < 4; k++)
|
|
if (iter[k] < MAX_ITER)
|
|
iter[k]++;
|
|
}
|
|
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
double smooth = iter[k];
|
|
if (iter[k] < MAX_ITER)
|
|
{
|
|
double zr_s = ((double*)&zr)[k];
|
|
double zi_s = ((double*)&zi)[k];
|
|
double mag = sqrt(zr_s * zr_s + zi_s * zi_s);
|
|
smooth = iter[k] + 1 - log2(log2(mag));
|
|
}
|
|
|
|
uint8_t c = (uint8_t)(255.0 * smooth / MAX_ITER);
|
|
framebuffer[y * WIDTH + x + k] =
|
|
(c << 16) | (c << 8) | c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void render_mandelbrot()
|
|
{
|
|
std::vector<std::thread> threads;
|
|
|
|
int tile = HEIGHT / numThreads;
|
|
|
|
for (int i = 0; i < numThreads; i++)
|
|
{
|
|
int yStart = i * tile;
|
|
int yEnd = (i == numThreads - 1) ? HEIGHT : yStart + tile;
|
|
threads.emplace_back(render_tile, yStart, yEnd);
|
|
}
|
|
|
|
for (auto& t : threads)
|
|
t.join();
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
scale *= (delta > 0) ? 0.8 : 1.25;
|
|
render_mandelbrot();
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
dragging = 1;
|
|
lastX = LOWORD(lParam);
|
|
lastY = HIWORD(lParam);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if (dragging)
|
|
{
|
|
int x = LOWORD(lParam);
|
|
int y = HIWORD(lParam);
|
|
|
|
centerX -= (x - lastX) * scale;
|
|
centerY -= (y - lastY) * scale;
|
|
|
|
lastX = x;
|
|
lastY = y;
|
|
|
|
render_mandelbrot();
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
dragging = 0;
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hwnd, &ps);
|
|
|
|
BITMAPINFO bmi = {};
|
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi.bmiHeader.biWidth = WIDTH;
|
|
bmi.bmiHeader.biHeight = -HEIGHT;
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
StretchDIBits(hdc,
|
|
0, 0, WIDTH, HEIGHT,
|
|
0, 0, WIDTH, HEIGHT,
|
|
framebuffer,
|
|
&bmi,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
|
|
{
|
|
render_mandelbrot();
|
|
|
|
WNDCLASS wc = {};
|
|
wc.lpfnWndProc = WndProc;
|
|
wc.hInstance = hInstance;
|
|
wc.lpszClassName = "MandelbrotSIMD";
|
|
|
|
RegisterClass(&wc);
|
|
|
|
HWND hwnd = CreateWindowEx(
|
|
0,
|
|
"MandelbrotSIMD",
|
|
"Mandelbrot Explorer (SIMD + Threads)",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
WIDTH, HEIGHT,
|
|
NULL, NULL, hInstance, NULL);
|
|
|
|
ShowWindow(hwnd, nCmdShow);
|
|
|
|
MSG msg;
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return 0;
|
|
}
|