Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
3D Graphics Rendering Cookbook

You're reading from   3D Graphics Rendering Cookbook A comprehensive guide to exploring rendering algorithms in modern OpenGL and Vulkan

Arrow left icon
Product type Paperback
Published in Aug 2021
Publisher Packt
ISBN-13 9781838986193
Length 670 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Viktor Latypov Viktor Latypov
Author Profile Icon Viktor Latypov
Viktor Latypov
Sergey Kosarevsky Sergey Kosarevsky
Author Profile Icon Sergey Kosarevsky
Sergey Kosarevsky
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Chapter 1: Establishing a Build Environment 2. Chapter 2: Using Essential Libraries FREE CHAPTER 3. Chapter 3: Getting Started with OpenGL and Vulkan 4. Chapter 4: Adding User Interaction and Productivity Tools 5. Chapter 5: Working with Geometry Data 6. Chapter 6: Physically Based Rendering Using the glTF2 Shading Model 7. Chapter 7: Graphics Rendering Pipeline 8. Chapter 8: Image-Based Techniques 9. Chapter 9: Working with Scene Graphs 10. Chapter 10: Advanced Rendering Techniques and Optimizations 11. Other Books You May Enjoy

Multithreading with Taskflow

Modern graphical applications require us to harness the power of multiple CPUs to be performant. Taskflow is a fast C++ header-only library that can help you write parallel programs with complex task dependencies quickly. This library is extremely useful as it allows you to jump into the development of multithreaded graphical applications that make use of advanced rendering concepts, such as frame graphs and multithreaded command buffers generation.

Getting ready

Here, we use Taskflow version 3.1.0. You can download it using the following Bootstrap snippet:

{
  "name": "taskflow",
  "source": {
    "type": "git",
    "url": "https://github.com/taskflow/taskflow.git",
    "revision": "v3.1.0"
  }
}

To debug dependency graphs produced by Taskflow, it is recommended that you install the GraphViz tool from https://www.graphviz.org.

The complete source code for this recipe can be found in Chapter2/09_Taskflow.

How to do it...

Let's create and run a set of concurrent dependent tasks via the for_each() algorithm. Each task will print a single value from an array in a concurrent fashion. The processing order can vary between different runs of the program:

  1. Include the taskflow.hpp header file:
    #include <taskflow/taskflow.hpp>
    using namespace std;
    int main() {
  2. The tf::Taskflow class is the main place to create a task dependency graph. Declare an instance and a data vector to process:
      tf::Taskflow taskflow;
      std::vector<int> items{ 1, 2, 3, 4, 5, 6, 7, 8 };
  3. The for_each() member function returns a task that implements a parallel-for loop algorithm. The task can be used for synchronization purposes:
      auto task = taskflow.for_each(     items.begin(), items.end(),     [](int item) { std::cout << item; }  );
  4. Let's attach some work before and after the parallel-for task so that we can view Start and End messages in the output. Let's call the new S and T tasks accordingly:
      taskflow.emplace(     []() {       std::cout << "\nS - Start\n";     }).name("S").precede(task);
      taskflow.emplace(     []() {       std::cout << "\nT - End\n";     }).name("T").succeed(task);
  5. Save the generated tasks dependency graph in .dot format so that we can process it later with the GraphViz dot tool:
      std::ofstream os("taskflow.dot");
      taskflow.dump(os);
  6. Now we can create an executor object and run the constructed taskflow graph:
      Tf::Executor executor;
      executor.run(taskflow).wait();
      return 0;
    }

One important part to mention here is that the dependency graph can only be constructed once. Then, it can be reused in every frame to run concurrent tasks efficiently.

The output from the preceding program should look similar to the following listing:

S - Start
39172 runs 6
46424 runs 5
17900 runs 2
26932 runs 1
26932 runs 8
23888 runs 3
45464 runs 7
32064 runs 4
T - End

Here, we can see our S and T tasks. Between them, there are multiple threads with different IDs processing different elements of the items[] vector in parallel.

There's more...

The application saved the dependency graph inside the taskflow.dot file. It can be converted into a visual representation by GraphViz using the following command:

dot -Tpng taskflow.dot > output.png

The resulting .png image should look similar to the following screenshot:

Figure 2.7 – The Taskflow dependency graph for for_each()

Figure 2.7 – The Taskflow dependency graph for for_each()

This functionality is extremely useful when you are debugging complex dependency graphs (and producing complex-looking images for your books and papers).

The Taskflow library functionality is vast and provides implementations for numerous parallel algorithms and profiling capabilities. Please refer to the official documentation for in-depth coverage at https://taskflow.github.io/taskflow/index.html.

You have been reading a chapter from
3D Graphics Rendering Cookbook
Published in: Aug 2021
Publisher: Packt
ISBN-13: 9781838986193
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime