System/Synchronization.hpp: Add the sw::TaskEvents interface

This is implemented by sw::WaitGroup, and vk::Fence.

The goal here is to abstract the underlying implementation of the task tracker. We currently (ab)use vk::Fence for queue tracking when a sw::WaitGroup could be used.

This also allows us to implement a task logger in the future that can show what's inflight at any time.

Bug: b/133127573
Change-Id: I0177b26e7577ffd5cb687a5105a69a22070a7ac6
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31682
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/System/Synchronization.hpp b/src/System/Synchronization.hpp
index a6d0df2..7988a59 100644
--- a/src/System/Synchronization.hpp
+++ b/src/System/Synchronization.hpp
@@ -24,13 +24,28 @@
 namespace sw
 {
 
+// TaskEvents is an interface for notifying when tasks begin and end.
+// Tasks can be nested and/or overlapping.
+// TaskEvents is used for task queue synchronization.
+class TaskEvents
+{
+public:
+	// start() is called before a task begins.
+	virtual void start() = 0;
+	// finish() is called after a task ends. finish() must only be called after
+	// a corresponding call to start().
+	virtual void finish() = 0;
+	// complete() is a helper for calling start() followed by finish().
+	inline void complete() { start(); finish(); }
+};
+
 // WaitGroup is a synchronization primitive that allows you to wait for
 // collection of asynchronous tasks to finish executing.
 // Call add() before each task begins, and then call done() when after each task
 // is finished.
 // At the same time, wait() can be used to block until all tasks have finished.
 // WaitGroup takes its name after Golang's sync.WaitGroup.
-class WaitGroup
+class WaitGroup : public TaskEvents
 {
 public:
 	// add() begins a new task.
@@ -82,6 +97,10 @@
 		return count_;
 	}
 
+	// TaskEvents compliance
+	void start() override { add(); }
+	void finish() override { done(); }
+
 private:
 	int32_t count_ = 0; // guarded by mutex
 	std::mutex mutex;
diff --git a/src/Vulkan/VkFence.hpp b/src/Vulkan/VkFence.hpp
index d3951c4..b803230 100644
--- a/src/Vulkan/VkFence.hpp
+++ b/src/Vulkan/VkFence.hpp
@@ -21,7 +21,7 @@
 namespace vk
 {
 
-class Fence : public Object<Fence, VkFence>
+class Fence : public Object<Fence, VkFence>, public sw::TaskEvents
 {
 public:
 	Fence() : signaled(sw::Event::ClearMode::Manual, false) {}
@@ -57,13 +57,14 @@
 		return signaled.wait(timeout) ? VK_SUCCESS : VK_TIMEOUT;
 	}
 
-	void start()
+	// TaskEvents compliance
+	void start() override
 	{
 		ASSERT(!signaled);
 		wg.add();
 	}
 
-	void finish()
+	void finish() override
 	{
 		ASSERT(!signaled);
 		if (wg.done())
diff --git a/src/WSI/VkSwapchainKHR.cpp b/src/WSI/VkSwapchainKHR.cpp
index 54cea09..bdd459e 100644
--- a/src/WSI/VkSwapchainKHR.cpp
+++ b/src/WSI/VkSwapchainKHR.cpp
@@ -193,8 +193,7 @@
 
 			if(fence)
 			{
-				vk::Cast(fence)->start();
-				vk::Cast(fence)->finish();
+				vk::Cast(fence)->complete();
 			}
 
 			return VK_SUCCESS;