aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2018-03-17 17:51:19 +0100
committerAndreas Grois <andi@grois.info>2018-03-17 17:51:19 +0100
commitb05e87dc5004f5badc02508570ddc55f31b451cc (patch)
tree98448778398e9d452b8ebc686f16d83e8211e323
parent5a9c8599daee7d1d569ade3647d0078e56123c7a (diff)
Correct line endings.
-rw-r--r--BuddhaTest/CMakeLists.txt42
-rw-r--r--BuddhaTest/Shaders/BuddhaCompute.glsl538
-rw-r--r--BuddhaTest/Shaders/BuddhaFragment.glsl74
-rw-r--r--BuddhaTest/Shaders/BuddhaVertex.glsl20
-rw-r--r--BuddhaTest/include/Helpers.h168
-rw-r--r--BuddhaTest/src/BuddhaTest.cpp460
-rw-r--r--BuddhaTest/src/Helpers.cpp832
-rw-r--r--CMakeLists.txt20
8 files changed, 1077 insertions, 1077 deletions
diff --git a/BuddhaTest/CMakeLists.txt b/BuddhaTest/CMakeLists.txt
index 72230af..dc4deae 100644
--- a/BuddhaTest/CMakeLists.txt
+++ b/BuddhaTest/CMakeLists.txt
@@ -1,21 +1,21 @@
-add_executable(
- BuddhaShader
- "src/BuddhaTest.cpp"
- "src/glad.c"
- "src/Helpers.cpp"
-)
-
-configure_file("Shaders/BuddhaFragment.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaFragment.glsl)
-configure_file("Shaders/BuddhaCompute.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaCompute.glsl)
-configure_file("Shaders/BuddhaVertex.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaVertex.glsl)
-
-find_package(OpenGL REQUIRED)
-find_package(PNG REQUIRED)
-target_include_directories(BuddhaShader PRIVATE "include" ${OPENGL_INCLUDE_DIR} ${PNG_INCLUDE_DIRS})
-target_link_libraries(BuddhaShader glfw ${OPENGL_gl_LIBRARY} ${PNG_LIBRARIES})
-add_definitions(${PNG_DEFINITIONS})
-add_definitions(-DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
-# on Linux we need to link against libdl. Maybe add id here?
-
-install(DIRECTORY Shaders DESTINATION share/BuddhaShader FILES_MATCHING PATTERN *.glsl)
-install(TARGETS BuddhaShader RUNTIME DESTINATION bin)
+add_executable(
+ BuddhaShader
+ "src/BuddhaTest.cpp"
+ "src/glad.c"
+ "src/Helpers.cpp"
+)
+
+configure_file("Shaders/BuddhaFragment.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaFragment.glsl)
+configure_file("Shaders/BuddhaCompute.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaCompute.glsl)
+configure_file("Shaders/BuddhaVertex.glsl" ${CMAKE_CURRENT_BINARY_DIR}/Shaders/BuddhaVertex.glsl)
+
+find_package(OpenGL REQUIRED)
+find_package(PNG REQUIRED)
+target_include_directories(BuddhaShader PRIVATE "include" ${OPENGL_INCLUDE_DIR} ${PNG_INCLUDE_DIRS})
+target_link_libraries(BuddhaShader glfw ${OPENGL_gl_LIBRARY} ${PNG_LIBRARIES})
+add_definitions(${PNG_DEFINITIONS})
+add_definitions(-DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
+# on Linux we need to link against libdl. Maybe add id here?
+
+install(DIRECTORY Shaders DESTINATION share/BuddhaShader FILES_MATCHING PATTERN *.glsl)
+install(TARGETS BuddhaShader RUNTIME DESTINATION bin)
diff --git a/BuddhaTest/Shaders/BuddhaCompute.glsl b/BuddhaTest/Shaders/BuddhaCompute.glsl
index c94df12..61bbb6d 100644
--- a/BuddhaTest/Shaders/BuddhaCompute.glsl
+++ b/BuddhaTest/Shaders/BuddhaCompute.glsl
@@ -1,269 +1,269 @@
-//commented out, added by c-code that loads this shader.
-//#version 430
-//layout (local_size_x = 1024) in; //to be safe, we limit our local work group size to 1024. That's the minimum a GL 4.3 capable driver must support.
-
-layout(std430, binding=2) restrict buffer renderedDataRed
-{
- restrict uint counts_SSBORed[];
-};
-layout(std430, binding=3) restrict buffer renderedDataGreen
-{
- restrict uint counts_SSBOGreen[];
-};
-layout(std430, binding=4) restrict buffer renderedDataBlue
-{
- restrict uint counts_SSBOBlue[];
-};
-
-struct individualData
-{
- uint phase;
- uint orbitNumber;
- uint doneIterations;
- vec2 lastPosition;
-};
-
-layout(std430, binding=5) restrict buffer statusBuffer
-{
- restrict individualData state[];
-};
-
-uniform uint width;
-uniform uint height;
-
-uniform uvec3 orbitLength;
-
-uniform uint iterationsPerDispatch;
-
-void addToColorOfCell(uvec2 cell, uvec3 toAdd)
-{
- uint firstIndex = (cell.x + cell.y * width);
- atomicAdd(counts_SSBORed[firstIndex],toAdd.x);
- atomicAdd(counts_SSBOGreen[firstIndex],toAdd.y);
- atomicAdd(counts_SSBOBlue[firstIndex],toAdd.z);
-}
-
-uvec2 getCell(vec2 complex)
-{
- vec2 uv = clamp(vec2((complex.x+2.5)/3.5, (abs(complex.y))),vec2(0.0),vec2(1.0));
- return uvec2(width * uv.x, height * uv.y);
-}
-
-void addToColorAt(vec2 complex, uvec3 toAdd)
-{
- uvec2 cell = getCell(complex);
- addToColorOfCell(cell,toAdd);
-}
-
-uint intHash(uint x) {
- x = ((x >> 16) ^ x) * 0x45d9f3bU;
- x = ((x >> 16) ^ x) * 0x45d9f3bU;
- x = (x >> 16) ^ x;
- return x;
-}
-
-float hash1(uint seed, out uint hash)
-{
- hash = intHash(seed);
- return float(hash)/float(0xffffffffU);
-}
-
-vec2 compSqr(in vec2 v)
-{
- return vec2(v.x*v.x-v.y*v.y, 2.0*v.x*v.y);
-}
-
-bool isInMainCardioid(vec2 v)
-{
- /*
- The condition that a point c is in the main cardioid is that its orbit has
- an attracting fixed point. In other words, it must fulfill
- z**2 -z + v = 0 (z**2+v has a fixed point at z)
- and
- d/dz(z**2+v) < 1 (fixed point at z is attractive)
- Solving these equations yields
- v = u/2*(1-u/2), where u is a complex number inside the unit circle
-
- Sadly we only know v, not u, and inverting this formula leads to a complex square root.
- This is the old code that uses the compSqrt method, which is rather slow...
-
- vec2 toRoot = (vec2(1.0,0.0)-vec2(4.0)*v);
- vec2 root = compSqrt(toRoot);
- vec2 t1,t2;
- t1=vec2(1,0)-root;
- t2=vec2(1,0)+root;
- float retval = step(dot(t1,t1),0.99999);
- retval += step(dot(t2,t2),0.99999);
- retval = min(retval,1.0);
- return retval;
-
- On several websites (and several mandelbrot-related shaders on ShaderToy) one can
- find various faster formulas that check the same inequality.
- What however is hard to find is the actual derivation of those formulas,
- and they are not as trivial as one might think at first.
- That's why I'm writing this lengthy comment, to preserve the scrap notes I made
- for future reuse by myself and others...
-
- Now the actual derivation looks like this:
- We start with the following line
- u = 1 +- sqrt(1-4*v)
- don't mind the +-, that's just me being too lazy to check which solution is the right one
- We know that |u| < 1, and we immediately square the whole beast to get rid of the root
- Some definitions: r := sqrt(1-4*v), z = 1-4*v
-
- 1 > Re(u)**2+Im(u)**2 = (1 +- Re(r))**2+Im(r)**2
- 1 > 1 +- 2*Re(r) + Re(r)**2+Im(r)**2
- 1 > 1 +- 2*Re(r) + |r|**2
- For complex values the square root of the norm is the same as the norm of the square root
- 1 > 1 +- 2*Re(r) + |z|
- +-2*Re(r) > |z|
- 4*Re(r)**2 > |z|**2
- This step is now a bit arcane. If one solves the two coupled equations
- (a+i*b) = (x+i*y)*(x+i*y) component-wise for x and y,
- one can see that x**2 = (|a+i*b|+a)/2
- With this follows
- 2*(|z|+Re(z)) > |z|**2
- |z| > 1/2*|z|**2-Re(z)
- |z|**2 > (1/2*|z|**2-Re(z))**2
-
- And long story short, the result is the following few operations.
- */
- vec2 z = vec2(1.0,0.0)-4.0*v;
- float zNormSqr = dot(z,z);
- float rhsSqrt = 0.5*zNormSqr - z.x;
- return rhsSqrt*rhsSqrt<zNormSqr;
-}
-
-bool isInMainBulb(vec2 v)
-{
- //The condition for this is that f(f(z)) = z
- //where f(z) = z*z+v
- //This is an equation of fourth order, but two solutions are the same as
- //for f(z) = z, which has been solved for the cardioid above.
- //Factoring those out, you end up with a second order equation.
- //Again, the solutions need to be attractive (|d/dz f(f(z))| < 1)
- //Well, after a bit of magic one finds out it's a circle around -1, radius 1/4.
- vec2 shifted = v + vec2(1,0);
- float sqrRadius = dot(shifted,shifted);
- return sqrRadius<0.062499999;
-}
-
-bool isGoingToBeDrawn(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations, out bool result)
-{
- uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
- for(uint i = doneIterations; i < endCount;++i)
- {
- lastVal = compSqr(lastVal) + offset;
- if(dot(lastVal,lastVal) > 4.0)
- {
- result = true;
- iterationsLeftThisFrame -= ((i+1)-doneIterations);
- doneIterations = i+1;
- return true;
- }
- }
- iterationsLeftThisFrame -= (endCount - doneIterations);
- doneIterations = endCount;
- result = false;
- return endCount == totalIterations;
-}
-
-bool drawOrbit(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations)
-{
- uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
- for(uint i = doneIterations; i < endCount;++i)
- {
- lastVal = compSqr(lastVal) + offset;
- if(dot(lastVal,lastVal) > 20.0)
- {
- iterationsLeftThisFrame -= ((i+1)-doneIterations);
- doneIterations = i+1;
- return true; //done.
- }
- if(lastVal.x > -2.5 && lastVal.x < 1.0 && lastVal.y > -1.0 && lastVal.y < 1.0)
- {
- addToColorAt(lastVal,uvec3(i < orbitLength.r,i < orbitLength.g,i < orbitLength.b));
- }
- }
- iterationsLeftThisFrame -= (endCount - doneIterations);
- doneIterations = endCount;
- return endCount == totalIterations;
-}
-
-vec2 getCurrentOrbitOffset(const uint orbitNumber, const uint totalWorkers, const uint uniqueWorkerID)
-{
- uint seed = orbitNumber * totalWorkers + uniqueWorkerID;
- float x = hash1(seed,seed);
- seed = (seed ^ (intHash(orbitNumber+totalWorkers)));
- float y = hash1(seed,seed);
- vec2 random = vec2(x,y);
- return vec2(random.x * 3.5-2.5,random.y*1.55);
-}
-
-void main() {
- //we need to know how many total work groups are running this iteration
-
- const uvec3 totalWorkersPerDimension = gl_WorkGroupSize * gl_NumWorkGroups;
- const uint totalWorkers = totalWorkersPerDimension.x*totalWorkersPerDimension.y*totalWorkersPerDimension.z;
-
- const uint uniqueWorkerID = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y*totalWorkersPerDimension.x + gl_GlobalInvocationID.z*(totalWorkersPerDimension.x * totalWorkersPerDimension.y);
-
- const uint _totalIterations = orbitLength.x > orbitLength.y ? orbitLength.x : orbitLength.y;
- const uint totalIterations = _totalIterations > orbitLength.z ? _totalIterations : orbitLength.z;
-
- //getIndividualState(in uint CellID, out vec2 offset, out vec2 coordinates, out uint phase, out uint orbitNumber, out uint doneIterations)
- uint iterationsLeftToDo = iterationsPerDispatch;
- vec2 offset = getCurrentOrbitOffset(state[uniqueWorkerID].orbitNumber, totalWorkers, uniqueWorkerID);
-
- while(iterationsLeftToDo != 0)
- {
- if(state[uniqueWorkerID].phase == 0)
- {
- //new orbit:
- //we know that iterationsLeftToDo is at least 1 by the while condition.
- --iterationsLeftToDo; //count this as 1 iteration.
- offset = getCurrentOrbitOffset(state[uniqueWorkerID].orbitNumber, totalWorkers, uniqueWorkerID);
- if(isInMainBulb(offset) || isInMainCardioid(offset))
- {
- // do not waste time drawing this orbit
- ++state[uniqueWorkerID].orbitNumber;
- }
- else
- {
- //cool orbit!
- state[uniqueWorkerID].lastPosition = vec2(0);
- state[uniqueWorkerID].phase = 1;
- state[uniqueWorkerID].doneIterations = 0;
- }
- }
- if(state[uniqueWorkerID].phase == 1)
- {
- //check if this orbit is going to be drawn
- bool result;
- if(isGoingToBeDrawn(offset,totalIterations, state[uniqueWorkerID].lastPosition, iterationsLeftToDo, state[uniqueWorkerID].doneIterations , result))
- {
- if(result)
- {
- //on to step 2: drawing
- state[uniqueWorkerID].phase = 2;
- state[uniqueWorkerID].lastPosition = vec2(0);
- state[uniqueWorkerID].doneIterations = 0;
- }
- else
- {
- //back to step 0
- ++state[uniqueWorkerID].orbitNumber;
- state[uniqueWorkerID].phase = 0;
- }
- }
- }
- if(state[uniqueWorkerID].phase == 2)
- {
- if(drawOrbit(offset, totalIterations, state[uniqueWorkerID].lastPosition, iterationsLeftToDo, state[uniqueWorkerID].doneIterations))
- {
- ++state[uniqueWorkerID].orbitNumber;
- state[uniqueWorkerID].phase = 0;
- }
- }
- }
-}
+//commented out, added by c-code that loads this shader.
+//#version 430
+//layout (local_size_x = 1024) in; //to be safe, we limit our local work group size to 1024. That's the minimum a GL 4.3 capable driver must support.
+
+layout(std430, binding=2) restrict buffer renderedDataRed
+{
+ restrict uint counts_SSBORed[];
+};
+layout(std430, binding=3) restrict buffer renderedDataGreen
+{
+ restrict uint counts_SSBOGreen[];
+};
+layout(std430, binding=4) restrict buffer renderedDataBlue
+{
+ restrict uint counts_SSBOBlue[];
+};
+
+struct individualData
+{
+ uint phase;
+ uint orbitNumber;
+ uint doneIterations;
+ vec2 lastPosition;
+};
+
+layout(std430, binding=5) restrict buffer statusBuffer
+{
+ restrict individualData state[];
+};
+
+uniform uint width;
+uniform uint height;
+
+uniform uvec3 orbitLength;
+
+uniform uint iterationsPerDispatch;
+
+void addToColorOfCell(uvec2 cell, uvec3 toAdd)
+{
+ uint firstIndex = (cell.x + cell.y * width);
+ atomicAdd(counts_SSBORed[firstIndex],toAdd.x);
+ atomicAdd(counts_SSBOGreen[firstIndex],toAdd.y);
+ atomicAdd(counts_SSBOBlue[firstIndex],toAdd.z);
+}
+
+uvec2 getCell(vec2 complex)
+{
+ vec2 uv = clamp(vec2((complex.x+2.5)/3.5, (abs(complex.y))),vec2(0.0),vec2(1.0));
+ return uvec2(width * uv.x, height * uv.y);
+}
+
+void addToColorAt(vec2 complex, uvec3 toAdd)
+{
+ uvec2 cell = getCell(complex);
+ addToColorOfCell(cell,toAdd);
+}
+
+uint intHash(uint x) {
+ x = ((x >> 16) ^ x) * 0x45d9f3bU;
+ x = ((x >> 16) ^ x) * 0x45d9f3bU;
+ x = (x >> 16) ^ x;
+ return x;
+}
+
+float hash1(uint seed, out uint hash)
+{
+ hash = intHash(seed);
+ return float(hash)/float(0xffffffffU);
+}
+
+vec2 compSqr(in vec2 v)
+{
+ return vec2(v.x*v.x-v.y*v.y, 2.0*v.x*v.y);
+}
+
+bool isInMainCardioid(vec2 v)
+{
+ /*
+ The condition that a point c is in the main cardioid is that its orbit has
+ an attracting fixed point. In other words, it must fulfill
+ z**2 -z + v = 0 (z**2+v has a fixed point at z)
+ and
+ d/dz(z**2+v) < 1 (fixed point at z is attractive)
+ Solving these equations yields
+ v = u/2*(1-u/2), where u is a complex number inside the unit circle
+
+ Sadly we only know v, not u, and inverting this formula leads to a complex square root.
+ This is the old code that uses the compSqrt method, which is rather slow...
+
+ vec2 toRoot = (vec2(1.0,0.0)-vec2(4.0)*v);
+ vec2 root = compSqrt(toRoot);
+ vec2 t1,t2;
+ t1=vec2(1,0)-root;
+ t2=vec2(1,0)+root;
+ float retval = step(dot(t1,t1),0.99999);
+ retval += step(dot(t2,t2),0.99999);
+ retval = min(retval,1.0);
+ return retval;
+
+ On several websites (and several mandelbrot-related shaders on ShaderToy) one can
+ find various faster formulas that check the same inequality.
+ What however is hard to find is the actual derivation of those formulas,
+ and they are not as trivial as one might think at first.
+ That's why I'm writing this lengthy comment, to preserve the scrap notes I made
+ for future reuse by myself and others...
+
+ Now the actual derivation looks like this:
+ We start with the following line
+ u = 1 +- sqrt(1-4*v)
+ don't mind the +-, that's just me being too lazy to check which solution is the right one
+ We know that |u| < 1, and we immediately square the whole beast to get rid of the root
+ Some definitions: r := sqrt(1-4*v), z = 1-4*v
+
+ 1 > Re(u)**2+Im(u)**2 = (1 +- Re(r))**2+Im(r)**2
+ 1 > 1 +- 2*Re(r) + Re(r)**2+Im(r)**2
+ 1 > 1 +- 2*Re(r) + |r|**2
+ For complex values the square root of the norm is the same as the norm of the square root
+ 1 > 1 +- 2*Re(r) + |z|
+ +-2*Re(r) > |z|
+ 4*Re(r)**2 > |z|**2
+ This step is now a bit arcane. If one solves the two coupled equations
+ (a+i*b) = (x+i*y)*(x+i*y) component-wise for x and y,
+ one can see that x**2 = (|a+i*b|+a)/2
+ With this follows
+ 2*(|z|+Re(z)) > |z|**2
+ |z| > 1/2*|z|**2-Re(z)
+ |z|**2 > (1/2*|z|**2-Re(z))**2
+
+ And long story short, the result is the following few operations.
+ */
+ vec2 z = vec2(1.0,0.0)-4.0*v;
+ float zNormSqr = dot(z,z);
+ float rhsSqrt = 0.5*zNormSqr - z.x;
+ return rhsSqrt*rhsSqrt<zNormSqr;
+}
+
+bool isInMainBulb(vec2 v)
+{
+ //The condition for this is that f(f(z)) = z
+ //where f(z) = z*z+v
+ //This is an equation of fourth order, but two solutions are the same as
+ //for f(z) = z, which has been solved for the cardioid above.
+ //Factoring those out, you end up with a second order equation.
+ //Again, the solutions need to be attractive (|d/dz f(f(z))| < 1)
+ //Well, after a bit of magic one finds out it's a circle around -1, radius 1/4.
+ vec2 shifted = v + vec2(1,0);
+ float sqrRadius = dot(shifted,shifted);
+ return sqrRadius<0.062499999;
+}
+
+bool isGoingToBeDrawn(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations, out bool result)
+{
+ uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
+ for(uint i = doneIterations; i < endCount;++i)
+ {
+ lastVal = compSqr(lastVal) + offset;
+ if(dot(lastVal,lastVal) > 4.0)
+ {
+ result = true;
+ iterationsLeftThisFrame -= ((i+1)-doneIterations);
+ doneIterations = i+1;
+ return true;
+ }
+ }
+ iterationsLeftThisFrame -= (endCount - doneIterations);
+ doneIterations = endCount;
+ result = false;
+ return endCount == totalIterations;
+}
+
+bool drawOrbit(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations)
+{
+ uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
+ for(uint i = doneIterations; i < endCount;++i)
+ {
+ lastVal = compSqr(lastVal) + offset;
+ if(dot(lastVal,lastVal) > 20.0)
+ {
+ iterationsLeftThisFrame -= ((i+1)-doneIterations);
+ doneIterations = i+1;
+ return true; //done.
+ }
+ if(lastVal.x > -2.5 && lastVal.x < 1.0 && lastVal.y > -1.0 && lastVal.y < 1.0)
+ {
+ addToColorAt(lastVal,uvec3(i < orbitLength.r,i < orbitLength.g,i < orbitLength.b));
+ }
+ }
+ iterationsLeftThisFrame -= (endCount - doneIterations);
+ doneIterations = endCount;
+ return endCount == totalIterations;
+}
+
+vec2 getCurrentOrbitOffset(const uint orbitNumber, const uint totalWorkers, const uint uniqueWorkerID)
+{
+ uint seed = orbitNumber * totalWorkers + uniqueWorkerID;
+ float x = hash1(seed,seed);
+ seed = (seed ^ (intHash(orbitNumber+totalWorkers)));
+ float y = hash1(seed,seed);
+ vec2 random = vec2(x,y);
+ return vec2(random.x * 3.5-2.5,random.y*1.55);
+}
+
+void main() {
+ //we need to know how many total work groups are running this iteration
+
+ const uvec3 totalWorkersPerDimension = gl_WorkGroupSize * gl_NumWorkGroups;
+ const uint totalWorkers = totalWorkersPerDimension.x*totalWorkersPerDimension.y*totalWorkersPerDimension.z;
+
+ const uint uniqueWorkerID = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y*totalWorkersPerDimension.x + gl_GlobalInvocationID.z*(totalWorkersPerDimension.x * totalWorkersPerDimension.y);
+
+ const uint _totalIterations = orbitLength.x > orbitLength.y ? orbitLength.x : orbitLength.y;
+ const uint totalIterations = _totalIterations > orbitLength.z ? _totalIterations : orbitLength.z;
+
+ //getIndividualState(in uint CellID, out vec2 offset, out vec2 coordinates, out uint phase, out uint orbitNumber, out uint doneIterations)
+ uint iterationsLeftToDo = iterationsPerDispatch;
+ vec2 offset = getCurrentOrbitOffset(state[uniqueWorkerID].orbitNumber, totalWorkers, uniqueWorkerID);
+
+ while(iterationsLeftToDo != 0)
+ {
+ if(state[uniqueWorkerID].phase == 0)
+ {
+ //new orbit:
+ //we know that iterationsLeftToDo is at least 1 by the while condition.
+ --iterationsLeftToDo; //count this as 1 iteration.
+ offset = getCurrentOrbitOffset(state[uniqueWorkerID].orbitNumber, totalWorkers, uniqueWorkerID);
+ if(isInMainBulb(offset) || isInMainCardioid(offset))
+ {
+ // do not waste time drawing this orbit
+ ++state[uniqueWorkerID].orbitNumber;
+ }
+ else
+ {
+ //cool orbit!
+ state[uniqueWorkerID].lastPosition = vec2(0);
+ state[uniqueWorkerID].phase = 1;
+ state[uniqueWorkerID].doneIterations = 0;
+ }
+ }
+ if(state[uniqueWorkerID].phase == 1)
+ {
+ //check if this orbit is going to be drawn
+ bool result;
+ if(isGoingToBeDrawn(offset,totalIterations, state[uniqueWorkerID].lastPosition, iterationsLeftToDo, state[uniqueWorkerID].doneIterations , result))
+ {
+ if(result)
+ {
+ //on to step 2: drawing
+ state[uniqueWorkerID].phase = 2;
+ state[uniqueWorkerID].lastPosition = vec2(0);
+ state[uniqueWorkerID].doneIterations = 0;
+ }
+ else
+ {
+ //back to step 0
+ ++state[uniqueWorkerID].orbitNumber;
+ state[uniqueWorkerID].phase = 0;
+ }
+ }
+ }
+ if(state[uniqueWorkerID].phase == 2)
+ {
+ if(drawOrbit(offset, totalIterations, state[uniqueWorkerID].lastPosition, iterationsLeftToDo, state[uniqueWorkerID].doneIterations))
+ {
+ ++state[uniqueWorkerID].orbitNumber;
+ state[uniqueWorkerID].phase = 0;
+ }
+ }
+ }
+}
diff --git a/BuddhaTest/Shaders/BuddhaFragment.glsl b/BuddhaTest/Shaders/BuddhaFragment.glsl
index 24edcd9..b33ee1d 100644
--- a/BuddhaTest/Shaders/BuddhaFragment.glsl
+++ b/BuddhaTest/Shaders/BuddhaFragment.glsl
@@ -1,37 +1,37 @@
-#version 430 core
-
-in vec2 uv;
-
-out vec3 color;
-
-layout(std430, binding=2) restrict readonly buffer renderedDataRed
-{
- restrict readonly uint counts_SSBORed[];
-};
-layout(std430, binding=3) restrict readonly buffer renderedDataGreen
-{
- restrict readonly uint counts_SSBOGreen[];
-};
-layout(std430, binding=4) restrict readonly buffer renderedDataBlue
-{
- restrict readonly uint counts_SSBOBlue[];
-};
-
-uniform uint width;
-uniform uint height;
-
-uvec3 getColorAt(vec2 fragCoord)
-{
- uint xIndex = uint(max(0.0,(fragCoord.x+1.0)*0.5*width));
- uint yIndex = uint(max(0.0,abs(fragCoord.y)*height));
- uint firstIndex = (xIndex + yIndex * width);
- return uvec3(counts_SSBORed[firstIndex],counts_SSBOGreen[firstIndex],counts_SSBOBlue[firstIndex]);
-}
-
-void main(){
- uvec3 totalCount = getColorAt(uv);
- uvec3 brightness = getColorAt(vec2(-0.2390625,0));
-
- vec3 scaled = vec3(totalCount)/max(length(vec3(brightness)),1.0);
- color = scaled;
-}
+#version 430 core
+
+in vec2 uv;
+
+out vec3 color;
+
+layout(std430, binding=2) restrict readonly buffer renderedDataRed
+{
+ restrict readonly uint counts_SSBORed[];
+};
+layout(std430, binding=3) restrict readonly buffer renderedDataGreen
+{
+ restrict readonly uint counts_SSBOGreen[];
+};
+layout(std430, binding=4) restrict readonly buffer renderedDataBlue
+{
+ restrict readonly uint counts_SSBOBlue[];
+};
+
+uniform uint width;
+uniform uint height;
+
+uvec3 getColorAt(vec2 fragCoord)
+{
+ uint xIndex = uint(max(0.0,(fragCoord.x+1.0)*0.5*width));
+ uint yIndex = uint(max(0.0,abs(fragCoord.y)*height));
+ uint firstIndex = (xIndex + yIndex * width);
+ return uvec3(counts_SSBORed[firstIndex],counts_SSBOGreen[firstIndex],counts_SSBOBlue[firstIndex]);
+}
+
+void main(){
+ uvec3 totalCount = getColorAt(uv);
+ uvec3 brightness = getColorAt(vec2(-0.2390625,0));
+
+ vec3 scaled = vec3(totalCount)/max(length(vec3(brightness)),1.0);
+ color = scaled;
+}
diff --git a/BuddhaTest/Shaders/BuddhaVertex.glsl b/BuddhaTest/Shaders/BuddhaVertex.glsl
index 686eeb4..58ae10d 100644
--- a/BuddhaTest/Shaders/BuddhaVertex.glsl
+++ b/BuddhaTest/Shaders/BuddhaVertex.glsl
@@ -1,11 +1,11 @@
-#version 430 core
-
-layout(location = 0) in vec3 vertexPosition_modelspace;
-
-out vec2 uv;
-
-void main(){
- gl_Position.xyz = vertexPosition_modelspace;
- gl_Position.w = 1.0;
- uv = gl_Position.xy;
+#version 430 core
+
+layout(location = 0) in vec3 vertexPosition_modelspace;
+
+out vec2 uv;
+
+void main(){
+ gl_Position.xyz = vertexPosition_modelspace;
+ gl_Position.w = 1.0;
+ uv = gl_Position.xy;
} \ No newline at end of file
diff --git a/BuddhaTest/include/Helpers.h b/BuddhaTest/include/Helpers.h
index 76f790b..a585b88 100644
--- a/BuddhaTest/include/Helpers.h
+++ b/BuddhaTest/include/Helpers.h
@@ -1,84 +1,84 @@
-#pragma once
-#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-#include <vector>
-#include <stdio.h> //includes FILE typedef
-#include <string>
-
-namespace Helpers
-{
- GLuint LoadShaders(const std::string &vertex_file_path, const std::string &fragment_file_path);
- GLuint LoadComputeShader(const std::string &compute_file_path, unsigned int localSizeX, unsigned int localSizeY, unsigned int localSizeZ);
-
- bool DoesFileExist(const std::string& path);
-
- 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
- {
- public:
- ScopedCFileDescriptor(const char *path, const char *mode);
- ~ScopedCFileDescriptor();
- FILE * Get() const;
- bool IsValid() const;
- private:
- FILE * Descriptor;
- };
-
- struct RenderSettings
- {
- unsigned int imageWidth = 1024;
- unsigned int imageHeight = 576;
-
- 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;
-
- unsigned int targetFrameRate = 60;
-
- std::string pngFilename = "";
- double pngGamma = 1.0;
- double pngColorScale = 2.0;
-
- unsigned int ignoreMaxBufferSize = 0;
-
- bool CheckValidity();
- bool ParseCommandLine(int argc, char * argv[]);
- };
-
- template<typename ValueType, typename TimeType>
- class PIDController
- {
- public:
- PIDController(ValueType prop, ValueType diff, ValueType integral) : propFactor(prop), diffFactor(diff), intFactor(integral) {}
-
- ValueType Update(TimeType dT, ValueType Error)
- {
- errorSum += Error * dT;
- const auto differential{(Error - lastError)/dT};
- lastError = Error;
- return Error * propFactor + errorSum * intFactor + differential * diffFactor;
- }
-
- protected:
- ValueType propFactor{};
- ValueType diffFactor{};
- ValueType intFactor{};
-
- private:
- ValueType errorSum{};
- ValueType lastError{};
- };
-}
+#pragma once
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
+#include <vector>
+#include <stdio.h> //includes FILE typedef
+#include <string>
+
+namespace Helpers
+{
+ GLuint LoadShaders(const std::string &vertex_file_path, const std::string &fragment_file_path);
+ GLuint LoadComputeShader(const std::string &compute_file_path, unsigned int localSizeX, unsigned int localSizeY, unsigned int localSizeZ);
+
+ bool DoesFileExist(const std::string& path);
+
+ 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
+ {
+ public:
+ ScopedCFileDescriptor(const char *path, const char *mode);
+ ~ScopedCFileDescriptor();
+ FILE * Get() const;
+ bool IsValid() const;
+ private:
+ FILE * Descriptor;
+ };
+
+ struct RenderSettings
+ {
+ unsigned int imageWidth = 1024;
+ unsigned int imageHeight = 576;
+
+ 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;
+
+ unsigned int targetFrameRate = 60;
+
+ std::string pngFilename = "";
+ double pngGamma = 1.0;
+ double pngColorScale = 2.0;
+
+ unsigned int ignoreMaxBufferSize = 0;
+
+ bool CheckValidity();
+ bool ParseCommandLine(int argc, char * argv[]);
+ };
+
+ template<typename ValueType, typename TimeType>
+ class PIDController
+ {
+ public:
+ PIDController(ValueType prop, ValueType diff, ValueType integral) : propFactor(prop), diffFactor(diff), intFactor(integral) {}
+
+ ValueType Update(TimeType dT, ValueType Error)
+ {
+ errorSum += Error * dT;
+ const auto differential{(Error - lastError)/dT};
+ lastError = Error;
+ return Error * propFactor + errorSum * intFactor + differential * diffFactor;
+ }
+
+ protected:
+ ValueType propFactor{};
+ ValueType diffFactor{};
+ ValueType intFactor{};
+
+ private:
+ ValueType errorSum{};
+ ValueType lastError{};
+ };
+}
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();
+ }
+
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b2db5df..45262fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,10 @@
-cmake_minimum_required(VERSION 2.6)
-
-project(BuddhaShader)
-
-set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
-set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
-set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
-set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
-add_subdirectory("glfw-3.2.1")
-add_subdirectory("BuddhaTest")
+cmake_minimum_required(VERSION 2.6)
+
+project(BuddhaShader)
+
+set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
+set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
+set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
+set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
+add_subdirectory("glfw-3.2.1")
+add_subdirectory("BuddhaTest")