Compare commits

..

2 Commits
v1.3 ... main

  1. 9
      src/GLWidget.cpp
  2. 4
      src/GLWidget.h
  3. 9
      src/MainWindow.cpp
  4. 2
      src/MainWindow.h
  5. 106
      src/Simulation.cpp
  6. 12
      src/Simulation.h
  7. 5
      src/main.cpp

@ -46,6 +46,7 @@ void GLWidget::initializeGL() {
simulation->semaphore.acquire();
uploadStaticDataToGPU();
makeCurrent();
overlay->init();
}
@ -102,6 +103,8 @@ bool GLWidget::AnyDialogOpen() {
}
void GLWidget::uploadStaticDataToGPU() {
makeCurrent();
auto pendula = &simulation->pendula;
size_t pointCount = std::transform_reduce(pendula->begin(), pendula->end(), 0, [](size_t prev, size_t curr){
@ -183,10 +186,14 @@ void GLWidget::uploadStaticDataToGPU() {
glBindVertexArray(0);
doneCurrent();
simulation->semaphore.release();
}
void GLWidget::changePosition() {
makeCurrent();
auto t1 = system_clock::now();
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
auto positions = (GLfloat*) glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(positionCount * sizeof(float)), GL_MAP_WRITE_BIT);
@ -201,6 +208,8 @@ void GLWidget::changePosition() {
}
glUnmapBuffer(GL_ARRAY_BUFFER);
// std::cout << duration_cast<microseconds>(system_clock::now() - t1).count() << std::endl;
doneCurrent();
simulation->semaphore.release();
}

@ -1,7 +1,7 @@
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLExtraFunctions>
#include <QOpenGLFunctions_3_3_Core>
#include <QMatrix3x3>
class Pendulum;
@ -9,7 +9,7 @@ class Simulation;
class QOpenGLShaderProgram;
class Overlay;
class GLWidget : public QOpenGLWidget, protected QOpenGLExtraFunctions {
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core {
public:
explicit GLWidget(Simulation *);
Overlay * overlay;

@ -18,13 +18,14 @@
#include <QTimer>
#include "Overlay.h"
#include "Slider.h"
#include <QOffscreenSurface>
MainWindow::MainWindow() {
simulationThread = new QThread(this);
simulationThread->setObjectName("Simulation");
simulation = new Simulation;
simulation->moveToThread(simulationThread);
simulationThread->start();
connect(simulationThread, &QThread::started, simulation, &Simulation::initialize);
masses = std::vector<double>(MaxSegments);
lengths = std::vector<double>(MaxSegments);
@ -36,7 +37,9 @@ MainWindow::MainWindow() {
normalizeLengths();
QMetaObject::invokeMethod(simulation, &Simulation::initialize);
auto offSurface = new QOffscreenSurface;
offSurface->create();
simulation->offSurface = offSurface;
}
void MainWindow::buildUI() {
@ -383,13 +386,13 @@ QWidget * MainWindow::buildSimulationUI() {
// GPU-Acceleration
{
auto useGPU = new QCheckBox("Use GPU-Acceleration");
useGPU->setCheckState(Qt::Checked);
connect(useGPU, &QCheckBox::stateChanged, simulation, &Simulation::useGPUChanged);
connect(simulation, &Simulation::gpuNotSupported, [useGPU](const std::string &message){
useGPU->setCheckState(Qt::Unchecked);
useGPU->setEnabled(false);
useGPU->setToolTip(QString::fromStdString(message));
});
useGPU->setCheckState(Qt::Unchecked);
lyt->addWidget(useGPU);
}

@ -15,12 +15,12 @@ class MainWindow : public QWidget {
public:
explicit MainWindow();
~MainWindow() override;
QThread * simulationThread;
private:
void buildUI();
QWidget * buildAddUI();
QWidget * buildSimulationUI();
Simulation * simulation;
QThread * simulationThread;
GLWidget * glWidget = nullptr;

@ -23,15 +23,19 @@ void Simulation::initialize() {
// OpenGL Offscreen functions
{
auto context = new QOpenGLContext(this);
context->setFormat(QSurfaceFormat::defaultFormat());
context = new QOpenGLContext;
auto format = QSurfaceFormat::defaultFormat();
format.setVersion(4, 3);
context->setFormat(format);
context->create();
auto surface = new QOffscreenSurface(nullptr, this);
surface->setFormat(context->format());
surface->create();
context->makeCurrent(surface);
initializeOpenGLFunctions();
if (!context->hasExtension("GL_ARB_gpu_shader_fp64")){
context->makeCurrent(offSurface);
f = new QOpenGLFunctions_4_3_Core;
if (!f->initializeOpenGLFunctions()){
std::string message = "OpenGL 4.3 is required for Compute Shaders! Falling back to CPU-Multithreading.";
printf("%s\n", message.c_str());
useGPUAcceleration = false;
emit gpuNotSupported(message);
} else if (!context->hasExtension("GL_ARB_gpu_shader_fp64")){
std::string message = "Double precision not supported by OpenGL! Falling back to CPU-Multithreading.";
printf("%s\n", message.c_str());
useGPUAcceleration = false;
@ -39,13 +43,19 @@ void Simulation::initialize() {
}
}
if (!useGPUAcceleration){
context->doneCurrent();
return;
}
// Read GPU Limits
{
for (int i = 0; i < 3; i++){
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, i, gpuLimits.maxWGCount + i);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, i, gpuLimits.maxWGSize + i);
f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, i, gpuLimits.maxWGCount + i);
f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, i, gpuLimits.maxWGSize + i);
}
glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &gpuLimits.maxWGInvocations);
f->glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &gpuLimits.maxWGInvocations);
printf("Max work group count: (%d, %d, %d)\n", gpuLimits.maxWGCount[0], gpuLimits.maxWGCount[1], gpuLimits.maxWGCount[2]);
printf("Max work group size: (%d, %d, %d)\n", gpuLimits.maxWGSize[0], gpuLimits.maxWGSize[1], gpuLimits.maxWGSize[2]);
@ -58,19 +68,22 @@ void Simulation::initialize() {
program->addShaderFromSourceFile(QOpenGLShader::Compute, ":/shaders/compute.glsl");
program->link();
glGenBuffers(6, &SSBO.positions);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, SSBO.positions);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, SSBO.velocities);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO.invMasses);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, SSBO.lengths);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, SSBO.indices);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, SSBO.segmentCounts);
f->glGenBuffers(6, &SSBO.positions);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, SSBO.positions);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, SSBO.velocities);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO.invMasses);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, SSBO.lengths);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, SSBO.indices);
f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, SSBO.segmentCounts);
}
context->doneCurrent();
}
// When the layout changes
void Simulation::updateGPUData() {
context->makeCurrent(offSurface);
std::vector<GLdouble> positions;
std::vector<GLdouble> velocities;
std::vector<GLdouble> invMasses;
@ -100,18 +113,20 @@ void Simulation::updateGPUData() {
auto indicesSize = GLsizeiptr(indices.size() * sizeof(GLuint));
auto segmentCountsSize = GLsizeiptr(segmentCounts.size() * sizeof(GLuint));
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.positions);
glBufferData(GL_SHADER_STORAGE_BUFFER, posSize, positions.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.velocities);
glBufferData(GL_SHADER_STORAGE_BUFFER, velSize, velocities.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.invMasses);
glBufferData(GL_SHADER_STORAGE_BUFFER, invMassSize, invMasses.data(), GL_STATIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.lengths);
glBufferData(GL_SHADER_STORAGE_BUFFER, lengthSize, lengths.data(), GL_STATIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.indices);
glBufferData(GL_SHADER_STORAGE_BUFFER, indicesSize, indices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.segmentCounts);
glBufferData(GL_SHADER_STORAGE_BUFFER, segmentCountsSize, segmentCounts.data(), GL_STATIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.positions);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, posSize, positions.data(), GL_DYNAMIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.velocities);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, velSize, velocities.data(), GL_DYNAMIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.invMasses);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, invMassSize, invMasses.data(), GL_STATIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.lengths);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, lengthSize, lengths.data(), GL_STATIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.indices);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, indicesSize, indices.data(), GL_STATIC_DRAW);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.segmentCounts);
f->glBufferData(GL_SHADER_STORAGE_BUFFER, segmentCountsSize, segmentCounts.data(), GL_STATIC_DRAW);
context->doneCurrent();
}
// Every few milliseconds
@ -129,18 +144,19 @@ void Simulation::update() {
double newKineticEnergy = 0;
if (useGPUAcceleration) {
context->makeCurrent(offSurface);
program->bind();
glUniform1d(program->uniformLocation("h"), h);
glUniform1d(program->uniformLocation("gravity"), gravity);
glUniform1ui(program->uniformLocation("substeps"), substeps);
f->glUniform1d(program->uniformLocation("h"), h);
f->glUniform1d(program->uniformLocation("gravity"), gravity);
f->glUniform1ui(program->uniformLocation("substeps"), substeps);
glDispatchCompute(pendula.size(), 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
f->glDispatchCompute(pendula.size(), 1, 1);
f->glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
// Read updated positions
{
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.positions);
auto * newPositions = (double*) glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.positions);
auto * newPositions = (double*) f->glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
int index = 0;
for (Pendulum * p : pendula){
for (Vector &point : p->X){
@ -148,13 +164,13 @@ void Simulation::update() {
point.y = newPositions[index++];
}
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
f->glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
// Read updated velocities
{
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.velocities);
auto * newVelocities = (double*) glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO.velocities);
auto * newVelocities = (double*) f->glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
int index = 0;
for (Pendulum * p : pendula){
for (Vector &velocity : p->V){
@ -162,7 +178,7 @@ void Simulation::update() {
velocity.y = newVelocities[index++];
}
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
f->glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
#pragma omp parallel for
@ -174,7 +190,7 @@ void Simulation::update() {
#pragma omp atomic
newKineticEnergy += localKineticEnergy;
}
context->doneCurrent();
} else {
// CPU-Multithreading
#pragma omp parallel for
@ -208,7 +224,8 @@ void Simulation::clearPendula() {
pendula.shrink_to_fit();
updateEnergy();
updateGPUData();
if (useGPUAcceleration)
updateGPUData();
emit layoutChanged();
}
@ -220,7 +237,8 @@ void Simulation::addPendula(const std::vector<Pendulum *> &add) {
pendula.push_back(p);
updateEnergy();
updateGPUData();
if (useGPUAcceleration)
updateGPUData();
emit layoutChanged();
}

@ -5,11 +5,13 @@
#include <semaphore>
#include <QOpenGLFunctions_4_3_Core>
class QOffscreenSurface;
class QTimer;
class FPS;
class Pendulum;
class QOpenGLShaderProgram;
class Simulation : public QObject, protected QOpenGLFunctions_4_3_Core {
class Simulation : public QObject {
Q_OBJECT
public:
explicit Simulation();
@ -29,19 +31,23 @@ public:
std::binary_semaphore semaphore = std::binary_semaphore(1);
FPS * ups;
void initialize();
QOffscreenSurface* offSurface;
QOpenGLContext* context;
void updateEnergy();
signals:
void gpuNotSupported(const std::string &message);
void layoutChanged();
void positionChanged();
public slots:
void initialize();
void clearPendula();
void addPendula(const std::vector<Pendulum *> &add);
void useGPUChanged(int);
private slots:
void update();
private:
QOpenGLFunctions_4_3_Core * f;
void updateGPUData();
struct GPULimits {
int maxWGCount[3];
@ -59,5 +65,5 @@ private:
} SSBO;
QTimer * timer;
int updateInterval = 16;
bool useGPUAcceleration = false;
bool useGPUAcceleration = true;
};

@ -2,13 +2,14 @@
#include <QSurfaceFormat>
#include "MainWindow.h"
#include <QIcon>
#include <QThread>
int main(int argc, char* argv[]) {
QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
fmt.setDepthBufferSize(24);
fmt.setSamples(8);
fmt.setSwapInterval(1);
fmt.setVersion(4, 3);
fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(fmt);
@ -19,5 +20,7 @@ int main(int argc, char* argv[]) {
w.setWindowIcon(QIcon(":/icons/app_icon.ico"));
w.show();
w.simulationThread->start();
return QApplication::exec();
}

Loading…
Cancel
Save