diff options
Diffstat (limited to 'BuddhaTest/src')
-rw-r--r-- | BuddhaTest/src/BuddhaTest.cpp | 460 | ||||
-rw-r--r-- | BuddhaTest/src/Helpers.cpp | 832 |
2 files changed, 646 insertions, 646 deletions
diff --git a/BuddhaTest/src/BuddhaTest.cpp b/BuddhaTest/src/BuddhaTest.cpp index ccbb011..354cdcb 100644 --- a/BuddhaTest/src/BuddhaTest.cpp +++ b/BuddhaTest/src/BuddhaTest.cpp @@ -1,230 +1,230 @@ -#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-#include <Helpers.h>
-#include <iostream>
-#include <vector>
-#include <chrono>
-#include <algorithm>
-
-void error_callback(int error, const char* description)
-{
- std::cerr << "Error: " << description << std::endl;
-}
-
-void framebuffer_size_callback(GLFWwindow* window, int width, int height)
-{
- glViewport(0, 0, width, height);
-}
-
-int main(int argc, char * argv[])
-{
- Helpers::RenderSettings settings;
-
- if(!settings.ParseCommandLine(argc,argv))
- {
- return 2;
- }
-
- unsigned int bufferHeight = settings.imageHeight/2;
-
- GLFWwindow* window;
-
- /* Initialize the library */
- if (!glfwInit())
- return -1;
-
- glfwSetErrorCallback(error_callback);
-
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- /* Create a windowed mode window and its OpenGL context */
- window = glfwCreateWindow(settings.windowWidth, settings.windowHeight, "Buddhabrot", NULL, NULL);
- if (!window)
- {
- std::cerr << "Failed to create OpenGL 4.3 core context. We do not support compatibility contexts." << std::endl;
- glfwTerminate();
- return -1;
- }
-
- //register callback on window resize:
- glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
-
- /* Make the window's context current */
- glfwMakeContextCurrent(window);
- gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
-
- //disable vsync
- glfwSwapInterval(0);
-
- //we have a context. Let's check if input is sane.
- //calcualte buffer size, and make sure it's allowed by the driver.
- const unsigned int pixelCount{(settings.imageWidth * bufferHeight)};
- if(!settings.CheckValidity())
- {
- glfwTerminate();
- return 1;
- }
-
- // Create and compile our GLSL program from the shaders
- std::string vertexPath("Shaders/BuddhaVertex.glsl");
- std::string fragmentPath("Shaders/BuddhaFragment.glsl");
- std::string computePath("Shaders/BuddhaCompute.glsl");
-
- if(!Helpers::DoesFileExist(vertexPath))
- {
-#if defined _WIN32 || defined __CYGWIN__
- std::string separator("\\");
-#else
- std::string separator("/");
-#endif
- vertexPath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + vertexPath;
- fragmentPath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + fragmentPath;
- computePath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + computePath;
- }
-
- GLuint VertexAndFragmentShaders = Helpers::LoadShaders(vertexPath, fragmentPath);
- //Do the same for the compute shader:
- GLuint ComputeShader = Helpers::LoadComputeShader(computePath, settings.localWorkgroupSizeX, settings.localWorkgroupSizeY, settings.localWorkgroupSizeZ);
- if(VertexAndFragmentShaders == 0 || ComputeShader == 0)
- {
- std::cerr << "Something went wrong with loading the shaders. Abort." << std::endl;
- glfwTerminate();
- return 1;
- }
-
- GLuint VertexArrayID;
- glGenVertexArrays(1, &VertexArrayID);
- glBindVertexArray(VertexArrayID);
-
- const GLfloat g_vertex_buffer_data[] = {
- -1.0f, -1.0f, 0.0f,
- 1.0f, -1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f,
- 1.0f, 1.0f, 0.0f
- };
-
- GLuint vertexbuffer;
- glGenBuffers(1, &vertexbuffer);
- glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
-
-
- GLuint drawBuffer[3];
- glGenBuffers(3, drawBuffer);
- for(int i=0; i < 3; ++i)
- {
- glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer[i]);
- {
- glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * pixelCount, nullptr, GL_DYNAMIC_COPY);
- glClearBufferData(GL_SHADER_STORAGE_BUFFER,GL_R8,GL_RED,GL_UNSIGNED_INT,nullptr);
- }
- glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2+i, drawBuffer[i]);
- }
- glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
-
- const uint32_t workersPerFrame = settings.globalWorkGroupSizeX*settings.globalWorkGroupSizeY*settings.globalWorkGroupSizeZ*settings.localWorkgroupSizeX*settings.localWorkgroupSizeY*settings.localWorkgroupSizeZ;
- GLuint stateBuffer;
- glGenBuffers(1,&stateBuffer);
- glBindBuffer(GL_SHADER_STORAGE_BUFFER,stateBuffer);
- glBufferData(GL_SHADER_STORAGE_BUFFER, 4*(5*workersPerFrame),nullptr,GL_DYNAMIC_COPY);
- glClearBufferData(GL_SHADER_STORAGE_BUFFER,GL_R8,GL_RED,GL_UNSIGNED_INT,nullptr);
- glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, stateBuffer);
-
- glUseProgram(ComputeShader);
- GLint orbitLengthUniformHandle = glGetUniformLocation(ComputeShader, "orbitLength");
- GLint widthUniformComputeHandle = glGetUniformLocation(ComputeShader, "width");
- GLint heightUniformComputeHandle = glGetUniformLocation(ComputeShader, "height");
- GLint iterationsPerDispatchHandle = glGetUniformLocation(ComputeShader, "iterationsPerDispatch");
- glUniform3ui(orbitLengthUniformHandle,settings.orbitLengthRed,settings.orbitLengthGreen,settings.orbitLengthBlue);
- glUniform1ui(widthUniformComputeHandle, settings.imageWidth);
- glUniform1ui(heightUniformComputeHandle, bufferHeight);
-
- glUseProgram(VertexAndFragmentShaders);
- GLint widthUniformFragmentHandle = glGetUniformLocation(VertexAndFragmentShaders, "width");
- GLint heightUniformFragmentHandle = glGetUniformLocation(VertexAndFragmentShaders, "height");
- glUniform1ui(widthUniformFragmentHandle, settings.imageWidth);
- glUniform1ui(heightUniformFragmentHandle, bufferHeight);
- glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
-
- uint32_t iterationsPerFrame = 1;
-
- Helpers::PIDController<float, std::chrono::high_resolution_clock::time_point::rep> pid{0.0f,0.0f,1e-4f};
- const uint32_t targetFrameDuration{1000000/settings.targetFrameRate};
-
- /* Loop until the user closes the window */
- while (!glfwWindowShouldClose(window))
- {
- auto frameStart{std::chrono::high_resolution_clock::now()};
- //let the compute shader do something
- glUseProgram(ComputeShader);
- glUniform1ui(iterationsPerDispatchHandle, iterationsPerFrame);
- glDispatchCompute(settings.globalWorkGroupSizeX, settings.globalWorkGroupSizeY, settings.globalWorkGroupSizeZ);
-
- //before reading the values in the ssbo, we need a memory barrier:
- glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); //I hope this is the correct (and only required) bit
-
- /* Render here */
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glUseProgram(VertexAndFragmentShaders);
-
- glEnableVertexAttribArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
- glVertexAttribPointer(
- 0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized?
- 0, // stride
- (void*)0 // array buffer offset
- );
- // Draw the triangle strip!
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Triangle strip with 4 vertices -> quad.
- glDisableVertexAttribArray(0);
-
- /* Swap front and back buffers */
- glfwSwapBuffers(window);
-
- /* Poll for and process events */
- glfwPollEvents();
- auto frameStop{std::chrono::high_resolution_clock::now()};
- const auto dur{std::chrono::duration_cast<std::chrono::microseconds>(frameStop-frameStart)};
- auto frameDuration{dur.count()};
- if(frameDuration > 0)
- {
- const auto error{targetFrameDuration - frameDuration};
- const auto pidOutput{pid.Update(1,float(error))};
- iterationsPerFrame = std::max(1,static_cast<int>(pidOutput));
-
- //std::cout << iterationsPerFrame << " " << pidOutput << std::endl;
- }
- }
-
- if(!settings.pngFilename.empty())
- {
- glMemoryBarrier(GL_ALL_BARRIER_BITS);
- std::vector<std::vector<uint32_t>> readBackBuffers(3,std::vector<uint32_t>(pixelCount));
- for(int i = 0; i < 3; ++i)
- {
- glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer[i]);
- glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0,4 * pixelCount,readBackBuffers[i].data());
- }
-
- //too lazy to change WriteOutputPng...
- std::vector<uint32_t> combinedBuffer(3*pixelCount);
- for(uint32_t i=0;i<3*pixelCount;++i)
- {
- combinedBuffer[i] = readBackBuffers[i%3][i/3];
- }
- Helpers::WriteOutputPNG(settings.pngFilename,combinedBuffer,settings.imageWidth,bufferHeight, settings.pngGamma, settings.pngColorScale);
- }
-
- //a bit of cleanup
- glDeleteBuffers(1,&vertexbuffer);
- glDeleteBuffers(3,drawBuffer);
- glDeleteBuffers(1,&stateBuffer);
-
- glfwTerminate();
- return 0;
-}
+#include <glad/glad.h> +#include <GLFW/glfw3.h> +#include <Helpers.h> +#include <iostream> +#include <vector> +#include <chrono> +#include <algorithm> + +void error_callback(int error, const char* description) +{ + std::cerr << "Error: " << description << std::endl; +} + +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(int argc, char * argv[]) +{ + Helpers::RenderSettings settings; + + if(!settings.ParseCommandLine(argc,argv)) + { + return 2; + } + + unsigned int bufferHeight = settings.imageHeight/2; + + GLFWwindow* window; + + /* Initialize the library */ + if (!glfwInit()) + return -1; + + glfwSetErrorCallback(error_callback); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + /* Create a windowed mode window and its OpenGL context */ + window = glfwCreateWindow(settings.windowWidth, settings.windowHeight, "Buddhabrot", NULL, NULL); + if (!window) + { + std::cerr << "Failed to create OpenGL 4.3 core context. We do not support compatibility contexts." << std::endl; + glfwTerminate(); + return -1; + } + + //register callback on window resize: + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + /* Make the window's context current */ + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); + + //disable vsync + glfwSwapInterval(0); + + //we have a context. Let's check if input is sane. + //calcualte buffer size, and make sure it's allowed by the driver. + const unsigned int pixelCount{(settings.imageWidth * bufferHeight)}; + if(!settings.CheckValidity()) + { + glfwTerminate(); + return 1; + } + + // Create and compile our GLSL program from the shaders + std::string vertexPath("Shaders/BuddhaVertex.glsl"); + std::string fragmentPath("Shaders/BuddhaFragment.glsl"); + std::string computePath("Shaders/BuddhaCompute.glsl"); + + if(!Helpers::DoesFileExist(vertexPath)) + { +#if defined _WIN32 || defined __CYGWIN__ + std::string separator("\\"); +#else + std::string separator("/"); +#endif + vertexPath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + vertexPath; + fragmentPath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + fragmentPath; + computePath = INSTALL_PREFIX + separator + "share" + separator + "BuddhaShader" + separator + computePath; + } + + GLuint VertexAndFragmentShaders = Helpers::LoadShaders(vertexPath, fragmentPath); + //Do the same for the compute shader: + GLuint ComputeShader = Helpers::LoadComputeShader(computePath, settings.localWorkgroupSizeX, settings.localWorkgroupSizeY, settings.localWorkgroupSizeZ); + if(VertexAndFragmentShaders == 0 || ComputeShader == 0) + { + std::cerr << "Something went wrong with loading the shaders. Abort." << std::endl; + glfwTerminate(); + return 1; + } + + GLuint VertexArrayID; + glGenVertexArrays(1, &VertexArrayID); + glBindVertexArray(VertexArrayID); + + const GLfloat g_vertex_buffer_data[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f + }; + + GLuint vertexbuffer; + glGenBuffers(1, &vertexbuffer); + glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); + + + GLuint drawBuffer[3]; + glGenBuffers(3, drawBuffer); + for(int i=0; i < 3; ++i) + { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer[i]); + { + glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * pixelCount, nullptr, GL_DYNAMIC_COPY); + glClearBufferData(GL_SHADER_STORAGE_BUFFER,GL_R8,GL_RED,GL_UNSIGNED_INT,nullptr); + } + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2+i, drawBuffer[i]); + } + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + const uint32_t workersPerFrame = settings.globalWorkGroupSizeX*settings.globalWorkGroupSizeY*settings.globalWorkGroupSizeZ*settings.localWorkgroupSizeX*settings.localWorkgroupSizeY*settings.localWorkgroupSizeZ; + GLuint stateBuffer; + glGenBuffers(1,&stateBuffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER,stateBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, 4*(5*workersPerFrame),nullptr,GL_DYNAMIC_COPY); + glClearBufferData(GL_SHADER_STORAGE_BUFFER,GL_R8,GL_RED,GL_UNSIGNED_INT,nullptr); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, stateBuffer); + + glUseProgram(ComputeShader); + GLint orbitLengthUniformHandle = glGetUniformLocation(ComputeShader, "orbitLength"); + GLint widthUniformComputeHandle = glGetUniformLocation(ComputeShader, "width"); + GLint heightUniformComputeHandle = glGetUniformLocation(ComputeShader, "height"); + GLint iterationsPerDispatchHandle = glGetUniformLocation(ComputeShader, "iterationsPerDispatch"); + glUniform3ui(orbitLengthUniformHandle,settings.orbitLengthRed,settings.orbitLengthGreen,settings.orbitLengthBlue); + glUniform1ui(widthUniformComputeHandle, settings.imageWidth); + glUniform1ui(heightUniformComputeHandle, bufferHeight); + + glUseProgram(VertexAndFragmentShaders); + GLint widthUniformFragmentHandle = glGetUniformLocation(VertexAndFragmentShaders, "width"); + GLint heightUniformFragmentHandle = glGetUniformLocation(VertexAndFragmentShaders, "height"); + glUniform1ui(widthUniformFragmentHandle, settings.imageWidth); + glUniform1ui(heightUniformFragmentHandle, bufferHeight); + glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + + uint32_t iterationsPerFrame = 1; + + Helpers::PIDController<float, std::chrono::high_resolution_clock::time_point::rep> pid{0.0f,0.0f,1e-4f}; + const uint32_t targetFrameDuration{1000000/settings.targetFrameRate}; + + /* Loop until the user closes the window */ + while (!glfwWindowShouldClose(window)) + { + auto frameStart{std::chrono::high_resolution_clock::now()}; + //let the compute shader do something + glUseProgram(ComputeShader); + glUniform1ui(iterationsPerDispatchHandle, iterationsPerFrame); + glDispatchCompute(settings.globalWorkGroupSizeX, settings.globalWorkGroupSizeY, settings.globalWorkGroupSizeZ); + + //before reading the values in the ssbo, we need a memory barrier: + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); //I hope this is the correct (and only required) bit + + /* Render here */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUseProgram(VertexAndFragmentShaders); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + glVertexAttribPointer( + 0, // attribute 0. No particular reason for 0, but must match the layout in the shader. + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + // Draw the triangle strip! + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Triangle strip with 4 vertices -> quad. + glDisableVertexAttribArray(0); + + /* Swap front and back buffers */ + glfwSwapBuffers(window); + + /* Poll for and process events */ + glfwPollEvents(); + auto frameStop{std::chrono::high_resolution_clock::now()}; + const auto dur{std::chrono::duration_cast<std::chrono::microseconds>(frameStop-frameStart)}; + auto frameDuration{dur.count()}; + if(frameDuration > 0) + { + const auto error{targetFrameDuration - frameDuration}; + const auto pidOutput{pid.Update(1,float(error))}; + iterationsPerFrame = std::max(1,static_cast<int>(pidOutput)); + + //std::cout << iterationsPerFrame << " " << pidOutput << std::endl; + } + } + + if(!settings.pngFilename.empty()) + { + glMemoryBarrier(GL_ALL_BARRIER_BITS); + std::vector<std::vector<uint32_t>> readBackBuffers(3,std::vector<uint32_t>(pixelCount)); + for(int i = 0; i < 3; ++i) + { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer[i]); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0,4 * pixelCount,readBackBuffers[i].data()); + } + + //too lazy to change WriteOutputPng... + std::vector<uint32_t> combinedBuffer(3*pixelCount); + for(uint32_t i=0;i<3*pixelCount;++i) + { + combinedBuffer[i] = readBackBuffers[i%3][i/3]; + } + Helpers::WriteOutputPNG(settings.pngFilename,combinedBuffer,settings.imageWidth,bufferHeight, settings.pngGamma, settings.pngColorScale); + } + + //a bit of cleanup + glDeleteBuffers(1,&vertexbuffer); + glDeleteBuffers(3,drawBuffer); + glDeleteBuffers(1,&stateBuffer); + + glfwTerminate(); + return 0; +} diff --git a/BuddhaTest/src/Helpers.cpp b/BuddhaTest/src/Helpers.cpp index 94c21a9..ed895c4 100644 --- a/BuddhaTest/src/Helpers.cpp +++ b/BuddhaTest/src/Helpers.cpp @@ -1,416 +1,416 @@ -#include "Helpers.h"
-#include <string>
-#include <fstream>
-#include <sstream>
-#include <iostream>
-#include <vector>
-#include <png.h>
-#include <cmath>
-#include <unordered_map>
-#include <string>
-#include <algorithm>
-
-namespace Helpers
-{
- GLuint LoadShaders(const std::string& vertex_file_path, const std::string& fragment_file_path) {
-
- // Create the shaders
- GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
- GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
-
- // Read the Vertex Shader code from the file
- std::string VertexShaderCode;
- std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
- if (VertexShaderStream.is_open()) {
- std::stringstream sstr;
- sstr << VertexShaderStream.rdbuf();
- VertexShaderCode = sstr.str();
- VertexShaderStream.close();
- }
- else {
- std::cerr << "Failed to load vertex shader file from " << vertex_file_path << ". Is this installed correctly?" << std::endl;
- return 0;
- }
-
- // Read the Fragment Shader code from the file
- std::string FragmentShaderCode;
- std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
- if (FragmentShaderStream.is_open()) {
- std::stringstream sstr;
- sstr << FragmentShaderStream.rdbuf();
- FragmentShaderCode = sstr.str();
- FragmentShaderStream.close();
- }
- else
- {
- std::cerr << "Failed to load fragment shader file from " << fragment_file_path << ". Is this installed correctly?" << std::endl;
- return 0;
- }
-
- GLint Result = GL_FALSE;
- int InfoLogLength;
-
- // Compile Vertex Shader
- //std::cout << "Compiling shader : " << vertex_file_path << std::endl;
- char const * VertexSourcePointer = VertexShaderCode.c_str();
- glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
- glCompileShader(VertexShaderID);
-
- // Check Vertex Shader
- glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- if (InfoLogLength > 0) {
- std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1);
- glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
- printf("%s\n", &VertexShaderErrorMessage[0]);
- }
-
-
- // Compile Fragment Shader
- //std::cout << "Compiling shader : " << fragment_file_path << std::endl;
- char const * FragmentSourcePointer = FragmentShaderCode.c_str();
- glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
- glCompileShader(FragmentShaderID);
-
- // Check Fragment Shader
- glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- if (InfoLogLength > 0) {
- std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1);
- glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
- printf("%s\n", &FragmentShaderErrorMessage[0]);
- }
-
- // Link the program
- //printf("Linking program\n");
- GLuint ProgramID = glCreateProgram();
- glAttachShader(ProgramID, VertexShaderID);
- glAttachShader(ProgramID, FragmentShaderID);
- glLinkProgram(ProgramID);
-
- // Check the program
- glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
- glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- if (InfoLogLength > 0) {
- std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
- glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
- printf("%s\n", &ProgramErrorMessage[0]);
- }
-
- glDetachShader(ProgramID, VertexShaderID);
- glDetachShader(ProgramID, FragmentShaderID);
-
- glDeleteShader(VertexShaderID);
- glDeleteShader(FragmentShaderID);
-
- return ProgramID;
- }
-
- GLuint LoadComputeShader(const std::string& compute_file_path, unsigned int localSizeX, unsigned int localSizeY, unsigned int localSizeZ)
- {
- GLuint ComputeShaderID = glCreateShader(GL_COMPUTE_SHADER);
- // Read the compute shader
- std::string ComputeShaderCode;
- {
- std::ifstream ComputeShaderCodeStream(compute_file_path, std::ios::in);
- if (ComputeShaderCodeStream.is_open()) {
- std::stringstream sstr;
- sstr << "#version 430" <<
- std::endl <<
- "layout (local_size_x = " << localSizeX <<
- ", local_size_y = " << localSizeY <<
- ", local_size_z = " << localSizeZ << ") in;" << std::endl;
- sstr << ComputeShaderCodeStream.rdbuf();
- ComputeShaderCode = sstr.str();
- ComputeShaderCodeStream.close();
- }
- else
- {
- std::cerr << "Failed to load compute shader file from " << compute_file_path << ". Is this installed correctly?" << std::endl;
- return 0;
- }
- }
-
- GLint Result = GL_FALSE;
- int InfoLogLength;
-
- {
- // Compile Compute Shader
- //std::cout << "Compiling shader : " << compute_file_path << std::endl;
- char const * ComputeSourcePointer = ComputeShaderCode.c_str();
- glShaderSource(ComputeShaderID, 1, &ComputeSourcePointer, NULL);
- glCompileShader(ComputeShaderID);
-
- // Check Compute Shader
- glGetShaderiv(ComputeShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(ComputeShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- if (InfoLogLength > 0) {
- std::vector<char> ComputeShaderErrorMessage(InfoLogLength + 1);
- glGetShaderInfoLog(ComputeShaderID, InfoLogLength, NULL, &ComputeShaderErrorMessage[0]);
- printf("%s\n", &ComputeShaderErrorMessage[0]);
- }
- }
- GLuint ProgramID = glCreateProgram();
- glAttachShader(ProgramID, ComputeShaderID);
- glLinkProgram(ProgramID);
-
- // Check the program
- glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
- glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- if (InfoLogLength > 0) {
- std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
- glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
- printf("%s\n", &ProgramErrorMessage[0]);
- }
- glDetachShader(ProgramID, ComputeShaderID);
- glDeleteShader(ComputeShaderID);
- return ProgramID;
- }
-
- void WriteOutputPNG(const std::string &path, const std::vector<uint32_t>& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale)
- {
- std::vector<png_byte> pngData(3*width*2*bufferHeight);
- std::vector<png_byte *> rows{2*bufferHeight};
- for(unsigned int i = 0; i < 2*bufferHeight ; ++i)
- {
- rows[i] = pngData.data()+3*width*i;
- }
-
- uint32_t maxValue{UINT32_C(1)};
- for(unsigned int i = 0; i < data.size();++i)
- {
- maxValue = std::max(maxValue,data[i]);
- }
- for(unsigned int i = 0; i < data.size();++i)
- {
- if(fabs(gamma - 1.0) > 0.0001 || fabs(colorScale - 1.0) > 0.0001)
- {
- pngData[data.size() + i] = static_cast<png_byte>(255.0 * pow(std::min(1.0,colorScale*static_cast<double>(data[i])/static_cast<double>(maxValue)),gamma));
- }
- else
- {
- pngData[data.size() + i] = (255*data[i] + (maxValue/2))/maxValue;
- }
- }
- for(unsigned int i = 0; i < bufferHeight;++i)
- {
- for(unsigned int j = 0; j < width*3;++j)
- {
- rows[i][j] =rows[2*bufferHeight-i-1][j];
- }
- }
-
- ScopedCFileDescriptor fd(path.c_str(), "wb");
- if(!fd.IsValid())
- {
- std::cerr << "Failed to open image.png for writing." << std::endl;
- return;
- }
- png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
- if(!png_ptr)
- {
- return;
- }
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if(!info_ptr)
- {
- png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
- return;
- }
- if(setjmp(png_jmpbuf(png_ptr)))
- {
- png_destroy_write_struct(&png_ptr, &info_ptr);
- return;
- }
- png_init_io(png_ptr, fd.Get());
- png_set_IHDR(png_ptr, info_ptr, width, 2*bufferHeight, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- png_write_info(png_ptr, info_ptr);
- //header written.
-
- png_write_image(png_ptr, rows.data());
-
- png_write_end(png_ptr, info_ptr);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- }
-
- ScopedCFileDescriptor::ScopedCFileDescriptor(const char *path, const char *mode)
- {
- Descriptor = fopen(path,mode);
- }
-
- ScopedCFileDescriptor::~ScopedCFileDescriptor()
- {
- if(IsValid())
- fclose(Descriptor);
- }
-
- FILE * ScopedCFileDescriptor::Get() const
- {
- return Descriptor;
- }
-
- bool ScopedCFileDescriptor::IsValid() const
- {
- return Descriptor != nullptr;
- }
-
- bool RenderSettings::CheckValidity()
- {
- if(imageHeight%2 != 0)
- {
- std::cerr << "Image height has to be an even number." << std::endl;
- return false;
- }
- int maxSSBOSize;
- glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE,&maxSSBOSize);
- if((static_cast<unsigned long>(imageWidth) * static_cast<unsigned long>(imageHeight)) > static_cast<unsigned long>(maxSSBOSize)/2) //divided by 2, as we have 4 bytes per int, but only half the image height
- {
- std::cerr << "Requested buffer size is larger than maximum allowed by graphics driver. Max pixel number: " << maxSSBOSize/2 << std::endl;
- std::cerr << "You can override this limit check using the --ignoreMaxBufferSize 1 command line parameter, but doing so is your own risk." << std::endl;
- if(ignoreMaxBufferSize == 0)
- return false;
- }
- int WorkGroupSizeLimitX, WorkGroupSizeLimitY, WorkGroupSizeLimitZ;
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,0,&WorkGroupSizeLimitX);
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,1,&WorkGroupSizeLimitY);
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,2,&WorkGroupSizeLimitZ);
- if(globalWorkGroupSizeX > static_cast<unsigned int>(WorkGroupSizeLimitX) ||
- globalWorkGroupSizeY > static_cast<unsigned int>(WorkGroupSizeLimitY) ||
- globalWorkGroupSizeZ > static_cast<unsigned int>(WorkGroupSizeLimitZ))
- {
- std::cerr << "Requested global work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl;
- return false;
- }
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,0,&WorkGroupSizeLimitX);
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,1,&WorkGroupSizeLimitY);
- glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,2,&WorkGroupSizeLimitZ);
- if(localWorkgroupSizeX > static_cast<unsigned int>(WorkGroupSizeLimitX) ||
- localWorkgroupSizeY > static_cast<unsigned int>(WorkGroupSizeLimitY) ||
- localWorkgroupSizeZ > static_cast<unsigned int>(WorkGroupSizeLimitZ))
- {
- std::cerr << "Requested local work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl;
- return false;
- }
- int maxInvocations;
- glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS,&maxInvocations);
- if(static_cast<unsigned long>(localWorkgroupSizeX)*static_cast<unsigned long>(localWorkgroupSizeY)*static_cast<unsigned long>(localWorkgroupSizeZ) > static_cast<unsigned long>(maxInvocations))
- {
- std::cerr << "Requested local work group size exceeds maximum total count (Product of x*y*z dimensions). Limit: " << maxInvocations << std::endl;
- return false;
- }
- return true;
- }
-
- bool RenderSettings::ParseCommandLine(int argc, char *argv[])
- {
- struct SettingsPointer
- {
- public:
- SettingsPointer(unsigned int * ptr) : intPtr(ptr), dblPtr(nullptr), stringPtr(nullptr) {}
- SettingsPointer(double * ptr) : intPtr(nullptr), dblPtr(ptr), stringPtr(nullptr) {}
- SettingsPointer(std::string * ptr) : intPtr(nullptr), dblPtr(nullptr), stringPtr(ptr) {}
- unsigned int * GetIntPtr() const { return intPtr; }
- double * GetDblPtr() const { return dblPtr;}
- std::string * GetStringPtr() const { return stringPtr;}
- private:
- unsigned int * intPtr = nullptr;
- double * dblPtr = nullptr;
- std::string * stringPtr = nullptr;
- };
- std::unordered_map<std::string, SettingsPointer> commandMap{
- {"--imageWidth",&imageWidth},
- {"--imageHeight",&imageHeight},
- {"--windowWidth",&windowWidth},
- {"--windowHeight",&windowHeight},
- {"--orbitLengthRed",&orbitLengthRed},
- {"--orbitLengthGreen",&orbitLengthGreen},
- {"--orbitLengthBlue",&orbitLengthBlue},
- {"--localWorkgroupSizeX", &localWorkgroupSizeX},
- {"--localWorkgroupSizeY", &localWorkgroupSizeY},
- {"--localWorkgroupSizeZ", &localWorkgroupSizeZ},
- {"--globalWorkgroupSizeX", &globalWorkGroupSizeX},
- {"--globalWorkgroupSizeY", &globalWorkGroupSizeY},
- {"--globalWorkgroupSizeZ", &globalWorkGroupSizeZ},
- {"--targetFrameRate", &targetFrameRate},
- {"--imageGamma",&pngGamma},
- {"--imageColorScale",&pngColorScale},
- {"--output", &pngFilename},
- {"--ignoreMaxBufferSize", &ignoreMaxBufferSize}
- };
-
- for(int i=1; i < argc;++i)
- {
- std::string argAsString(argv[i]);
- if(argAsString == "--help") //help is a special case.
- {
- std::cout << "Draws a buddhabrot and iterates until the user closes the window. If a --output filename is given, a png file will afterwards be written there." <<std::endl <<
- "Supported options are:" << std::endl << std::endl <<
- "--output [path] : File to write output to. Empty by default, meaning no output is written." << std::endl <<
- "--imageWidth [integer] : Width of the to be written image. 1024 by default. If no --output is given, this still detrmines the buffer size for rendering." << std::endl <<
- "--imageHeight [integer] : Height of the to be written image. 576 by default. If no --output is given, this still detrmines the buffer size for rendering." << std::endl <<
- "--imageGamma [float] : Gamma to use when writing the image. 1.0 by default. Ignored if no --output is given." << std::endl <<
- "--imageColorScale [float] : Image brightness is scaled by the brightest pixel. The result is multiplied by this value. 2.0 by default, as 1.0 leaves very little dynamic range." << std::endl <<
- "--windowWidth [integer] : Width of the preview window. 1024 by default." << std::endl <<
- "--windowHeight [integer] : Height of the preview window. 576 by default." << std::endl <<
- "--orbitLengthRed [integer] : Maximum number of iterations for escaping orbits to color red. 10 by default." << std::endl <<
- "--orbitLengthGreen [integer] : Maximum number of iterations for escaping orbits to color green. 100 by default." << std::endl <<
- "--orbitLengthBlue [integer] : Maximum number of iterations for escaping orbits to color blue. 1000 by default." << std::endl <<
- "--localWorkgroupSizeX [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default is 1024. Values up to 1024 are guaranteed to work." << std::endl <<
- "--localWorkgroupSizeY [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default is 1. Values up to 1024 are guaranteed to work." << std::endl <<
- "--localWorkgroupSizeZ [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default of 1. Values up to 1024 are guaranteed to work." << std::endl <<
- "\tNOTE: There's also a limit on the product of the three local workgroup sizes, for which a number smaller or equal to 1024 is guaranteed to work. Higher numbers might work and run faster. Feel free to experiment." << std::endl <<
- "--globalWorkgroupSizeX [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1024." << std::endl <<
- "--globalWorkgroupSizeY [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1." << std::endl <<
- "--globalWorkgroupSizeZ [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1." << std::endl <<
- "--targetFrameRate [integer] : The number of iterations per frame will dynamically adjust to approximately reach this framerate. Default: 60." << std::endl <<
- "--ignoreMaxBufferSize [0,1] : If set to 1, a failed maximum buffer size check is not treated as error. Some graphics drivers report lower values than their absolute limit. Do this on your own risk, though." << std::endl;
- return false;
- }
- }
- //apart from --help all parameters consist of 2 entries.
- if(argc%2 != 1)
- {
- std::cerr << "Invalid number of arguments. See --help for usage." << std::endl;
- return false;
- }
- for(int i=1 ; i < argc; i += 2)
- {
- std::string argAsString(argv[i]);
- std::string valueAsString(argv[i+1]);
- auto setting = commandMap.find(argAsString);
- if(setting == commandMap.end())
- {
- std::cerr << "Unknown option: " << argAsString << std::endl;
- return false;
- }
- const SettingsPointer& ptr = setting->second;
- if(auto intProp = ptr.GetIntPtr())
- {
- *intProp = std::stoi(valueAsString);
- }
- else if(auto dblProp = ptr.GetDblPtr())
- {
- *dblProp = std::stod(valueAsString);
- }
- else if(auto strPtr = ptr.GetStringPtr())
- {
- *strPtr = valueAsString;
- }
- else
- {
- std::cerr << "Something went horribly wrong with command line parsing. This is a bug." << std::endl;
- return false;
- }
- }
-
- return true;
- }
-
- bool DoesFileExist(const std::string &path)
- {
- std::ifstream f(path);
- return f.good();
- }
-
-}
+#include "Helpers.h" +#include <string> +#include <fstream> +#include <sstream> +#include <iostream> +#include <vector> +#include <png.h> +#include <cmath> +#include <unordered_map> +#include <string> +#include <algorithm> + +namespace Helpers +{ + GLuint LoadShaders(const std::string& vertex_file_path, const std::string& fragment_file_path) { + + // Create the shaders + GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); + GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + + // Read the Vertex Shader code from the file + std::string VertexShaderCode; + std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); + if (VertexShaderStream.is_open()) { + std::stringstream sstr; + sstr << VertexShaderStream.rdbuf(); + VertexShaderCode = sstr.str(); + VertexShaderStream.close(); + } + else { + std::cerr << "Failed to load vertex shader file from " << vertex_file_path << ". Is this installed correctly?" << std::endl; + return 0; + } + + // Read the Fragment Shader code from the file + std::string FragmentShaderCode; + std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); + if (FragmentShaderStream.is_open()) { + std::stringstream sstr; + sstr << FragmentShaderStream.rdbuf(); + FragmentShaderCode = sstr.str(); + FragmentShaderStream.close(); + } + else + { + std::cerr << "Failed to load fragment shader file from " << fragment_file_path << ". Is this installed correctly?" << std::endl; + return 0; + } + + GLint Result = GL_FALSE; + int InfoLogLength; + + // Compile Vertex Shader + //std::cout << "Compiling shader : " << vertex_file_path << std::endl; + char const * VertexSourcePointer = VertexShaderCode.c_str(); + glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); + glCompileShader(VertexShaderID); + + // Check Vertex Shader + glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (InfoLogLength > 0) { + std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1); + glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); + printf("%s\n", &VertexShaderErrorMessage[0]); + } + + + // Compile Fragment Shader + //std::cout << "Compiling shader : " << fragment_file_path << std::endl; + char const * FragmentSourcePointer = FragmentShaderCode.c_str(); + glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); + glCompileShader(FragmentShaderID); + + // Check Fragment Shader + glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (InfoLogLength > 0) { + std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1); + glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); + printf("%s\n", &FragmentShaderErrorMessage[0]); + } + + // Link the program + //printf("Linking program\n"); + GLuint ProgramID = glCreateProgram(); + glAttachShader(ProgramID, VertexShaderID); + glAttachShader(ProgramID, FragmentShaderID); + glLinkProgram(ProgramID); + + // Check the program + glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (InfoLogLength > 0) { + std::vector<char> ProgramErrorMessage(InfoLogLength + 1); + glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); + printf("%s\n", &ProgramErrorMessage[0]); + } + + glDetachShader(ProgramID, VertexShaderID); + glDetachShader(ProgramID, FragmentShaderID); + + glDeleteShader(VertexShaderID); + glDeleteShader(FragmentShaderID); + + return ProgramID; + } + + GLuint LoadComputeShader(const std::string& compute_file_path, unsigned int localSizeX, unsigned int localSizeY, unsigned int localSizeZ) + { + GLuint ComputeShaderID = glCreateShader(GL_COMPUTE_SHADER); + // Read the compute shader + std::string ComputeShaderCode; + { + std::ifstream ComputeShaderCodeStream(compute_file_path, std::ios::in); + if (ComputeShaderCodeStream.is_open()) { + std::stringstream sstr; + sstr << "#version 430" << + std::endl << + "layout (local_size_x = " << localSizeX << + ", local_size_y = " << localSizeY << + ", local_size_z = " << localSizeZ << ") in;" << std::endl; + sstr << ComputeShaderCodeStream.rdbuf(); + ComputeShaderCode = sstr.str(); + ComputeShaderCodeStream.close(); + } + else + { + std::cerr << "Failed to load compute shader file from " << compute_file_path << ". Is this installed correctly?" << std::endl; + return 0; + } + } + + GLint Result = GL_FALSE; + int InfoLogLength; + + { + // Compile Compute Shader + //std::cout << "Compiling shader : " << compute_file_path << std::endl; + char const * ComputeSourcePointer = ComputeShaderCode.c_str(); + glShaderSource(ComputeShaderID, 1, &ComputeSourcePointer, NULL); + glCompileShader(ComputeShaderID); + + // Check Compute Shader + glGetShaderiv(ComputeShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(ComputeShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (InfoLogLength > 0) { + std::vector<char> ComputeShaderErrorMessage(InfoLogLength + 1); + glGetShaderInfoLog(ComputeShaderID, InfoLogLength, NULL, &ComputeShaderErrorMessage[0]); + printf("%s\n", &ComputeShaderErrorMessage[0]); + } + } + GLuint ProgramID = glCreateProgram(); + glAttachShader(ProgramID, ComputeShaderID); + glLinkProgram(ProgramID); + + // Check the program + glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (InfoLogLength > 0) { + std::vector<char> ProgramErrorMessage(InfoLogLength + 1); + glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); + printf("%s\n", &ProgramErrorMessage[0]); + } + glDetachShader(ProgramID, ComputeShaderID); + glDeleteShader(ComputeShaderID); + return ProgramID; + } + + void WriteOutputPNG(const std::string &path, const std::vector<uint32_t>& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale) + { + std::vector<png_byte> pngData(3*width*2*bufferHeight); + std::vector<png_byte *> rows{2*bufferHeight}; + for(unsigned int i = 0; i < 2*bufferHeight ; ++i) + { + rows[i] = pngData.data()+3*width*i; + } + + uint32_t maxValue{UINT32_C(1)}; + for(unsigned int i = 0; i < data.size();++i) + { + maxValue = std::max(maxValue,data[i]); + } + for(unsigned int i = 0; i < data.size();++i) + { + if(fabs(gamma - 1.0) > 0.0001 || fabs(colorScale - 1.0) > 0.0001) + { + pngData[data.size() + i] = static_cast<png_byte>(255.0 * pow(std::min(1.0,colorScale*static_cast<double>(data[i])/static_cast<double>(maxValue)),gamma)); + } + else + { + pngData[data.size() + i] = (255*data[i] + (maxValue/2))/maxValue; + } + } + for(unsigned int i = 0; i < bufferHeight;++i) + { + for(unsigned int j = 0; j < width*3;++j) + { + rows[i][j] =rows[2*bufferHeight-i-1][j]; + } + } + + ScopedCFileDescriptor fd(path.c_str(), "wb"); + if(!fd.IsValid()) + { + std::cerr << "Failed to open image.png for writing." << std::endl; + return; + } + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if(!png_ptr) + { + return; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return; + } + if(setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + return; + } + png_init_io(png_ptr, fd.Get()); + png_set_IHDR(png_ptr, info_ptr, width, 2*bufferHeight, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr, info_ptr); + //header written. + + png_write_image(png_ptr, rows.data()); + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + } + + ScopedCFileDescriptor::ScopedCFileDescriptor(const char *path, const char *mode) + { + Descriptor = fopen(path,mode); + } + + ScopedCFileDescriptor::~ScopedCFileDescriptor() + { + if(IsValid()) + fclose(Descriptor); + } + + FILE * ScopedCFileDescriptor::Get() const + { + return Descriptor; + } + + bool ScopedCFileDescriptor::IsValid() const + { + return Descriptor != nullptr; + } + + bool RenderSettings::CheckValidity() + { + if(imageHeight%2 != 0) + { + std::cerr << "Image height has to be an even number." << std::endl; + return false; + } + int maxSSBOSize; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE,&maxSSBOSize); + if((static_cast<unsigned long>(imageWidth) * static_cast<unsigned long>(imageHeight)) > static_cast<unsigned long>(maxSSBOSize)/2) //divided by 2, as we have 4 bytes per int, but only half the image height + { + std::cerr << "Requested buffer size is larger than maximum allowed by graphics driver. Max pixel number: " << maxSSBOSize/2 << std::endl; + std::cerr << "You can override this limit check using the --ignoreMaxBufferSize 1 command line parameter, but doing so is your own risk." << std::endl; + if(ignoreMaxBufferSize == 0) + return false; + } + int WorkGroupSizeLimitX, WorkGroupSizeLimitY, WorkGroupSizeLimitZ; + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,0,&WorkGroupSizeLimitX); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,1,&WorkGroupSizeLimitY); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT,2,&WorkGroupSizeLimitZ); + if(globalWorkGroupSizeX > static_cast<unsigned int>(WorkGroupSizeLimitX) || + globalWorkGroupSizeY > static_cast<unsigned int>(WorkGroupSizeLimitY) || + globalWorkGroupSizeZ > static_cast<unsigned int>(WorkGroupSizeLimitZ)) + { + std::cerr << "Requested global work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl; + return false; + } + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,0,&WorkGroupSizeLimitX); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,1,&WorkGroupSizeLimitY); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE,2,&WorkGroupSizeLimitZ); + if(localWorkgroupSizeX > static_cast<unsigned int>(WorkGroupSizeLimitX) || + localWorkgroupSizeY > static_cast<unsigned int>(WorkGroupSizeLimitY) || + localWorkgroupSizeZ > static_cast<unsigned int>(WorkGroupSizeLimitZ)) + { + std::cerr << "Requested local work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl; + return false; + } + int maxInvocations; + glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS,&maxInvocations); + if(static_cast<unsigned long>(localWorkgroupSizeX)*static_cast<unsigned long>(localWorkgroupSizeY)*static_cast<unsigned long>(localWorkgroupSizeZ) > static_cast<unsigned long>(maxInvocations)) + { + std::cerr << "Requested local work group size exceeds maximum total count (Product of x*y*z dimensions). Limit: " << maxInvocations << std::endl; + return false; + } + return true; + } + + bool RenderSettings::ParseCommandLine(int argc, char *argv[]) + { + struct SettingsPointer + { + public: + SettingsPointer(unsigned int * ptr) : intPtr(ptr), dblPtr(nullptr), stringPtr(nullptr) {} + SettingsPointer(double * ptr) : intPtr(nullptr), dblPtr(ptr), stringPtr(nullptr) {} + SettingsPointer(std::string * ptr) : intPtr(nullptr), dblPtr(nullptr), stringPtr(ptr) {} + unsigned int * GetIntPtr() const { return intPtr; } + double * GetDblPtr() const { return dblPtr;} + std::string * GetStringPtr() const { return stringPtr;} + private: + unsigned int * intPtr = nullptr; + double * dblPtr = nullptr; + std::string * stringPtr = nullptr; + }; + std::unordered_map<std::string, SettingsPointer> commandMap{ + {"--imageWidth",&imageWidth}, + {"--imageHeight",&imageHeight}, + {"--windowWidth",&windowWidth}, + {"--windowHeight",&windowHeight}, + {"--orbitLengthRed",&orbitLengthRed}, + {"--orbitLengthGreen",&orbitLengthGreen}, + {"--orbitLengthBlue",&orbitLengthBlue}, + {"--localWorkgroupSizeX", &localWorkgroupSizeX}, + {"--localWorkgroupSizeY", &localWorkgroupSizeY}, + {"--localWorkgroupSizeZ", &localWorkgroupSizeZ}, + {"--globalWorkgroupSizeX", &globalWorkGroupSizeX}, + {"--globalWorkgroupSizeY", &globalWorkGroupSizeY}, + {"--globalWorkgroupSizeZ", &globalWorkGroupSizeZ}, + {"--targetFrameRate", &targetFrameRate}, + {"--imageGamma",&pngGamma}, + {"--imageColorScale",&pngColorScale}, + {"--output", &pngFilename}, + {"--ignoreMaxBufferSize", &ignoreMaxBufferSize} + }; + + for(int i=1; i < argc;++i) + { + std::string argAsString(argv[i]); + if(argAsString == "--help") //help is a special case. + { + std::cout << "Draws a buddhabrot and iterates until the user closes the window. If a --output filename is given, a png file will afterwards be written there." <<std::endl << + "Supported options are:" << std::endl << std::endl << + "--output [path] : File to write output to. Empty by default, meaning no output is written." << std::endl << + "--imageWidth [integer] : Width of the to be written image. 1024 by default. If no --output is given, this still detrmines the buffer size for rendering." << std::endl << + "--imageHeight [integer] : Height of the to be written image. 576 by default. If no --output is given, this still detrmines the buffer size for rendering." << std::endl << + "--imageGamma [float] : Gamma to use when writing the image. 1.0 by default. Ignored if no --output is given." << std::endl << + "--imageColorScale [float] : Image brightness is scaled by the brightest pixel. The result is multiplied by this value. 2.0 by default, as 1.0 leaves very little dynamic range." << std::endl << + "--windowWidth [integer] : Width of the preview window. 1024 by default." << std::endl << + "--windowHeight [integer] : Height of the preview window. 576 by default." << std::endl << + "--orbitLengthRed [integer] : Maximum number of iterations for escaping orbits to color red. 10 by default." << std::endl << + "--orbitLengthGreen [integer] : Maximum number of iterations for escaping orbits to color green. 100 by default." << std::endl << + "--orbitLengthBlue [integer] : Maximum number of iterations for escaping orbits to color blue. 1000 by default." << std::endl << + "--localWorkgroupSizeX [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default is 1024. Values up to 1024 are guaranteed to work." << std::endl << + "--localWorkgroupSizeY [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default is 1. Values up to 1024 are guaranteed to work." << std::endl << + "--localWorkgroupSizeZ [integer] : How \"parallel\" the computation should be. Maximum possible value depends on GPU and drivers. The default of 1. Values up to 1024 are guaranteed to work." << std::endl << + "\tNOTE: There's also a limit on the product of the three local workgroup sizes, for which a number smaller or equal to 1024 is guaranteed to work. Higher numbers might work and run faster. Feel free to experiment." << std::endl << + "--globalWorkgroupSizeX [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1024." << std::endl << + "--globalWorkgroupSizeY [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1." << std::endl << + "--globalWorkgroupSizeZ [integer] : How often the local work group should be invoked per frame. Values up to 65535 are guaranteed to work. Default is 1." << std::endl << + "--targetFrameRate [integer] : The number of iterations per frame will dynamically adjust to approximately reach this framerate. Default: 60." << std::endl << + "--ignoreMaxBufferSize [0,1] : If set to 1, a failed maximum buffer size check is not treated as error. Some graphics drivers report lower values than their absolute limit. Do this on your own risk, though." << std::endl; + return false; + } + } + //apart from --help all parameters consist of 2 entries. + if(argc%2 != 1) + { + std::cerr << "Invalid number of arguments. See --help for usage." << std::endl; + return false; + } + for(int i=1 ; i < argc; i += 2) + { + std::string argAsString(argv[i]); + std::string valueAsString(argv[i+1]); + auto setting = commandMap.find(argAsString); + if(setting == commandMap.end()) + { + std::cerr << "Unknown option: " << argAsString << std::endl; + return false; + } + const SettingsPointer& ptr = setting->second; + if(auto intProp = ptr.GetIntPtr()) + { + *intProp = std::stoi(valueAsString); + } + else if(auto dblProp = ptr.GetDblPtr()) + { + *dblProp = std::stod(valueAsString); + } + else if(auto strPtr = ptr.GetStringPtr()) + { + *strPtr = valueAsString; + } + else + { + std::cerr << "Something went horribly wrong with command line parsing. This is a bug." << std::endl; + return false; + } + } + + return true; + } + + bool DoesFileExist(const std::string &path) + { + std::ifstream f(path); + return f.good(); + } + +} |