From b05e87dc5004f5badc02508570ddc55f31b451cc Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sat, 17 Mar 2018 17:51:19 +0100 Subject: Correct line endings. --- BuddhaTest/CMakeLists.txt | 42 +- BuddhaTest/Shaders/BuddhaCompute.glsl | 538 ++++++++++----------- BuddhaTest/Shaders/BuddhaFragment.glsl | 74 +-- BuddhaTest/Shaders/BuddhaVertex.glsl | 20 +- BuddhaTest/include/Helpers.h | 168 +++---- BuddhaTest/src/BuddhaTest.cpp | 460 +++++++++--------- BuddhaTest/src/Helpers.cpp | 832 ++++++++++++++++----------------- 7 files changed, 1067 insertions(+), 1067 deletions(-) (limited to 'BuddhaTest') 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 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 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 -#include -#include -#include //includes FILE typedef -#include - -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& 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 - 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 +#include +#include +#include //includes FILE typedef +#include + +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& 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 + 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 -#include -#include -#include -#include -#include -#include - -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 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(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(pidOutput)); - - //std::cout << iterationsPerFrame << " " << pidOutput << std::endl; - } - } - - if(!settings.pngFilename.empty()) - { - glMemoryBarrier(GL_ALL_BARRIER_BITS); - std::vector> readBackBuffers(3,std::vector(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 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 +#include +#include +#include +#include +#include +#include + +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 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(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(pidOutput)); + + //std::cout << iterationsPerFrame << " " << pidOutput << std::endl; + } + } + + if(!settings.pngFilename.empty()) + { + glMemoryBarrier(GL_ALL_BARRIER_BITS); + std::vector> readBackBuffers(3,std::vector(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 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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 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 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 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 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& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale) - { - std::vector pngData(3*width*2*bufferHeight); - std::vector 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(255.0 * pow(std::min(1.0,colorScale*static_cast(data[i])/static_cast(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(imageWidth) * static_cast(imageHeight)) > static_cast(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(WorkGroupSizeLimitX) || - globalWorkGroupSizeY > static_cast(WorkGroupSizeLimitY) || - globalWorkGroupSizeZ > static_cast(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(WorkGroupSizeLimitX) || - localWorkgroupSizeY > static_cast(WorkGroupSizeLimitY) || - localWorkgroupSizeZ > static_cast(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(localWorkgroupSizeX)*static_cast(localWorkgroupSizeY)*static_cast(localWorkgroupSizeZ) > static_cast(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 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." <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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 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 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& data, unsigned int width, unsigned int bufferHeight, double gamma, double colorScale) + { + std::vector pngData(3*width*2*bufferHeight); + std::vector 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(255.0 * pow(std::min(1.0,colorScale*static_cast(data[i])/static_cast(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(imageWidth) * static_cast(imageHeight)) > static_cast(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(WorkGroupSizeLimitX) || + globalWorkGroupSizeY > static_cast(WorkGroupSizeLimitY) || + globalWorkGroupSizeZ > static_cast(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(WorkGroupSizeLimitX) || + localWorkgroupSizeY > static_cast(WorkGroupSizeLimitY) || + localWorkgroupSizeZ > static_cast(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(localWorkgroupSizeX)*static_cast(localWorkgroupSizeY)*static_cast(localWorkgroupSizeZ) > static_cast(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 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." <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(); + } + +} -- cgit v1.2.3