Scheduler enhancement: task ordering support

Background
-----
This pull request enhances the way ```DynamoScheduler``` prioritize those ```AsyncTask``` objects in its task queue. Prior to this update, the tasks are reordered based solely on the return value of ```AsyncTask.CompareCore``` methods. The problem with that was: if an ```AsyncTask``` derived class chooses to override ```CompareCore``` method, it needs to be aware of the other ```AsyncTask``` it is compared to, and the other task must agreed with this comparison.

For example, the following ```LargeTask``` type determines that it should always come before ```SmallTask``` type:

```cs
class LargeTask : AsyncTask
{
    protected override int CompareCore(AsyncTask otherTask)
    {
        if (otherTask is SmallTask) // LargeTask comes before SmallTask
            return -1;
        
        return base.CompareCore(otherTask);
    }
}
```

However, this alone is not sufficient. Sorting a queue with ```[SmallTask][LargeTask]``` will not result in sorted queue ```[LargeTask][SmallTask]```, because ```SmallTask.CompareCore``` by default returns ```0```, which means *these two tasks have equal weight*. The following must be overridden in addition to the one in ```LargeTask``` to provide the expected outcome:

```cs
class SmallTask : AsyncTask
{
    protected override int CompareCore(AsyncTask otherTask)
    {
        if (otherTask is LargeTask) // SmallTask comes after LargeTask
            return 1;
        
        return base.CompareCore(otherTask);
    }
}
```

Changes
-----
```DynamoScheduler``` now sorts its task queue based on two factors: the ```AsyncTask.Priority``` property, then the value returned from ```AsyncTask.Compare``` method.

To mitigate the issue highlighted in *Background* section above, and to lighten the burden of an ```AsyncTask``` class author, ```AsyncTask``` derived classes override ```GetPriorityCore``` method and return one of several predetermined ```TaskPriority``` values. This return value helps ```DynamoScheduler``` determine the *relative ordering* of any two given ```AsyncTask```.

The following ```TaskPriority``` values are defined:

```cs
internal enum TaskPriority
{
    Critical,
    Highest,
    AboveNormal,
    Normal,
    BelowNormal,
    Lowest,
    Idle
}
```

In addition to the above change, the following ```AsyncTask``` classes now override ```CanMergeWithCore``` method to help ```DynamoScheduler``` discard duplicated tasks (e.g. for two ```UpdateRenderPackageAsyncTask``` objects that target the same ```NodeModel```):

    AggregateRenderPackageAsyncTask
    NotifyRenderPackagesReadyAsyncTask
    UpdateRenderPackageAsyncTask

Unit tests
-----
1. All existing scheduler queue processing tests continue to pass.

2. Added the following unit tests to verify the behaviour of built-in ```AsyncTask``` classes:

  - SchedulerIntegrationTests.TestTaskQueuePreProcessing00
  - SchedulerIntegrationTests.TestTaskQueuePreProcessing01
  - SchedulerIntegrationTests.TestTaskQueuePreProcessing02

Notes to reviewer
-----
Hi @lukechurch, I have added more test cases to further validate ```DynamoScheduler``` task ordering behaviour with respect to all the built-in ```AsyncTask``` classes in the system. Thanks in advance!
Good point, I have renamed ```XyzAsyncTaskExt``` to ```FakeXyzAsyncTask```, making this very clear.