Skip to content

Commit f262c96

Browse files
committed
Arbitrary precision in the CPU
1 parent 43e4423 commit f262c96

26 files changed

+1391
-4024
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ docs/manim/build.bat
1010
build-web
1111
*emsdk
1212
./emsdk
13+
boost_1_84_0/

_deps/glfw-src

Lines changed: 0 additions & 1 deletion
This file was deleted.

_deps/glm-src

Lines changed: 0 additions & 1 deletion
This file was deleted.

_deps/imgui-src

Lines changed: 0 additions & 1 deletion
This file was deleted.

dependencies/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ FetchContent_Declare(
88
GIT_TAG docking
99
)
1010

11+
12+
add_library(boost_headers INTERFACE)
13+
target_include_directories(boost_headers INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/boost_1_84_0")
14+
1115
if(NOT EMSCRIPTEN)
1216
FetchContent_Declare(
1317
glfw
@@ -43,7 +47,7 @@ add_library(learnopengl
4347
"learnopengl/src/shader.cpp"
4448
"learnopengl/include/shader/shader.h"
4549
"learnopengl/include/stb_image/stb_image.h"
46-
"learnopengl/src/stb_image.cpp" "learnopengl/include/camera/camera.h" "learnopengl/src/camera.cpp" "learnopengl/include/VAO/VAO.h" "learnopengl/include/VBO/VBO.h" "learnopengl/src/VAO.cpp" "learnopengl/src/VBO.cpp" "learnopengl/include/texture/texture.h" "learnopengl/src/texture.cpp" "learnopengl/include/stb_image/stb_image_write.h" "learnopengl/src/stb_image_write.cpp" "learnopengl/include/emscripten_browser_clipboard.h")
50+
"learnopengl/src/stb_image.cpp" "learnopengl/include/camera/camera.h" "learnopengl/src/camera.cpp" "learnopengl/include/VAO/VAO.h" "learnopengl/include/VBO/VBO.h" "learnopengl/src/VAO.cpp" "learnopengl/src/VBO.cpp" "learnopengl/include/texture/texture.h" "learnopengl/src/texture.cpp" "learnopengl/include/stb_image/stb_image_write.h" "learnopengl/src/stb_image_write.cpp" "learnopengl/include/emscripten_browser_clipboard.h" )
4751

4852
target_include_directories(learnopengl PUBLIC learnopengl/include)
4953
if(EMSCRIPTEN)

src/CMakeLists.txt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1+
add_executable(math-transpiler "generate_cpp_complex_functions.cpp")
2+
3+
set(TRANSPILED_MATH_H "${CMAKE_CURRENT_SOURCE_DIR}/glsl_generated/generated_big_math.h")
4+
set(TRANSPILED_MAPPER_CPP "${CMAKE_CURRENT_SOURCE_DIR}/glsl_generated/generated_math_mapper.cpp")
5+
set(MATH_GLSL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders/complex_functions.frag")
6+
7+
add_custom_command(
8+
OUTPUT ${TRANSPILED_MATH_H} ${TRANSPILED_MAPPER_CPP}
9+
COMMAND math-transpiler ${MATH_GLSL_SRC} "${CMAKE_CURRENT_SOURCE_DIR}/glsl_generated"
10+
DEPENDS math-transpiler ${MATH_GLSL_SRC}
11+
COMMENT "[Custom] Transpiling high-precision GLSL to C++"
12+
)
13+
114
set(SOURCE_FILES
215
"graphics/graphics.cpp"
316
"graphics/graphics.h"
4-
"types/types.h" "parser/tokenizer.h" "parser/checker.h" "parser/rpn.h" "parser/parser.h" "parser/checker.cpp" "parser/parser.cpp" "parser/rpn.cpp" "parser/tokenizer.cpp" "parser/simplifier.h" "parser/simplifier.cpp" "transformer/constant.h" "transformer/constant.cpp" "preprocessor/preprocessor.h" "preprocessor/string_builder.h" "transformer/operator.h" "transformer/operator.cpp" "transformer/transformer.h" "transformer/transformer.cpp" "preprocessor/string_builder.cpp" "preprocessor/preprocessor.cpp" "interactions/interactions.h" "interactions/interactions.cpp" "parser/validator.cpp" "parser/validator.h" "graphics/ui_init.h" "graphics/ui_init.cpp" "graphics/ui.h" "graphics/ui.cpp" "types/type_mapper.h" "types/type_mapper.cpp" "compiler/compiler_shader.h" "compiler/compiler_shader.cpp" "transformer/tree.h" "transformer/tree.cpp" "higher_order/derivative.h" "higher_order/higher_order.h" "higher_order/derivative.cpp" "higher_order/higher_order.cpp" "graphics/3d/mesh.h" "graphics/3d/mesh.cpp" "graphics/3d/camera_state.h" "graphics/3d/camera_state.cpp" "graphics/picker.cpp" "glad_include_guard.h" "interactions/export.h" "preprocessor/transpiler.cpp" "preprocessor/transpiler.h" "glsl_transpiled/glsl_big_number.h" "glsl_transpiled/glsl_types.h" "glsl_transpiled/glsl_constants.h" "glsl_transpiled/glsl_big_number.cpp" "high_precision/high_precision_constant.h" "high_precision/high_precision_constant.cpp")
17+
"types/types.h" "parser/tokenizer.h" "parser/checker.h" "parser/rpn.h" "parser/parser.h" "parser/checker.cpp" "parser/parser.cpp" "parser/rpn.cpp" "parser/tokenizer.cpp" "parser/simplifier.h" "parser/simplifier.cpp" "transformer/constant.h" "transformer/constant.cpp" "preprocessor/preprocessor.h" "preprocessor/string_builder.h" "transformer/operator.h" "transformer/operator.cpp" "transformer/transformer.h" "transformer/transformer.cpp" "preprocessor/string_builder.cpp" "preprocessor/preprocessor.cpp" "interactions/interactions.h" "interactions/interactions.cpp" "parser/validator.cpp" "parser/validator.h" "graphics/ui_init.h" "graphics/ui_init.cpp" "graphics/ui.h" "graphics/ui.cpp" "types/type_mapper.h" "types/type_mapper.cpp" "compiler/compiler_shader.h" "compiler/compiler_shader.cpp" "transformer/tree.h" "transformer/tree.cpp" "higher_order/derivative.h" "higher_order/higher_order.h" "higher_order/derivative.cpp" "higher_order/higher_order.cpp" "graphics/3d/mesh.h" "graphics/3d/mesh.cpp" "graphics/3d/camera_state.h" "graphics/3d/camera_state.cpp" "graphics/picker.cpp" "glad_include_guard.h" "interactions/export.h" "preprocessor/transpiler.cpp" "preprocessor/transpiler.h" "glsl_generated/types.h"
18+
"glsl_transpiled/glsl_big_number.h" "glsl_transpiled/glsl_types.h" "glsl_transpiled/glsl_constants.h" "glsl_transpiled/glsl_big_number.cpp" "high_precision/high_precision_constant.h" "high_precision/high_precision_constant.cpp" "cpu_drawing/cpu_render.h" "cpu_drawing/cpu_render.cpp")
519
set(SHADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/shaders")
620
set(HEADER_BUILD "${CMAKE_CURRENT_BINARY_DIR}/shaders/embedded_shaders.h")
721
set(HEADER_SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders/embedded_shaders.h")
@@ -31,7 +45,7 @@ add_custom_command(
3145
)
3246

3347

34-
add_executable(complex-plotter ${SOURCE_FILES} ${HEADER_SRC} "main.cpp" "interactions/export.cpp")
48+
add_executable(complex-plotter ${SOURCE_FILES} ${HEADER_SRC} "main.cpp" ${TRANSPILED_MAPPER_CPP})
3549

3650
if(NOT EMSCRIPTEN)
3751
find_package(OpenGL REQUIRED)
@@ -43,6 +57,7 @@ if(EMSCRIPTEN)
4357
glm::glm
4458
learnopengl
4559
imgui
60+
boost_headers
4661
)
4762
else()
4863
target_link_libraries(complex-plotter PRIVATE
@@ -52,6 +67,7 @@ else()
5267
OpenGL::GL
5368
learnopengl
5469
imgui
70+
boost_headers
5571
)
5672
endif()
5773

@@ -74,6 +90,7 @@ target_include_directories(complex-plotter PRIVATE
7490
${CMAKE_CURRENT_BINARY_DIR}
7591
"${CMAKE_CURRENT_SOURCE_DIR}"
7692
"${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/learnopengl/include"
93+
"${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/boost"
7794
"."
7895
)
7996

src/cpu_drawing/cpu_render.cpp

Whitespace-only changes.

src/cpu_drawing/cpu_render.h

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include <thread>
2+
#include <vector>
3+
#include <algorithm>
4+
#include <glsl_generated/generated_big_math.h>
5+
#include <glsl_generated/generated_math_mapper.h>
6+
#include <array>
7+
#include <interactions/interactions.h>
8+
#include <graphics/ui.h>
9+
#include <types/types.h>
10+
#include <types/type_mapper.h>
11+
#include <glm/common.hpp>
12+
13+
thread_local std::vector<big_vec2> reg_eval_stack(512);
14+
thread_local size_t reg_sp;
15+
thread_local big_vec2 reg_operand_a;
16+
thread_local big_vec2 reg_operand_b;
17+
thread_local big_vec2 reg_math_res;
18+
19+
void evaluate_ast_stack(
20+
const std::vector<TokenOperator>& tokens,
21+
const big_vec2& z_in,
22+
const CPU_Interpreter& interpreter,
23+
big_vec2& out_result
24+
) {
25+
reg_sp = 0;
26+
27+
for (const TokenOperator& token : tokens) {
28+
29+
const Operator curr_op = token.op;
30+
if (curr_op == Operator::VARIABLEZ) {
31+
reg_eval_stack[reg_sp] = z_in;
32+
reg_sp++;
33+
}
34+
else if (curr_op == Operator::CONSTANT) {
35+
const glm::vec2 original_vec = token.value;
36+
reg_eval_stack[reg_sp] = big_vec2(original_vec.x, original_vec.y);
37+
reg_sp++;
38+
}
39+
else if (token.arity == Arity::UNARY) {
40+
reg_sp--;
41+
reg_operand_a = reg_eval_stack[reg_sp];
42+
43+
reg_math_res = interpreter.unary_ops.at(token.op)(reg_operand_a);
44+
45+
reg_eval_stack[reg_sp] = reg_math_res;
46+
reg_sp++;
47+
}
48+
else if (token.arity == Arity::BINARY) {
49+
reg_sp--;
50+
reg_operand_b = reg_eval_stack[reg_sp];
51+
52+
reg_sp--;
53+
reg_operand_a = reg_eval_stack[reg_sp];
54+
55+
reg_math_res = interpreter.binary_ops.at(token.op)(reg_operand_a, reg_operand_b);
56+
57+
reg_eval_stack[reg_sp] = reg_math_res;
58+
reg_sp++;
59+
}
60+
}
61+
reg_sp--;
62+
out_result = reg_eval_stack[reg_sp];
63+
}
64+
65+
inline const big_float HALF = big_float("0.5");
66+
67+
void convert_coordinates(big_vec2& z, const ViewState* view_state) {
68+
big_float w(view_state->hp_width);
69+
big_float h(view_state->hp_height);
70+
71+
z = view_state->hp_range * (z - HALF * big_vec2(w, h)) / h;
72+
z = z + big_vec2(view_state->hp_shift.x, view_state->hp_shift.y);
73+
}
74+
75+
std::array<uint8_t, 3> domain_color(const big_vec2& z) {
76+
big_float angle_bf = boost::multiprecision::atan2(z.y, z.x);
77+
big_float mag_bf = length(z);
78+
79+
double angle = static_cast<double>(angle_bf);
80+
double mag = static_cast<double>(mag_bf);
81+
82+
double hue = angle / (2.0 * 3.14159265358979323846);
83+
if (hue < 0.0) hue += 1.0;
84+
85+
double light = (2.0 / 3.14159265358979323846) * std::atan(mag);
86+
double sat = 1.0;
87+
88+
auto glsl_mod = [](double x, double y) { return x - y * std::floor(x / y); };
89+
90+
double r_t = std::clamp(std::abs(glsl_mod(hue * 6.0 + 0.0, 6.0) - 3.0) - 1.0, 0.0, 1.0);
91+
double g_t = std::clamp(std::abs(glsl_mod(hue * 6.0 + 4.0, 6.0) - 3.0) - 1.0, 0.0, 1.0);
92+
double b_t = std::clamp(std::abs(glsl_mod(hue * 6.0 + 2.0, 6.0) - 3.0) - 1.0, 0.0, 1.0);
93+
94+
double chroma = 1.0 - std::abs(2.0 * light - 1.0);
95+
96+
double r = light + sat * (r_t - 0.5) * chroma;
97+
double g = light + sat * (g_t - 0.5) * chroma;
98+
double b = light + sat * (b_t - 0.5) * chroma;
99+
100+
return std::array<uint8_t, 3>{static_cast<uint8_t>(r * 255.0), static_cast<uint8_t>(g * 255.0), static_cast<uint8_t>(b * 255.0)};
101+
}
102+
103+
void render_band(int start_y, int end_y, int width, int height, unsigned char* pixel_buffer, const CPU_Interpreter& interpreter, const std::vector<TokenOperator>& stack, const ViewState* view_state) {
104+
for (int y = start_y; y < end_y; ++y) {
105+
for (int x = 0; x < width; ++x) {
106+
107+
big_vec2 z(big_float(x), big_float(height - y));
108+
109+
convert_coordinates(z, view_state);
110+
111+
big_vec2 result;
112+
evaluate_ast_stack(stack, z, interpreter, result);
113+
114+
std::array<uint8_t, 3> color_val = domain_color(result);
115+
116+
int index = (y * width + x) * 4;
117+
pixel_buffer[index + 0] = color_val[0];
118+
pixel_buffer[index + 1] = color_val[1];
119+
pixel_buffer[index + 2] = color_val[2];
120+
pixel_buffer[index + 3] = 255;
121+
}
122+
}
123+
}
124+
125+
void dispatch_render(int width, int height, unsigned char* pixel_buffer, CPU_Interpreter& interpreter, int user_thread_limit, const std::vector<TokenOperator>& stack, const ViewState* view_state) {
126+
int hardware_threads = std::thread::hardware_concurrency();
127+
if (hardware_threads == 0) hardware_threads = 4;
128+
129+
int num_threads = std::min(hardware_threads, user_thread_limit);
130+
if (num_threads < 1) num_threads = 1;
131+
132+
std::vector<std::thread> threads;
133+
int rows_per_thread = height / num_threads;
134+
135+
for (int i = 0; i < num_threads; ++i) {
136+
int start_y = i * rows_per_thread;
137+
int end_y = (i == num_threads - 1) ? height : start_y + rows_per_thread;
138+
threads.emplace_back(
139+
render_band,
140+
start_y, end_y, width, height,
141+
pixel_buffer, std::ref(interpreter), std::ref(stack), view_state
142+
);
143+
}
144+
for (auto& t : threads) {
145+
if (t.joinable()) {
146+
t.join();
147+
}
148+
}
149+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include <iostream>
2+
#include <fstream>
3+
#include <string>
4+
#include <vector>
5+
#include <regex>
6+
#include <sstream>
7+
#include <filesystem>
8+
#include <set>
9+
10+
std::string transpile_glsl_to_cpp(std::string code) {
11+
code = std::regex_replace(code, std::regex(R"(//.*|/\*[\s\S]*?\*/)"), "");
12+
code = std::regex_replace(code, std::regex(R"(=\s*(?:big_)?float\s*\[\s*\]\s*\(((?:.|\n)*?)\)\s*;)"), "= { $1 };");
13+
code = std::regex_replace(code, std::regex(R"(\bvec2\b)"), "big_vec2");
14+
code = std::regex_replace(code, std::regex(R"(\bfloat\b)"), "big_float");
15+
code = std::regex_replace(code, std::regex(R"(\bconst\b)"), "");
16+
code = std::regex_replace(code, std::regex(R"(\b([0-9]+\.[0-9]+(?:[eE][+-]?[0-9]+)?)(?:f|F)?\b)"), "big_float(\"$1\")");
17+
code = std::regex_replace(code, std::regex(R"(\bisnan\b)"), "boost::math::isnan");
18+
code = std::regex_replace(code, std::regex(R"(\bsqrt\b)"), "boost::multiprecision::sqrt");
19+
code = std::regex_replace(code, std::regex(R"(\bbig_vec2\s+([a-zA-Z_]\w*)\s*\()"), "inline big_vec2 $1(");
20+
return code;
21+
}
22+
23+
std::vector<std::pair<std::string, int>> extract_functions(const std::string& code) {
24+
std::vector<std::pair<std::string, int>> funcs;
25+
std::regex func_regex(R"(big_vec2\s+(\w+)\s*\(([^)]+)\))");
26+
27+
auto words_begin = std::sregex_iterator(code.begin(), code.end(), func_regex);
28+
auto words_end = std::sregex_iterator();
29+
30+
for (std::sregex_iterator it = words_begin; it != words_end; ++it) {
31+
std::string func_name = (*it)[1].str();
32+
std::string params = (*it)[2].str();
33+
int arity = std::count(params.begin(), params.end(), ',') + 1;
34+
funcs.push_back({ func_name, arity });
35+
}
36+
return funcs;
37+
}
38+
39+
void generate_cpp_files(const std::string& glsl_source, const std::string& out_dir) {
40+
std::string cpp_math_code = transpile_glsl_to_cpp(glsl_source);
41+
auto functions = extract_functions(cpp_math_code);
42+
43+
std::filesystem::create_directories(out_dir);
44+
45+
std::string header_path = out_dir + "/generated_big_math.h";
46+
std::ofstream math_out(header_path);
47+
math_out << "#pragma once\n"
48+
<< "#include <glsl_generated/types.h>"
49+
<< "// --- GLSL Math implementation for big_vec2 ---\n"
50+
<< "inline big_float length(const big_vec2& v) { return boost::multiprecision::sqrt(v.x*v.x + v.y*v.y); }\n"
51+
<< "inline big_float log(big_float f) { return boost::multiprecision::log(f); }\n"
52+
<< "inline big_float exp(big_float f) { return boost::multiprecision::exp(f); }\n"
53+
<< "inline big_float sin(big_float f) { return boost::multiprecision::sin(f); }\n"
54+
<< "inline big_float cos(big_float f) { return boost::multiprecision::cos(f); }\n"
55+
<< "inline big_float sinh(big_float f) { return boost::multiprecision::sinh(f); }\n"
56+
<< "inline big_float cosh(big_float f) { return boost::multiprecision::cosh(f); }\n"
57+
<< "inline big_float atan(big_float y, big_float x) { return boost::multiprecision::atan2(y, x); }\n"
58+
<< "inline big_vec2 vector_floor(big_vec2 v) { return big_vec2(boost::multiprecision::floor(v.x), boost::multiprecision::floor(v.y)); }\n\n"
59+
<< "// --- Transpiled GLSL Functions ---\n"
60+
<< cpp_math_code << "\n";
61+
math_out.close();
62+
63+
std::string mapper_path = out_dir + "/generated_math_mapper.h";
64+
std::ofstream map_out(mapper_path);
65+
map_out << "#pragma once \n #include \"generated_big_math.h\"\n"
66+
<< "#include \"types/type_mapper.h\"\n"
67+
<< "#include <functional>\n"
68+
<< "#include <map>\n\n"
69+
<< "struct CPU_Interpreter {\n"
70+
<< " std::map<Operator, std::function<big_vec2(big_vec2)>> unary_ops;\n"
71+
<< " std::map<Operator, std::function<big_vec2(big_vec2, big_vec2)>> binary_ops;\n\n"
72+
<< " CPU_Interpreter() {\n";
73+
74+
for (const auto& [name, arity] : functions) {
75+
map_out << " try {\n"
76+
<< " Operator op = get_token_operator_from_glsl(\"" << name << "\").op;\n";
77+
78+
if (arity == 1) {
79+
map_out << " unary_ops[op] = [](big_vec2 a) { return " << name << "(a); };\n";
80+
}
81+
else if (arity == 2) {
82+
map_out << " binary_ops[op] = [](big_vec2 a, big_vec2 b) { return " << name << "(a, b); };\n";
83+
}
84+
85+
map_out << " } catch (...) { }\n\n";
86+
}
87+
88+
map_out << " }\n"
89+
<< "};\n";
90+
map_out.close();
91+
}
92+
93+
int main(int argc, char* argv[]) {
94+
if (argc < 3) {
95+
std::cerr << "Usage: " << argv[0] << " <input_glsl_file> <output_directory>\n";
96+
return 1;
97+
}
98+
99+
std::string input_file = argv[1];
100+
std::string output_dir = argv[2];
101+
102+
std::ifstream file(input_file);
103+
if (!file.is_open()) {
104+
std::cerr << "Error: Could not open input file: " << input_file << "\n";
105+
return 1;
106+
}
107+
108+
std::stringstream buffer;
109+
buffer << file.rdbuf();
110+
111+
try {
112+
generate_cpp_files(buffer.str(), output_dir);
113+
std::cout << "[Transpiler] Successfully generated C++ math files in " << output_dir << "\n";
114+
}
115+
catch (const std::exception& e) {
116+
std::cerr << "[Transpiler] Error during generation: " << e.what() << "\n";
117+
return 1;
118+
}
119+
120+
return 0;
121+
}

0 commit comments

Comments
 (0)