diff options
Diffstat (limited to 'BuddhaTest')
-rw-r--r-- | BuddhaTest/Shaders/BuddhaCompute.glsl | 173 | ||||
-rw-r--r-- | BuddhaTest/Shaders/BuddhaFragment.glsl | 12 | ||||
-rw-r--r-- | BuddhaTest/include/Helpers.h | 26 | ||||
-rw-r--r-- | BuddhaTest/src/BuddhaTest.cpp | 45 | ||||
-rw-r--r-- | BuddhaTest/src/Helpers.cpp | 2 |
5 files changed, 196 insertions, 62 deletions
diff --git a/BuddhaTest/Shaders/BuddhaCompute.glsl b/BuddhaTest/Shaders/BuddhaCompute.glsl index bca9025..b1ce2bc 100644 --- a/BuddhaTest/Shaders/BuddhaCompute.glsl +++ b/BuddhaTest/Shaders/BuddhaCompute.glsl @@ -2,25 +2,60 @@ //#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) buffer renderedDataRed
+layout(std430, binding=2) restrict buffer renderedDataRed
{
- uint counts_SSBORed[];
+ restrict uint counts_SSBORed[];
};
-layout(std430, binding=3) buffer renderedDataGreen
+layout(std430, binding=3) restrict buffer renderedDataGreen
{
- uint counts_SSBOGreen[];
+ restrict uint counts_SSBOGreen[];
};
-layout(std430, binding=4) buffer renderedDataBlue
+layout(std430, binding=4) restrict buffer renderedDataBlue
{
- uint counts_SSBOBlue[];
+ restrict uint counts_SSBOBlue[];
+};
+layout(std430, binding=5) restrict buffer statusBuffer
+{
+ restrict uint individualState[];
};
uniform uint width;
uniform uint height;
-uniform uint iterationCount;
uniform uvec3 orbitLength;
+uniform uint iterationsPerDispatch;
+
+void getIndividualState(in uint CellID, out vec2 offset, out vec2 coordinates, out uint phase, out uint orbitNumber, out uint doneIterations)
+{
+ uint startIndex = 7*CellID;
+ uint x = individualState[startIndex];
+ uint y = individualState[startIndex+1];
+ phase = individualState[startIndex+2];
+ orbitNumber = individualState[startIndex+3];
+ doneIterations = individualState[startIndex+4];
+ uint offx = individualState[startIndex+5];
+ uint offy = individualState[startIndex+6];
+ coordinates = vec2(uintBitsToFloat(x),uintBitsToFloat(y));
+ offset = vec2(uintBitsToFloat(offx),uintBitsToFloat(offy));
+}
+
+void setIndividualState(in uint CellID, in vec2 offset, in vec2 coordinates, in uint phase, in uint orbitNumber, in uint doneIterations)
+{
+ uint startIndex = 7*CellID;
+ uint x=floatBitsToUint(coordinates.x);
+ uint y=floatBitsToUint(coordinates.y);
+ uint offx = floatBitsToUint(offset.x);
+ uint offy = floatBitsToUint(offset.y);
+ atomicExchange(individualState[startIndex],x);
+ atomicExchange(individualState[startIndex+1],y);
+ atomicExchange(individualState[startIndex+2],phase);
+ atomicExchange(individualState[startIndex+3],orbitNumber);
+ atomicExchange(individualState[startIndex+4],doneIterations);
+ atomicExchange(individualState[startIndex+5],offx);
+ atomicExchange(individualState[startIndex+6],offy);
+}
+
void addToColorOfCell(uvec2 cell, uvec3 toAdd)
{
uint firstIndex = (cell.x + cell.y * width);
@@ -59,7 +94,7 @@ vec2 compSqr(in vec2 v) return vec2(v.x*v.x-v.y*v.y, 2.0*v.x*v.y);
}
-float isInMainCardioid(vec2 v)
+bool isInMainCardioid(vec2 v)
{
/*
The condition that a point c is in the main cardioid is that its orbit has
@@ -117,10 +152,10 @@ float isInMainCardioid(vec2 v) vec2 z = vec2(1.0,0.0)-4.0*v;
float zNormSqr = dot(z,z);
float rhsSqrt = 0.5*zNormSqr - z.x;
- return step(rhsSqrt*rhsSqrt,zNormSqr);
+ return rhsSqrt*rhsSqrt<zNormSqr;
}
-float isInMainBulb(vec2 v)
+bool isInMainBulb(vec2 v)
{
//The condition for this is that f(f(z)) = z
//where f(z) = z*z+v
@@ -131,60 +166,66 @@ float isInMainBulb(vec2 v) //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 step(sqrRadius,0.062499999);
+ return sqrRadius<0.062499999;
}
vec2 getStartValue(uint seed, uint yDecoupler)
{
uint hash = seed;
- vec2 retval = vec2(0);
- for(int i = 0; i < 5; ++i)
+ for(uint i = 0; i < 5;++i)
{
float x = hash1(hash,hash);
hash = (hash ^ intHash(yDecoupler));
float y = hash1(hash,hash);
vec2 random = vec2(x,y);
vec2 point = vec2(random.x * 3.5-2.5,random.y*1.55);
- float useThisPoint =1.0-(isInMainBulb(point) + isInMainCardioid(point));
- retval = mix(retval,point,useThisPoint);
+ if(!(isInMainBulb(point) || isInMainCardioid(point)))
+ return point;
}
- return retval;
+ return vec2(0);
}
-bool isGoingToBeDrawn(vec2 offset)
+bool isGoingToBeDrawn(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations, out bool result)
{
- vec2 val = vec2(0);
- uint limit = orbitLength.x > orbitLength.y ? orbitLength.x : orbitLength.y;
- limit = limit > orbitLength.z ? limit : orbitLength.z;
- for(uint i = 0; i < limit;++i)
+ uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
+ for(uint i = doneIterations; i < endCount;++i)
{
- val = compSqr(val) + offset;
- if(dot(val,val) > 4.0)
+ lastVal = compSqr(lastVal) + offset;
+ if(dot(lastVal,lastVal) > 4.0)
{
+ result = true;
+ iterationsLeftThisFrame -= i+1-doneIterations;
+ doneIterations = i+1;
return true;
}
}
- return false;
+ doneIterations = endCount;
+ iterationsLeftThisFrame = 0;
+ result = false;
+ return endCount == totalIterations;
}
-void drawOrbit(vec2 offset)
+bool drawOrbit(in vec2 offset, in uint totalIterations, inout vec2 lastVal, inout uint iterationsLeftThisFrame, inout uint doneIterations)
{
- vec2 val = vec2(0);
- uint limit = orbitLength.x > orbitLength.y ? orbitLength.x : orbitLength.y;
- limit = limit > orbitLength.z ? limit : orbitLength.z;
- for(uint i = 0; i < limit;++i)
+ uint endCount = doneIterations + iterationsLeftThisFrame > totalIterations ? totalIterations : doneIterations + iterationsLeftThisFrame;
+ for(uint i = doneIterations; i < endCount;++i)
{
- val = compSqr(val) + offset;
- if(dot(val,val) > 20.0)
+ lastVal = compSqr(lastVal) + offset;
+ if(dot(lastVal,lastVal) > 20.0)
{
- return;
+ iterationsLeftThisFrame -= i+1-doneIterations;
+ doneIterations = i+1;
+ return true; //done.
}
- if(val.x > -2.5 && val.x < 1.0 && val.y > -1.0 && val.y < 1.0)
+ if(lastVal.x > -2.5 && lastVal.x < 1.0 && lastVal.y > -1.0 && lastVal.y < 1.0)
{
- addToColorAt(val,uvec3(i < orbitLength.r,i < orbitLength.g,i < orbitLength.b));
+ addToColorAt(lastVal,uvec3(i < orbitLength.r,i < orbitLength.g,i < orbitLength.b));
}
}
+ doneIterations = endCount;
+ iterationsLeftThisFrame = 0;
+ return endCount == totalIterations;
}
void main() {
@@ -193,12 +234,64 @@ void main() { uvec3 totalWorkersPerDimension = gl_WorkGroupSize * gl_NumWorkGroups;
uint totalWorkers = totalWorkersPerDimension.x*totalWorkersPerDimension.y*totalWorkersPerDimension.z;
- uint seed = iterationCount * totalWorkers + gl_GlobalInvocationID.x + gl_GlobalInvocationID.y*totalWorkersPerDimension.x + gl_GlobalInvocationID.z*(totalWorkersPerDimension.x * totalWorkersPerDimension.y);
- uint yDecoupler = iterationCount;
- vec2 offset = getStartValue(seed, yDecoupler);
+ //TODO: Check this once I've had some sleep. Anyhow, I'm using 1D, so y and z components globalInfocationID should be zero anyhow.
+ uint uniqueWorkerID = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y*totalWorkersPerDimension.x + gl_GlobalInvocationID.z*(totalWorkersPerDimension.x * totalWorkersPerDimension.y);
+
+ uint totalIterations = orbitLength.x > orbitLength.y ? orbitLength.x : orbitLength.y;
+ totalIterations = totalIterations > orbitLength.z ? totalIterations : orbitLength.z;
+
+ //getIndividualState(in uint CellID, out vec2 coordinates, out uint phase, out uint remainingIterations)
+ vec2 lastPosition;
+ uint phase;
+ uint doneIterations;
+ uint orbitNumber;
+ vec2 offset;
+ //getIndividualState(in uint CellID, out vec2 offset, out vec2 coordinates, out uint phase, out uint orbitNumber, out uint doneIterations)
+ getIndividualState(uniqueWorkerID, offset, lastPosition, phase, orbitNumber, doneIterations);
+ uint iterationsLeftToDo = iterationsPerDispatch;
+ while(iterationsLeftToDo != 0)
+ {
+ if(phase == 0)
+ {
+ //new orbit:
+ uint seed = orbitNumber * totalWorkers + uniqueWorkerID;
+ uint yDecoupler = orbitNumber;
+ offset = getStartValue(seed, yDecoupler);
+ lastPosition = vec2(0);
+ phase = 1;
+ doneIterations = 0;
+ }
+ if(phase == 1)
+ {
+ //check if this orbit is going to be drawn
+ bool result;
+ if(isGoingToBeDrawn(offset,totalIterations, lastPosition, iterationsLeftToDo, doneIterations , result))
+ {
+ if(result)
+ {
+ //on to step 2: drawing
+ phase = 2;
+ lastPosition = vec2(0);
+ doneIterations = 0;
+ }
+ else
+ {
+ //back to step 0
+ ++orbitNumber;
+ phase = 0;
+ }
+ }
+ }
+ else if(phase == 2)
+ {
+ if(drawOrbit(offset, totalIterations, lastPosition, iterationsLeftToDo, doneIterations))
+ {
+ ++orbitNumber;
+ phase = 0;
+ }
+ }
+ }
- if(!isGoingToBeDrawn(offset))
- return;
- drawOrbit(offset);
+ setIndividualState(uniqueWorkerID, offset, lastPosition, phase, orbitNumber, doneIterations);
}
diff --git a/BuddhaTest/Shaders/BuddhaFragment.glsl b/BuddhaTest/Shaders/BuddhaFragment.glsl index 24fb6a7..24edcd9 100644 --- a/BuddhaTest/Shaders/BuddhaFragment.glsl +++ b/BuddhaTest/Shaders/BuddhaFragment.glsl @@ -4,17 +4,17 @@ in vec2 uv; out vec3 color;
-layout(std430, binding=2) buffer renderedDataRed
+layout(std430, binding=2) restrict readonly buffer renderedDataRed
{
- uint counts_SSBORed[];
+ restrict readonly uint counts_SSBORed[];
};
-layout(std430, binding=3) buffer renderedDataGreen
+layout(std430, binding=3) restrict readonly buffer renderedDataGreen
{
- uint counts_SSBOGreen[];
+ restrict readonly uint counts_SSBOGreen[];
};
-layout(std430, binding=4) buffer renderedDataBlue
+layout(std430, binding=4) restrict readonly buffer renderedDataBlue
{
- uint counts_SSBOBlue[];
+ restrict readonly uint counts_SSBOBlue[];
};
uniform uint width;
diff --git a/BuddhaTest/include/Helpers.h b/BuddhaTest/include/Helpers.h index 6358066..76f790b 100644 --- a/BuddhaTest/include/Helpers.h +++ b/BuddhaTest/include/Helpers.h @@ -46,6 +46,8 @@ namespace Helpers unsigned int globalWorkGroupSizeY = 1;
unsigned int globalWorkGroupSizeZ = 1;
+ unsigned int targetFrameRate = 60;
+
std::string pngFilename = "";
double pngGamma = 1.0;
double pngColorScale = 2.0;
@@ -55,4 +57,28 @@ namespace Helpers 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 5d03ad4..24f5893 100644 --- a/BuddhaTest/src/BuddhaTest.cpp +++ b/BuddhaTest/src/BuddhaTest.cpp @@ -3,6 +3,7 @@ #include <Helpers.h>
#include <iostream>
#include <vector>
+#include <chrono>
void error_callback(int error, const char* description)
{
@@ -122,12 +123,19 @@ int main(int argc, char * argv[]) }
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
- uint32_t iterationCount{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*(7*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 iterationCountUniformHandle = glGetUniformLocation(ComputeShader, "iterationCount");
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);
@@ -137,27 +145,20 @@ int main(int argc, char * argv[]) GLint heightUniformFragmentHandle = glGetUniformLocation(VertexAndFragmentShaders, "height");
glUniform1ui(widthUniformFragmentHandle, settings.imageWidth);
glUniform1ui(heightUniformFragmentHandle, bufferHeight);
-
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
- //uint32_t iterationsPerFrame = settings.globalWorkGroupSizeX*settings.globalWorkGroupSizeY*settings.globalWorkGroupSizeZ*settings.localWorkgroupSizeX*settings.localWorkgroupSizeY*settings.localWorkgroupSizeZ;
- //bool bWarningShown{false};
+ uint32_t iterationsPerFrame = 1;
+
+ Helpers::PIDController<float, uint32_t> pid{0.0f,0.0f,1e-8f};
+ const uint32_t targetFrameDuration{1000000/settings.targetFrameRate};
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
- //commented out, as with the new y-decoupling we will get more points in 2D even after we had all integer values for x.
- //check if we are "done"
- //if(iterationsPerFrame * iterationCount <= iterationsPerFrame && iterationCount > 1 && !bWarningShown)
- //{
- // std::cout << "The program covered all possible integer values for starting point computation. Leaving it running will not improve the image any more." << std::endl;
- // bWarningShown = true;
- //}
-
+ auto frameStart{std::chrono::high_resolution_clock::now()};
//let the compute shader do something
- glUseProgram(ComputeShader);
- //increase iterationCount, which is used for pseudo random generation
- glUniform1ui(iterationCountUniformHandle,++iterationCount);
+ glUseProgram(ComputeShader);
+ glUniform1ui(iterationsPerDispatchHandle, iterationsPerFrame);
glDispatchCompute(settings.globalWorkGroupSizeX, settings.globalWorkGroupSizeY, settings.globalWorkGroupSizeZ);
//before reading the values in the ssbo, we need a memory barrier:
@@ -186,6 +187,17 @@ int main(int argc, char * argv[]) /* 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(frameDuration,error)};
+ iterationsPerFrame = std::max(1,static_cast<int>(pidOutput));
+
+ //std::cout << iterationsPerFrame << " " << pidOutput << std::endl;
+ }
}
if(!settings.pngFilename.empty())
@@ -210,6 +222,7 @@ int main(int argc, char * argv[]) //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 3cb786c..94c21a9 100644 --- a/BuddhaTest/src/Helpers.cpp +++ b/BuddhaTest/src/Helpers.cpp @@ -332,6 +332,7 @@ namespace Helpers {"--globalWorkgroupSizeX", &globalWorkGroupSizeX},
{"--globalWorkgroupSizeY", &globalWorkGroupSizeY},
{"--globalWorkgroupSizeZ", &globalWorkGroupSizeZ},
+ {"--targetFrameRate", &targetFrameRate},
{"--imageGamma",&pngGamma},
{"--imageColorScale",&pngColorScale},
{"--output", &pngFilename},
@@ -362,6 +363,7 @@ namespace Helpers "--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;
}
|