Browse Source

Move module stepping code from Engine to Module. Change meter to a time plot.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
7003adffe2
6 changed files with 155 additions and 91 deletions
  1. +1
    -0
      include/common.hpp
  2. +7
    -4
      include/engine/Module.hpp
  3. +1
    -1
      src/app/MenuBar.cpp
  4. +39
    -13
      src/app/ModuleWidget.cpp
  5. +6
    -65
      src/engine/Engine.cpp
  6. +101
    -8
      src/engine/Module.cpp

+ 1
- 0
include/common.hpp View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
// Include most of the C stdlib for convenience // Include most of the C stdlib for convenience
#include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>
#include <cstdint> #include <cstdint>


+ 7
- 4
include/engine/Module.hpp View File

@@ -225,6 +225,8 @@ struct Module {
Defined by `1 / sampleRate`. Defined by `1 / sampleRate`.
*/ */
float sampleTime; float sampleTime;
/** Number of audio samples since the Engine's first sample. */
int64_t frame;
}; };
/** Advances the module by one audio sample. /** Advances the module by one audio sample.
Override this method to read Inputs and Params and to write Outputs and Lights. Override this method to read Inputs and Params and to write Outputs and Lights.
@@ -342,10 +344,11 @@ struct Module {
/** DEPRECATED. Override `onSampleRateChange(e)` instead. */ /** DEPRECATED. Override `onSampleRateChange(e)` instead. */
virtual void onSampleRateChange() {} virtual void onSampleRateChange() {}


/** Unstable API, do not use in plugins. */
float& cpuTime();
/** Unstable API, do not use in plugins. */
bool& bypass();
PRIVATE bool& bypass();
PRIVATE const float* meterBuffer();
PRIVATE int meterLength();
PRIVATE int meterIndex();
PRIVATE void step(const ProcessArgs& args);
}; };






+ 1
- 1
src/app/MenuBar.cpp View File

@@ -553,7 +553,7 @@ struct EngineButton : MenuButton {
menu->box.size.x = box.size.x; menu->box.size.x = box.size.x;


CpuMeterItem* cpuMeterItem = new CpuMeterItem; CpuMeterItem* cpuMeterItem = new CpuMeterItem;
cpuMeterItem->text = "CPU meter";
cpuMeterItem->text = "Performance meters";
cpuMeterItem->rightText = "F3 "; cpuMeterItem->rightText = "F3 ";
cpuMeterItem->rightText += CHECKMARK(settings::cpuMeter); cpuMeterItem->rightText += CHECKMARK(settings::cpuMeter);
menu->addChild(cpuMeterItem); menu->addChild(cpuMeterItem);


+ 39
- 13
src/app/ModuleWidget.cpp View File

@@ -394,27 +394,53 @@ void ModuleWidget::draw(const DrawArgs& args) {


Widget::draw(args); Widget::draw(args);


// Power meter
// Meter
if (module && settings::cpuMeter) { if (module && settings::cpuMeter) {
float sampleRate = APP->engine->getSampleRate();
const float* meterBuffer = module->meterBuffer();
int meterLength = module->meterLength();
int meterIndex = module->meterIndex();

float meterAvg = 0.f;
for (int i = 0; i < meterLength; i++) {
meterAvg += meterBuffer[i];
}
meterAvg /= meterLength;

// Text background
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
nvgRect(args.vg,
0, box.size.y - 35,
65, 35);
nvgRect(args.vg, 0, box.size.y - 20, box.size.x, 20);
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75)); nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
nvgFill(args.vg); nvgFill(args.vg);


float percent = module->cpuTime() * APP->engine->getSampleRate() * 100;
float microseconds = module->cpuTime() * 1e6f;
std::string cpuText = string::f("%.1f%%\n%.2f ÎĽs", percent, microseconds);
bndLabel(args.vg, 2.0, box.size.y - 34.0, INFINITY, INFINITY, -1, cpuText.c_str());
// Text
float percent = meterAvg * sampleRate * 100.f;
float microseconds = meterAvg * 1e6f;
std::string meterText = string::f("%.1f%% %.2f ÎĽs", percent, microseconds);
bndLabel(args.vg, 0.0, box.size.y - 20.0, INFINITY, INFINITY, -1, meterText.c_str());


float p = math::clamp(module->cpuTime() / APP->engine->getSampleTime(), 0.f, 1.f);
// Draw time plot
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
nvgRect(args.vg,
0, (1.f - p) * box.size.y,
5, p * box.size.y);
nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 1.0));
nvgMoveTo(args.vg, box.size.x, box.size.y);
for (int i = 0; i < meterLength; i++) {
int index = (meterIndex - i + meterLength) % meterLength;
math::Vec p;
p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x;
p.y = (1.f - meterBuffer[index] * sampleRate * 1.f) * (box.size.y - 20);
nvgLineTo(args.vg, p.x, p.y);
}
NVGcolor color = nvgRGBAf(0.6, 0, 0, 0.6);
nvgLineTo(args.vg, 0.0, box.size.y);
nvgClosePath(args.vg);
nvgFillColor(args.vg, color);
nvgFill(args.vg); nvgFill(args.vg);

// Draw border
nvgStrokeColor(args.vg, color);
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
nvgStrokeWidth(args.vg, 2.0);
nvgStroke(args.vg);
} }


// if (module) { // if (module) {


+ 6
- 65
src/engine/Engine.cpp View File

@@ -289,43 +289,17 @@ static void Engine_relaunchWorkers(Engine* that, int threadCount) {
} }




static void Port_step(Port* that, float deltaTime) {
// Set plug lights
if (that->channels == 0) {
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setBrightness(0.f);
}
else if (that->channels == 1) {
float v = that->getVoltage() / 10.f;
that->plugLights[0].setSmoothBrightness(v, deltaTime);
that->plugLights[1].setSmoothBrightness(-v, deltaTime);
that->plugLights[2].setBrightness(0.f);
}
else {
float v2 = 0.f;
for (int c = 0; c < that->channels; c++) {
v2 += std::pow(that->getVoltage(c), 2);
}
float v = std::sqrt(v2) / 10.f;
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setSmoothBrightness(v, deltaTime);
}
}


static void Engine_stepModulesWorker(Engine* that, int threadId) {
static void Engine_stepWorker(Engine* that, int threadId) {
Engine::Internal* internal = that->internal; Engine::Internal* internal = that->internal;


// int threadCount = internal->threadCount; // int threadCount = internal->threadCount;
int modulesLen = internal->modules.size(); int modulesLen = internal->modules.size();


// Build ProcessArgs
Module::ProcessArgs processArgs; Module::ProcessArgs processArgs;
processArgs.sampleRate = internal->sampleRate; processArgs.sampleRate = internal->sampleRate;
processArgs.sampleTime = internal->sampleTime; processArgs.sampleTime = internal->sampleTime;

bool cpuMeter = settings::cpuMeter;
processArgs.frame = internal->frame;


// Step each module // Step each module
while (true) { while (true) {
@@ -336,40 +310,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) {
break; break;


Module* module = internal->modules[i]; Module* module = internal->modules[i];

// Start CPU timer
double startTime;
if (cpuMeter) {
startTime = system::getRuntime();
}

// Step module
if (!module->bypass())
module->process(processArgs);
else
module->processBypass(processArgs);

// Stop CPU timer
if (cpuMeter) {
double endTime = system::getRuntime();
float duration = endTime - startTime;

// Smooth CPU time
const float cpuTau = 2.f /* seconds */;
module->cpuTime() += (duration - module->cpuTime()) * processArgs.sampleTime / cpuTau;
}

// Iterate ports to step plug lights
const int portDivider = 8;
if (internal->frame % portDivider == 0) {
float portTime = processArgs.sampleTime * portDivider;
for (Input& input : module->inputs) {
Port_step(&input, portTime);
}
for (Output& output : module->outputs) {
Port_step(&output, portTime);
}
}
module->step(processArgs);
} }
} }


@@ -441,7 +382,7 @@ static void Engine_stepFrame(Engine* that) {
// Step modules along with workers // Step modules along with workers
internal->workerModuleIndex = 0; internal->workerModuleIndex = 0;
internal->engineBarrier.wait(); internal->engineBarrier.wait();
Engine_stepModulesWorker(that, 0);
Engine_stepWorker(that, 0);
internal->workerBarrier.wait(); internal->workerBarrier.wait();


internal->frame++; internal->frame++;
@@ -1156,7 +1097,7 @@ void EngineWorker::run() {
engine->internal->engineBarrier.wait(); engine->internal->engineBarrier.wait();
if (!running) if (!running)
return; return;
Engine_stepModulesWorker(engine, id);
Engine_stepWorker(engine, id);
engine->internal->workerBarrier.wait(); engine->internal->workerBarrier.wait();
} }
} }


+ 101
- 8
src/engine/Module.cpp View File

@@ -1,5 +1,7 @@
#include <engine/Module.hpp> #include <engine/Module.hpp>
#include <plugin.hpp> #include <plugin.hpp>
#include <system.hpp>
#include <settings.hpp>
#include <asset.hpp> #include <asset.hpp>




@@ -7,12 +9,21 @@ namespace rack {
namespace engine { namespace engine {





// Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors.
static const int meterDivider = 23;
static const int samplesCount = 64;
static const int meterBufferLength = 128;



struct Module::Internal { struct Module::Internal {
/** Seconds spent in the process() method, with exponential smoothing.
Only written when CPU timing is enabled, since time measurement is expensive.
*/
float cpuTime = 0.f;
bool bypass = false; bool bypass = false;

float meterTimeTotal = 0.f;
int meterSamples = 0;
int meterIndex = 0;
float meterBuffer[meterBufferLength] = {};
}; };




@@ -269,13 +280,95 @@ void Module::onRandomize(const RandomizeEvent& e) {
} }




float& Module::cpuTime() {
return internal->cpuTime;
bool& Module::bypass() {
return internal->bypass;
} }




bool& Module::bypass() {
return internal->bypass;
const float* Module::meterBuffer() {
return internal->meterBuffer;
}


int Module::meterLength() {
return meterBufferLength;
}


int Module::meterIndex() {
return internal->meterIndex;
}


static void Port_step(Port* that, float deltaTime) {
// Set plug lights
if (that->channels == 0) {
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setBrightness(0.f);
}
else if (that->channels == 1) {
float v = that->getVoltage() / 10.f;
that->plugLights[0].setSmoothBrightness(v, deltaTime);
that->plugLights[1].setSmoothBrightness(-v, deltaTime);
that->plugLights[2].setBrightness(0.f);
}
else {
float v2 = 0.f;
for (int c = 0; c < that->channels; c++) {
v2 += std::pow(that->getVoltage(c), 2);
}
float v = std::sqrt(v2) / 10.f;
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setSmoothBrightness(v, deltaTime);
}
}


void Module::step(const ProcessArgs& args) {
// This global setting can change while the function is running, so use a local variable.
bool meterEnabled = settings::cpuMeter && (args.frame % meterDivider == 0);

// Start CPU timer
double startTime;
if (meterEnabled) {
startTime = system::getTime();
}

// Step module
if (!internal->bypass)
process(args);
else
processBypass(args);

// Stop CPU timer
if (meterEnabled) {
double endTime = system::getTime();
float duration = endTime - startTime;

internal->meterTimeTotal += duration;
if (++internal->meterSamples >= samplesCount) {
// Push time to buffer
internal->meterBuffer[internal->meterIndex++] = internal->meterTimeTotal / samplesCount;
internal->meterIndex %= meterBufferLength;
// Reset total
internal->meterSamples = 0;
internal->meterTimeTotal = 0.f;
}
}

// Iterate ports to step plug lights
const int portDivider = 8;
if (args.frame % portDivider == 0) {
float portTime = args.sampleTime * portDivider;
for (Input& input : inputs) {
Port_step(&input, portTime);
}
for (Output& output : outputs) {
Port_step(&output, portTime);
}
}
} }






Loading…
Cancel
Save