From 5127d05dfdc877b0fc06e6ec228cfef09b98539a Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sat, 10 Mar 2018 18:48:52 +0100 Subject: Add command line parsing --- BuddhaTest/include/Helpers.h | 31 ++++++++- BuddhaTest/src/BuddhaTest.cpp | 96 ++++++++------------------ BuddhaTest/src/Helpers.cpp | 154 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 209 insertions(+), 72 deletions(-) (limited to 'BuddhaTest') diff --git a/BuddhaTest/include/Helpers.h b/BuddhaTest/include/Helpers.h index a25aa31..61bd7a0 100644 --- a/BuddhaTest/include/Helpers.h +++ b/BuddhaTest/include/Helpers.h @@ -3,13 +3,14 @@ #include #include #include //includes FILE typedef +#include namespace Helpers { GLuint LoadShaders(const char * vertex_file_path, const char * fragment_file_path); GLuint LoadComputeShader(const char * compute_file_path, unsigned int localSizeX, unsigned int localSizeY, unsigned int localSizeZ); - void WriteOutputPNG(const std::vector& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale); + void WriteOutputPNG(const std::string& path, const std::vector& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale); /** Wraps around a C file descriptor. Libpng could be taught to use C++ streams, but I'm too lazy and rather wrap this ugly thing up, so it gets cleaned... */ class ScopedCFileDescriptor @@ -22,4 +23,32 @@ namespace Helpers private: FILE * Descriptor; }; + + struct RenderSettings + { + unsigned int imageWidth = 1920; + unsigned int imageHeight = 1080; + + unsigned int windowWidth = 1024; + unsigned int windowHeight = 576; + + unsigned int orbitLengthRed = 10; + unsigned int orbitLengthGreen = 100; + unsigned int orbitLengthBlue = 1000; + + unsigned int localWorkgroupSizeX = 1024; + unsigned int localWorkgroupSizeY = 1; + unsigned int localWorkgroupSizeZ = 1; + + unsigned int globalWorkGroupSizeX = 1024; + unsigned int globalWorkGroupSizeY = 1; + unsigned int globalWorkGroupSizeZ = 1; + + std::string pngFilename = ""; + double pngGamma = 1.0; + double pngColorScale = 2.0; + + bool CheckValidity(); + bool ParseCommandLine(int argc, char * argv[]); + }; } diff --git a/BuddhaTest/src/BuddhaTest.cpp b/BuddhaTest/src/BuddhaTest.cpp index 0df3421..38bd0b5 100644 --- a/BuddhaTest/src/BuddhaTest.cpp +++ b/BuddhaTest/src/BuddhaTest.cpp @@ -9,28 +9,16 @@ void error_callback(int error, const char* description) std::cerr << "Error: " << description << std::endl; } -int main() +int main(int argc, char * argv[]) { - unsigned int bufferWidth = 6304; - unsigned int bufferHeight = 1773; + Helpers::RenderSettings settings; - unsigned int windowWidth = 1600; - unsigned int windowHeight = 900; - - unsigned int orbitLengthRed = 10; - unsigned int orbitLengthGreen = 100; - unsigned int orbitLengthBlue = 1000; - - unsigned int localWorkgroupSizeX = 1024; - unsigned int localWorkgroupSizeY = 1; - unsigned int localWorkgroupSizeZ = 1; - - unsigned int globalWorkGroupSizeX = 1024; - unsigned int globalWorkGroupSizeY = 1; - unsigned int globalWorkGroupSizeZ = 1; + if(!settings.ParseCommandLine(argc,argv)) + { + return 2; + } - double pngGamma = 1.0; - double pngColorScale = 2.0; + unsigned int bufferHeight = settings.imageHeight/2; GLFWwindow* window; @@ -45,7 +33,7 @@ int main() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); /* Create a windowed mode window and its OpenGL context */ - window = glfwCreateWindow(windowWidth, windowHeight, "Buddhabrot", NULL, NULL); + 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; @@ -59,46 +47,13 @@ int main() //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{(bufferWidth * bufferHeight)*3}; //*3 -> RGB + const unsigned int pixelCount{(settings.imageWidth * bufferHeight)*3}; //*3 -> RGB + if(!settings.CheckValidity()) { - int maxSSBOSize; - glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE,&maxSSBOSize); - if(pixelCount * 4 > maxSSBOSize) - { - std::cout << "Requested buffer size larger than maximum allowed by graphics driver. Max pixel number: " << maxSSBOSize/12 << std::endl; - glfwTerminate(); - return 0; - } - 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 > WorkGroupSizeLimitX || globalWorkGroupSizeY > WorkGroupSizeLimitY || globalWorkGroupSizeZ > WorkGroupSizeLimitZ) - { - std::cout << "Requested global work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl; - glfwTerminate(); - return 0; - } - 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 > WorkGroupSizeLimitX || localWorkgroupSizeY > WorkGroupSizeLimitY || localWorkgroupSizeZ > WorkGroupSizeLimitZ) - { - std::cout << "Requested local work group size exceeds maximum dimension. Limits: " << WorkGroupSizeLimitX << ", " << WorkGroupSizeLimitY << ", " << WorkGroupSizeLimitZ << std::endl; - glfwTerminate(); - return 0; - } - int maxInvocations; - glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS,&maxInvocations); - if(localWorkgroupSizeX*localWorkgroupSizeY*localWorkgroupSizeZ > maxInvocations) - { - std::cout << "Requested local work group size exceeds maximum total count (Product of x*y*z dimensions). Limit: " << maxInvocations << std::endl; - glfwTerminate(); - return 0; - } + glfwTerminate(); + return 1; } - GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); @@ -129,7 +84,7 @@ int main() // Create and compile our GLSL program from the shaders GLuint VertexAndFragmentShaders = Helpers::LoadShaders("Shaders/BuddhaVertex.glsl", "Shaders/BuddhaFragment.glsl"); //Do the same for the compute shader: - GLuint ComputeShader = Helpers::LoadComputeShader("Shaders/BuddhaCompute.glsl", localWorkgroupSizeX, localWorkgroupSizeY, localWorkgroupSizeZ); + GLuint ComputeShader = Helpers::LoadComputeShader("Shaders/BuddhaCompute.glsl", settings.localWorkgroupSizeX, settings.localWorkgroupSizeY, settings.localWorkgroupSizeZ); uint32_t iterationCount{0}; glUseProgram(ComputeShader); @@ -137,15 +92,15 @@ int main() GLint orbitLengthUniformHandle = glGetUniformLocation(ComputeShader, "orbitLength"); GLint widthUniformComputeHandle = glGetUniformLocation(ComputeShader, "width"); GLint heightUniformComputeHandle = glGetUniformLocation(ComputeShader, "height"); - glUniform3ui(orbitLengthUniformHandle,orbitLengthRed,orbitLengthGreen,orbitLengthBlue); - glUniform1ui(widthUniformComputeHandle, bufferWidth); - glUniform1ui(heightUniformComputeHandle,bufferHeight); + 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, bufferWidth); - glUniform1ui(heightUniformFragmentHandle,bufferHeight); + glUniform1ui(widthUniformFragmentHandle, settings.imageWidth); + glUniform1ui(heightUniformFragmentHandle, bufferHeight); glClearColor(0.0f, 0.0f, 0.4f, 0.0f); @@ -156,7 +111,7 @@ int main() glUseProgram(ComputeShader); //increase iterationCount, which is used for pseudo random generation glUniform1ui(iterationCountUniformHandle,++iterationCount); - glDispatchCompute(globalWorkGroupSizeX, globalWorkGroupSizeY, globalWorkGroupSizeZ); + 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 @@ -186,12 +141,15 @@ int main() glfwPollEvents(); } - glMemoryBarrier(GL_ALL_BARRIER_BITS); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer); + if(!settings.pngFilename.empty()) { - std::vector readBackBuffer(pixelCount); - glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0,4 * pixelCount,readBackBuffer.data()); - Helpers::WriteOutputPNG(readBackBuffer,bufferWidth,bufferHeight, pngGamma, pngColorScale); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawBuffer); + { + std::vector readBackBuffer(pixelCount); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0,4 * pixelCount,readBackBuffer.data()); + Helpers::WriteOutputPNG(settings.pngFilename,readBackBuffer,settings.imageWidth,bufferHeight, settings.pngGamma, settings.pngColorScale); + } } //a bit of cleanup diff --git a/BuddhaTest/src/Helpers.cpp b/BuddhaTest/src/Helpers.cpp index eacaf5d..889db7e 100644 --- a/BuddhaTest/src/Helpers.cpp +++ b/BuddhaTest/src/Helpers.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include namespace Helpers { @@ -155,7 +157,7 @@ namespace Helpers return ProgramID; } - void WriteOutputPNG(const std::vector& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale) + void WriteOutputPNG(const std::string &path, const std::vector& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale) { std::vector pngData(3*width*2*bufferHeight); std::vector rows{2*bufferHeight}; @@ -188,7 +190,7 @@ namespace Helpers } } - ScopedCFileDescriptor fd("image.png", "wb"); + ScopedCFileDescriptor fd(path.c_str(), "wb"); if(!fd.IsValid()) { std::cout << "Failed to open image.png for writing." << std::endl; @@ -243,4 +245,152 @@ namespace Helpers return Descriptor != nullptr; } + bool RenderSettings::CheckValidity() + { + if(imageHeight%2 != 0) + { + std::cout << "Image height has to be an even number." << std::endl; + return false; + } + const unsigned int bufferHeight = imageHeight/2; + const unsigned int pixelCount{(imageWidth * imageHeight/2)*3}; //*3 -> RGB + int maxSSBOSize; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE,&maxSSBOSize); + if(pixelCount * 4 > maxSSBOSize) + { + std::cout << "Requested buffer size larger than maximum allowed by graphics driver. Max pixel number: " << maxSSBOSize/12 << std::endl; + 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 > WorkGroupSizeLimitX || + globalWorkGroupSizeY > WorkGroupSizeLimitY || + globalWorkGroupSizeZ > WorkGroupSizeLimitZ) + { + std::cout << "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 > WorkGroupSizeLimitX || + localWorkgroupSizeY > WorkGroupSizeLimitY || + localWorkgroupSizeZ > WorkGroupSizeLimitZ) + { + std::cout << "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(localWorkgroupSizeX*localWorkgroupSizeY*localWorkgroupSizeZ > maxInvocations) + { + std::cout << "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 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}, + {"--imageGamma",&pngGamma}, + {"--imageColorScale",&pngColorScale}, + {"--output", &pngFilename} + }; + + 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." <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; + } + } -- cgit v1.2.3