summaryrefslogtreecommitdiff
path: root/julia/julia.cu
blob: ed7b00a192f430599ab1542e97daf0a425c8c434 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <cstdint>
#include <cstdio>
#include <cstdlib>

struct Pixel {
  uint8_t r, g, b;
};

struct Complex {
  float r, i;

  __device__ float norm2() const { return r * r + i * i; }
};

__device__ Complex operator*(Complex a, Complex b) {
  return Complex{(a.r * b.r) - (a.i * b.i), (a.i * b.r) + (a.r * b.i)};
}

__device__ Complex operator+(Complex a, Complex b) {
  return Complex{a.r + b.r, a.i + b.i};
}

__device__ int julia(int width, int height, int x, int y) {
  constexpr float scale = 1.5;
  constexpr int   N     = 200;

  const float jx = scale * (width / 2 - x) / (width / 2);
  const float jy = scale * (height / 2 - y) / (height / 2);

  const Complex c{-0.8, 0.156};
  Complex       a{jx, jy};

  for (int i = 0; i < N; ++i) {
    a = a * a + c;
    if (a.norm2() > 1000) {
      return 0;
    }
  }
  return 1;
}

__global__ void juliaMain(Pixel* image) {
  const int x = blockIdx.x;
  const int y = blockIdx.y;

  constexpr Pixel background{41, 95, 152};
  constexpr Pixel juliaColour{228, 192, 135};

  const Pixel pixel =
      julia(gridDim.x, gridDim.y, x, y) == 1 ? juliaColour : background;

  image[y * gridDim.x + x] = pixel;
}

bool write_pbm(const Pixel* image, int width, int height, const char* path) {
  const size_t num_pixels = width * height;

  FILE* file = fopen(path, "wb");
  if (!file) {
    return false;
  }

  fprintf(file, "P6\n%d %d\n255\n", width, height);
  if (fwrite(image, sizeof(Pixel), num_pixels, file) != num_pixels) {
    fclose(file);
    return false;
  }

  fclose(file);
  return true;
}

int main(int argc, const char** argv) {
  const int width  = argc > 1 ? atoi(argv[1]) : 1920;
  const int height = argc > 2 ? atoi(argv[2]) : 1080;

  bool success = false;

  const dim3 dim(width, height);
  const int  image_size_bytes = width * height * sizeof(Pixel);
  auto       image_host       = new Pixel[width * height];
  Pixel*     image_dev        = nullptr;

  if (cudaMalloc(&image_dev, image_size_bytes) != cudaSuccess) {
    goto cleanup;
  }

  juliaMain<<<dim, 1>>>(image_dev);

  if (cudaMemcpy(
          image_host, image_dev, image_size_bytes, cudaMemcpyDeviceToHost) !=
      cudaSuccess) {
    goto cleanup;
  }

  if (!write_pbm(image_host, width, height, "julia.pbm")) {
    goto cleanup;
  }

  success = true;

cleanup:
  delete[] image_host;
  if (image_dev) {
    cudaFree(image_dev);
  }
  return success ? 0 : 1;
}