Open 3D Engine GradientSignal Gem API Reference 23.10.0
O3DE is an open-source, fully-featured, high-fidelity, modular 3D engine for building games and simulations, available to every industry.
GradientSignal::EditorGradientPreviewUpdateJob Class Reference

#include <EditorGradientPreviewRenderer.h>

Inherits AZ::Job.

Public Types

using SampleFilterFunc = AZStd::function< float(float, const GradientSampleParams &)>
 

Public Member Functions

 AZ_CLASS_ALLOCATOR (EditorGradientPreviewUpdateJob, AZ::ThreadPoolAllocator)
 
 EditorGradientPreviewUpdateJob (AZ::JobContext *context=nullptr)
 
bool CancelAndWait ()
 
void Wait ()
 
bool ShouldRefreshUI ()
 
void RefreshPreview (GradientSampler sampler, SampleFilterFunc filterFunc, QSize imageResolution, QImage *previewImage)
 Perform any main-thread and one-time setup needed to refresh the preview, then kick off the job.
 
void Process () override
 Process runs exactly once for each time Start() is called on a Job, and processes on a Job worker thread.
 

Detailed Description

EditorGradientPreviewUpdateJob offloads the creation of a gradient preview image to another thread. This is necessary for Editor responsiveness. With complex gradient hierarchies, large previews, and/or multiple gradient previews visible at the same time (like in Landscape Canvas), it's possible for the preview generation to take multiple seconds, or even minutes in degenerate data cases.

In offloading the work, we also incrementally update the preview via an adaptive interlacing scheme, similar to GIF or PNG interlacing, so that it becomes visible and usable even before the work has completed.

Implementation notes:

  • This directly modifies the m_previewImage from a Job thread in a non-threadsafe way while it is also being used from Qt in the main thread. This doesn't cause any issues because we synchronously cancel the job thread any time we delete or recreate m_previewImage (such as during resizing).
  • The interlacing scheme is loosely based on the "Adam7" algorithm, which is used in the PNG format. Unlike Adam7, which uses a 7-pass system to operate on 8x8 interlace patterns, the code below uses an N-pass system. Roughly speaking, each pass doubles the number of pixels drawn relative to the previous pass. For a 256x256 image, the passes will draw 1 pixel, 1 pixel, 2 pixels, 4 pixels, 8 pixels, 16 pixels, 32 pixels, 64 pixels, 128 pixels, ..., 32768 pixels.
  • We only create a single job instance per gradient preview, and the Process() function of the job runs once for each time we need to refresh the preview. It remains dormant the rest of the time. We can't use a fire-and-forget job because we need the ability to synchronously cancel it and wait for it to be cancelled. This requirement comes from the way we reuse data that exists in the parent Preview widget class. We need to manage the lifetime to be exactly the same as the widget, and we can't have multiple jobs running in parallel that modify the same widget. (If we use fire-and-forget, even if we cancel asynchronously, it would be easy to start a new one before the old one finishes)

Member Function Documentation

◆ ShouldRefreshUI()

bool GradientSignal::EditorGradientPreviewUpdateJob::ShouldRefreshUI ( )
inline

This enables our widget to know whenever the preview image has changed. We also clear the refreshUI flag here on polling, so that we only detect changes since the last time we asked. We don't rely on watching for the thread to be running, since it's possible for the thread to run and finish before we ever poll for the first time.


The documentation for this class was generated from the following file: