void RenderContextPolicy< ShaderCollection::VertexSolidVertexColor, ShaderCollection::FragmentSolidVertexColor, ShaderCollection::No>::BindParameters(const CoreState& params) { const Math::mat4 proj_view_world = params.m_projection * params.m_view * params.m_world; SetUniformMatrix4f(uProjViewWorld, &proj_view_world[0]); SetUpOpenGL(params); }
int main(void) { printf("\n" "Controls: - Left/Right Click to zoom in/out, centring on cursor position.\n" " - Left Click and Drag to pan.\n" " - r to reset view.\n" " - q,w to decrease, increase max iteration count\n" " - a,s to decrease, increase colour period\n" " - g to toggle Gaussian Blur after computation\n" " - b to run some benchmarks.\n" " - p to show a double-precision limited zoom.\n" " - h to save a high resolution image of the current view to current directory.\n" " - Esc to quit.\n\n"); // Set render function, dependent on compile time flag. All have the same signature, // with all necessary variables defined inside the structs. #ifdef WITHOPENCL RenderMandelbrotPtr RenderMandelbrot = &RenderMandelbrotOpenCL; #elif defined(WITHAVX) RenderMandelbrotPtr RenderMandelbrot = &RenderMandelbrotAVXCPU; // AVX double prec vector width (4) must divide horizontal (x) resolution assert(XRESOLUTION % 4 == 0); #elif defined(WITHGMP) RenderMandelbrotPtr RenderMandelbrot = &RenderMandelbrotGMPCPU; #else RenderMandelbrotPtr RenderMandelbrot = &RenderMandelbrotCPU; #endif // Define and initialize structs imageStruct image; renderStruct render; // Set image resolution image.xRes = XRESOLUTION; image.yRes = YRESOLUTION; // Initial values for boundaries, iteration count SetInitialValues(&image); // Update OpenGL texture on render. This is disabled when rendering high resolution images render.updateTex = 1; // Allocate host memory, used to set up OpenGL texture, even if we are using interop OpenCL image.pixels = malloc(image.xRes * image.yRes * sizeof *(image.pixels) *3); // OpenGL variables and setup render.window = NULL; GLuint vertexShader, fragmentShader, shaderProgram; GLuint vao, vbo, ebo, tex; SetUpOpenGL(&(render.window), image.xRes, image.yRes, &vertexShader, &fragmentShader, &shaderProgram, &vao, &vbo, &ebo, &tex); #ifdef WITHOPENCL // OpenCL variables and setup cl_platform_id *platform; cl_device_id **device_id; cl_program program; cl_int err; render.globalSize = image.xRes * image.yRes; render.localSize = OPENCLLOCALSIZE; assert(render.globalSize % render.localSize == 0); // Initially set variable that controls interop of OpenGL and OpenCL to 0, set to 1 if // interop device found successfully render.glclInterop = 0; if (InitialiseCLEnvironment(&platform, &device_id, &program, &render) == EXIT_FAILURE) { printf("Error initialising OpenCL environment\n"); return EXIT_FAILURE; } size_t sizeBytes = image.xRes * image.yRes * 3 * sizeof(float); render.pixelsDevice = clCreateBuffer(render.contextCL, CL_MEM_READ_WRITE, sizeBytes, NULL, &err); // if we aren't using interop, allocate another buffer on the device for output, on the pointer // for the texture if (render.glclInterop == 0) { render.pixelsTex = clCreateBuffer(render.contextCL, CL_MEM_READ_WRITE, sizeBytes, NULL, &err); } // finish texture initialization so that we can use with OpenCL if glclInterop glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.xRes, image.yRes, 0, GL_RGB, GL_FLOAT, image.pixels); // Configure image from OpenGL texture "tex" if (render.glclInterop) { render.pixelsTex = clCreateFromGLTexture(render.contextCL, CL_MEM_WRITE_ONLY, GL_TEXTURE_2D, 0, tex, &err); CheckOpenCLError(err, __LINE__); } // Create kernels render.renderMandelbrotKernel = clCreateKernel(program, "renderMandelbrotKernel", &err); CheckOpenCLError(err, __LINE__); render.gaussianBlurKernel = clCreateKernel(program, "gaussianBlurKernel", &err); CheckOpenCLError(err, __LINE__); render.gaussianBlurKernel2 = clCreateKernel(program, "gaussianBlurKernel2", &err); CheckOpenCLError(err, __LINE__); #endif // Start main loop: Update until we encounter user input. Look for Esc key (quit), left and right mount // buttons (zoom in on cursor position, zoom out on cursor position), "r" -- reset back to initial coords, // "b" -- run some benchmarks, "p" -- display a double precision limited zoom. // Re-render the Mandelbrot set as and when we need, in the user input conditionals. // Initial render: RenderMandelbrot(&render, &image); while (!glfwWindowShouldClose(render.window)) { // draw glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // Swap buffers glfwSwapBuffers(render.window); // USER INPUT TESTS. // Continuous render, poll for input: //glfwPollEvents(); // Render only on update, wait for input: glfwWaitEvents(); // if user presses Esc, close window to leave loop if (glfwGetKey(render.window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(render.window, GL_TRUE); } // if user left-clicks in window, zoom in, centring on cursor position // if click and drag, simply re-position centre without zooming else if (glfwGetMouseButton(render.window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { // Get Press cursor location double xPressPos, yPressPos, xReleasePos, yReleasePos; int shift = 0; glfwGetCursorPos(render.window, &xPressPos, &yPressPos); // Wait for mousebutton release, re-rendering as mouse moves while (glfwGetMouseButton(render.window, GLFW_MOUSE_BUTTON_LEFT) != GLFW_RELEASE) { glfwGetCursorPos(render.window, &xReleasePos, &yReleasePos); if (fabs(xReleasePos-xPressPos) > DRAGPIXELS || fabs(yReleasePos-yPressPos) > DRAGPIXELS) { // Set shift variable. Don't zoom after button release if this is 1 shift = 1; // Determine shift in mandelbrot coords double xShift = (xReleasePos-xPressPos)/(double)image.xRes*(image.xMax-image.xMin); double yShift = (yReleasePos-yPressPos)/(double)image.yRes*(image.yMax-image.yMin); // Add shift to boundaries image.xMin = image.xMin - xShift; image.xMax = image.xMax - xShift; image.yMin = image.yMin - yShift; image.yMax = image.yMax - yShift; // Update "current" (press) position xPressPos = xReleasePos; yPressPos = yReleasePos; // Re-render and draw RenderMandelbrot(&render, &image); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwSwapBuffers(render.window); } glfwPollEvents(); } // else, zoom in smoothly over ZOOMSTEPS frames if (!shift) { SmoothZoom(&render, &image, RenderMandelbrot, xReleasePos, yReleasePos, ZOOMFACTOR, ITERSFACTOR); } } // if user right-clicks in window, zoom out, centring on cursor position else if (glfwGetMouseButton(render.window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { while (glfwGetMouseButton(render.window, GLFW_MOUSE_BUTTON_RIGHT) != GLFW_RELEASE) { glfwPollEvents(); } // Get cursor position, in *screen coordinates* double xReleasePos, yReleasePos; glfwGetCursorPos(render.window, &xReleasePos, &yReleasePos); // Zooming out, so use 1/FACTORs. SmoothZoom(&render, &image, RenderMandelbrot, xReleasePos, yReleasePos, 1.0/ZOOMFACTOR, 1.0/ITERSFACTOR); } // if user presses "r", reset view else if (glfwGetKey(render.window, GLFW_KEY_R) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_R) != GLFW_RELEASE) { glfwPollEvents(); } printf("Resetting...\n"); SetInitialValues(&image); RenderMandelbrot(&render, &image); } // if user presses "g", toggle gaussian blur else if (glfwGetKey(render.window, GLFW_KEY_G) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_G) != GLFW_RELEASE) { glfwPollEvents(); } if (image.gaussianBlur == 1) { printf("Toggling Gaussian Blur Off...\n"); image.gaussianBlur = 0; } else { printf("Toggling Gaussian Blur On...\n"); image.gaussianBlur = 1; } RenderMandelbrot(&render, &image); } // if user presses "q", decrease max iteration count else if (glfwGetKey(render.window, GLFW_KEY_Q) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_Q) != GLFW_RELEASE) { glfwPollEvents(); } printf("Decreasing max iteration count from %d to %d\n", image.maxIters, (int)(image.maxIters/ITERSFACTOR)); image.maxIters /= ITERSFACTOR; RenderMandelbrot(&render, &image); } // if user presses "w", increase max iteration count else if (glfwGetKey(render.window, GLFW_KEY_W) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_W) != GLFW_RELEASE) { glfwPollEvents(); } printf("Increasing max iteration count from %d to %d\n", image.maxIters, (int)(image.maxIters*ITERSFACTOR)); image.maxIters *= ITERSFACTOR; RenderMandelbrot(&render, &image); } // if user presses "a", decrease colour period else if (glfwGetKey(render.window, GLFW_KEY_A) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_A) != GLFW_RELEASE) { glfwPollEvents(); } printf("Decreasing colour period from %.0lf to %.0lf\n", image.colourPeriod, fmax(32, image.colourPeriod-32)); image.colourPeriod = fmax(32, image.colourPeriod-32); RenderMandelbrot(&render, &image); } // if user presses "s", increase colour period else if (glfwGetKey(render.window, GLFW_KEY_S) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_S) != GLFW_RELEASE) { glfwPollEvents(); } printf("Increasing colour period from %.0lf to %.0lf\n", image.colourPeriod, image.colourPeriod+32); image.colourPeriod += 32; RenderMandelbrot(&render, &image); } // if user presses "b", run some benchmarks. else if (glfwGetKey(render.window, GLFW_KEY_B) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_B) != GLFW_RELEASE) { glfwPollEvents(); } printf("Running Benchmarks...\n"); printf("Whole fractal:\n"); SetInitialValues(&image); RunBenchmark(&render, &image, RenderMandelbrot); printf("Early Bail-out:\n"); image.xMin = -0.8153143016681144; image.xMax = -0.6839170011300622; image.yMin = -0.0365167077914237; image.yMax = 0.0373942737612310; image.maxIters = 112; RunBenchmark(&render, &image, RenderMandelbrot); printf("Spiral:\n"); image.xMin = -0.8673755781976442; image.xMax = -0.8673711898931797; image.yMin = -0.2156059883952151; image.yMax = -0.2156035199739536; image.maxIters = 1757; RunBenchmark(&render, &image, RenderMandelbrot); printf("Highly zoomed:\n"); image.xMin = -0.8712903154956539; image.xMax = -0.8712903108993595; image.yMin = -0.2293516610223087; image.yMax = -0.2293516584368930; image.maxIters = 10750; RunBenchmark(&render, &image, RenderMandelbrot); printf("Complete.\n"); // Re-render with original coords SetInitialValues(&image); RenderMandelbrot(&render, &image); } // if user presses "p", zoom in, such that the double precision algorithm looks pixellated else if (glfwGetKey(render.window, GLFW_KEY_P) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_P) != GLFW_RELEASE) { glfwPollEvents(); } printf("Precision test...\n"); image.xMin = -1.25334325335487362; image.xMax = -1.25334325335481678; image.yMin = -0.34446232396119353; image.yMax = -0.34446232396116155; image.maxIters = 1389952; RenderMandelbrot(&render, &image); } // if user presses "h", render a high resolution version of the current view, and // save it to disk as an image else if (glfwGetKey(render.window, GLFW_KEY_H) == GLFW_PRESS) { while (glfwGetKey(render.window, GLFW_KEY_H) != GLFW_RELEASE) { glfwPollEvents(); } double startTime = GetWallTime(); printf("Saving high resolution (%d x %d) image...\n", image.xRes*HIGHRESOLUTIONMULTIPLIER, image.yRes*HIGHRESOLUTIONMULTIPLIER); HighResolutionRender(&render, &image, RenderMandelbrot); printf(" --- done. Total time: %lfs\n", GetWallTime()-startTime); } } // clean up #ifdef WITHOPENCL CleanUpCLEnvironment(&platform, &device_id, &(render.contextCL), &(render.queue), &program); #endif glDeleteProgram(shaderProgram); glDeleteShader(fragmentShader); glDeleteShader(vertexShader); glDeleteBuffers(1, &ebo); glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); // Close OpenGL window and terminate GLFW glfwDestroyWindow(render.window); glfwTerminate(); // Free dynamically allocated memory free(image.pixels); return 0; }