521 lines
21 KiB
C++
521 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2015 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.
|
|
*/
|
|
|
|
//! \file
|
|
|
|
#ifndef TNT_FILAMENT_RENDERER_H
|
|
#define TNT_FILAMENT_RENDERER_H
|
|
|
|
#include <filament/FilamentAPI.h>
|
|
|
|
#include <utils/compiler.h>
|
|
|
|
#include <backend/PresentCallable.h>
|
|
|
|
#include <math/vec4.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
namespace filament {
|
|
|
|
class Engine;
|
|
class RenderTarget;
|
|
class SwapChain;
|
|
class View;
|
|
class Viewport;
|
|
|
|
namespace backend {
|
|
class PixelBufferDescriptor;
|
|
} // namespace backend
|
|
|
|
/**
|
|
* A Renderer instance represents an operating system's window.
|
|
*
|
|
* Typically, applications create a Renderer per window. The Renderer generates drawing commands
|
|
* for the render thread and manages frame latency.
|
|
*
|
|
* A Renderer generates drawing commands from a View, itself containing a Scene description.
|
|
*
|
|
* Creation and Destruction
|
|
* ========================
|
|
*
|
|
* A Renderer is created using Engine.createRenderer() and destroyed using
|
|
* Engine.destroy(const Renderer*).
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* #include <filament/Renderer.h>
|
|
* #include <filament/Engine.h>
|
|
* using namespace filament;
|
|
*
|
|
* Engine* engine = Engine::create();
|
|
*
|
|
* Renderer* renderer = engine->createRenderer();
|
|
* engine->destroy(&renderer);
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* @see Engine, View
|
|
*/
|
|
class UTILS_PUBLIC Renderer : public FilamentAPI {
|
|
public:
|
|
|
|
/**
|
|
* Use DisplayInfo to set important Display properties. This is used to achieve correct
|
|
* frame pacing and dynamic resolution scaling.
|
|
*/
|
|
struct DisplayInfo {
|
|
// refresh-rate of the display in Hz. set to 0 for offscreen or turn off frame-pacing.
|
|
float refreshRate = 60.0f;
|
|
|
|
// how far in advance a buffer must be queued for presentation at a given time in ns
|
|
uint64_t presentationDeadlineNanos = 0;
|
|
|
|
// offset by which vsyncSteadyClockTimeNano provided in beginFrame() is offset in ns
|
|
uint64_t vsyncOffsetNanos = 0;
|
|
};
|
|
|
|
/**
|
|
* Use FrameRateOptions to set the desired frame rate and control how quickly the system
|
|
* reacts to GPU load changes.
|
|
*
|
|
* interval: desired frame interval in multiple of the refresh period, set in DisplayInfo
|
|
* (as 1 / DisplayInfo::refreshRate)
|
|
*
|
|
* The parameters below are relevant when some Views are using dynamic resolution scaling:
|
|
*
|
|
* headRoomRatio: additional headroom for the GPU as a ratio of the targetFrameTime.
|
|
* Useful for taking into account constant costs like post-processing or
|
|
* GPU drivers on different platforms.
|
|
* history: History size. higher values, tend to filter more (clamped to 30)
|
|
* scaleRate: rate at which the gpu load is adjusted to reach the target frame rate
|
|
* This value can be computed as 1 / N, where N is the number of frames
|
|
* needed to reach 64% of the target scale factor.
|
|
* Higher values make the dynamic resolution react faster.
|
|
*
|
|
* @see View::DynamicResolutionOptions
|
|
* @see Renderer::DisplayInfo
|
|
*
|
|
*/
|
|
struct FrameRateOptions {
|
|
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
|
|
float scaleRate = 0.125f; //!< rate at which the system reacts to load changes
|
|
uint8_t history = 3; //!< history size
|
|
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
|
|
};
|
|
|
|
/**
|
|
* ClearOptions are used at the beginning of a frame to clear or retain the SwapChain content.
|
|
*/
|
|
struct ClearOptions {
|
|
/** Color to use to clear the SwapChain */
|
|
math::float4 clearColor = {};
|
|
/**
|
|
* Whether the SwapChain should be cleared using the clearColor. Use this if translucent
|
|
* View will be drawn, for instance.
|
|
*/
|
|
bool clear = false;
|
|
/**
|
|
* Whether the SwapChain content should be discarded. clear implies discard. Set this
|
|
* to false (along with clear to false as well) if the SwapChain already has content that
|
|
* needs to be preserved
|
|
*/
|
|
bool discard = true;
|
|
};
|
|
|
|
/**
|
|
* Information about the display this Renderer is associated to. This information is needed
|
|
* to accurately compute dynamic-resolution scaling and for frame-pacing.
|
|
*/
|
|
void setDisplayInfo(const DisplayInfo& info) noexcept;
|
|
|
|
/**
|
|
* Set options controlling the desired frame-rate.
|
|
*/
|
|
void setFrameRateOptions(FrameRateOptions const& options) noexcept;
|
|
|
|
/**
|
|
* Set ClearOptions which are used at the beginning of a frame to clear or retain the
|
|
* SwapChain content.
|
|
*/
|
|
void setClearOptions(const ClearOptions& options);
|
|
|
|
/**
|
|
* Get the Engine that created this Renderer.
|
|
*
|
|
* @return A pointer to the Engine instance this Renderer is associated to.
|
|
*/
|
|
Engine* getEngine() noexcept;
|
|
|
|
/**
|
|
* Get the Engine that created this Renderer.
|
|
*
|
|
* @return A constant pointer to the Engine instance this Renderer is associated to.
|
|
*/
|
|
inline Engine const* getEngine() const noexcept {
|
|
return const_cast<Renderer *>(this)->getEngine();
|
|
}
|
|
|
|
/**
|
|
* Render a View into this renderer's window.
|
|
*
|
|
* This is filament main rendering method, most of the CPU-side heavy lifting is performed
|
|
* here. render() main function is to generate render commands which are asynchronously
|
|
* executed by the Engine's render thread.
|
|
*
|
|
* render() generates commands for each of the following stages:
|
|
*
|
|
* 1. Shadow map pass, if needed (currently only a single shadow map is supported).
|
|
* 2. Depth pre-pass.
|
|
* 3. Color pass.
|
|
* 4. Post-processing pass.
|
|
*
|
|
* A typical render loop looks like this:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* #include <filament/Renderer.h>
|
|
* #include <filament/View.h>
|
|
* using namespace filament;
|
|
*
|
|
* void renderLoop(Renderer* renderer, SwapChain* swapChain) {
|
|
* do {
|
|
* // typically we wait for VSYNC and user input events
|
|
* if (renderer->beginFrame(swapChain)) {
|
|
* renderer->render(mView);
|
|
* renderer->endFrame();
|
|
* }
|
|
* } while (!quit());
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
*
|
|
* @param view A pointer to the view to render.
|
|
*
|
|
* @attention
|
|
* render() must be called *after* beginFrame() and *before* endFrame().
|
|
*
|
|
* @note
|
|
* render() must be called from the Engine's main thread (or external synchronization
|
|
* must be provided). In particular, calls to render() on different Renderer instances
|
|
* **must** be synchronized.
|
|
*
|
|
* @remark
|
|
* render() perform potentially heavy computations and cannot be multi-threaded. However,
|
|
* internally, render() is highly multi-threaded to both improve performance in mitigate
|
|
* the call's latency.
|
|
*
|
|
* @remark
|
|
* render() is typically called once per frame (but not necessarily).
|
|
*
|
|
* @see
|
|
* beginFrame(), endFrame(), View
|
|
*
|
|
*/
|
|
void render(View const* view);
|
|
|
|
/**
|
|
* Flags used to configure the behavior of copyFrame().
|
|
*
|
|
* @see
|
|
* copyFrame()
|
|
*/
|
|
using CopyFrameFlag = uint32_t;
|
|
|
|
/**
|
|
* Indicates that the dstSwapChain passed into copyFrame() should be
|
|
* committed after the frame has been copied.
|
|
*
|
|
* @see
|
|
* copyFrame()
|
|
*/
|
|
static constexpr CopyFrameFlag COMMIT = 0x1;
|
|
/**
|
|
* Indicates that the presentation time should be set on the dstSwapChain
|
|
* passed into copyFrame to the monotonic clock time when the frame is
|
|
* copied.
|
|
*
|
|
* @see
|
|
* copyFrame()
|
|
*/
|
|
static constexpr CopyFrameFlag SET_PRESENTATION_TIME = 0x2;
|
|
/**
|
|
* Indicates that the dstSwapChain passed into copyFrame() should be
|
|
* cleared to black before the frame is copied into the specified viewport.
|
|
*
|
|
* @see
|
|
* copyFrame()
|
|
*/
|
|
static constexpr CopyFrameFlag CLEAR = 0x4;
|
|
|
|
/**
|
|
* Copy the currently rendered view to the indicated swap chain, using the
|
|
* indicated source and destination rectangle.
|
|
*
|
|
* @param dstSwapChain The swap chain into which the frame should be copied.
|
|
* @param dstViewport The destination rectangle in which to draw the view.
|
|
* @param srcViewport The source rectangle to be copied.
|
|
* @param flags One or more CopyFrameFlag behavior configuration flags.
|
|
*
|
|
* @remark
|
|
* copyFrame() should be called after a frame is rendered using render()
|
|
* but before endFrame() is called.
|
|
*/
|
|
void copyFrame(SwapChain* dstSwapChain, Viewport const& dstViewport,
|
|
Viewport const& srcViewport, uint32_t flags = 0);
|
|
|
|
/**
|
|
* Reads back the content of the SwapChain associated with this Renderer.
|
|
*
|
|
* @param xoffset Left offset of the sub-region to read back.
|
|
* @param yoffset Bottom offset of the sub-region to read back.
|
|
* @param width Width of the sub-region to read back.
|
|
* @param height Height of the sub-region to read back.
|
|
* @param buffer Client-side buffer where the read-back will be written.
|
|
*
|
|
* The following format are always supported:
|
|
* - PixelBufferDescriptor::PixelDataFormat::RGBA
|
|
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
|
|
*
|
|
* The following types are always supported:
|
|
* - PixelBufferDescriptor::PixelDataType::UBYTE
|
|
* - PixelBufferDescriptor::PixelDataType::UINT
|
|
* - PixelBufferDescriptor::PixelDataType::INT
|
|
* - PixelBufferDescriptor::PixelDataType::FLOAT
|
|
*
|
|
* Other combination of format/type may be supported. If a combination is
|
|
* not supported, this operation may fail silently. Use a DEBUG build
|
|
* to get some logs about the failure.
|
|
*
|
|
*
|
|
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
|
|
* screen
|
|
* +--------------------+
|
|
* | | .stride .alignment
|
|
* | | ----------------------->-->
|
|
* | | O----------------------+--+ low addresses
|
|
* | | | | | |
|
|
* | w | | | .top | |
|
|
* | <---------> | | V | |
|
|
* | +---------+ | | +---------+ | |
|
|
* | | ^ | | ======> | | | | |
|
|
* | x | h| | | |.left| | | |
|
|
* +------>| v | | +---->| | | |
|
|
* | +.........+ | | +.........+ | |
|
|
* | ^ | | | |
|
|
* | y | | +----------------------+--+ high addresses
|
|
* O------------+-------+
|
|
*
|
|
*
|
|
* Typically readPixels() will be called after render() and before endFrame().
|
|
*
|
|
* After issuing this method, the callback associated with `buffer` will be invoked on the
|
|
* main thread, indicating that the read-back has completed. Typically, this will happen
|
|
* after multiple calls to beginFrame(), render(), endFrame().
|
|
*
|
|
* It is also possible to use a Fence to wait for the read-back.
|
|
*
|
|
* @remark
|
|
* readPixels() is intended for debugging and testing. It will impact performance significantly.
|
|
*
|
|
*/
|
|
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
|
backend::PixelBufferDescriptor&& buffer);
|
|
|
|
|
|
/**
|
|
* Reads back the content of the provided RenderTarget.
|
|
*
|
|
* @param renderTarget RenderTarget to read back from.
|
|
* @param xoffset Left offset of the sub-region to read back.
|
|
* @param yoffset Bottom offset of the sub-region to read back.
|
|
* @param width Width of the sub-region to read back.
|
|
* @param height Height of the sub-region to read back.
|
|
* @param buffer Client-side buffer where the read-back will be written.
|
|
*
|
|
* The following format are always supported:
|
|
* - PixelBufferDescriptor::PixelDataFormat::RGBA
|
|
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
|
|
*
|
|
* The following types are always supported:
|
|
* - PixelBufferDescriptor::PixelDataType::UBYTE
|
|
* - PixelBufferDescriptor::PixelDataType::UINT
|
|
* - PixelBufferDescriptor::PixelDataType::INT
|
|
* - PixelBufferDescriptor::PixelDataType::FLOAT
|
|
*
|
|
* Other combination of format/type may be supported. If a combination is
|
|
* not supported, this operation may fail silently. Use a DEBUG build
|
|
* to get some logs about the failure.
|
|
*
|
|
*
|
|
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
|
|
* screen
|
|
* +--------------------+
|
|
* | | .stride .alignment
|
|
* | | ----------------------->-->
|
|
* | | O----------------------+--+ low addresses
|
|
* | | | | | |
|
|
* | w | | | .top | |
|
|
* | <---------> | | V | |
|
|
* | +---------+ | | +---------+ | |
|
|
* | | ^ | | ======> | | | | |
|
|
* | x | h| | | |.left| | | |
|
|
* +------>| v | | +---->| | | |
|
|
* | +.........+ | | +.........+ | |
|
|
* | ^ | | | |
|
|
* | y | | +----------------------+--+ high addresses
|
|
* O------------+-------+
|
|
*
|
|
*
|
|
* Typically readPixels() will be called after render() and before endFrame().
|
|
*
|
|
* After issuing this method, the callback associated with `buffer` will be invoked on the
|
|
* main thread, indicating that the read-back has completed. Typically, this will happen
|
|
* after multiple calls to beginFrame(), render(), endFrame().
|
|
*
|
|
* It is also possible to use a Fence to wait for the read-back.
|
|
*
|
|
* @remark
|
|
* readPixels() is intended for debugging and testing. It will impact performance significantly.
|
|
*
|
|
*/
|
|
void readPixels(RenderTarget* renderTarget,
|
|
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
|
backend::PixelBufferDescriptor&& buffer);
|
|
|
|
/**
|
|
* Set-up a frame for this Renderer.
|
|
*
|
|
* beginFrame() manages frame pacing, and returns whether or not a frame should be drawn. The
|
|
* goal of this is to skip frames when the GPU falls behind in order to keep the frame
|
|
* latency low.
|
|
*
|
|
* If a given frame takes too much time in the GPU, the CPU will get ahead of the GPU. The
|
|
* display will draw the same frame twice producing a stutter. At this point, the CPU is
|
|
* ahead of the GPU and depending on how many frames are buffered, latency increases.
|
|
*
|
|
* beginFrame() attempts to detect this situation and returns false in that case, indicating
|
|
* to the caller to skip the current frame.
|
|
*
|
|
* When beginFrame() returns true, it is mandatory to render the frame and call endFrame().
|
|
* However, when beginFrame() returns false, the caller has the choice to either skip the
|
|
* frame and not call endFrame(), or proceed as though true was returned.
|
|
*
|
|
* Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain.
|
|
* If a backend::FrameFinishedCallback is provided, however, the application bares the
|
|
* responsibility of scheduling a frame for presentation by calling the backend::PresentCallable
|
|
* passed to the callback function. Currently this functionality is only supported by the Metal
|
|
* backend.
|
|
*
|
|
* @param vsyncSteadyClockTimeNano The time in nanosecond of when the current frame started,
|
|
* or 0 if unknown. This value should be the timestamp of
|
|
* the last h/w vsync. It is expressed in the
|
|
* std::chrono::steady_clock time base.
|
|
* @param swapChain A pointer to the SwapChain instance to use.
|
|
* @param callback A callback function that will be called when the backend has finished
|
|
* processing the frame.
|
|
* @param user User data to be passed to the callback function.
|
|
*
|
|
* @return
|
|
* *false* the current frame should be skipped,
|
|
* *true* the current frame must be drawn and endFrame() must be called.
|
|
*
|
|
* @remark
|
|
* When skipping a frame, the whole frame is canceled, and endFrame() must not be called.
|
|
*
|
|
* @note
|
|
* All calls to render() must happen *after* beginFrame().
|
|
*
|
|
* @see
|
|
* endFrame(), backend::PresentCallable, backend::FrameFinishedCallback
|
|
*/
|
|
bool beginFrame(SwapChain* swapChain,
|
|
uint64_t vsyncSteadyClockTimeNano = 0u,
|
|
backend::FrameFinishedCallback callback = nullptr, void* user = nullptr);
|
|
|
|
/**
|
|
* Finishes the current frame and schedules it for display.
|
|
*
|
|
* endFrame() schedules the current frame to be displayed on the Renderer's window.
|
|
*
|
|
* @note
|
|
* All calls to render() must happen *before* endFrame(). endFrame() must be called if
|
|
* beginFrame() returned true, otherwise, endFrame() must not be called unless the caller
|
|
* ignored beginFrame()'s return value.
|
|
*
|
|
* @see
|
|
* beginFrame()
|
|
*/
|
|
void endFrame();
|
|
|
|
/**
|
|
* Returns the time in second of the last call to beginFrame(). This value is constant for all
|
|
* views rendered during a frame. The epoch is set with resetUserTime().
|
|
*
|
|
* In materials, this value can be queried using `vec4 getUserTime()`. The value returned
|
|
* is a highp vec4 encoded as follows:
|
|
*
|
|
* time.x = (float)Renderer.getUserTime();
|
|
* time.y = Renderer.getUserTime() - time.x;
|
|
*
|
|
* It follows that the following invariants are true:
|
|
*
|
|
* (double)time.x + (double)time.y == Renderer.getUserTime()
|
|
* time.x == (float)Renderer.getUserTime()
|
|
*
|
|
* This encoding allows the shader code to perform high precision (i.e. double) time
|
|
* calculations when needed despite the lack of double precision in the shader, for e.g.:
|
|
*
|
|
* To compute (double)time * vertex in the material, use the following construct:
|
|
*
|
|
* vec3 result = time.x * vertex + time.y * vertex;
|
|
*
|
|
*
|
|
* Most of the time, high precision computations are not required, but be aware that the
|
|
* precision of time.x rapidly diminishes as time passes:
|
|
*
|
|
* time | precision
|
|
* --------+----------
|
|
* 16.7s | us
|
|
* 4h39 | ms
|
|
* 77h | 1/60s
|
|
*
|
|
*
|
|
* In other words, it only possible to get microsecond accuracy for about 16s or millisecond
|
|
* accuracy for just under 5h.
|
|
*
|
|
* This problem can be mitigated by calling resetUserTime(), or using high precision time as
|
|
* described above.
|
|
*
|
|
* @return The time is seconds since resetUserTime() was last called.
|
|
*
|
|
* @see
|
|
* resetUserTime()
|
|
*/
|
|
double getUserTime() const;
|
|
|
|
/**
|
|
* Sets the user time epoch to now, i.e. resets the user time to zero.
|
|
*
|
|
* Use this method used to keep the precision of time high in materials, in practice it should
|
|
* be called at least when the application is paused, e.g. Activity.onPause() in Android.
|
|
*
|
|
* @see
|
|
* getUserTime()
|
|
*/
|
|
void resetUserTime();
|
|
};
|
|
|
|
} // namespace filament
|
|
|
|
#endif // TNT_FILAMENT_RENDERER_H
|