diff options
author | Andreas Grois <andi@grois.info> | 2018-03-10 18:48:52 +0100 |
---|---|---|
committer | Andreas Grois <andi@grois.info> | 2018-03-10 18:48:52 +0100 |
commit | 5127d05dfdc877b0fc06e6ec228cfef09b98539a (patch) | |
tree | b19026c071757e87dad660a623b5862d88a02bd7 /BuddhaTest | |
parent | f57e2e048fbbea265c513769879e39c27b2b9c8e (diff) |
Add command line parsing
Diffstat (limited to 'BuddhaTest')
-rw-r--r-- | BuddhaTest/include/Helpers.h | 31 | ||||
-rw-r--r-- | BuddhaTest/src/BuddhaTest.cpp | 96 | ||||
-rw-r--r-- | BuddhaTest/src/Helpers.cpp | 154 |
3 files changed, 209 insertions, 72 deletions
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 <GLFW/glfw3.h>
#include <vector>
#include <stdio.h> //includes FILE typedef
+#include <string>
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<uint32_t>& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale);
+ void WriteOutputPNG(const std::string& path, const std::vector<uint32_t>& 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<uint32_t> 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<uint32_t> 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 <vector>
#include <png.h>
#include <cmath>
+#include <unordered_map>
+#include <string>
namespace Helpers
{
@@ -155,7 +157,7 @@ namespace Helpers return ProgramID;
}
- void WriteOutputPNG(const std::vector<uint32_t>& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale)
+ 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};
@@ -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<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},
+ {"--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." <<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. 1920 by default. Ignored if no --output is given." << std::endl <<
+ "--imageHeight [integer] : Height of the to be written image. 1080 by default. Ignored if no --output is given." << 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;
+ return false;
+ }
+ }
+ //apart from --help all parameters consist of 2 entries.
+ if(argc%2 != 1)
+ {
+ std::cout << "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::cout << "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;
+ }
+
}
|