/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VIEWER_AUTOMATION_ENGINE_H #define VIEWER_AUTOMATION_ENGINE_H #include namespace filament { class ColorGrading; class Engine; class LightManager; class MaterialInstance; class Renderer; class View; namespace viewer { /** * The AutomationEngine makes it easy to push a bag of settings values to Filament. * It can also be used to iterate through settings permutations for testing purposes. * * When creating an automation engine for testing purposes, clients give it an immutable reference * to an AutomationSpec. It is always in one of two states: running or idle. The running state can * be entered immediately (startRunning) or by requesting batch mode (startBatchMode). * * When executing a test, clients should call tick() after each frame is rendered, which gives * automation an opportunity to push settings to Filament, increment the current test index (if * enough time has elapsed), and request an asynchronous screenshot. * * The time to sleep between tests is configurable and can be set to zero. Automation also waits a * specified minimum number of frames between tests. * * Batch mode is meant for non-interactive applications. In batch mode, automation defers applying * the first test case until the client unblocks it via signalBatchMode(). This is useful when * waiting for a large model file to become fully loaded. Batch mode also offers a query * (shouldClose) that is triggered after the last test has been invoked. */ class UTILS_PUBLIC AutomationEngine { public: /** * Allows users to toggle screenshots, change the sleep duration between tests, etc. */ struct Options { /** * Minimum time that automation waits between applying a settings object and advancing * to the next test case. Specified in seconds. */ float sleepDuration = 0.2; /** * Similar to sleepDuration, but expressed as a frame count. Both the minimum sleep time * and the minimum frame count must be elapsed before automation advances to the next test. */ int minFrameCount = 2; /** * If true, test progress is dumped to the utils Log (info priority). */ bool verbose = true; /** * If true, the tick function writes out a screenshot before advancing to the next test. */ bool exportScreenshots = false; /** * If true, the tick function writes out a settings JSON file before advancing. */ bool exportSettings = false; }; /** * Collection of Filament objects that can be modified by the automation engine. */ struct ViewerContent { View* view; Renderer* renderer; MaterialInstance* const* materials; size_t materialCount; LightManager* lightManager; Scene* scene; IndirectLight* indirectLight; utils::Entity sunlight; utils::Entity* assetLights; size_t assetLightCount; }; /** * Creates an automation engine and places it in an idle state. * * @param spec Specifies a set of settings permutations (owned by the client). * @param settings Client-owned settings object. This not only supplies the initial * state, it also receives changes during tick(). This is useful when * building automation into an application that has a settings UI. * * @see setOptions * @see startRunning */ AutomationEngine(const AutomationSpec* spec, Settings* settings) : mSpec(spec), mSettings(settings) {} /** * Shortcut constructor that creates an automation engine from a JSON string. * * This constructor can be used if the user does not need to monitor how the settings * change over time and does not need ownership over the AutomationSpec. * * An example of a JSON spec can be found by searching the repo for DEFAULT_AUTOMATION. * This is documented using a JSON schema (look for viewer/schemas/automation.json). * * @param jsonSpec Valid JSON string that conforms to the automation schema. * @param size Number of characters in the JSON string. * @return Automation engine or null if unable to read the JSON. */ static AutomationEngine* createFromJSON(const char* jsonSpec, size_t size); /** * Creates an automation engine for the sole purpose of pushing settings, or for executing * the default test sequence. * * To see how the default test sequence is generated, search for DEFAULT_AUTOMATION. */ static AutomationEngine* createDefault(); /** * Activates the automation test. During the subsequent call to tick(), the first test is * applied and automation enters the running state. */ void startRunning(); /** * Activates the automation test, but enters a paused state until the user calls * signalBatchMode(). */ void startBatchMode(); /** * Notifies the automation engine that time has passed, a new frame has been rendered. * * This is when settings get applied, screenshots are (optionally) exported, and the internal * test counter is potentially incremented. * * @param content Contains the Filament View, Materials, and Renderer that get modified. * @param deltaTime The amount of time that has passed since the previous tick in seconds. */ void tick(const ViewerContent& content, float deltaTime); /** * Mutates a set of client-owned Filament objects according to a JSON string. * * This method is an alternative to tick(). It allows clients to use the automation engine as a * remote control, as opposed to iterating through a predetermined test sequence. * * This updates the stashed Settings object, then pushes those settings to the given * Filament objects. Clients can optionally call getColorGrading() after calling this method. * * @param json Contains the JSON string with a set of changes that need to be pushed. * @param jsonLength Number of characters in the json string. * @param content Contains a set of Filament objects that you want to mutate. */ void applySettings(const char* json, size_t jsonLength, const ViewerContent& content); /** * Gets a color grading object that corresponds to the latest settings. * * This method either returns a cached instance, or it destroys the cached instance and creates * a new one. */ ColorGrading* getColorGrading(Engine* engine); /** * Gets the current viewer options. * * NOTE: Focal length here might be different from the user-specified value, due to DoF options. */ ViewerOptions getViewerOptions() const; /** * Signals that batch mode can begin. Call this after all meshes and textures finish loading. */ void signalBatchMode() { mBatchModeAllowed = true; } /** * Cancels an in-progress automation session. */ void stopRunning() { mIsRunning = false; } /** * Signals that the application is closing, so all pending screenshots should be cancelled. */ void terminate(); /** * Configures the automation engine for users who wish to set up a custom sleep time * between tests, etc. */ void setOptions(Options options) { mOptions = options; } /** * Returns true if automation is in batch mode and all tests have finished. */ bool shouldClose() const { return mShouldClose; } /** * Convenience function that writes out a JSON file to disk containing all settings. * * @param Settings State vector to serialize. * @param filename Desired JSON filename. */ static void exportSettings(const Settings& settings, const char* filename); Options getOptions() const { return mOptions; } bool isRunning() const { return mIsRunning; } size_t currentTest() const { return mCurrentTest; } size_t testCount() const { return mSpec->size(); } bool isBatchModeEnabled() const { return mBatchModeEnabled; } const char* getStatusMessage() const; ~AutomationEngine(); private: AutomationSpec const * const mSpec; Settings * const mSettings; Options mOptions; Engine* mColorGradingEngine = nullptr; ColorGrading* mColorGrading = nullptr; ColorGradingSettings mColorGradingSettings = {}; size_t mCurrentTest; float mElapsedTime; int mElapsedFrames; bool mIsRunning = false; bool mBatchModeEnabled = false; bool mRequestStart = false; bool mShouldClose = false; bool mBatchModeAllowed = false; bool mTerminated = false; bool mOwnsSettings = false; public: // For internal use from a screenshot callback. void requestClose() { mShouldClose = true; } bool isTerminated() const { return mTerminated; } }; } // namespace viewer } // namespace filament #endif // VIEWER_AUTOMATION_ENGINE_H