Skip to content

Commit

Permalink
Fix pdlua multi-layer rendering, fix pdlua issue with multi-window
Browse files Browse the repository at this point in the history
  • Loading branch information
timothyschoen committed Sep 26, 2024
1 parent 1d9deb0 commit b9d2eab
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Libraries/pd-lua
Submodule pd-lua updated 2 files
+2 −1 pdlua.h
+25 −30 pdlua_gfx.h
8 changes: 8 additions & 0 deletions Source/Canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ void Canvas::parentHierarchyChanged()

bool Canvas::updateFramebuffers(NVGcontext* nvg, Rectangle<int> invalidRegion)
{
for(auto& object : objects)
{
if(object->gui)
{
object->gui->updateFramebuffers();
}
}

auto pixelScale = getRenderScale();
auto zoom = getValue<float>(zoomScale);

Expand Down
11 changes: 11 additions & 0 deletions Source/Objects/GraphOnParent.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,17 @@ class GraphOnParent final : public ObjectBase {
drawTicksForGraph(nvg, graph.get(), this);
}
}

void updateFramebuffers() override
{
if(canvas)
{
for(auto& object : canvas->objects)
{
if(object->gui) object->gui->updateFramebuffers();
}
}
}

static void drawTicksForGraph(NVGcontext* nvg, t_glist* x, ObjectBase* parent)
{
Expand Down
84 changes: 33 additions & 51 deletions Source/Objects/LuaObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void pdlua_gfx_repaint(t_pdlua* o, int firsttime);
}

class LuaObject final : public ObjectBase
, public Timer {
{

Colour currentColour;

Expand All @@ -30,7 +30,6 @@ class LuaObject final : public ObjectBase
std::unique_ptr<Dialog> saveDialog;

std::map<int, NVGFramebuffer> framebuffers;
int activeLayer = -1;

struct LuaGuiMessage {
t_symbol* symbol;
Expand Down Expand Up @@ -66,8 +65,8 @@ class LuaObject final : public ObjectBase
}
};

std::vector<LuaGuiMessage> guiCommandBuffer;
moodycamel::ReaderWriterQueue<LuaGuiMessage> guiMessageQueue;
std::map<int, std::vector<LuaGuiMessage>> guiCommandBuffer;
std::map<int, moodycamel::ReaderWriterQueue<LuaGuiMessage>> guiMessageQueue;

static inline std::map<t_pdlua*, std::vector<LuaObject*>> allDrawTargets = std::map<t_pdlua*, std::vector<LuaObject*>>();

Expand All @@ -81,7 +80,6 @@ class LuaObject final : public ObjectBase
}

parentHierarchyChanged();
startTimerHz(60);
}

~LuaObject()
Expand Down Expand Up @@ -204,7 +202,7 @@ class LuaObject final : public ObjectBase
sendRepaintMessage();
}

void handleGuiMessage(t_symbol* sym, int argc, t_atom* argv)
void handleGuiMessage(int layer, t_symbol* sym, int argc, t_atom* argv)
{
NVGcontext* nvg = cnv->editor->nvgSurface.getRawContext();
if (!nvg)
Expand All @@ -214,32 +212,25 @@ class LuaObject final : public ObjectBase
// First check functions that don't need an active graphics context, of modify the active graphics context
switch (hashsym) {
case hash("lua_start_paint"): {
if (getLocalBounds().isEmpty() || !argc)
if (getLocalBounds().isEmpty())
break;
auto scale = getValue<float>(zoomScale) * 2.0f; // Multiply by 2 for hi-dpi screens
int imageWidth = std::ceil(getWidth() * scale);
int imageHeight = std::ceil(getHeight() * scale);
int layer = atom_getfloat(argv);
if (!imageWidth || !imageHeight)
return;

activeLayer = layer;
framebuffers[layer].bind(nvg, imageWidth, imageHeight);

nvgViewport(0, 0, imageWidth, imageHeight);
nvgClear(nvg);
nvgBeginFrame(nvg, getWidth(), getHeight(), scale);
nvgSave(nvg);
return;
}
case hash("lua_end_paint"): {
if(!argc) return;
int layer = atom_getfloat(argv);

if (!framebuffers[layer].isValid()) return;

activeLayer = -1;


auto scale = getValue<float>(zoomScale) * 2.0f; // Multiply by 2 for hi-dpi screens
nvgGlobalScissor(nvg, 0, 0, getWidth() * scale, getHeight() * scale);
nvgEndFrame(nvg);
Expand All @@ -261,10 +252,7 @@ class LuaObject final : public ObjectBase
return;
}
}

if (activeLayer < 0 || !framebuffers[activeLayer].isValid())
return; // If there is no active framebuffer at this point, return


switch (hashsym) {
case hash("lua_set_color"): {
if (argc == 1) {
Expand Down Expand Up @@ -475,31 +463,28 @@ class LuaObject final : public ObjectBase
}
}

void timerCallback() override
// We need to update the framebuffer in a place where the current graphics context is active (for multi-window support, thanks to Alex for figuring that out)
// but we also need to be outside of calls to beginFrame/endFrame
// So we have this separate callback function that occurs after activating the GPU context, but before starting the frame
void updateFramebuffers() override
{
LuaGuiMessage guiMessage;
while (guiMessageQueue.try_dequeue(guiMessage)) {
guiCommandBuffer.push_back(guiMessage);
}

auto* startMesage = pd->generateSymbol("lua_start_paint");
auto* endMessage = pd->generateSymbol("lua_end_paint");

// TODO: this can be optimised more
int numLayers = framebuffers.size() + 2;
for(int layer = 1; layer < numLayers; layer++) {
for(auto& [layer, layerQueue] : guiMessageQueue) {
while (layerQueue.try_dequeue(guiMessage)) {
guiCommandBuffer[layer].push_back(guiMessage);
}

auto* startMesage = pd->generateSymbol("lua_start_paint");
auto* endMessage = pd->generateSymbol("lua_end_paint");

int startIdx = -1, endIdx = -1;
int currentLayer = -1;
bool updateScene = false;
for (int i = guiCommandBuffer.size() - 1; i >= 0; i--) {
if (guiCommandBuffer[i].symbol == startMesage) {
currentLayer = atom_getfloat(&guiCommandBuffer[i].data[0]);
if(currentLayer != layer) continue;
for (int i = guiCommandBuffer[layer].size() - 1; i >= 0; i--) {
if (guiCommandBuffer[layer][i].symbol == startMesage)
startIdx = i;
}
if (guiCommandBuffer[i].symbol == endMessage) {
if (guiCommandBuffer[layer][i].symbol == endMessage)
endIdx = i + 1;
}

if (startIdx != -1 && endIdx != -1) {
updateScene = true;
break;
Expand All @@ -509,26 +494,23 @@ class LuaObject final : public ObjectBase
if (updateScene) {
if (endIdx > startIdx) {
for (int i = startIdx; i < endIdx; i++) {
handleGuiMessage(guiCommandBuffer[i].symbol, guiCommandBuffer[i].size, guiCommandBuffer[i].data.data());
handleGuiMessage(layer, guiCommandBuffer[layer][i].symbol, guiCommandBuffer[layer][i].size, guiCommandBuffer[layer][i].data.data());
}
}
guiCommandBuffer[layer].erase(guiCommandBuffer[layer].begin(), guiCommandBuffer[layer].begin() + endIdx);
}

if (isSelected != object->isSelected() || !framebuffers[layer].isValid()) {
isSelected = object->isSelected();
sendRepaintMessage();
}
//guiCommandBuffer.erase(guiCommandBuffer.begin() + startIdx, guiCommandBuffer.begin() + endIdx);
}

guiCommandBuffer.clear();

auto needsFramebufferUpdate = framebuffers.size() == 0 || !framebuffers[0].isValid();
if (isSelected != object->isSelected() || needsFramebufferUpdate) {
isSelected = object->isSelected();
sendRepaintMessage();
}
}

static void drawCallback(void* target, t_symbol* sym, int argc, t_atom* argv)
static void drawCallback(void* target, int layer, t_symbol* sym, int argc, t_atom* argv)
{
for (auto* object : allDrawTargets[static_cast<t_pdlua*>(target)]) {
object->guiMessageQueue.enqueue({ sym, argc, argv });
object->guiMessageQueue[layer].enqueue({ sym, argc, argv });
}
}

Expand Down
3 changes: 3 additions & 0 deletions Source/Objects/ObjectBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ class ObjectBase : public Component

// Flag to make object visible or hidden inside a GraphOnParent
virtual bool hideInGraph();

// Override function if you need to update framebuffers outside of the render loop (but with the correct active context)
virtual void updateFramebuffers() {};

// Most objects ignore mouseclicks when locked
// Objects can override this to do custom locking behaviour
Expand Down

0 comments on commit b9d2eab

Please sign in to comment.