Skip to content

Commit

Permalink
NEW UIElement can have two new flags:
Browse files Browse the repository at this point in the history
  - flagDrawAloneRaw means the widget will not be drawn in parallel with other widgets in the Raw draw pass.
    Such a widget can then use the graphics thread pool for itself ONLY in onDrawRaw, using the context.globalThreadPool() call.

  - flagDrawAlonePBR means the widget will not be drawn in parallel with other widgets in the PBR draw pass.
    Such a widget can then use the graphics thread pool for itself ONLY in onDrawPBR, using the context.globalThreadPool() call.

As an optimization, PBR backgrounds in pbrbackgroundgui.d are now resized in parallel.
  • Loading branch information
Guillaume Piolat committed Jan 12, 2023
1 parent 4093f78 commit a3453db
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 42 deletions.
2 changes: 2 additions & 0 deletions core/dplug/core/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ else

/// Calls the delegate in parallel, with 0..count as index.
/// Immediate waiting for completion.
/// If there is only one task, it is run directly on this thread.
/// IMPORTANT to be reentrant there! widget drawn alone can then launch same threadpool.
void parallelFor(int count, scope ThreadPoolDelegate dg)
{
assert(_state == State.initial);
Expand Down
15 changes: 14 additions & 1 deletion gui/dplug/gui/context.d
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import core.stdc.string : strcmp;

import dplug.core.vec;
import dplug.core.nogc;
import dplug.core.thread;

import dplug.window.window;

Expand Down Expand Up @@ -77,10 +78,17 @@ nothrow @nogc:
/// so it can be better if this is a shared resource.
/// It is lazily constructed.
/// See_also: `ImageResizer`.
/// Note: do not use this resizer concurrently (in `onDrawRaw`, `onDrawPBR`, etc.).
/// Note: do not use this resizer concurrently (in `onDrawRaw`, `onDrawPBR`, etc.)
/// unless you have `flagDrawAloneRaw` or `flagDrawAlonePBR`.
/// Usually intended for `reflow()`.
ImageResizer* globalImageResizer();

/// A shared threadpool, used to draw widgets concurrently.
/// NEW: A widget can opt to be drawn alone, and use the threadpool for its own drawing itself.
/// Can ONLY be called from `onDrawRaw` AND when the flag `flagDrawAloneRaw` is used,
/// or from `onDrawPBR` AND when the flag `flagDrawAlonePBR` is used.
ThreadPool* globalThreadPool();

/// Returns a UI-wide profiler that records UI performance, as long as Dplug_ProfileUI version is
/// defined. Else, it is a null IProfiler that forgets everything.
/// For performance purpose, it is recommended:
Expand Down Expand Up @@ -211,6 +219,11 @@ nothrow:
return &_globalResizer;
}

final override ThreadPool* globalThreadPool()
{
return &_owner._threadPool;
}

final override IProfiler profiler()
{
return _profiler;
Expand Down
20 changes: 18 additions & 2 deletions gui/dplug/gui/element.d
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,14 @@ enum : UIFlags
/// to be called sucessively.
flagPBR = 2,

// This `UIElement` is animated and as such the `onAnimate` callback should be called regularly.
flagAnimated = 4
/// This `UIElement` is animated and as such the `onAnimate` callback should be called regularly.
flagAnimated = 4,

/// Is not drawn in parallel with other widgets, when draw to the Raw layer.
flagDrawAloneRaw = 8,

/// Is not drawn in parallel with other widgets, when draw to the PBR layer.
flagDrawAlonePBR = 16,
}

/// Used by `setDirty` calls to figure which layer should be invalidated.
Expand Down Expand Up @@ -924,6 +930,16 @@ nothrow:
return (_flags & flagAnimated) != 0;
}

final bool isDrawAloneRaw() pure const
{
return (_flags & flagDrawAloneRaw) != 0;
}

final bool isDrawAlonePBR() pure const
{
return (_flags & flagDrawAlonePBR) != 0;
}

/// Appends the Elements that should be drawn, in order.
/// You should empty it before calling this function.
/// Everything visible get into the draw list, but that doesn't mean they
Expand Down
84 changes: 61 additions & 23 deletions gui/dplug/gui/graphics.d
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ protected:
IWindow _window;

// Task pool for multi-threaded image work
ThreadPool _threadPool;
package ThreadPool _threadPool;

// Size constraints of this UI.
// Currently can only be a fixed size.
Expand Down Expand Up @@ -1031,24 +1031,36 @@ protected:

while(drawn < N)
{
// See: redrawElementsPBR below for a remark on performance there.

int canBeDrawn = 1; // at least one can be drawn without collision

// Search max number of parallelizable draws until the end of the list or a collision is found
bool foundIntersection = false;
for ( ; (drawn + canBeDrawn < N); ++canBeDrawn)
// Does this first widget in the FIFO wants to be draw alone?
if (! _elemsToDrawRaw[drawn].isDrawAloneRaw())
{
box2i candidate = _elemsToDrawRaw[drawn + canBeDrawn].position;

for (int j = 0; j < canBeDrawn; ++j)
// Search max number of parallelizable draws until the end of the list or a collision is found
bool foundIntersection = false;
for ( ; (drawn + canBeDrawn < N); ++canBeDrawn)
{
if (_elemsToDrawRaw[drawn + j].position.intersects(candidate))
// Should we include this element to the assembled set of widgets to draw?
UIElement candidateWidget = _elemsToDrawRaw[drawn + canBeDrawn];

if (candidateWidget.isDrawAloneRaw())
break; // wants to be drawn alone

box2i candidatePos = candidateWidget.position;

for (int j = 0; j < canBeDrawn; ++j) // PERF: aaaand this is nicely quadratic
{
foundIntersection = true;
break;
if (_elemsToDrawRaw[drawn + j].position.intersects(candidatePos))
{
foundIntersection = true;
break;
}
}
if (foundIntersection)
break;
}
if (foundIntersection)
break;
}

assert(canBeDrawn >= 1);
Expand Down Expand Up @@ -1105,24 +1117,50 @@ protected:

while(drawn < N)
{
// <Scheduling remark>
// PERF: scheduling here is not entirely optimal: consecutive overalapping widgets
// would block further parallel draw if the next widget doesn't overlap the other two.
//
// ________ _____
// | | | |
// | B |______ | C | <---- Will not draw A and C in parallel if
// |______| | |___| Z(A) < Z(B) < Z(C)
// | A |
// |________|
//
// PERF: to go further, could use the disjointed rects to draw even more in parallel.
// Real updated graphics is intersection(position, union(_rectsToUpdateDisjointedPBR)),
// not simply the widget position.
// </Scheduling remark>

int canBeDrawn = 1; // at least one can be drawn without collision

// Search max number of parallelizable draws until the end of the list or a collision is found
bool foundIntersection = false;
for ( ; (drawn + canBeDrawn < N); ++canBeDrawn)
// Does this first widget in the FIFO wants to be draw alone?
if (! _elemsToDrawPBR[drawn].isDrawAlonePBR())
{
box2i candidate = _elemsToDrawPBR[drawn + canBeDrawn].position;

for (int j = 0; j < canBeDrawn; ++j)
// Search max number of parallelizable draws until the end of the list or a collision is found
bool foundIntersection = false;
for ( ; (drawn + canBeDrawn < N); ++canBeDrawn)
{
if (_elemsToDrawPBR[drawn + j].position.intersects(candidate))
// Should we include this element to the assembled set of widgets to draw?
UIElement candidateWidget = _elemsToDrawPBR[drawn + canBeDrawn];

if (candidateWidget.isDrawAlonePBR())
break; // wants to be drawn alone

box2i candidatePos = _elemsToDrawPBR[drawn + canBeDrawn].position;

for (int j = 0; j < canBeDrawn; ++j) // check with each former selected widget, PERF quadratic
{
foundIntersection = true;
break;
if (_elemsToDrawPBR[drawn + j].position.intersects(candidatePos))
{
foundIntersection = true;
break;
}
}
if (foundIntersection)
break;
}
if (foundIntersection)
break;
}

assert(canBeDrawn >= 1);
Expand Down
42 changes: 26 additions & 16 deletions pbrwidgets/dplug/pbrwidgets/pbrbackgroundgui.d
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ nothrow:

this(SizeConstraints sizeConstraints)
{
super(sizeConstraints, flagPBR | flagAnimated);
super(sizeConstraints, flagPBR | flagAnimated | flagDrawAlonePBR);

_diffuseResized = mallocNew!(OwnedImage!RGBA);
_materialResized = mallocNew!(OwnedImage!RGBA);
Expand Down Expand Up @@ -114,8 +114,7 @@ nothrow:
{
int W = position.width;
int H = position.height;
ImageResizer resizer;


if (_forceResizeUpdate || _diffuseResized.w != W || _diffuseResized.h != H)
{
// Decompress images lazily for this size
Expand All @@ -126,23 +125,34 @@ nothrow:
loadBackgroundImagesFromStaticData();
}

version(Dplug_ProfileUI) context.profiler.category("image").begin("set image size");
_diffuseResized.size(W, H);
_materialResized.size(W, H);
_depthResized.size(W, H);
version(Dplug_ProfileUI) context.profiler.end;

version(Dplug_ProfileUI) context.profiler.begin("resize Diffuse background");
resizer.resizeImageDiffuse(_diffuse.toRef, _diffuseResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;

version(Dplug_ProfileUI) context.profiler.begin("resize Material background");
resizer.resizeImageMaterial(_material.toRef, _materialResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;

version(Dplug_ProfileUI) context.profiler.begin("resize Depth background");
resizer.resizeImageDepth(_depth.toRef, _depthResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;
// Draw a number of UIElement in parallel
void resizeOneImage(int i, int threadIndex) nothrow @nogc
{
ImageResizer resizer;
if (i == 0)
{
version(Dplug_ProfileUI) context.profiler.begin("resize Diffuse background");
resizer.resizeImageDiffuse(_diffuse.toRef, _diffuseResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;
}
if (i == 1)
{
version(Dplug_ProfileUI) context.profiler.begin("resize Material background");
resizer.resizeImageMaterial(_material.toRef, _materialResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;
}
if (i == 2)
{
version(Dplug_ProfileUI) context.profiler.begin("resize Depth background");
resizer.resizeImageDepth(_depth.toRef, _depthResized.toRef);
version(Dplug_ProfileUI) context.profiler.end;
}
}
context.globalThreadPool.parallelFor(3, &resizeOneImage);

_forceResizeUpdate = false;

Expand Down

0 comments on commit a3453db

Please sign in to comment.