update headers to Filament v1.25.0

This commit is contained in:
Nick Fisher
2022-07-10 17:49:56 +10:00
parent 804d0ef89b
commit ea2964c6c6
82 changed files with 11802 additions and 890 deletions

View File

@@ -33,7 +33,6 @@
#include <stddef.h>
#include <stdint.h>
namespace filament {
/**
* Types and enums used by filament's driver.
*
@@ -41,24 +40,24 @@ namespace filament {
* internal redeclaration of these types.
* For e.g. Use Texture::Sampler instead of filament::SamplerType.
*/
namespace backend {
namespace filament::backend {
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_BINDING_COUNT = 8;
static constexpr size_t CONFIG_BINDING_COUNT = 12; // This is guaranteed by OpenGL ES.
/**
* Selects which driver a particular Engine should use.
@@ -129,7 +128,6 @@ inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept
enum class BufferUsage : uint8_t {
STATIC, //!< content modified once, used many times
DYNAMIC, //!< content modified frequently, used many times
STREAM, //!< content invalidated and modified frequently, used many times
};
/**
@@ -142,9 +140,9 @@ struct Viewport {
uint32_t width; //!< width in pixels
uint32_t height; //!< height in pixels
//! get the right coordinate in window space of the viewport
int32_t right() const noexcept { return left + width; }
int32_t right() const noexcept { return left + int32_t(width); }
//! get the top coordinate in window space of the viewport
int32_t top() const noexcept { return bottom + height; }
int32_t top() const noexcept { return bottom + int32_t(height); }
};
/**
@@ -160,7 +158,7 @@ struct DepthRange {
* @see Fence, Fence::wait()
*/
enum class FenceStatus : int8_t {
ERROR = -1, //!< An error occured. The Fence condition is not satisfied.
ERROR = -1, //!< An error occurred. The Fence condition is not satisfied.
CONDITION_SATISFIED = 0, //!< The Fence condition is satisfied.
TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied.
};
@@ -169,7 +167,7 @@ enum class FenceStatus : int8_t {
* Status codes for sync objects
*/
enum class SyncStatus : int8_t {
ERROR = -1, //!< An error occured. The Sync is not signaled.
ERROR = -1, //!< An error occurred. The Sync is not signaled.
SIGNALED = 0, //!< The Sync is signaled.
NOT_SIGNALED = 1, //!< The Sync is not signaled yet
};
@@ -720,7 +718,7 @@ enum class SamplerCompareMode : uint8_t {
COMPARE_TO_TEXTURE = 1
};
//! comparison function for the depth sampler
//! comparison function for the depth / stencil sampler
enum class SamplerCompareFunc : uint8_t {
// don't change the enums values
LE = 0, //!< Less or equal
@@ -729,11 +727,11 @@ enum class SamplerCompareFunc : uint8_t {
G, //!< Strictly greater than
E, //!< Equal
NE, //!< Not equal
A, //!< Always. Depth testing is deactivated.
N //!< Never. The depth test always fails.
A, //!< Always. Depth / stencil testing is deactivated.
N //!< Never. The depth / stencil test always fails.
};
//! Sampler paramters
//! Sampler parameters
struct SamplerParams { // NOLINT
union {
struct {
@@ -786,10 +784,21 @@ enum class BlendFunction : uint8_t {
SRC_ALPHA_SATURATE //!< f(src, dst) = (1,1,1) * min(src.a, 1 - dst.a), 1
};
//! stencil operation
enum class StencilOperation : uint8_t {
KEEP, //!< Keeps the current value.
ZERO, //!< Sets the value to 0.
REPLACE, //!< Sets the value to the stencil reference value.
INCR, //!< Increments the current value. Clamps to the maximum representable unsigned value.
INCR_WRAP, //!< Increments the current value. Wraps value to zero when incrementing the maximum representable unsigned value.
DECR, //!< Decrements the current value. Clamps to 0.
DECR_WRAP, //!< Decrements the current value. Wraps value to the maximum representable unsigned value when decrementing a value of zero.
INVERT, //!< Bitwise inverts the current value.
};
//! Stream for external textures
enum class StreamType {
NATIVE, //!< Not synchronized but copy-free. Good for video.
TEXTURE_ID, //!< Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26.
ACQUIRED, //!< Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+.
};
@@ -819,9 +828,11 @@ struct RasterState {
using DepthFunc = backend::SamplerCompareFunc;
using BlendEquation = backend::BlendEquation;
using BlendFunction = backend::BlendFunction;
using StencilFunction = backend::SamplerCompareFunc;
using StencilOperation = backend::StencilOperation;
RasterState() noexcept { // NOLINT
static_assert(sizeof(RasterState) == sizeof(uint32_t),
static_assert(sizeof(RasterState) == sizeof(uint64_t),
"RasterState size not what was intended");
culling = CullingMode::BACK;
blendEquationRGB = BlendEquation::ADD;
@@ -830,6 +841,10 @@ struct RasterState {
blendFunctionSrcAlpha = BlendFunction::ONE;
blendFunctionDstRGB = BlendFunction::ZERO;
blendFunctionDstAlpha = BlendFunction::ZERO;
stencilFunc = StencilFunction::A;
stencilOpStencilFail = StencilOperation::KEEP;
stencilOpDepthFail = StencilOperation::KEEP;
stencilOpDepthStencilPass = StencilOperation::KEEP;
}
bool operator == (RasterState rhs) const noexcept { return u == rhs.u; }
@@ -858,40 +873,56 @@ struct RasterState {
union {
struct {
//! culling mode
CullingMode culling : 2; // 2
CullingMode culling : 2; // 2
//! blend equation for the red, green and blue components
BlendEquation blendEquationRGB : 3; // 5
BlendEquation blendEquationRGB : 3; // 5
//! blend equation for the alpha component
BlendEquation blendEquationAlpha : 3; // 8
BlendEquation blendEquationAlpha : 3; // 8
//! blending function for the source color
BlendFunction blendFunctionSrcRGB : 4; // 12
BlendFunction blendFunctionSrcRGB : 4; // 12
//! blending function for the source alpha
BlendFunction blendFunctionSrcAlpha : 4; // 16
BlendFunction blendFunctionSrcAlpha : 4; // 16
//! blending function for the destination color
BlendFunction blendFunctionDstRGB : 4; // 20
BlendFunction blendFunctionDstRGB : 4; // 20
//! blending function for the destination alpha
BlendFunction blendFunctionDstAlpha : 4; // 24
BlendFunction blendFunctionDstAlpha : 4; // 24
//! Whether depth-buffer writes are enabled
bool depthWrite : 1; // 25
bool depthWrite : 1; // 25
//! Depth test function
DepthFunc depthFunc : 3; // 28
DepthFunc depthFunc : 3; // 28
//! Whether color-buffer writes are enabled
bool colorWrite : 1; // 29
bool colorWrite : 1; // 29
//! use alpha-channel as coverage mask for anti-aliasing
bool alphaToCoverage : 1; // 30
bool alphaToCoverage : 1; // 30
//! whether front face winding direction must be inverted
bool inverseFrontFaces : 1; // 31
bool inverseFrontFaces : 1; // 31
//! Whether stencil-buffer writes are enabled
bool stencilWrite : 1; // 32
//! Stencil reference value
uint8_t stencilRef : 8; // 40
//! Stencil test function
StencilFunction stencilFunc : 3; // 43
//! Stencil operation when stencil test fails
StencilOperation stencilOpStencilFail : 3; // 46
//! padding, must be 0
uint8_t padding : 1; // 32
uint8_t padding0 : 2; // 48
//! Stencil operation when stencil test passes but depth test fails
StencilOperation stencilOpDepthFail : 3; // 51
//! Stencil operation when both stencil and depth test pass
StencilOperation stencilOpDepthStencilPass : 3; // 54
//! padding, must be 0
uint8_t padding1 : 2; // 56
//! padding, must be 0
uint8_t padding2 : 8; // 64
};
uint32_t u = 0;
uint64_t u = 0;
};
};
@@ -914,7 +945,7 @@ struct ShaderStageFlags {
(fragment && type == ShaderType::FRAGMENT);
}
};
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { .vertex = true, .fragment = true };
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { true, true };
/**
* Selects which buffers to clear at the beginning of the render pass, as well as which buffers
@@ -998,8 +1029,7 @@ enum class Workaround : uint16_t {
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP
};
} // namespace backend
} // namespace filament
} // namespace filament::backend
template<> struct utils::EnableBitMaskOperators<filament::backend::TargetBufferFlags>
: public std::true_type {};

View File

@@ -26,6 +26,7 @@
#include <stdint.h>
#include <limits>
#include <type_traits>
namespace filament::backend {

View File

@@ -80,7 +80,7 @@ public:
* underlying platform is returned and \p backendHint is updated
* accordingly. Can't be nullptr.
*
* @return A pointer to the Plaform object.
* @return A pointer to the Platform object.
*
* @see destroy
*/

View File

@@ -388,6 +388,9 @@ public:
//! Enable / disable depth based culling (enabled by default, material instances can override).
MaterialBuilder& depthCulling(bool enable) noexcept;
//! Enable / disable instanced primitives (disabled by default).
MaterialBuilder& instanced(bool enable) noexcept;
/**
* Double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE).
* doubleSided() overrides culling() if called.
@@ -731,6 +734,7 @@ private:
bool mDoubleSidedCapability = false;
bool mColorWrite = true;
bool mDepthTest = true;
bool mInstanced = true;
bool mDepthWrite = true;
bool mDepthWriteSet = false;

View File

@@ -260,7 +260,7 @@ public:
* range can be adjusted with this method.
*
* @param intensity Scale factor applied to the environment and irradiance such that
* the result is in lux, or <i>lumen/m^2(default = 30000)
* the result is in lux, or <i>lumen/m^2</i> (default = 30000)
*/
void setIntensity(float intensity) noexcept;

View File

@@ -72,6 +72,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialColorWrite = charTo64bitNum("MAT_CWRIT"),
MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"),
MaterialDepthTest = charTo64bitNum("MAT_DTEST"),
MaterialInstanced = charTo64bitNum("MAT_INSTA"),
MaterialCullingMode = charTo64bitNum("MAT_CUMO"),
MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"),

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 21;
static constexpr size_t MATERIAL_VERSION = 25;
/**
* Supported shading models

View File

@@ -78,43 +78,49 @@ public:
};
/**
* Updates the position of morph target at the index.
* Updates positions for the given morph target.
*
* This is equivalent to the float4 method, but uses 1.0 for the 4th component.
*
* Both positions and tangents must be provided.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
* @param positions pointer to at least "count" positions
* @param count number of float3 vectors in positions
* @param offset offset into the target buffer, expressed as a number of float4 vectors
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float3 const* positions, size_t count, size_t offset = 0);
/**
* Updates the position of morph target at the index.
* Updates positions for the given morph target.
*
* Both positions and tangents must be provided.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
* @see setPositionsAt
* @param positions pointer to at least "count" positions
* @param count number of float4 vectors in positions
* @param offset offset into the target buffer, expressed as a number of float4 vectors
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float4 const* positions, size_t count, size_t offset = 0);
/**
* Updates the position of morph target at the index.
* Updates tangents for the given morph target.
*
* Both positions and tangents must be provided.
* These quaternions must be represented as signed shorts, where real numbers in the [-1,+1]
* range multiplied by 32767.
*
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param tangents pointer to at least count tangents
* @param count number of tangent elements in tangents
* @see setTangentsAt
* @param tangents pointer to at least "count" tangents
* @param count number of short4 quaternions in tangents
* @param offset offset into the target buffer, expressed as a number of short4 vectors
* @see setPositionsAt
*/
void setTangentsAt(Engine& engine, size_t targetIndex,
math::short4 const* tangents, size_t count, size_t offset = 0);

View File

@@ -25,6 +25,9 @@ namespace filament {
class Texture;
/**
* Generic quality level.
*/
enum class QualityLevel : uint8_t {
LOW,
MEDIUM,
@@ -69,8 +72,8 @@ enum class BlendMode : uint8_t {
*
*/
struct DynamicResolutionOptions {
math::float2 minScale = math::float2(0.5f); //!< minimum scale factors in x and y
math::float2 maxScale = math::float2(1.0f); //!< maximum scale factors in x and y
math::float2 minScale = {0.5f, 0.5f}; //!< minimum scale factors in x and y %codegen_java_float%
math::float2 maxScale = {1.0f, 1.0f}; //!< maximum scale factors in x and y %codegen_java_float%
float sharpness = 0.9f; //!< sharpness when QualityLevel::MEDIUM or higher is used [0 (disabled), 1 (sharpest)]
bool enabled = false; //!< enable or disable dynamic resolution
bool homogeneousScaling = false; //!< set to true to force homogeneous scaling
@@ -125,8 +128,8 @@ struct BloomOptions {
ADD, //!< Bloom is modulated by the strength parameter and added to the scene
INTERPOLATE //!< Bloom is interpolated with the scene using the strength parameter
};
Texture* dirt = nullptr; //!< user provided dirt texture
float dirtStrength = 0.2f; //!< strength of the dirt texture
Texture* dirt = nullptr; //!< user provided dirt texture %codegen_skip_json% %codegen_skip_javascript%
float dirtStrength = 0.2f; //!< strength of the dirt texture %codegen_skip_json% %codegen_skip_javascript%
float strength = 0.10f; //!< bloom's strength between 0.0 and 1.0
uint32_t resolution = 360; //!< resolution of vertical axis (2^levels to 2048)
float anamorphism = 1.0f; //!< bloom x/y aspect-ratio (1/32 to 32)
@@ -151,16 +154,16 @@ struct BloomOptions {
* Options to control fog in the scene
*/
struct FogOptions {
float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
float height = 0.0f; //!< fog's floor in world units
float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
LinearColor color{0.5f}; //!< fog's color (linear), see fogColorFromIbl
float density = 0.1f; //!< fog's density at altitude given by 'height'
float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction.
bool enabled = false; //!< enable or disable fog
float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
float height = 0.0f; //!< fog's floor in world units
float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
LinearColor color = {0.5f, 0.5f, 0.5f};//!< fog's color (linear), see fogColorFromIbl
float density = 0.1f; //!< fog's density at altitude given by 'height'
float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction.
bool enabled = false; //!< enable or disable fog
};
/**
@@ -174,8 +177,9 @@ struct FogOptions {
*/
struct DepthOfFieldOptions {
enum class Filter : uint8_t {
NONE = 0,
MEDIAN = 2
NONE,
UNUSED,
MEDIAN
};
float cocScale = 1.0f; //!< circle of confusion scale factor (amount of blur)
float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation)
@@ -227,7 +231,7 @@ struct VignetteOptions {
float midPoint = 0.5f; //!< high values restrict the vignette closer to the corners, between 0 and 1
float roundness = 0.5f; //!< controls the shape of the vignette, from a rounded rectangle (0.0), to an oval (0.5), to a circle (1.0)
float feather = 0.5f; //!< softening amount of the vignette effect, between 0 and 1
LinearColorA color{0.0f, 0.0f, 0.0f, 1.0f}; //!< color of the vignette effect, alpha is currently ignored
LinearColorA color = {0.0f, 0.0f, 0.0f, 1.0f}; //!< color of the vignette effect, alpha is currently ignored
bool enabled = false; //!< enables or disables the vignette effect
};
@@ -271,17 +275,18 @@ struct AmbientOcclusionOptions {
* Ambient shadows from dominant light
*/
struct Ssct {
float lightConeRad = 1.0f; //!< full cone angle in radian, between 0 and pi/2
float shadowDistance = 0.3f; //!< how far shadows can be cast
float contactDistanceMax = 1.0f; //!< max distance for contact
float intensity = 0.8f; //!< intensity
math::float3 lightDirection{ 0, -1, 0 }; //!< light direction
float depthBias = 0.01f; //!< depth bias in world units (mitigate self shadowing)
float depthSlopeBias = 0.01f; //!< depth slope bias (mitigate self shadowing)
uint8_t sampleCount = 4; //!< tracing sample count, between 1 and 255
uint8_t rayCount = 1; //!< # of rays to trace, between 1 and 255
bool enabled = false; //!< enables or disables SSCT
} ssct;
float lightConeRad = 1.0f; //!< full cone angle in radian, between 0 and pi/2
float shadowDistance = 0.3f; //!< how far shadows can be cast
float contactDistanceMax = 1.0f; //!< max distance for contact
float intensity = 0.8f; //!< intensity
math::float3 lightDirection = { 0, -1, 0 }; //!< light direction
float depthBias = 0.01f; //!< depth bias in world units (mitigate self shadowing)
float depthSlopeBias = 0.01f; //!< depth slope bias (mitigate self shadowing)
uint8_t sampleCount = 4; //!< tracing sample count, between 1 and 255
uint8_t rayCount = 1; //!< # of rays to trace, between 1 and 255
bool enabled = false; //!< enables or disables SSCT
};
Ssct ssct; // %codegen_skip_javascript% %codegen_java_flatten%
};
/**
@@ -328,21 +333,31 @@ struct ScreenSpaceReflectionsOptions {
bool enabled = false;
};
/**
* Options for the screen-space guard band.
* A guard band can be enabled to avoid some artifacts towards the edge of the screen when
* using screen-space effects such as SSAO. Enabling the guard band reduces performance slightly.
* Currently the guard band can only be enabled or disabled.
*/
struct GuardBandOptions {
bool enabled = false;
};
/**
* List of available post-processing anti-aliasing techniques.
* @see setAntiAliasing, getAntiAliasing, setSampleCount
*/
enum class AntiAliasing : uint8_t {
NONE = 0, //!< no anti aliasing performed as part of post-processing
FXAA = 1 //!< FXAA is a low-quality but very efficient type of anti-aliasing. (default).
NONE, //!< no anti aliasing performed as part of post-processing
FXAA //!< FXAA is a low-quality but very efficient type of anti-aliasing. (default).
};
/**
* List of available post-processing dithering techniques.
*/
enum class Dithering : uint8_t {
NONE = 0, //!< No dithering
TEMPORAL = 1 //!< Temporal dithering (default)
NONE, //!< No dithering
TEMPORAL //!< Temporal dithering (default)
};
/**

View File

@@ -205,10 +205,12 @@ public:
* avoid using a separate View for the HUD. Note that priority is completely orthogonal to
* Builder::layerMask, which merely controls visibility.
*
* \see Builder::blendOrder()
* @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority
* (rendered last).
*
* The priority is clamped to the range [0..7], defaults to 4; 7 is lowest priority
* (rendered last).
* @return Builder reference for chaining calls.
*
* @see Builder::blendOrder(), RenderableManager::setBlendOrderAt()
*/
Builder& priority(uint8_t priority) noexcept;
@@ -337,23 +339,44 @@ public:
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept;
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer) noexcept;
/**
* Sets an ordering index for blended primitives that all live at the same Z value.
* Sets the drawing order for blended primitives. The drawing order is either global or
* local (default) to this Renderable. In either case, the Renderable priority takes
* precedence.
*
* @param primitiveIndex the primitive of interest
* @param order draw order number (0 by default). Only the lowest 15 bits are used.
*
* @return Builder reference for chaining calls.
*
* @see globalBlendOrderEnabled
*/
Builder& blendOrder(size_t primitiveIndex, uint16_t order) noexcept;
/**
* Sets whether the blend order is global or local to this Renderable (by default).
*
* @param primitiveIndex the primitive of interest
* @param enabled true for global, false for local blend ordering.
*
* @return Builder reference for chaining calls.
*
* @see blendOrder
*/
Builder& globalBlendOrderEnabled(size_t primitiveIndex, bool enabled) noexcept;
/**
* Specifies the number of draw instance of this renderable. The default is 1 instance and
* the maximum number of instances allowed is 65535. 0 is invalid.
* All instances are culled using the same bounding box, so care must be taken to make
* sure all instances render inside the specified bounding box.
* The material can use getInstanceIndex() in the vertex shader to get the instance index and
* The material must set its `instanced` parameter to `true` in order to use
* getInstanceIndex() in the vertex or fragment shader to get the instance index and
* possibly adjust the position or transform.
*
* @param instanceCount the number of instances silently clamped between 1 and 65535.
@@ -394,6 +417,7 @@ public:
MaterialInstance const* materialInstance = nullptr;
PrimitiveType type = PrimitiveType::TRIANGLES;
uint16_t blendOrder = 0;
bool globalBlendOrderEnabled = false;
struct {
MorphTargetBuffer* buffer = nullptr;
size_t offset = 0;
@@ -497,7 +521,15 @@ public:
void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0); //!< \overload
/**
* Associates a SkinningBuffer to a renderable instance
* Associates a region of a SkinningBuffer to a renderable instance
*
* Note: due to hardware limitations offset + 256 must be smaller or equal to
* skinningBuffer->getBoneCount()
*
* @param instance Instance of the component obtained from getInstance().
* @param skinningBuffer skinning buffer to associate to the instance
* @param count Size of the region in bones, must be smaller or equal to 256.
* @param offset Start offset of the region in bones
*/
void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer,
size_t count, size_t offset);
@@ -511,7 +543,7 @@ public:
* @param instance Instance of the component obtained from getInstance().
* @param weights Pointer to morph target weights to be update.
* @param count Number of morph target weights.
* @param offset Index of the first first morph target weight to set at instance.
* @param offset Index of the first morph target weight to set at instance.
*/
void setMorphWeights(Instance instance,
float const* weights, size_t count, size_t offset = 0);
@@ -578,24 +610,28 @@ public:
size_t offset, size_t count) noexcept;
/**
* Changes the active range of indices or topology for the given primitive.
*
* \see Builder::geometry()
*/
void setGeometryAt(Instance instance, size_t primitiveIndex,
PrimitiveType type, size_t offset, size_t count) noexcept;
/**
* Changes the ordering index for blended primitives that all live at the same Z value.
*
* \see Builder::blendOrder()
* Changes the drawing order for blended primitives. The drawing order is either global or
* local (default) to this Renderable. In either case, the Renderable priority takes precedence.
*
* @param instance the renderable of interest
* @param primitiveIndex the primitive of interest
* @param order draw order number (0 by default). Only the lowest 15 bits are used.
*
* @see Builder::blendOrder(), setGlobalBlendOrderEnabledAt()
*/
void setBlendOrderAt(Instance instance, size_t primitiveIndex, uint16_t order) noexcept;
/**
* Changes whether the blend order is global or local to this Renderable (by default).
*
* @param instance the renderable of interest
* @param primitiveIndex the primitive of interest
* @param enabled true for global, false for local blend ordering.
*
* @see Builder::globalBlendOrderEnabled(), setBlendOrderAt()
*/
void setGlobalBlendOrderEnabledAt(Instance instance, size_t primitiveIndex, bool enabled) noexcept;
/**
* Retrieves the set of enabled attribute slots in the given primitive's VertexBuffer.
*/

View File

@@ -80,11 +80,8 @@ public:
// 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;
[[deprecated]] uint64_t presentationDeadlineNanos = 0;
[[deprecated]] uint64_t vsyncOffsetNanos = 0;
};
/**
@@ -243,6 +240,20 @@ public:
bool beginFrame(SwapChain* swapChain,
uint64_t vsyncSteadyClockTimeNano = 0u);
/**
* Set the time at which the frame must be presented to the display.
*
* This must be called between beginFrame() and endFrame().
*
* @param monotonic_clock_ns the time in nanoseconds corresponding to the system monotonic up-time clock.
* the presentation time is typically set in the middle of the period
* of interest. The presentation time cannot be too far in the
* future because it is limited by how many buffers are available in
* the display sub-system. Typically it is set to 1 or 2 vsync periods
* away.
*/
void setPresentationTime(int64_t monotonic_clock_ns);
/**
* Render a View into this renderer's window.
*
@@ -444,6 +455,10 @@ public:
*
* It is also possible to use a Fence to wait for the read-back.
*
* OpenGL only: if issuing a readPixels on a RenderTarget backed by a Texture that had data
* uploaded to it via setImage, the data returned from readPixels will be y-flipped with respect
* to the setImage call.
*
* @remark
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*

View File

@@ -33,6 +33,7 @@ namespace filament {
/**
* SkinningBuffer is used to hold skinning data (bones). It is a simple wraper around
* a structured UBO.
* @see RenderableManager::setSkinningBuffer
*/
class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
@@ -93,6 +94,7 @@ public:
* @param transforms pointer to at least count Bone
* @param count number of Bone elements in transforms
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
* @see RenderableManager::setSkinningBuffer
*/
void setBones(Engine& engine, RenderableManager::Bone const* transforms,
size_t count, size_t offset = 0);
@@ -103,6 +105,7 @@ public:
* @param transforms pointer to at least count mat4f
* @param count number of mat4f elements in transforms
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
* @see RenderableManager::setSkinningBuffer
*/
void setBones(Engine& engine, math::mat4f const* transforms,
size_t count, size_t offset = 0);

View File

@@ -124,7 +124,7 @@ public:
*
* Ignored if an environment is set.
*
* @param color
* @param color the constant color
*
* @return This Builder, for chaining calls.
*/

View File

@@ -35,10 +35,9 @@ class Engine;
/**
* Stream is used to attach a video stream to a Filament `Texture`.
*
* Note that the `Stream` class is fairly Android centric. It supports three different
* Note that the `Stream` class is fairly Android centric. It supports two different
* configurations:
*
* - TEXTURE_ID...takes an OpenGL texture ID and incurs a copy
* - ACQUIRED.....connects to an Android AHardwareBuffer
* - NATIVE.......connects to an Android SurfaceTexture
*
@@ -67,10 +66,6 @@ class Engine;
* - Filament invokes low-level graphics commands on the \em{driver thread}.
* - The thread that calls `beginFrame` is called the \em{main thread}.
*
* The TEXTURE_ID configuration achieves synchronization automatically. In this mode, Filament
* performs a copy on the main thread during `beginFrame` by blitting the external image into
* an internal round-robin queue of images. This copy has a run-time cost.
*
* For ACQUIRED streams, there is no need to perform the copy because Filament explictly acquires
* the stream, then releases it later via a callback function. This configuration is especially
* useful when the Vulkan backend is enabled.
@@ -97,7 +92,7 @@ public:
* By default, Stream objects are ACQUIRED and must have external images pushed to them via
* <pre>Stream::setAcquiredImage</pre>.
*
* To create a NATIVE or TEXTURE_ID stream, call one of the <pre>stream</pre> methods
* To create a NATIVE stream, call one of the <pre>stream</pre> methods
* on the builder.
*/
class Builder : public BuilderBase<BuilderDetails> {
@@ -122,23 +117,6 @@ public:
*/
Builder& stream(void* stream) noexcept;
/**
* Creates a TEXTURE_ID stream. This will sample data from the supplied
* external texture and copy it into an internal private texture.
*
* @param externalTextureId An opaque texture id (typically a GLuint created with glGenTextures)
* In a context shared with filament. In that case this texture's
* target must be GL_TEXTURE_EXTERNAL_OES and the wrap mode must
* be CLAMP_TO_EDGE.
*
* @return This Builder, for chaining calls.
*
* @see Texture::setExternalStream()
* @deprecated this method existed only for ARCore which doesn't need this anymore, use Texture::import() instead.
*/
UTILS_DEPRECATED
Builder& stream(intptr_t externalTextureId) noexcept;
/**
*
* @param width initial width of the incoming stream. Whether this value is used is
@@ -173,7 +151,7 @@ public:
};
/**
* Indicates whether this stream is a NATIVE stream, TEXTURE_ID stream, or ACQUIRED stream.
* Indicates whether this stream is a NATIVE stream or ACQUIRED stream.
*/
StreamType getStreamType() const noexcept;
@@ -190,7 +168,7 @@ public:
* also where the callback is invoked. This method can only be used for streams that were
* constructed without calling the `stream` method on the builder.
*
* @see Stream for more information about NATIVE, TEXTURE_ID, and ACQUIRED configurations.
* @see Stream for more information about NATIVE and ACQUIRED configurations.
*
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
* @param callback This is triggered by Filament when it wishes to release the image.
@@ -222,63 +200,6 @@ public:
*/
void setDimensions(uint32_t width, uint32_t height) noexcept;
/**
* Read-back the content of the last frame of a Stream since the last call to
* Renderer.beginFrame().
*
* The Stream must be of type externalTextureId. This function is a no-op otherwise.
*
* @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.
*
* Stream buffer User buffer (PixelBufferDescriptor&)
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* Typically readPixels() will be called after Renderer.beginFrame().
*
* 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) noexcept;
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*

View File

@@ -134,12 +134,12 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
* constructor parameters approximate an ACES tone mapping curve
* and the maximum input value is set to 10.0.
*
* @param contrast: controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax: defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
* @param contrast controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
* @param midGrayIn sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
*/
explicit GenericToneMapper(
float contrast = 1.55f,

View File

@@ -84,6 +84,7 @@ public:
using VsmShadowOptions = VsmShadowOptions;
using SoftShadowOptions = SoftShadowOptions;
using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions;
using GuardBandOptions = GuardBandOptions;
/**
* Sets the View's name. Only useful for debugging.
@@ -345,6 +346,20 @@ public:
*/
ScreenSpaceReflectionsOptions const& getScreenSpaceReflectionsOptions() const noexcept;
/**
* Enables or disable screen-space guard band. Disabled by default.
*
* @param options guard band options
*/
void setGuardBandOptions(GuardBandOptions options) noexcept;
/**
* Returns screen-space guard band options.
*
* @return guard band options
*/
GuardBandOptions const& getGuardBandOptions() const noexcept;
/**
* Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default.
*

View File

@@ -43,11 +43,6 @@ public:
*/
Viewport() noexcept : backend::Viewport{} {}
Viewport(const Viewport& viewport) noexcept = default;
Viewport(Viewport&& viewport) noexcept = default;
Viewport& operator=(const Viewport& viewport) noexcept = default;
Viewport& operator=(Viewport&& viewport) noexcept = default;
/**
* Creates a Viewport from its left-bottom coordinates, width and height in pixels
*
@@ -67,16 +62,7 @@ public:
*/
bool empty() const noexcept { return !width || !height; }
/**
* Computes a new scaled Viewport
* @param s scaling factor on the x and y axes.
* @return A new scaled Viewport. The coordinates and dimensions of the new Viewport are
* rounded to the nearest integer value.
*/
Viewport scale(math::float2 s) const noexcept;
private:
/**
* Compares two Viewports for equality
* @param lhs reference to the left hand side Viewport

View File

@@ -63,7 +63,7 @@ public:
void unregisterAll();
std::size_t numRegistered() const noexcept;
size_t numRegistered() const noexcept;
void getRegisteredMaterials(filament::MaterialInstance** materialList,
utils::CString* materialNameList) const;

View File

@@ -31,6 +31,7 @@ enum class ComponentType {
SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1]
USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1]
HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits.
FLOAT, //!< Standard 32-bit float
};
/**

View File

@@ -20,7 +20,7 @@
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
namespace gltfio {
namespace filament::gltfio {
struct FFilamentAsset;
struct FFilamentInstance;
@@ -56,6 +56,32 @@ public:
*/
void updateBoneMatrices();
/**
* Applies a blended transform to the union of nodes affected by two animations.
* Used for cross-fading from a previous skinning-based animation or rigid body animation.
*
* First, this stashes the current transform hierarchy into a transient memory buffer.
*
* Next, this applies previousAnimIndex / previousAnimTime to the actual asset by internally
* calling applyAnimation().
*
* Finally, the stashed local transforms are lerped (via the scale / translation / rotation
* components) with their live counterparts, and the results are pushed to the asset.
*
* To achieve a cross fade effect with skinned models, clients will typically call animator
* methods in this order: (1) applyAnimation (2) applyCrossFade (3) updateBoneMatrices. The
* animation that clients pass to applyAnimation is the "current" animation corresponding to
* alpha=1, while the "previous" animation passed to applyCrossFade corresponds to alpha=0.
*/
void applyCrossFade(size_t previousAnimIndex, float previousAnimTime, float alpha);
/**
* Pass the identity matrix into all bone nodes, useful for returning to the T pose.
*
* NOTE: this operation is independent of \c animation.
*/
void resetBoneMatrices();
/** Returns the number of \c animation definitions in the glTF asset. */
size_t getAnimationCount() const;
@@ -88,6 +114,6 @@ private:
AnimatorImpl* mImpl;
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_ANIMATOR_H

View File

@@ -34,7 +34,9 @@ namespace utils {
/**
* Loader and pipeline for glTF 2.0 assets.
*/
namespace gltfio {
namespace filament::gltfio {
class NodeManager;
/**
* \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h
@@ -47,7 +49,7 @@ struct AssetConfiguration {
//! Controls whether the loader uses filamat to generate materials on the fly, or loads a small
//! set of precompiled ubershader materials. Deleting the MaterialProvider is the client's
//! responsibility. See createMaterialGenerator() and createUbershaderLoader().
//! responsibility. See createJitShaderProvider() and createUbershaderProvider().
MaterialProvider* materials;
//! Optional manager for associating string names with entities in the transform hierarchy.
@@ -70,7 +72,8 @@ struct AssetConfiguration {
* object, which is a bundle of Filament entities, material instances, textures, vertex buffers,
* and index buffers.
*
* Clients must use AssetLoader to create and destroy FilamentAsset objects.
* Clients must use AssetLoader to create and destroy FilamentAsset objects. This is similar to
* how filament::Engine is used to create and destroy core objects like VertexBuffer.
*
* AssetLoader does not fetch external buffer data or create textures on its own. Clients can use
* ResourceLoader for this, which obtains the URI list from the asset. This is demonstrated in the
@@ -83,7 +86,8 @@ struct AssetConfiguration {
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto engine = Engine::create();
* auto materials = createMaterialGenerator(engine);
* auto materials = createJitShaderProvider(engine);
* auto decoder = createStbProvider(engine);
* auto loader = AssetLoader::create({engine, materials});
*
* // Parse the glTF content and create Filament entities.
@@ -92,7 +96,10 @@ struct AssetConfiguration {
* content.clear();
*
* // Load buffers and textures from disk.
* ResourceLoader({engine, ".", true}).loadResources(asset);
* ResourceLoader resourceLoader({engine, ".", true});
* resourceLoader.addTextureProvider("image/png", decoder)
* resourceLoader.addTextureProvider("image/jpeg", decoder)
* resourceLoader.loadResources(asset);
*
* // Free the glTF hierarchy as it is no longer needed.
* asset->releaseSourceData();
@@ -114,6 +121,7 @@ struct AssetConfiguration {
* loader->destroyAsset(asset);
* materials->destroyMaterials();
* delete materials;
* delete decoder;
* AssetLoader::destroy(&loader);
* Engine::destroy(&engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -221,7 +229,9 @@ public:
utils::NameComponentManager* getNames() const noexcept;
MaterialProvider* getMaterialProvider() const noexcept;
NodeManager& getNodeManager() noexcept;
MaterialProvider& getMaterialProvider() noexcept;
/*! \cond PRIVATE */
protected:
@@ -236,6 +246,6 @@ public:
/*! \endcond */
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_ASSETLOADER_H

View File

@@ -20,6 +20,8 @@
#include <filament/Box.h>
#include <filament/TextureSampler.h>
#include <gltfio/NodeManager.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
@@ -27,9 +29,10 @@ namespace filament {
class Camera;
class Engine;
class MaterialInstance;
class Scene;
}
namespace gltfio {
namespace filament::gltfio {
class Animator;
class FilamentInstance;
@@ -42,7 +45,7 @@ class FilamentInstance;
*
* This class owns a hierarchy of entities that have been loaded from a glTF asset. Every entity has
* a filament::TransformManager component, and some entities also have \c Name, \c Renderable,
* \c Light, or \c Camera components.
* \c Light, \c Camera, or \c Node components.
*
* In addition to the aforementioned entities, an asset has strong ownership over a list of
* filament::VertexBuffer, filament::IndexBuffer, filament::MaterialInstance, filament::Texture,
@@ -55,12 +58,14 @@ class FilamentInstance;
*/
class UTILS_PUBLIC FilamentAsset {
public:
using Entity = utils::Entity;
using SceneMask = NodeManager::SceneMask;
/**
* Gets the list of entities, one for each glTF node. All of these have a Transform component.
* Some of the returned entities may also have a Renderable component and/or a Light component.
*/
const utils::Entity* getEntities() const noexcept;
const Entity* getEntities() const noexcept;
/**
* Gets the number of entities returned by getEntities().
@@ -70,13 +75,23 @@ public:
/**
* Gets the list of entities in the scene representing lights. All of these have a Light component.
*/
const utils::Entity* getLightEntities() const noexcept;
const Entity* getLightEntities() const noexcept;
/**
* Gets the number of entities returned by getLightEntities().
*/
size_t getLightEntityCount() const noexcept;
/**
* Gets the list of entities in the asset that have renderable components.
*/
const utils::Entity* getRenderableEntities() const noexcept;
/**
* Gets the number of entities returned by getRenderableEntities().
*/
size_t getRenderableEntityCount() const noexcept;
/**
* Gets the list of entities in the scene representing cameras. All of these have a \c Camera
* component.
@@ -95,7 +110,7 @@ public:
*
* @see filament::Camera::setScaling
*/
const utils::Entity* getCameraEntities() const noexcept;
const Entity* getCameraEntities() const noexcept;
/**
* Gets the number of entities returned by getCameraEntities().
@@ -109,7 +124,7 @@ public:
* assets, this is a "super root" where each of its children is a root in a particular instance.
* This allows users to transform all instances en masse if they wish to do so.
*/
utils::Entity getRoot() const noexcept;
Entity getRoot() const noexcept;
/**
* Pops a ready renderable off the queue, or returns 0 if no renderables have become ready.
@@ -122,12 +137,12 @@ public:
* textures gradually become ready through asynchronous loading. For example, on every frame
* progressive applications can do something like this:
*
* while (utils::Entity e = popRenderable()) { scene.addEntity(e); }
* while (Entity e = popRenderable()) { scene.addEntity(e); }
*
* \see ResourceLoader#asyncBeginLoad
* \see popRenderables()
*/
utils::Entity popRenderable() noexcept;
Entity popRenderable() noexcept;
/**
* Pops up to "count" ready renderables off the queue, or returns the available number.
@@ -138,7 +153,7 @@ public:
*
* \see ResourceLoader#asyncBeginLoad
*/
size_t popRenderables(utils::Entity* entities, size_t count) noexcept;
size_t popRenderables(Entity* entities, size_t count) noexcept;
/** Gets all material instances. These are already bound to renderables. */
const filament::MaterialInstance* const* getMaterialInstances() const noexcept;
@@ -159,10 +174,10 @@ public:
filament::Aabb getBoundingBox() const noexcept;
/** Gets the NameComponentManager label for the given entity, if it exists. */
const char* getName(utils::Entity) const noexcept;
const char* getName(Entity) const noexcept;
/** Returns the first entity with the given name, or 0 if none exist. */
utils::Entity getFirstEntityByName(const char* name) noexcept;
Entity getFirstEntityByName(const char* name) noexcept;
/**
* Gets a list of entities with the given name.
@@ -174,7 +189,7 @@ public:
* @return If entities is non-null, the number of entities written to the entity pointer.
* Otherwise this returns the number of entities with the given name.
*/
size_t getEntitiesByName(const char* name, utils::Entity* entities,
size_t getEntitiesByName(const char* name, Entity* entities,
size_t maxCount) const noexcept;
/**
@@ -187,11 +202,11 @@ public:
* @return If entities is non-null, the number of entities written to the entity pointer.
* Otherwise this returns the number of entities with the given prefix.
*/
size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities,
size_t getEntitiesByPrefix(const char* prefix, Entity* entities,
size_t maxCount) const noexcept;
/** Gets the glTF extras string for a specific node, or for the asset, if it exists. */
const char* getExtras(utils::Entity entity = {}) const noexcept;
const char* getExtras(Entity entity = {}) const noexcept;
/**
* Returns the animation engine.
@@ -221,17 +236,32 @@ public:
/**
* Gets joints at skin index.
*/
const utils::Entity* getJointsAt(size_t skinIndex) const noexcept;
const Entity* getJointsAt(size_t skinIndex) const noexcept;
/**
* Attaches the given skin to the given node, which must have an associated mesh with
* BONE_INDICES and BONE_WEIGHTS attributes.
*
* This is a no-op if the given skin index or target is invalid.
*/
void attachSkin(size_t skinIndex, Entity target) noexcept;
/**
* Detaches the given skin from the given node.
*
* This is a no-op if the given skin index or target is invalid.
*/
void detachSkin(size_t skinIndex, Entity target) noexcept;
/**
* Gets the morph target name at the given index in the given entity.
*/
const char* getMorphTargetNameAt(utils::Entity entity, size_t targetIndex) const noexcept;
const char* getMorphTargetNameAt(Entity entity, size_t targetIndex) const noexcept;
/**
* Returns the number of morph targets in the given entity.
*/
size_t getMorphTargetCountAt(utils::Entity entity) const noexcept;
size_t getMorphTargetCountAt(Entity entity) const noexcept;
/**
* Returns the number of material variants in the asset.
@@ -261,7 +291,7 @@ public:
* Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy
* for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it.
*/
utils::Entity getWireframe() noexcept;
Entity getWireframe() noexcept;
/**
* Returns the Filament engine associated with the AssetLoader that created this asset.
@@ -282,6 +312,28 @@ public:
*/
const void* getSourceAsset() noexcept;
/**
* Returns the number of scenes in the asset.
*/
size_t getSceneCount() const noexcept;
/**
* Returns the name of the given scene.
*
* Returns null if the given scene does not have a name or is out of bounds.
*/
const char* getSceneName(size_t sceneIndex) const noexcept;
/**
* Adds entities to a Filament scene only if they belong to at least one of the given glTF
* scenes.
*
* This is just a helper that provides an alternative to directly calling scene->addEntities()
* and provides filtering functionality.
*/
void addEntitiesToScene(filament::Scene& targetScene, const Entity* entities, size_t count,
SceneMask sceneFilter);
/*! \cond PRIVATE */
FilamentInstance** getAssetInstances() noexcept;
@@ -299,6 +351,6 @@ public:
/*! \endcond */
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_FILAMENTASSET_H

View File

@@ -20,7 +20,7 @@
#include <utils/compiler.h>
#include <utils/Entity.h>
namespace gltfio {
namespace filament::gltfio {
class Animator;
class FilamentAsset;
@@ -76,6 +76,6 @@ public:
Animator* getAnimator() noexcept;
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_FILAMENTINSTANCE_H

View File

@@ -26,7 +26,7 @@
#include <array>
#include <string>
namespace gltfio {
namespace filament::gltfio {
enum class AlphaMode : uint8_t {
OPAQUE,
@@ -118,12 +118,12 @@ inline uint8_t getNumUvSets(const UvMap& uvmap) {
* \class MaterialProvider MaterialProvider.h gltfio/MaterialProvider.h
* \brief Interface to a provider of glTF materials (has two implementations).
*
* - The \c MaterialGenerator implementation generates materials at run time (which can be slow) and
* requires the filamat library, but produces streamlined shaders. See createMaterialGenerator().
* - The \c JitShaderProvider implementation generates materials at run time (which can be slow) and
* requires the filamat library, but produces streamlined shaders. See createJitShaderProvider().
*
* - The \c UbershaderLoader implementation uses a small number of pre-built materials with complex
* - The \c UbershaderProvider implementation uses a small number of pre-built materials with complex
* fragment shaders, but does not require any run time work or usage of filamat. See
* createUbershaderLoader().
* createUbershaderProvider().
*
* Both implementations of MaterialProvider maintain a small cache of materials which must be
* explicitly freed using destroyMaterials(). These materials are not freed automatically when the
@@ -186,23 +186,22 @@ void processShaderString(std::string* shader, const UvMap& uvmap,
*
* Requires \c libfilamat to be linked in. Not available in \c libgltfio_core.
*
* @see createUbershaderLoader
* @see createUbershaderProvider
*/
UTILS_PUBLIC
MaterialProvider* createMaterialGenerator(filament::Engine* engine, bool optimizeShaders = false);
MaterialProvider* createJitShaderProvider(filament::Engine* engine, bool optimizeShaders = false);
/**
* Creates a material provider that loads a small set of pre-built materials.
*
* @return New material provider that can quickly load a material from a cache.
*
* Requires \c libgltfio_resources to be linked in.
*
* @see createMaterialGenerator
* @see createJitShaderProvider
*/
UTILS_PUBLIC
MaterialProvider* createUbershaderLoader(filament::Engine* engine);
MaterialProvider* createUbershaderProvider(filament::Engine* engine, const void* archive,
size_t archiveByteCount);
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_MATERIALPROVIDER_H

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 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 GLTFIO_NODEMANAGER_H
#define GLTFIO_NODEMANAGER_H
#include <filament/FilamentAPI.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/EntityInstance.h>
#include <utils/FixedCapacityVector.h>
namespace utils {
class Entity;
} // namespace utils
namespace filament::gltfio {
class FNodeManager;
/**
* NodeManager is used to add annotate entities with glTF-specific information.
*
* Node components are created by gltfio and exposed to users to allow inspection.
*
* Nodes do not store the glTF hierarchy or names; see TransformManager and NameComponentManager.
*/
class UTILS_PUBLIC NodeManager {
public:
using Instance = utils::EntityInstance<NodeManager>;
using Entity = utils::Entity;
using CString = utils::CString;
using SceneMask = utils::bitset32;
static constexpr size_t MAX_SCENE_COUNT = 32;
/**
* Returns whether a particular Entity is associated with a component of this NodeManager
* @param e An Entity.
* @return true if this Entity has a component associated with this manager.
*/
bool hasComponent(Entity e) const noexcept;
/**
* Gets an Instance representing the node component associated with the given Entity.
* @param e An Entity.
* @return An Instance object, which represents the node component associated with the Entity e.
* @note Use Instance::isValid() to make sure the component exists.
* @see hasComponent()
*/
Instance getInstance(Entity e) const noexcept;
/**
* Creates a node component and associates it with the given entity.
* @param entity An Entity to associate a node component with.
*
* If this component already exists on the given entity, it is first destroyed as if
* destroy(Entity e) was called.
*
* @see destroy()
*/
void create(Entity entity);
/**
* Destroys this component from the given entity.
* @param e An entity.
*
* @see create()
*/
void destroy(Entity e) noexcept;
void setMorphTargetNames(Instance ci, utils::FixedCapacityVector<CString> names) noexcept;
const utils::FixedCapacityVector<CString>& getMorphTargetNames(Instance ci) const noexcept;
void setExtras(Instance ci, CString extras) noexcept;
const CString& getExtras(Instance ci) const noexcept;
void setSceneMembership(Instance ci, SceneMask scenes) noexcept;
SceneMask getSceneMembership(Instance ci) const noexcept;
protected:
NodeManager() noexcept = default;
~NodeManager() = default;
public:
NodeManager(NodeManager const&) = delete;
NodeManager(NodeManager&&) = delete;
NodeManager& operator=(NodeManager const&) = delete;
NodeManager& operator=(NodeManager&&) = delete;
};
} // namespace filament::gltfio
#endif // GLTFIO_NODEMANAGER_H

View File

@@ -27,10 +27,11 @@ namespace filament {
class Engine;
}
namespace gltfio {
namespace filament::gltfio {
struct FFilamentAsset;
class AssetPool;
class TextureProvider;
/**
* \struct ResourceConfiguration ResourceLoader.h gltfio/ResourceLoader.h
@@ -53,8 +54,8 @@ struct ResourceConfiguration {
//! do not need this, but it is useful for robustness.
bool recomputeBoundingBoxes;
//! If true, ignore skinned primitives bind transform when compute bounding box. Implicitly true
//! for instanced asset. Only applicable when recomputeBoundingBoxes is set to true
//! If true, ignores skinning when computing bounding boxes. Implicitly true for instanced
//! assets. Only applicable when recomputeBoundingBoxes is set to true.
bool ignoreBindTransform;
};
@@ -94,6 +95,13 @@ public:
*/
void addResourceData(const char* uri, BufferDescriptor&& buffer);
/**
* Register a plugin that can consume PNG / JPEG content and produce filament::Texture objects.
*
* Destruction of the given provider is the client's responsibility.
*/
void addTextureProvider(const char* mimeType, TextureProvider* provider);
/**
* Checks if the given resource has already been added to the URI cache.
*/
@@ -153,7 +161,6 @@ public:
private:
bool loadResources(FFilamentAsset* asset, bool async);
void applySparseData(FFilamentAsset* asset) const;
void normalizeSkinningWeights(FFilamentAsset* asset) const;
void updateBoundingBoxes(FFilamentAsset* asset) const;
AssetPool* mPool;
@@ -161,7 +168,7 @@ private:
Impl* pImpl;
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_RESOURCELOADER_H

View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2022 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 GLTFIO_TEXTUREPROVIDER_H
#define GLTFIO_TEXTUREPROVIDER_H
#include <stddef.h>
#include <stdint.h>
#include <utils/compiler.h>
namespace filament {
class Engine;
class Texture;
}
namespace filament::gltfio {
/**
* TextureProvider is an interface that allows clients to implement their own texture decoding
* facility for JPEG, PNG, or KTX2 content. It constructs Filament Texture objects synchronously,
* but populates their miplevels asynchronously.
*
* gltfio calls all public methods from the foreground thread, i.e. the thread that the Filament
* engine was created with. However the implementation may create 0 or more background threads to
* perform decoding work.
*
* The following pseudocode illustrates how this interface could be used, but in practice the only
* client is the gltfio ResourceLoader.
*
* filament::Engine* engine = ...;
* TextureProvider* provider = createStbProvider(engine);
*
* for (auto filename : textureFiles) {
* std::vector<uint8_t> buf = readEntireFile(filename);
* Texture* texture = provider->pushTexture(buf.data(), buf.size(), "image/png", 0);
* if (texture == nullptr) { puts(provider->getPushMessage()); exit(1); }
* }
*
* // At this point, the returned textures can be bound to material instances, but none of their
* // miplevel images have been populated yet.
*
* while (provider->getPoppedCount() < provider->getPushedCount()) {
* sleep(200);
*
* // The following call gives the provider an opportunity to reap the results of any
* // background decoder work that has been completed (e.g. by calling Texture::setImage).
* provider->updateQueue();
*
* // Check for textures that now have all their miplevels initialized.
* while (Texture* texture = provider->popTexture()) {
* printf("%p has all its miplevels ready.\n", texture);
* }
* }
*
* delete provider;
*/
class UTILS_PUBLIC TextureProvider {
public:
using Texture = filament::Texture;
using FlagBits = uint64_t;
enum class Flags {
sRGB = 1 << 0,
};
/**
* Creates a Filament texture and pushes it to the asynchronous decoding queue.
*
* If construction fails, nothing is pushed to the queue and null is returned. The failure
* reason can be obtained with getPushMessage(). The given buffer pointer is not held, so the
* caller can free it immediately. It is also the caller's responsibility to free the returned
* Texture object, but it is only safe to do so after it has been popped from the queue.
*/
virtual Texture* pushTexture(const uint8_t* data, size_t byteCount,
const char* mimeType, FlagBits flags) = 0;
/**
* Checks if any texture is ready to be removed from the asynchronous decoding queue, and if so
* pops it off.
*
* Unless an error or cancellation occurred during the decoding process, the returned texture
* should have all its miplevels populated. If the texture is not complete, the reason can be
* obtained with getPopMessage().
*
* Due to concurrency, textures are not necessarily popped off in the same order they were
* pushed. Returns null if there are no textures that are ready to be popped.
*/
virtual Texture* popTexture() = 0;
/**
* Polls textures in the queue and uploads mipmap images if any have emerged from the decoder.
*
* This gives the provider an opportunity to call Texture::setImage() on the foreground thread.
* If needed, it can also call Texture::generateMipmaps() here.
*
* Items in the decoding queue can become "poppable" only during this call.
*/
virtual void updateQueue() = 0;
/**
* Returns a failure message for the most recent call to pushTexture(), or null for success.
*
* Note that this method does not pertain to the decoding process. If decoding fails, clients to
* can pop the incomplete texture off the queue and obtain a failure message using the
* getPopFailure() method.
*
* The returned string is owned by the provider and becomes invalid after the next call to
* pushTexture().
*/
virtual const char* getPushMessage() const = 0;
/**
* Returns a failure message for the most recent call to popTexture(), or null for success.
*
* If the most recent call to popTexture() returned null, then no error occurred and this
* returns null. If the most recent call to popTexture() returned a "complete" texture (i.e.
* all miplevels present), then this returns null. This returns non-null only if an error or
* cancellation occurred while decoding the popped texture.
*
* The returned string is owned by the provider and becomes invalid after the next call to
* popTexture().
*/
virtual const char* getPopMessage() const = 0;
/**
* Waits for all outstanding decoding jobs to complete.
*
* Clients should call updateQueue() afterwards if they wish to update the push / pop queue.
*/
virtual void waitForCompletion() = 0;
/**
* Cancels all not-yet-started decoding jobs and waits for all other jobs to complete.
*
* Jobs that have already started cannot be canceled. Textures whose decoding process has
* been cancelled will be made poppable on the subsequent call to updateQueue().
*/
virtual void cancelDecoding() = 0;
/** Total number of successful push calls since the provider was created. */
virtual size_t getPushedCount() const = 0;
/** Total number of successful pop calls since the provider was created. */
virtual size_t getPoppedCount() const = 0;
/** Total number of textures that have become ready-to-pop since the provider was created. */
virtual size_t getDecodedCount() const = 0;
virtual ~TextureProvider() = default;
};
/**
* Creates a simple decoder based on stb_image that can handle "image/png" and "image/jpeg".
* This works only if your build configuration includes STB.
*/
TextureProvider* createStbProvider(filament::Engine* engine);
/**
* Creates a decoder that can handle certain types of "image/ktx2" content as specified in
* the KHR_texture_basisu specification.
*/
TextureProvider* createKtx2Provider(filament::Engine* engine);
} // namespace filament::gltfio
#endif // GLTFIO_TEXTUREPROVIDER_H

View File

@@ -0,0 +1,13 @@
#ifndef UBERARCHIVE_H_
#define UBERARCHIVE_H_
#include <stdint.h>
extern "C" {
extern const uint8_t UBERARCHIVE_PACKAGE[];
extern int UBERARCHIVE_DEFAULT_OFFSET;
extern int UBERARCHIVE_DEFAULT_SIZE;
}
#define UBERARCHIVE_DEFAULT_DATA (UBERARCHIVE_PACKAGE + UBERARCHIVE_DEFAULT_OFFSET)
#endif

View File

@@ -23,7 +23,7 @@
#include <math/mat4.h>
#include <math/TVecHelpers.h>
namespace gltfio {
namespace filament::gltfio {
template <typename T>
UTILS_PUBLIC T cubicSpline(const T& vert0, const T& tang0, const T& vert1, const T& tang1, float t) {
@@ -121,6 +121,6 @@ inline filament::math::mat3f matrixFromUvTransform(const float offset[2], float
return filament::math::mat3f(sx * c, sx * s, tx, -sy * s, sy * c, ty, 0.0f, 0.0f, 1.0f);
};
} // namespace gltfio
} // namespace filament::gltfio
#endif // GLTFIO_MATH_H

View File

@@ -1,46 +0,0 @@
#ifndef GLTFRESOURCES_H_
#define GLTFRESOURCES_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_PACKAGE[];
extern int GLTFRESOURCES_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LIT_FADE_SIZE;
extern int GLTFRESOURCES_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LIT_MASKED_OFFSET;
extern int GLTFRESOURCES_LIT_MASKED_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_SIZE;
extern int GLTFRESOURCES_UNLIT_FADE_OFFSET;
extern int GLTFRESOURCES_UNLIT_FADE_SIZE;
extern int GLTFRESOURCES_UNLIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_UNLIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_UNLIT_MASKED_OFFSET;
extern int GLTFRESOURCES_UNLIT_MASKED_SIZE;
extern int GLTFRESOURCES_LIT_VOLUME_OFFSET;
extern int GLTFRESOURCES_LIT_VOLUME_SIZE;
extern int GLTFRESOURCES_LIT_TRANSMISSION_OFFSET;
extern int GLTFRESOURCES_LIT_TRANSMISSION_SIZE;
extern int GLTFRESOURCES_LIT_SHEEN_OFFSET;
extern int GLTFRESOURCES_LIT_SHEEN_SIZE;
}
#define GLTFRESOURCES_LIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_FADE_OFFSET)
#define GLTFRESOURCES_LIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_MASKED_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET)
#define GLTFRESOURCES_UNLIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_FADE_OFFSET)
#define GLTFRESOURCES_UNLIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_UNLIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_MASKED_OFFSET)
#define GLTFRESOURCES_LIT_VOLUME_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_VOLUME_OFFSET)
#define GLTFRESOURCES_LIT_TRANSMISSION_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_TRANSMISSION_OFFSET)
#define GLTFRESOURCES_LIT_SHEEN_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_SHEEN_OFFSET)
#endif

View File

@@ -1,16 +0,0 @@
#ifndef GLTFRESOURCES_LITE_H_
#define GLTFRESOURCES_LITE_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_LITE_PACKAGE[];
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LITE_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_FADE_SIZE;
}
#define GLTFRESOURCES_LITE_LIT_OPAQUE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LITE_LIT_FADE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_FADE_OFFSET)
#endif

View File

@@ -158,8 +158,8 @@ inline filament::math::float3 linearToSRGB(const filament::math::float3& color)
return sRGBColor;
}
// Creates a n-channel sRGB image from a linear floating-point image.
// The source image can have more than N channels, but only the first N are converted to sRGB.
// Creates a N-channel sRGB image from a linear floating-point image.
// The source image can have more than N channels, but only the first 3 are converted to sRGB.
template<typename T, int N = 3>
std::unique_ptr<uint8_t[]> fromLinearTosRGB(const LinearImage& image) {
const size_t w = image.getWidth();

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef IMAGE_KTXBUNDLE_H
#define IMAGE_KTXBUNDLE_H
#ifndef IMAGE_KTX1BUNDLE_H
#define IMAGE_KTX1BUNDLE_H
#include <math/vec3.h>
@@ -48,7 +48,7 @@ struct KtxBlobList;
struct KtxMetadata;
/**
* KtxBundle is a structured set of opaque data blobs that can be passed straight to the GPU, such
* Ktx1Bundle is a structured set of opaque data blobs that can be passed straight to the GPU, such
* that a single bundle corresponds to a single texture object. It is well suited for storing
* block-compressed texture data.
*
@@ -63,22 +63,22 @@ struct KtxMetadata;
*
* https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
*/
class UTILS_PUBLIC KtxBundle {
class UTILS_PUBLIC Ktx1Bundle {
public:
~KtxBundle();
~Ktx1Bundle();
/**
* Creates a hierarchy of empty texture blobs, to be filled later via setBlob().
*/
KtxBundle(uint32_t numMipLevels, uint32_t arrayLength, bool isCubemap);
Ktx1Bundle(uint32_t numMipLevels, uint32_t arrayLength, bool isCubemap);
/**
* Creates a new bundle by deserializing the given data.
*
* Typically, this constructor is used to consume the contents of a KTX file.
*/
KtxBundle(uint8_t const* bytes, uint32_t nbytes);
Ktx1Bundle(uint8_t const* bytes, uint32_t nbytes);
/**
* Serializes the bundle into the given target memory. Returns false if there's not enough
@@ -276,4 +276,4 @@ private:
} // namespace image
#endif /* IMAGE_KTXBUNDLE_H */
#endif /* IMAGE_Ktx1Bundle_H */

View File

@@ -1,367 +0,0 @@
/*
* Copyright (C) 2018 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 IMAGE_KTXUTILITY_H
#define IMAGE_KTXUTILITY_H
#include <filament/Engine.h>
#include <filament/Texture.h>
#include <image/KtxBundle.h>
namespace image {
/**
* Allows clients to create Filament textures from KtxBundle objects.
*
* Note that libimage does not have a dependency on libfilament, so for simplicity this is a
* header-only library with inlined functions.
*/
namespace ktx {
using Texture = filament::Texture;
using Engine = filament::Engine;
using TextureFormat = Texture::InternalFormat;
using CompressedPixelDataType = Texture::CompressedType;
using PixelDataType = Texture::Type;
using PixelDataFormat = Texture::Format;
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
using Callback = void(*)(void* userdata);
CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info);
PixelDataType toPixelDataType(const KtxInfo& info);
PixelDataFormat toPixelDataFormat(const KtxInfo& info);
bool isCompressed(const KtxInfo& info);
TextureFormat toTextureFormat(const KtxInfo& info);
TextureFormat toSrgbTextureFormat(TextureFormat tex);
/**
* Creates a Texture object from a KTX file and populates all of its faces and miplevels.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
* @param callback Gets called after all texture data has been uploaded to the GPU
* @param userdata Passed into the callback
*/
inline Texture* createTexture(Engine* engine, const KtxBundle& ktx, bool srgb,
Callback callback, void* userdata) {
using Sampler = Texture::Sampler;
const auto& ktxinfo = ktx.getInfo();
const uint32_t nmips = ktx.getNumMipLevels();
const auto cdatatype = toCompressedPixelDataType(ktxinfo);
const auto datatype = toPixelDataType(ktxinfo);
const auto dataformat = toPixelDataFormat(ktxinfo);
auto texformat = toTextureFormat(ktxinfo);
if (srgb) {
texformat = toSrgbTextureFormat(texformat);
}
Texture* texture = Texture::Builder()
.width(ktxinfo.pixelWidth)
.height(ktxinfo.pixelHeight)
.levels(static_cast<uint8_t>(nmips))
.sampler(ktx.isCubemap() ? Sampler::SAMPLER_CUBEMAP : Sampler::SAMPLER_2D)
.format(texformat)
.build(*engine);
struct Userdata {
uint32_t remainingBuffers;
Callback callback;
void* userdata;
};
Userdata* cbuser = new Userdata({nmips, callback, userdata});
PixelBufferDescriptor::Callback cb = [](void*, size_t, void* cbuserptr) {
Userdata* cbuser = (Userdata*) cbuserptr;
if (--cbuser->remainingBuffers == 0) {
if (cbuser->callback) {
cbuser->callback(cbuser->userdata);
}
delete cbuser;
}
};
uint8_t* data;
uint32_t size;
if (isCompressed(ktxinfo)) {
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
/**
* Creates a Texture object from a KTX bundle, populates all of its faces and miplevels,
* and automatically destroys the bundle after all the texture data has been uploaded.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
*/
inline Texture* createTexture(Engine* engine, KtxBundle* ktx, bool srgb) {
auto freeKtx = [] (void* userdata) {
KtxBundle* ktx = (KtxBundle*) userdata;
delete ktx;
};
return createTexture(engine, *ktx, srgb, freeKtx, ktx);
}
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case KtxBundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case KtxBundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case KtxBundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case KtxBundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case KtxBundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case KtxBundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case KtxBundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case KtxBundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case KtxBundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case KtxBundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case KtxBundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case KtxBundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case KtxBundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case KtxBundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case KtxBundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case KtxBundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case KtxBundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case KtxBundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case KtxBundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case KtxBundle::R11_EAC: return T::EAC_R11;
case KtxBundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case KtxBundle::RG11_EAC: return T::EAC_RG11;
case KtxBundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case KtxBundle::RGB8_ETC2: return T::ETC2_RGB8;
case KtxBundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case KtxBundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case KtxBundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case KtxBundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case KtxBundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
inline CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info) {
return toCompressedFilamentEnum<CompressedPixelDataType>(info.glInternalFormat);
}
inline PixelDataType toPixelDataType(const KtxInfo& info) {
switch (info.glType) {
case KtxBundle::UNSIGNED_BYTE: return PixelDataType::UBYTE;
case KtxBundle::UNSIGNED_SHORT: return PixelDataType::USHORT;
case KtxBundle::HALF_FLOAT: return PixelDataType::HALF;
case KtxBundle::FLOAT: return PixelDataType::FLOAT;
case KtxBundle::R11F_G11F_B10F: return PixelDataType::UINT_10F_11F_11F_REV;
}
return (PixelDataType) 0xff;
}
inline PixelDataFormat toPixelDataFormat(const KtxInfo& info) {
switch (info.glFormat) {
case KtxBundle::LUMINANCE:
case KtxBundle::RED: return PixelDataFormat::R;
case KtxBundle::RG: return PixelDataFormat::RG;
case KtxBundle::RGB: return PixelDataFormat::RGB;
case KtxBundle::RGBA: return PixelDataFormat::RGBA;
// glFormat should NOT be a sized format according to the spec
// however cmgen was generating incorrect files until after Filament 1.8.0
// so we keep this line here to preserve compatibility with older assets
case KtxBundle::R11F_G11F_B10F: return PixelDataFormat::RGB;
}
return (PixelDataFormat) 0xff;
}
inline bool isCompressed(const KtxInfo& info) {
return info.glFormat == 0;
}
inline TextureFormat toSrgbTextureFormat(TextureFormat format) {
switch(format) {
// Non-compressed
case Texture::InternalFormat::RGB8:
return Texture::InternalFormat::SRGB8;
case Texture::InternalFormat::RGBA8:
return Texture::InternalFormat::SRGB8_A8;
// ASTC
case Texture::InternalFormat::RGBA_ASTC_4x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_4x4;
case Texture::InternalFormat::RGBA_ASTC_5x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x4;
case Texture::InternalFormat::RGBA_ASTC_5x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x5;
case Texture::InternalFormat::RGBA_ASTC_6x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x5;
case Texture::InternalFormat::RGBA_ASTC_6x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x6;
case Texture::InternalFormat::RGBA_ASTC_8x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x5;
case Texture::InternalFormat::RGBA_ASTC_8x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x6;
case Texture::InternalFormat::RGBA_ASTC_8x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x8;
case Texture::InternalFormat::RGBA_ASTC_10x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x5;
case Texture::InternalFormat::RGBA_ASTC_10x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x6;
case Texture::InternalFormat::RGBA_ASTC_10x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x8;
case Texture::InternalFormat::RGBA_ASTC_10x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x10;
case Texture::InternalFormat::RGBA_ASTC_12x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x10;
case Texture::InternalFormat::RGBA_ASTC_12x12:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x12;
// ETC2
case Texture::InternalFormat::ETC2_RGB8:
return Texture::InternalFormat::ETC2_SRGB8;
case Texture::InternalFormat::ETC2_RGB8_A1:
return Texture::InternalFormat::ETC2_SRGB8_A1;
case Texture::InternalFormat::ETC2_EAC_RGBA8:
return Texture::InternalFormat::ETC2_EAC_SRGBA8;
// DXT
case Texture::InternalFormat::DXT1_RGB:
return Texture::InternalFormat::DXT1_SRGB;
case Texture::InternalFormat::DXT1_RGBA:
return Texture::InternalFormat::DXT1_SRGBA;
case Texture::InternalFormat::DXT3_RGBA:
return Texture::InternalFormat::DXT3_SRGBA;
case Texture::InternalFormat::DXT5_RGBA:
return Texture::InternalFormat::DXT5_SRGBA;
default:
return format;
}
}
inline TextureFormat toTextureFormat(const KtxInfo& info) {
switch (info.glInternalFormat) {
case KtxBundle::RED: return TextureFormat::R8;
case KtxBundle::RG: return TextureFormat::RG8;
case KtxBundle::RGB: return TextureFormat::RGB8;
case KtxBundle::RGBA: return TextureFormat::RGBA8;
case KtxBundle::LUMINANCE: return TextureFormat::R8;
case KtxBundle::LUMINANCE_ALPHA: return TextureFormat::RG8;
case KtxBundle::R8: return TextureFormat::R8;
case KtxBundle::R8_SNORM: return TextureFormat::R8_SNORM;
case KtxBundle::R8UI: return TextureFormat::R8UI;
case KtxBundle::R8I: return TextureFormat::R8I;
case KtxBundle::STENCIL_INDEX8: return TextureFormat::STENCIL8;
case KtxBundle::R16F: return TextureFormat::R16F;
case KtxBundle::R16UI: return TextureFormat::R16UI;
case KtxBundle::R16I: return TextureFormat::R16I;
case KtxBundle::RG8: return TextureFormat::RG8;
case KtxBundle::RG8_SNORM: return TextureFormat::RG8_SNORM;
case KtxBundle::RG8UI: return TextureFormat::RG8UI;
case KtxBundle::RG8I: return TextureFormat::RG8I;
case KtxBundle::RGB565: return TextureFormat::RGB565;
case KtxBundle::RGB9_E5: return TextureFormat::RGB9_E5;
case KtxBundle::RGB5_A1: return TextureFormat::RGB5_A1;
case KtxBundle::RGBA4: return TextureFormat::RGBA4;
case KtxBundle::DEPTH_COMPONENT16: return TextureFormat::DEPTH16;
case KtxBundle::RGB8: return TextureFormat::RGB8;
case KtxBundle::SRGB8: return TextureFormat::SRGB8;
case KtxBundle::RGB8_SNORM: return TextureFormat::RGB8_SNORM;
case KtxBundle::RGB8UI: return TextureFormat::RGB8UI;
case KtxBundle::RGB8I: return TextureFormat::RGB8I;
case KtxBundle::R32F: return TextureFormat::R32F;
case KtxBundle::R32UI: return TextureFormat::R32UI;
case KtxBundle::R32I: return TextureFormat::R32I;
case KtxBundle::RG16F: return TextureFormat::RG16F;
case KtxBundle::RG16UI: return TextureFormat::RG16UI;
case KtxBundle::RG16I: return TextureFormat::RG16I;
case KtxBundle::R11F_G11F_B10F: return TextureFormat::R11F_G11F_B10F;
case KtxBundle::RGBA8: return TextureFormat::RGBA8;
case KtxBundle::SRGB8_ALPHA8: return TextureFormat::SRGB8_A8;
case KtxBundle::RGBA8_SNORM: return TextureFormat::RGBA8_SNORM;
case KtxBundle::RGB10_A2: return TextureFormat::RGB10_A2;
case KtxBundle::RGBA8UI: return TextureFormat::RGBA8UI;
case KtxBundle::RGBA8I: return TextureFormat::RGBA8I;
case KtxBundle::DEPTH24_STENCIL8: return TextureFormat::DEPTH24_STENCIL8;
case KtxBundle::DEPTH32F_STENCIL8: return TextureFormat::DEPTH32F_STENCIL8;
case KtxBundle::RGB16F: return TextureFormat::RGB16F;
case KtxBundle::RGB16UI: return TextureFormat::RGB16UI;
case KtxBundle::RGB16I: return TextureFormat::RGB16I;
case KtxBundle::RG32F: return TextureFormat::RG32F;
case KtxBundle::RG32UI: return TextureFormat::RG32UI;
case KtxBundle::RG32I: return TextureFormat::RG32I;
case KtxBundle::RGBA16F: return TextureFormat::RGBA16F;
case KtxBundle::RGBA16UI: return TextureFormat::RGBA16UI;
case KtxBundle::RGBA16I: return TextureFormat::RGBA16I;
case KtxBundle::RGB32F: return TextureFormat::RGB32F;
case KtxBundle::RGB32UI: return TextureFormat::RGB32UI;
case KtxBundle::RGB32I: return TextureFormat::RGB32I;
case KtxBundle::RGBA32F: return TextureFormat::RGBA32F;
case KtxBundle::RGBA32UI: return TextureFormat::RGBA32UI;
case KtxBundle::RGBA32I: return TextureFormat::RGBA32I;
}
return toCompressedFilamentEnum<TextureFormat>(info.glInternalFormat);
}
} // namespace ktx
} // namespace image
#endif

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2022 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 KTXREADER_KTX1READER_H
#define KTXREADER_KTX1READER_H
#include <image/Ktx1Bundle.h>
#include <filament/Texture.h>
namespace filament {
class Engine;
}
namespace ktxreader {
using KtxInfo = image::KtxInfo;
using Ktx1Bundle = image::Ktx1Bundle;
/**
* Allows clients to create Filament textures from Ktx1Bundle objects.
*/
namespace Ktx1Reader {
using Texture = filament::Texture;
using Engine = filament::Engine;
using TextureFormat = Texture::InternalFormat;
using CompressedPixelDataType = Texture::CompressedType;
using PixelDataType = Texture::Type;
using PixelDataFormat = Texture::Format;
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
using Callback = void(*)(void* userdata);
CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info);
PixelDataType toPixelDataType(const KtxInfo& info);
PixelDataFormat toPixelDataFormat(const KtxInfo& info);
bool isCompressed(const KtxInfo& info);
TextureFormat toTextureFormat(const KtxInfo& info);
TextureFormat toSrgbTextureFormat(TextureFormat tex);
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case Ktx1Bundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case Ktx1Bundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case Ktx1Bundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case Ktx1Bundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case Ktx1Bundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case Ktx1Bundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case Ktx1Bundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case Ktx1Bundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case Ktx1Bundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case Ktx1Bundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case Ktx1Bundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case Ktx1Bundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case Ktx1Bundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case Ktx1Bundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case Ktx1Bundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case Ktx1Bundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case Ktx1Bundle::R11_EAC: return T::EAC_R11;
case Ktx1Bundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case Ktx1Bundle::RG11_EAC: return T::EAC_RG11;
case Ktx1Bundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case Ktx1Bundle::RGB8_ETC2: return T::ETC2_RGB8;
case Ktx1Bundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case Ktx1Bundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case Ktx1Bundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case Ktx1Bundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case Ktx1Bundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
/**
* Creates a Texture object from a KTX file and populates all of its faces and miplevels.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Requests an sRGB format from the KTX file
* @param callback Gets called after all texture data has been uploaded to the GPU
* @param userdata Passed into the callback
*/
Texture* createTexture(Engine* engine, const Ktx1Bundle& ktx, bool srgb,
Callback callback, void* userdata);
/**
* Creates a Texture object from a KTX bundle, populates all of its faces and miplevels,
* and automatically destroys the bundle after all the texture data has been uploaded.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Requests an sRGB format from the KTX file
*/
Texture* createTexture(Engine* engine, Ktx1Bundle* ktx, bool srgb);
CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info);
PixelDataType toPixelDataType(const KtxInfo& info);
PixelDataFormat toPixelDataFormat(const KtxInfo& info);
bool isCompressed(const KtxInfo& info);
bool isSrgbTextureFormat(TextureFormat format);
TextureFormat toTextureFormat(const KtxInfo& info);
} // namespace Ktx1Reader
} // namespace ktxreader
#endif

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) 2022 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 KTXREADER_KTX2READER_H
#define KTXREADER_KTX2READER_H
#include <cstdint>
#include <cstddef>
#include <filament/Texture.h>
#include <utils/FixedCapacityVector.h>
namespace filament {
class Engine;
}
namespace basist {
class ktx2_transcoder;
}
namespace ktxreader {
class Ktx2Reader {
public:
using Engine = filament::Engine;
using Texture = filament::Texture;
enum class TransferFunction { LINEAR, sRGB };
enum class Result {
SUCCESS,
COMPRESSED_TRANSCODE_FAILURE,
UNCOMPRESSED_TRANSCODE_FAILURE,
FORMAT_UNSUPPORTED,
FORMAT_ALREADY_REQUESTED,
};
Ktx2Reader(Engine& engine, bool quiet = false);
~Ktx2Reader();
/**
* Requests that the reader constructs Filament textures with given internal format.
*
* This MUST be called at least once before calling load().
*
* As a reminder, a basis-encoded KTX2 can be quickly transcoded to any number of formats,
* so you need to tell it what formats your hw supports. That's why this method exists.
*
* Call requestFormat as many times as needed; formats that are submitted early are
* considered higher priority.
*
* If BasisU knows a priori that the given format is not available (e.g. if the build has
* disabled it), the format is not added and FORMAT_UNSUPPORTED is returned.
*
* Returns FORMAT_ALREADY_REQUESTED if the given format has already been requested.
*
* Hint: BasisU supports the following uncompressed formats: RGBA8, RGB565, RGBA4.
*/
Result requestFormat(Texture::InternalFormat format) noexcept;
/**
* Removes the given format from the list, or does nothing if it hasn't been requested.
*/
void unrequestFormat(Texture::InternalFormat format) noexcept;
/**
* Attempts to create and load a Filament texture from the given KTX2 blob.
*
* If none of the requested formats can be extracted from the data, this returns null.
*
* This method iterates through the requested format list, checking each one against the
* platform's capabilities and its availability from the transcoder. When a suitable format
* is determined, it then performs lossless decompression (zstd) before transcoding the data
* into the final format.
*
* The transfer function specified here is used in two ways:
* 1) It is checked against the transfer function that was specified as metadata
* in the KTX2 blob. If they do not match, this method fails.
* 2) It is used as a filter when determining the final internal format.
*/
Texture* load(const void* data, size_t size, TransferFunction transfer);
/**
* Asynchronous Interface
* ======================
*
* Alternative API suitable for asynchronous transcoding of mipmap levels.
* If unsure that you need to use this, then don't, just call load() instead.
* Usage pseudocode:
*
* auto async = reader->asyncCreate(data, size, TransferFunction::LINEAR);
* mTexture = async->getTexture();
* auto backgroundThread = spawnThread({ async->doTranscoding(); })
* backgroundThread.wait();
* async->uploadImages();
* reader->asyncDestroy(async);
*
* In the documentation comments, "foreground thread" refers to the thread that the
* Filament Engine was created on.
*/
class Async {
public:
/**
* Retrieves the Texture object.
*
* The texture is available immediately, but does not have its miplevels ready until
* after doTranscoding() and the subsequent uploadImages() have been completed. The
* caller has ownership over this texture and is responsible for freeing it after all
* miplevels have been uploaded.
*/
Texture* getTexture() const noexcept;
/**
* Loads all mipmaps from the KTX2 file and transcodes them to the resolved format.
*
* This does not return until all mipmaps have been transcoded. This is typically
* called from a background thread.
*/
Result doTranscoding();
/**
* Uploads pending mipmaps to the texture.
*
* This can safely be called while doTranscoding() is still working in another thread.
* Since this calls Texture::setImage(), it should be called from the foreground thread;
* see "Thread safety" in the documentation for filament::Engine.
*/
void uploadImages();
protected:
Async() noexcept = default;
~Async() = default;
public:
Async(Async const&) = delete;
Async(Async&&) = delete;
Async& operator=(Async const&) = delete;
Async& operator=(Async&&) = delete;
friend class Ktx2Reader;
};
/**
* Creates a texture without starting the transcode process.
*
* This method is an alternative to load() that allows users to populate mipmap levels
* asynchronously. The texture object however is still created synchronously.
*
* - For a usage example, see the documentation for the Async object.
* - Creates a copy of the given buffer, allowing clients to free it immediately.
* - Returns null if none of the requested formats can be extracted from the data.
*
* This method iterates through the requested format list, checking each one against the
* platform's capabilities and its availability from the transcoder. When a suitable format
* is determined, it then performs lossless decompression (zstd) before transcoding the data
* into the final format.
*
* The transfer function specified here is used in two ways:
* 1) It is checked against the transfer function that was specified as metadata
* in the KTX2 blob. If they do not match, this method fails.
* 2) It is used as a filter when determining the final internal format.
*/
Async* asyncCreate(const void* data, size_t size, TransferFunction transfer);
/**
* Frees the given async object and sets it to null.
*
* This frees the original source data (i.e. the raw content of the KTX2 file) but does not
* free the associated Texture object. This can be done after transcoding has finished.
*/
void asyncDestroy(Async** async);
private:
Ktx2Reader(const Ktx2Reader&) = delete;
Ktx2Reader& operator=(const Ktx2Reader&) = delete;
Ktx2Reader(Ktx2Reader&& that) noexcept = delete;
Ktx2Reader& operator=(Ktx2Reader&& that) noexcept = delete;
Texture* createTexture(basist::ktx2_transcoder* transcoder, const void* data,
size_t size, TransferFunction transfer);
Engine& mEngine;
basist::ktx2_transcoder* const mTranscoder;
utils::FixedCapacityVector<Texture::InternalFormat> mRequestedFormats;
bool mQuiet;
};
} // namespace ktxreader
#endif

7529
ios/include/stb/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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 UBERZ_ARCHIVE_ENUMS_H
#define UBERZ_ARCHIVE_ENUMS_H
#include <stdint.h>
namespace filament::uberz {
enum class ArchiveFeature : uint64_t {
UNSUPPORTED,
OPTIONAL,
REQUIRED,
};
} // namespace filament::uberz
#endif // UBERZ_ARCHIVE_ENUMS_H

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 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 UBERZ_READABLE_ARCHIVE_H
#define UBERZ_READABLE_ARCHIVE_H
#include <stdint.h>
#include <uberz/ArchiveEnums.h>
#include <filament/MaterialEnums.h>
namespace filament::uberz {
// ArchiveSpec is a parse-free binary format. The client simply casts a word-aligned content blob
// into a ReadableArchive struct pointer, then calls the following function to convert all the
// offset fields into pointers.
void convertOffsetsToPointers(struct ReadableArchive* archive);
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
// Precompiled set of materials bundled with a list of features flags that each material supports.
// This is the readable counterpart to WriteableArchive.
// Used by gltfio; users do not need to access this class directly.
struct ReadableArchive {
uint32_t magic;
uint32_t version;
uint64_t specsCount;
union {
struct ArchiveSpec* specs;
uint64_t specsOffset;
};
};
static constexpr Shading INVALID_SHADING_MODEL = (Shading) 0xff;
static constexpr BlendingMode INVALID_BLENDING = (BlendingMode) 0xff;
struct ArchiveSpec {
Shading shadingModel;
BlendingMode blendingMode;
uint16_t flagsCount;
uint32_t packageByteCount;
union {
struct ArchiveFlag* flags;
uint64_t flagsOffset;
};
union {
uint8_t* package;
uint64_t packageOffset;
};
};
struct ArchiveFlag {
union {
const char* name;
uint64_t nameOffset;
};
ArchiveFeature value;
};
#pragma clang diagnostic pop
} // namespace filament::uberz
#endif // UBERZ_READABLE_ARCHIVE_H

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 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 UBERZ_WRITABLE_ARCHIVE_H
#define UBERZ_WRITABLE_ARCHIVE_H
#include <filament/MaterialEnums.h>
#include <uberz/ArchiveEnums.h>
#include <utils/FixedCapacityVector.h>
#include <utils/CString.h>
#include <string_view>
#include <tsl/robin_map.h>
namespace filament::uberz {
// Precompiled set of materials bundled with a list of features flags that each material supports.
// This is the writeable counterpart to ReadableArchive.
// Users do not need to access this class directly, they should go through gltfio.
class WritableArchive {
public:
WritableArchive(size_t materialCount) : mMaterials(materialCount) {}
void addMaterial(const char* name, const uint8_t* package, size_t packageSize);
void addSpecLine(std::string_view line);
utils::FixedCapacityVector<uint8_t> serialize() const;
// Low-level alternatives to addSpecLine that do not involve parsing:
void setShadingModel(Shading sm);
void setBlendingModel(BlendingMode bm);
void setFeatureFlag(const char* key, ArchiveFeature value);
private:
size_t mLineNumber = 1;
ssize_t mMaterialIndex = -1;
struct Material {
utils::CString name;
utils::FixedCapacityVector<uint8_t> package;
Shading shadingModel;
BlendingMode blendingMode;
tsl::robin_map<utils::CString, ArchiveFeature, utils::CString::Hasher> flags;
};
utils::FixedCapacityVector<Material> mMaterials;
};
} // namespace filament::uberz
#endif // UBERZ_WRITABLE_ARCHIVE_H

View File

@@ -162,7 +162,7 @@ public:
}
void free(void* p, size_t) noexcept {
free(p);
this->free(p);
}
~HeapAllocator() noexcept = default;
@@ -457,6 +457,12 @@ private:
void* mEnd = nullptr;
};
class NullArea {
public:
void* data() const noexcept { return nullptr; }
size_t size() const noexcept { return 0; }
};
} // namespace AreaPolicy
// ------------------------------------------------------------------------------------------------
@@ -498,6 +504,7 @@ struct HighWatermark {
void onFree(void* p, size_t size) noexcept;
void onReset() noexcept;
void onRewind(void const* addr) noexcept;
uint32_t getHighWatermark() const noexcept { return mHighWaterMark; }
protected:
const char* mName = nullptr;
void* mBase = nullptr;

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_BINARYTREEARRAY_H
#define TNT_UTILS_BINARYTREEARRAY_H
#include <utils/compiler.h>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<TYPE>::value>::type>
class stack {
TYPE mElements[CAPACITY];
size_t mSize = 0;
public:
bool empty() const noexcept { return mSize == 0; }
void push(TYPE const& v) noexcept {
assert(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
public:
static size_t count(size_t height) noexcept { return (1u << height) - 1; }
static size_t left(size_t i, size_t height) noexcept { return i + 1; }
static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); }
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>
static void traverse(size_t height, Leaf leaf, Node node) noexcept {
struct TNode {
uint32_t index;
uint32_t col;
uint32_t height;
uint32_t next;
bool isLeaf() const noexcept { return height == 1; }
size_t left() const noexcept { return BinaryTreeArray::left(index, height); }
size_t right() const noexcept { return BinaryTreeArray::right(index, height); }
};
stack<TNode, 16> stack;
stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) });
uint32_t prevLeft = 0;
uint32_t prevRight = 0;
uint32_t prevIndex = 0;
while (!stack.empty()) {
TNode const* const UTILS_RESTRICT curr = &stack.back();
const bool isLeaf = curr->isLeaf();
const uint32_t index = curr->index;
const uint32_t l = (uint32_t)curr->left();
const uint32_t r = (uint32_t)curr->right();
if (prevLeft == index || prevRight == index) {
if (!isLeaf) {
// the 'next' node of our left node's right descendants is our right child
stack.push({ l, 2 * curr->col, curr->height - 1, r });
}
} else if (l == prevIndex) {
if (!isLeaf) {
// the 'next' node of our right child is our own 'next' sibling
stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next });
}
} else {
if (!isLeaf) {
node(index, l, r, curr->next);
} else {
leaf(index, curr->col, curr->next);
}
stack.pop();
}
prevLeft = l;
prevRight = r;
prevIndex = index;
}
}
};
} // namespace utils
#endif //TNT_UTILS_BINARYTREEARRAY_H

View File

@@ -79,7 +79,7 @@ public:
using const_pointer = const value_type*;
using const_iterator = const value_type*;
constexpr StaticString() noexcept = default;
constexpr StaticString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
// initialization from a string literal
template<size_t N>
@@ -149,6 +149,14 @@ public:
size_type getHash() const noexcept { return mHash; }
struct Hasher {
typedef StaticString argument_type;
typedef size_t result_type;
result_type operator()(const argument_type& s) const noexcept {
return s.getHash();
}
};
private:
const_pointer mString = nullptr;
size_type mLength = 0;
@@ -200,7 +208,7 @@ public:
using iterator = value_type*;
using const_iterator = const value_type*;
CString() noexcept = default;
CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
@@ -220,7 +228,7 @@ public:
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {} // NOLINT(google-explicit-constructor)
CString(const CString& rhs);
@@ -309,11 +317,19 @@ public:
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t size, void* ptr) {
inline void* operator new(size_t, void* ptr) {
assert(ptr);
return ptr;
}
struct Hasher : private hashCStrings {
typedef CString argument_type;
typedef size_t result_type;
result_type operator()(const argument_type& s) const noexcept {
return hashCStrings::operator()(s.c_str());
}
};
private:
struct Data {
size_type length;
@@ -365,34 +381,4 @@ CString to_string(T value) noexcept;
} // namespace utils
// FIXME: how could we not include this one?
// needed for std::hash, since implementation is inline, this would not cause
// binaries incompatibilities if another STL version was used.
#include <functional>
namespace std {
//! \privatesection
template<>
struct hash<utils::CString> {
typedef utils::CString argument_type;
typedef size_t result_type;
utils::hashCStrings hasher;
size_t operator()(const utils::CString& s) const noexcept {
return hasher(s.c_str());
}
};
//! \privatesection
template<>
struct hash<utils::StaticString> {
typedef utils::StaticString argument_type;
typedef size_t result_type;
size_t operator()(const utils::StaticString& s) const noexcept {
return s.getHash();
}
};
} // namespace std
#endif // TNT_UTILS_CSTRING_H

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_CONDITION_H
#define TNT_UTILS_CONDITION_H
#if defined(__linux__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>
#endif
#endif // TNT_UTILS_CONDITION_H

View File

@@ -0,0 +1,91 @@
/*
* 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.
*/
#ifndef TNT_UTILS_COUNTDOWNLATCH_H
#define TNT_UTILS_COUNTDOWNLATCH_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A count down latch is used to block one or several threads until the latch is signaled
* a certain number of times.
*
* Threads entering the latch are blocked until the latch is signaled enough times.
*
* @see CyclicBarrier
*/
class CountDownLatch {
public:
/**
* Creates a count down latch with a specified count. The minimum useful value is 1.
* @param count the latch counter initial value
*/
explicit CountDownLatch(size_t count) noexcept;
~CountDownLatch() = default;
/**
* Blocks until latch() is called \p count times.
* @see CountDownLatch(size_t count)
*/
void await() noexcept;
/**
* Releases threads blocked in await() when called \p count times. Calling latch() more than
* \p count times has no effect.
* @see reset()
*/
void latch() noexcept;
/**
* Resets the count-down latch to the given value.
*
* @param new_count New latch count. A value of zero will immediately unblock all waiting
* threads.
*
* @warning Use with caution. It's only safe to reset the latch count when you're sure
* that no threads are waiting in await(). This can be guaranteed in various ways, for
* instance, if you have a single thread calling await(), you could call reset() from that
* thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch
* are at a known place (i.e.: not in await()) when reset() is called.
*/
void reset(size_t new_count) noexcept;
/**
* @return the number of times latch() has been called since construction or reset.
* @see reset(), CountDownLatch(size_t count)
*/
size_t getCount() const noexcept;
CountDownLatch() = delete;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
private:
uint32_t m_initial_count;
uint32_t m_remaining_count;
mutable Mutex m_lock;
mutable Condition m_cv;
};
} // namespace utils
#endif // TNT_UTILS_COUNTDOWNLATCH_H

View File

@@ -0,0 +1,84 @@
/*
* 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.
*/
#ifndef TNT_UTILS_CYCLIC_BARRIER_H
#define TNT_UTILS_CYCLIC_BARRIER_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A cyclic barrier is used to synchronize several threads to a particular execution point.
*
* Threads entering the barrier are halted until all threads reach the barrier.
*
* @see CountDownLatch
*/
class CyclicBarrier {
public:
/**
* Creates a cyclic barrier with a specified number of threads to synchronize. The minimum
* useful value is 2. A value of 0 is invalid and is silently changed to 1.
* @param num_threads Number of threads to synchronize.
*/
explicit CyclicBarrier(size_t num_threads) noexcept;
/**
* @return The number of thread that are synchronized.
*/
size_t getThreadCount() const noexcept;
/**
* @return Number of threads currently waiting on the barrier.
*/
size_t getWaitingThreadCount() const noexcept;
/**
* Blocks until getThreadCount()-1 other threads reach await().
*/
void await() noexcept;
/**
* Resets the cyclic barrier to its original state and releases all waiting threads.
*/
void reset() noexcept;
CyclicBarrier() = delete;
CyclicBarrier(const CyclicBarrier&) = delete;
CyclicBarrier& operator=(const CyclicBarrier&) = delete;
private:
enum class State {
TRAP, RELEASE
};
const size_t m_num_threads;
mutable Mutex m_lock;
mutable Condition m_cv;
State m_state = State::TRAP;
size_t m_trapped_threads = 0;
size_t m_released_threads = 0;
};
} // namespace utils
#endif // TNT_UTILS_CYCLIC_BARRIER_H

View File

@@ -19,9 +19,6 @@
#include <utils/compiler.h>
// FIXME: could we get rid of <functional>
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
@@ -30,7 +27,7 @@ namespace utils {
class UTILS_PUBLIC Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
Entity() noexcept { } // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
// Entities can be copied
Entity(const Entity& e) noexcept = default;
@@ -68,10 +65,17 @@ public:
return Entity{ Type(identity) };
}
struct Hasher {
typedef Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash<Entity>;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
@@ -81,18 +85,4 @@ private:
} // namespace utils
namespace std {
template<>
struct hash<utils::Entity> {
typedef utils::Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
} // namespace std
#endif // TNT_UTILS_ENTITY_H

View File

@@ -36,7 +36,7 @@ namespace utils {
class UTILS_PUBLIC EntityManager {
public:
// Get the global EntityManager. Is is recommended to cache this value.
// Get the global EntityManager. It is recommended to cache this value.
// Thread Safe.
static EntityManager& get() noexcept;

103
ios/include/utils/Hash.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_HASH_H
#define TNT_UTILS_HASH_H
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils::hash {
// Hash function that takes an arbitrary swath of word-aligned data.
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = *key++;
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
// The hash yields the same result for a given byte sequence regardless of alignment.
inline uint32_t murmurSlow(const uint8_t* key, size_t byteCount, uint32_t seed) noexcept {
const size_t wordCount = (byteCount + 3) / 4;
const uint8_t* const last = key + byteCount;
// The remainder is identical to murmur3() except an inner loop safely "reads" an entire word.
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = 0;
for (int i = 0; i < 4 && key < last; ++i, ++key) {
k >>= 8;
k |= uint32_t(*key) << 24;
}
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
template<typename T>
struct MurmurHashFn {
uint32_t operator()(const T& key) const noexcept {
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0);
}
};
// combines two hashes together
template<class T>
inline void combine(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
}
// combines two hashes together, faster but less good
template<class T>
inline void combine_fast(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) << 1u;
}
} // namespace utils::hash
#endif // TNT_UTILS_HASH_H

View File

@@ -62,7 +62,7 @@ public:
// Creates an Invocable from the functor passed in.
template<typename Fn, EnableIfFnMatchesInvocable<Fn, R, Args...> = 0>
Invocable(Fn&& fn) noexcept;
Invocable(Fn&& fn) noexcept; // NOLINT(google-explicit-constructor)
Invocable(const Invocable&) = delete;
Invocable(Invocable&& rhs) noexcept;
@@ -121,12 +121,9 @@ Invocable<R(Args...)>::Invocable(Invocable&& rhs) noexcept
template<typename R, typename... Args>
Invocable<R(Args...)>& Invocable<R(Args...)>::operator=(Invocable&& rhs) noexcept {
if (this != &rhs) {
mInvocable = rhs.mInvocable;
mDeleter = rhs.mDeleter;
mInvoker = rhs.mInvoker;
rhs.mInvocable = nullptr;
rhs.mDeleter = nullptr;
rhs.mInvoker = nullptr;
std::swap(mInvocable, rhs.mInvocable);
std::swap(mDeleter, rhs.mDeleter);
std::swap(mInvoker, rhs.mInvoker);
}
return *this;
}

View File

@@ -0,0 +1,530 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_JOBSYSTEM_H
#define TNT_UTILS_JOBSYSTEM_H
#include <assert.h>
#include <atomic>
#include <functional>
#include <thread>
#include <vector>
#include <tsl/robin_map.h>
#include <utils/Allocator.h>
#include <utils/architecture.h>
#include <utils/compiler.h>
#include <utils/Condition.h>
#include <utils/Log.h>
#include <utils/memalign.h>
#include <utils/Mutex.h>
#include <utils/Slice.h>
#include <utils/WorkStealingDequeue.h>
namespace utils {
class JobSystem {
static constexpr size_t MAX_JOB_COUNT = 16384;
static_assert(MAX_JOB_COUNT <= 0x7FFE, "MAX_JOB_COUNT must be <= 0x7FFE");
using WorkQueue = WorkStealingDequeue<uint16_t, MAX_JOB_COUNT>;
public:
class Job;
using JobFunc = void(*)(void*, JobSystem&, Job*);
class alignas(CACHELINE_SIZE) Job {
public:
Job() noexcept {} /* = default; */ /* clang bug */ // NOLINT(modernize-use-equals-default,cppcoreguidelines-pro-type-member-init)
Job(const Job&) = delete;
Job(Job&&) = delete;
private:
friend class JobSystem;
// Size is chosen so that we can store at least std::function<>
// the alignas() qualifier ensures we're multiple of a cache-line.
static constexpr size_t JOB_STORAGE_SIZE_BYTES =
sizeof(std::function<void()>) > 48 ? sizeof(std::function<void()>) : 48;
static constexpr size_t JOB_STORAGE_SIZE_WORDS =
(JOB_STORAGE_SIZE_BYTES + sizeof(void*) - 1) / sizeof(void*);
// keep it first, so it's correctly aligned with all architectures
// this is where we store the job's data, typically a std::function<>
// v7 | v8
void* storage[JOB_STORAGE_SIZE_WORDS]; // 48 | 48
JobFunc function; // 4 | 8
uint16_t parent; // 2 | 2
std::atomic<uint16_t> runningJobCount = { 1 }; // 2 | 2
mutable std::atomic<uint16_t> refCount = { 1 }; // 2 | 2
// 6 | 2 (padding)
// 64 | 64
};
explicit JobSystem(size_t threadCount = 0, size_t adoptableThreadsCount = 1) noexcept;
~JobSystem();
// Make the current thread part of the thread pool.
void adopt();
// Remove this adopted thread from the parent. This is intended to be used for
// shutting down a JobSystem. In particular, this doesn't allow the parent to
// adopt more thread.
void emancipate();
// If a parent is not specified when creating a job, that job will automatically take the
// root job as a parent.
// The root job is reset when waited on.
Job* setRootJob(Job* job) noexcept { return mRootJob = job; }
// use setRootJob() instead
UTILS_DEPRECATED
Job* setMasterJob(Job* job) noexcept { return setRootJob(job); }
Job* create(Job* parent, JobFunc func) noexcept;
// NOTE: All methods below must be called from the same thread and that thread must be
// owned by JobSystem's thread pool.
/*
* Job creation examples:
* ----------------------
*
* struct Functor {
* uintptr_t storage[6];
* void operator()(JobSystem&, Jobsystem::Job*);
* } functor;
*
* struct Foo {
* uintptr_t storage[6];
* void method(JobSystem&, Jobsystem::Job*);
* } foo;
*
* Functor and Foo size muse be <= uintptr_t[6]
*
* createJob()
* createJob(parent)
* createJob<Foo, &Foo::method>(parent, &foo)
* createJob<Foo, &Foo::method>(parent, foo)
* createJob<Foo, &Foo::method>(parent, std::ref(foo))
* createJob(parent, functor)
* createJob(parent, std::ref(functor))
* createJob(parent, [ up-to 6 uintptr_t ](JobSystem*, Jobsystem::Job*){ })
*
* Utility functions:
* ------------------
* These are less efficient, but handle any size objects using the heap if needed.
* (internally uses std::function<>), and don't require the callee to take
* a (JobSystem&, Jobsystem::Job*) as parameter.
*
* struct BigFoo {
* uintptr_t large[16];
* void operator()();
* void method(int answerToEverything);
* static void exec(BigFoo&) { }
* } bigFoo;
*
* jobs::createJob(js, parent, [ any-capture ](int answerToEverything){}, 42);
* jobs::createJob(js, parent, &BigFoo::method, &bigFoo, 42);
* jobs::createJob(js, parent, &BigFoo::exec, std::ref(bigFoo));
* jobs::createJob(js, parent, bigFoo);
* jobs::createJob(js, parent, std::ref(bigFoo));
* etc...
*
* struct SmallFunctor {
* uintptr_t storage[3];
* void operator()(T* data, size_t count);
* } smallFunctor;
*
* jobs::parallel_for(js, data, count, [ up-to 3 uintptr_t ](T* data, size_t count) { });
* jobs::parallel_for(js, data, count, smallFunctor);
* jobs::parallel_for(js, data, count, std::ref(smallFunctor));
*
*/
// creates an empty (no-op) job with an optional parent
Job* createJob(Job* parent = nullptr) noexcept {
return create(parent, nullptr);
}
// creates a job from a KNOWN method pointer w/ object passed by pointer
// the caller must ensure the object will outlive the Job
template<typename T, void(T::*method)(JobSystem&, Job*)>
Job* createJob(Job* parent, T* data) noexcept {
Job* job = create(parent, [](void* user, JobSystem& js, Job* job) {
(*static_cast<T**>(user)->*method)(js, job);
});
if (job) {
job->storage[0] = data;
}
return job;
}
// creates a job from a KNOWN method pointer w/ object passed by value
template<typename T, void(T::*method)(JobSystem&, Job*)>
Job* createJob(Job* parent, T data) noexcept {
static_assert(sizeof(data) <= sizeof(Job::storage), "user data too large");
Job* job = create(parent, [](void* user, JobSystem& js, Job* job) {
T* that = static_cast<T*>(user);
(that->*method)(js, job);
that->~T();
});
if (job) {
new(job->storage) T(std::move(data));
}
return job;
}
// creates a job from a functor passed by value
template<typename T>
Job* createJob(Job* parent, T functor) noexcept {
static_assert(sizeof(functor) <= sizeof(Job::storage), "functor too large");
Job* job = create(parent, [](void* user, JobSystem& js, Job* job){
T& that = *static_cast<T*>(user);
that(js, job);
that.~T();
});
if (job) {
new(job->storage) T(std::move(functor));
}
return job;
}
/*
* Jobs are normally finished automatically, this can be used to cancel a job before it is run.
*
* Never use this once a flavor of run() has been called.
*/
void cancel(Job*& job) noexcept;
/*
* Adds a reference to a Job.
*
* This allows the caller to waitAndRelease() on this job from multiple threads.
* Use runAndWait() if waiting from multiple threads is not needed.
*
* This job MUST BE waited on with waitAndRelease(), or released with release().
*/
Job* retain(Job* job) noexcept;
/*
* Releases a reference from a Job obtained with runAndRetain() or a call to retain().
*
* The job can't be used after this call.
*/
void release(Job*& job) noexcept;
void release(Job*&& job) noexcept {
Job* p = job;
release(p);
}
/*
* Add job to this thread's execution queue. It's reference will drop automatically.
* Current thread must be owned by JobSystem's thread pool. See adopt().
*
* The job can't be used after this call.
*/
void run(Job*& job) noexcept;
void run(Job*&& job) noexcept { // allows run(createJob(...));
Job* p = job;
run(p);
}
void signal() noexcept;
/*
* Add job to this thread's execution queue and and keep a reference to it.
* Current thread must be owned by JobSystem's thread pool. See adopt().
*
* This job MUST BE waited on with wait(), or released with release().
*/
Job* runAndRetain(Job* job) noexcept;
/*
* Wait on a job and destroys it.
* Current thread must be owned by JobSystem's thread pool. See adopt().
*
* The job must first be obtained from runAndRetain() or retain().
* The job can't be used after this call.
*/
void waitAndRelease(Job*& job) noexcept;
/*
* Runs and wait for a job. This is equivalent to calling
* runAndRetain(job);
* wait(job);
*
* The job can't be used after this call.
*/
void runAndWait(Job*& job) noexcept;
void runAndWait(Job*&& job) noexcept { // allows runAndWait(createJob(...));
Job* p = job;
runAndWait(p);
}
// for debugging
friend utils::io::ostream& operator << (utils::io::ostream& out, JobSystem const& js);
// utility functions...
// set the name of the current thread (on OSes that support it)
static void setThreadName(const char* threadName) noexcept;
enum class Priority {
NORMAL,
DISPLAY,
URGENT_DISPLAY
};
static void setThreadPriority(Priority priority) noexcept;
static void setThreadAffinityById(size_t id) noexcept;
size_t getParallelSplitCount() const noexcept {
return mParallelSplitCount;
}
private:
// this is just to avoid using std::default_random_engine, since we're in a public header.
class default_random_engine {
static constexpr uint32_t m = 0x7fffffffu;
uint32_t mState; // must be 0 < seed < 0x7fffffff
public:
inline constexpr explicit default_random_engine(uint32_t seed = 1u) noexcept
: mState(((seed % m) == 0u) ? 1u : seed % m) {
}
inline uint32_t operator()() noexcept {
return mState = uint32_t((uint64_t(mState) * 48271u) % m);
}
};
struct alignas(CACHELINE_SIZE) ThreadState { // this causes 40-bytes padding
// make sure storage is cache-line aligned
WorkQueue workQueue;
// these are not accessed by the worker threads
alignas(CACHELINE_SIZE) // this causes 56-bytes padding
JobSystem* js;
std::thread thread;
default_random_engine rndGen;
uint32_t id;
};
static_assert(sizeof(ThreadState) % CACHELINE_SIZE == 0,
"ThreadState doesn't align to a cache line");
ThreadState& getState() noexcept;
void incRef(Job const* job) noexcept;
void decRef(Job const* job) noexcept;
Job* allocateJob() noexcept;
JobSystem::ThreadState* getStateToStealFrom(JobSystem::ThreadState& state) noexcept;
bool hasJobCompleted(Job const* job) noexcept;
void requestExit() noexcept;
bool exitRequested() const noexcept;
bool hasActiveJobs() const noexcept;
void loop(ThreadState* state) noexcept;
bool execute(JobSystem::ThreadState& state) noexcept;
Job* steal(JobSystem::ThreadState& state) noexcept;
void finish(Job* job) noexcept;
void put(WorkQueue& workQueue, Job* job) noexcept;
Job* pop(WorkQueue& workQueue) noexcept;
Job* steal(WorkQueue& workQueue) noexcept;
void wait(std::unique_lock<Mutex>& lock, Job* job = nullptr) noexcept;
void wakeAll() noexcept;
void wakeOne() noexcept;
// these have thread contention, keep them together
utils::Mutex mWaiterLock;
utils::Condition mWaiterCondition;
std::atomic<uint32_t> mActiveJobs = { 0 };
utils::Arena<utils::ThreadSafeObjectPoolAllocator<Job>, LockingPolicy::NoLock> mJobPool;
template <typename T>
using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
// these are essentially const, make sure they're on a different cache-lines than the
// read-write atomics.
// We can't use "alignas(CACHELINE_SIZE)" because the standard allocator can't make this
// guarantee.
char padding[CACHELINE_SIZE];
alignas(16) // at least we align to half (or quarter) cache-line
aligned_vector<ThreadState> mThreadStates; // actual data is stored offline
std::atomic<bool> mExitRequested = { false }; // this one is almost never written
std::atomic<uint16_t> mAdoptedThreads = { 0 }; // this one is almost never written
Job* const mJobStorageBase; // Base for conversion to indices
uint16_t mThreadCount = 0; // total # of threads in the pool
uint8_t mParallelSplitCount = 0; // # of split allowable in parallel_for
Job* mRootJob = nullptr;
utils::SpinLock mThreadMapLock; // this should have very little contention
tsl::robin_map<std::thread::id, ThreadState *> mThreadMap;
};
// -------------------------------------------------------------------------------------------------
// Utility functions built on top of JobSystem
namespace jobs {
// These are convenience C++11 style job creation methods that support lambdas
//
// IMPORTANT: these are less efficient to call and may perform heap allocation
// depending on the capture and parameters
//
template<typename CALLABLE, typename ... ARGS>
JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent,
CALLABLE&& func, ARGS&&... args) noexcept {
struct Data {
std::function<void()> f;
// Renaming the method below could cause an Arrested Development.
void gob(JobSystem&, JobSystem::Job*) noexcept { f(); }
} user{ std::bind(std::forward<CALLABLE>(func),
std::forward<ARGS>(args)...) };
return js.createJob<Data, &Data::gob>(parent, std::move(user));
}
template<typename CALLABLE, typename T, typename ... ARGS,
typename = typename std::enable_if<
std::is_member_function_pointer<typename std::remove_reference<CALLABLE>::type>::value
>::type
>
JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent,
CALLABLE&& func, T&& o, ARGS&&... args) noexcept {
struct Data {
std::function<void()> f;
// Renaming the method below could cause an Arrested Development.
void gob(JobSystem&, JobSystem::Job*) noexcept { f(); }
} user{ std::bind(std::forward<CALLABLE>(func), std::forward<T>(o),
std::forward<ARGS>(args)...) };
return js.createJob<Data, &Data::gob>(parent, std::move(user));
}
namespace details {
template<typename S, typename F>
struct ParallelForJobData {
using SplitterType = S;
using Functor = F;
using JobData = ParallelForJobData;
using size_type = uint32_t;
ParallelForJobData(size_type start, size_type count, uint8_t splits,
Functor functor,
const SplitterType& splitter) noexcept
: start(start), count(count),
functor(std::move(functor)),
splits(splits),
splitter(splitter) {
}
void parallelWithJobs(JobSystem& js, JobSystem::Job* parent) noexcept {
assert(parent);
// this branch is often miss-predicted (it both sides happen 50% of the calls)
right_side:
if (splitter.split(splits, count)) {
const size_type lc = count / 2;
JobData ld(start, lc, splits + uint8_t(1), functor, splitter);
JobSystem::Job* l = js.createJob<JobData, &JobData::parallelWithJobs>(parent, std::move(ld));
if (UTILS_UNLIKELY(l == nullptr)) {
// couldn't create a job, just pretend we're done splitting
goto execute;
}
// start the left side before attempting the right side, so we parallelize in case
// of job creation failure -- rare, but still.
js.run(l);
// don't spawn a job for the right side, just reuse us -- spawning jobs is more
// costly than we'd like.
start += lc;
count -= lc;
++splits;
goto right_side;
} else {
execute:
// we're done splitting, do the real work here!
functor(start, count);
}
}
private:
size_type start; // 4
size_type count; // 4
Functor functor; // ?
uint8_t splits; // 1
SplitterType splitter; // 1
};
} // namespace details
// parallel jobs with start/count indices
template<typename S, typename F>
JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent,
uint32_t start, uint32_t count, F functor, const S& splitter) noexcept {
using JobData = details::ParallelForJobData<S, F>;
JobData jobData(start, count, 0, std::move(functor), splitter);
return js.createJob<JobData, &JobData::parallelWithJobs>(parent, std::move(jobData));
}
// parallel jobs with pointer/count
template<typename T, typename S, typename F>
JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent,
T* data, uint32_t count, F functor, const S& splitter) noexcept {
auto user = [data, f = std::move(functor)](uint32_t s, uint32_t c) {
f(data + s, c);
};
using JobData = details::ParallelForJobData<S, decltype(user)>;
JobData jobData(0, count, 0, std::move(user), splitter);
return js.createJob<JobData, &JobData::parallelWithJobs>(parent, std::move(jobData));
}
// parallel jobs on a Slice<>
template<typename T, typename S, typename F>
JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent,
utils::Slice<T> slice, F functor, const S& splitter) noexcept {
return parallel_for(js, parent, slice.data(), slice.size(), functor, splitter);
}
template <size_t COUNT, size_t MAX_SPLITS = 12>
class CountSplitter {
public:
bool split(size_t splits, size_t count) const noexcept {
return (splits < MAX_SPLITS && count >= COUNT * 2);
}
};
} // namespace jobs
} // namespace utils
#endif // TNT_UTILS_JOBSYSTEM_H

View File

@@ -17,40 +17,19 @@
#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/SingleInstanceComponentManager.h>
#include <functional>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class EntityManager;
namespace details {
class SafeString {
public:
SafeString() noexcept = default;
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
SafeString& operator=(SafeString&& rhs) noexcept {
std::swap(mCStr, rhs.mCStr);
return *this;
}
~SafeString() { free((void*)mCStr); }
const char* c_str() const noexcept { return mCStr; }
private:
char const* mCStr = nullptr;
};
} // namespace details
/**
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
* \brief Allows clients to associate string labels with entities.
@@ -69,7 +48,7 @@ private:
* printf("%s\n", names->getName(names->getInstance(myEntity));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<utils::CString> {
public:
using Instance = EntityInstance<NameComponentManager>;
@@ -93,7 +72,7 @@ public:
* @return Non-zero handle if the entity has a name component, 0 otherwise.
*/
Instance getInstance(Entity e) const noexcept {
return Instance(SingleInstanceComponentManager::getInstance(e));
return { SingleInstanceComponentManager::getInstance(e) };
}
/*! \cond PRIVATE */

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_PROFILER_H
#define TNT_UTILS_PROFILER_H
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <chrono> // note: This is safe (only used inline)
#if defined(__linux__)
# include <unistd.h>
# include <sys/ioctl.h>
# include <linux/perf_event.h>
#endif
#include <utils/compiler.h>
namespace utils {
class Profiler {
public:
enum {
INSTRUCTIONS = 0, // must be zero
CPU_CYCLES = 1,
DCACHE_REFS = 2,
DCACHE_MISSES = 3,
BRANCHES = 4,
BRANCH_MISSES = 5,
ICACHE_REFS = 6,
ICACHE_MISSES = 7,
// Must be last one
EVENT_COUNT
};
enum {
EV_CPU_CYCLES = 1u << CPU_CYCLES,
EV_L1D_REFS = 1u << DCACHE_REFS,
EV_L1D_MISSES = 1u << DCACHE_MISSES,
EV_BPU_REFS = 1u << BRANCHES,
EV_BPU_MISSES = 1u << BRANCH_MISSES,
EV_L1I_REFS = 1u << ICACHE_REFS,
EV_L1I_MISSES = 1u << ICACHE_MISSES,
// helpers
EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES,
EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES,
EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES,
};
Profiler() noexcept; // must call resetEvents()
explicit Profiler(uint32_t eventMask) noexcept;
~Profiler() noexcept;
Profiler(const Profiler& rhs) = delete;
Profiler(Profiler&& rhs) = delete;
Profiler& operator=(const Profiler& rhs) = delete;
Profiler& operator=(Profiler&& rhs) = delete;
// selects which events are enabled.
uint32_t resetEvents(uint32_t eventMask) noexcept;
uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; }
// could return false if performance counters are not supported/enabled
bool isValid() const { return mCountersFd[0] >= 0; }
class Counters {
friend class Profiler;
uint64_t nr;
uint64_t time_enabled;
uint64_t time_running;
struct {
uint64_t value;
uint64_t id;
} counters[Profiler::EVENT_COUNT];
friend Counters operator-(Counters lhs, const Counters& rhs) noexcept {
lhs.nr -= rhs.nr;
lhs.time_enabled -= rhs.time_enabled;
lhs.time_running -= rhs.time_running;
for (size_t i = 0; i < EVENT_COUNT; ++i) {
lhs.counters[i].value -= rhs.counters[i].value;
}
return lhs;
}
public:
uint64_t getInstructions() const { return counters[INSTRUCTIONS].value; }
uint64_t getCpuCycles() const { return counters[CPU_CYCLES].value; }
uint64_t getL1DReferences() const { return counters[DCACHE_REFS].value; }
uint64_t getL1DMisses() const { return counters[DCACHE_MISSES].value; }
uint64_t getL1IReferences() const { return counters[ICACHE_REFS].value; }
uint64_t getL1IMisses() const { return counters[ICACHE_MISSES].value; }
uint64_t getBranchInstructions() const { return counters[BRANCHES].value; }
uint64_t getBranchMisses() const { return counters[BRANCH_MISSES].value; }
std::chrono::duration<uint64_t, std::nano> getWallTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_enabled);
}
std::chrono::duration<uint64_t, std::nano> getRunningTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_running);
}
double getIPC() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(instructions) / double(cpuCycles);
}
double getCPI() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(cpuCycles) / double(instructions);
}
double getL1DMissRate() const noexcept {
uint64_t cacheReferences = getL1DReferences();
uint64_t cacheMisses = getL1DMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1DHitRate() const noexcept {
return 1.0 - getL1DMissRate();
}
double getL1IMissRate() const noexcept {
uint64_t cacheReferences = getL1IReferences();
uint64_t cacheMisses = getL1IMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1IHitRate() const noexcept {
return 1.0 - getL1IMissRate();
}
double getBranchMissRate() const noexcept {
uint64_t branchReferences = getBranchInstructions();
uint64_t branchMisses = getBranchMisses();
return double(branchMisses) / double(branchReferences);
}
double getBranchHitRate() const noexcept {
return 1.0 - getBranchMissRate();
}
double getMPKI(uint64_t misses) const noexcept {
return (misses * 1000.0) / getInstructions();
}
};
#if defined(__linux__)
void reset() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
}
void start() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
}
void stop() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
}
Counters readCounters() noexcept;
#else // !__linux__
void reset() noexcept { }
void start() noexcept { }
void stop() noexcept { }
Counters readCounters() noexcept { return {}; }
#endif // __linux__
bool hasBranchRates() const noexcept {
return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0);
}
bool hasICacheRates() const noexcept {
return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0);
}
private:
UTILS_UNUSED uint8_t mIds[EVENT_COUNT] = {};
int mCountersFd[EVENT_COUNT];
uint32_t mEnabledEvents = 0;
};
} // namespace utils
#endif // TNT_UTILS_PROFILER_H

88
ios/include/utils/Range.h Normal file
View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_RANGE_H
#define TNT_UTILS_RANGE_H
#include <stddef.h>
#include <iterator>
namespace utils {
template<typename T>
struct Range {
using value_type = T;
T first = 0;
T last = 0; // this actually refers to one past the last
size_t size() const noexcept { return last - first; }
bool empty() const noexcept { return !size(); }
bool contains(const T& t) const noexcept { return first <= t && t < last; }
bool overlaps(const Range<T>& that) const noexcept {
return that.first < this->last && that.last > this->first;
}
class const_iterator {
friend struct Range;
T value = {};
public:
const_iterator() noexcept = default;
explicit const_iterator(T value) noexcept : value(value) {}
using value_type = T;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
const value_type operator*() const { return value; }
const value_type operator[](size_t n) const { return value + n; }
const_iterator& operator++() { ++value; return *this; }
const_iterator& operator--() { --value; return *this; }
const const_iterator operator++(int) { const_iterator t(value); value++; return t; }
const const_iterator operator--(int) { const_iterator t(value); value--; return t; }
const_iterator operator+(size_t rhs) const { return { value + rhs }; }
const_iterator operator+(size_t rhs) { return { value + rhs }; }
const_iterator operator-(size_t rhs) const { return { value - rhs }; }
difference_type operator-(const_iterator const& rhs) const { return value - rhs.value; }
bool operator==(const_iterator const& rhs) const { return (value == rhs.value); }
bool operator!=(const_iterator const& rhs) const { return (value != rhs.value); }
bool operator>=(const_iterator const& rhs) const { return (value >= rhs.value); }
bool operator> (const_iterator const& rhs) const { return (value > rhs.value); }
bool operator<=(const_iterator const& rhs) const { return (value <= rhs.value); }
bool operator< (const_iterator const& rhs) const { return (value < rhs.value); }
};
const_iterator begin() noexcept { return const_iterator{ first }; }
const_iterator end() noexcept { return const_iterator{ last }; }
const_iterator begin() const noexcept { return const_iterator{ first }; }
const_iterator end() const noexcept { return const_iterator{ last }; }
const_iterator front() const noexcept { return const_iterator{ first }; }
const_iterator back() const noexcept { return const_iterator{ last - 1 }; }
};
} // namespace utils
#endif // TNT_UTILS_RANGE_H

View File

@@ -0,0 +1,308 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_RANGEMAP_H
#define TNT_UTILS_RANGEMAP_H
#include <utils/Panic.h>
#include <utils/Range.h>
#include <utils/debug.h>
#include <map>
namespace utils {
/**
* Sparse container for a series of ordered non-overlapping intervals.
*
* RangeMap has a low memory footprint if it contains fairly homogeneous data. Internally, the
* intervals are automatically split and merged as elements are added or removed.
*
* Each interval maps to an instance of ValueType, which should support cheap equality checks
* and copy assignment. (simple concrete types are ideal)
*
* KeyType should support operator< because intervals are internally sorted using std::map.
*/
template<typename KeyType, typename ValueType>
class RangeMap {
public:
/**
* Replaces all slots between first (inclusive) and last (exclusive).
*/
void add(KeyType first, KeyType last, const ValueType& value) noexcept {
// First check if an existing range contains "first".
Iterator iter = findRange(first);
if (iter != end()) {
const Range<KeyType> existing = getRange(iter);
// Check if the existing range be extended.
if (getValue(iter) == value) {
if (existing.last < last) {
wipe(existing.last, last);
iter = shrink(iter, existing.first, last);
mergeRight(iter);
}
return;
}
// Split the existing range into two ranges.
if (last < existing.last && first > existing.first) {
iter = shrink(iter, existing.first, first);
insert(first, last, value);
insert(last, existing.last, getValue(iter));
return;
}
clear(first, last);
insert(first, last, value);
return;
}
// Check if an existing range contains the end of the new range.
KeyType back = last;
iter = findRange(--back);
if (iter == end()) {
wipe(first, last);
insert(first, last, value);
return;
}
const Range<KeyType> existing = getRange(iter);
// Check if the existing range be extended.
if (getValue(iter) == value) {
if (existing.first > first) {
wipe(first, existing.first);
iter = shrink(iter, first, existing.last);
mergeLeft(iter);
}
return;
}
// Clip the beginning of the existing range and potentially remove it.
if (last < existing.last) {
shrink(iter, last, existing.last);
}
wipe(first, last);
insert(first, last, value);
}
/**
* Shorthand for the "add" method that inserts a single element.
*/
void set(KeyType key, const ValueType& value) noexcept {
KeyType begin = key;
add(begin, ++key, value);
}
/**
* Checks if a range exists that encompasses the given key.
*/
bool has(KeyType key) const noexcept {
return findRange(key) != mMap.end();
}
/**
* Retrieves the element at the given location, panics if no element exists.
*/
const ValueType& get(KeyType key) const {
ConstIterator iter = findRange(key);
ASSERT_PRECONDITION(iter != end(), "RangeMap: No element exists at the given key.");
return getValue(iter);
}
/**
* Removes all elements between begin (inclusive) and end (exclusive).
*/
void clear(KeyType first, KeyType last) noexcept {
// Check if an existing range contains "first".
Iterator iter = findRange(first);
if (iter != end()) {
const Range<KeyType> existing = getRange(iter);
// Split the existing range into two ranges.
if (last < existing.last && first > existing.first) {
iter = shrink(iter, existing.first, first);
insert(last, existing.last, getValue(iter));
return;
}
// Clip one of the ends of the existing range or remove it.
if (first > existing.first) {
shrink(iter, existing.first, first);
} else if (last < existing.last) {
shrink(iter, last, existing.last);
} else {
wipe(first, last);
}
// There might be another range that intersects the cleared range, so try again.
clear(first, last);
return;
}
// Check if an existing range contains the end of the new range.
KeyType back = last;
iter = findRange(--back);
if (iter == end()) {
wipe(first, last);
return;
}
const Range<KeyType> existing = getRange(iter);
// Clip the beginning of the existing range and potentially remove it.
if (last < existing.last) {
shrink(iter, last, existing.last);
}
wipe(first, last);
}
/**
* Shorthand for the "clear" method that clears a single element.
*/
void reset(KeyType key) noexcept {
KeyType begin = key;
clear(begin, ++key);
}
/**
* Returns the number of internal interval objects (rarely used).
*/
size_t rangeCount() const noexcept { return mMap.size(); }
private:
using Map = std::map<KeyType, std::pair<Range<KeyType>, ValueType>>;
using Iterator = typename Map::iterator;
using ConstIterator = typename Map::const_iterator;
ConstIterator begin() const noexcept { return mMap.begin(); }
ConstIterator end() const noexcept { return mMap.end(); }
Iterator begin() noexcept { return mMap.begin(); }
Iterator end() noexcept { return mMap.end(); }
Range<KeyType>& getRange(Iterator iter) const { return iter->second.first; }
ValueType& getValue(Iterator iter) const { return iter->second.second; }
const Range<KeyType>& getRange(ConstIterator iter) const { return iter->second.first; }
const ValueType& getValue(ConstIterator iter) const { return iter->second.second; }
// Private helper that assumes there is no existing range that overlaps the given range.
void insert(KeyType first, KeyType last, const ValueType& value) noexcept {
assert_invariant(!has(first));
assert_invariant(!has(last - 1));
// Check if there is an adjacent range to the left than can be extended.
KeyType previous = first;
if (Iterator iter = findRange(--previous); iter != end() && getValue(iter) == value) {
getRange(iter).last = last;
mergeRight(iter);
return;
}
// Check if there is an adjacent range to the right than can be extended.
if (Iterator iter = findRange(last); iter != end() && getValue(iter) == value) {
getRange(iter).first = first;
return;
}
mMap[first] = {Range<KeyType> { first, last }, value};
}
// Private helper that erases all intervals that are wholly contained within the given range.
// Note that this is quite different from the public "clear" method.
void wipe(KeyType first, KeyType last) noexcept {
// Find the first range whose beginning is greater than or equal to "first".
Iterator iter = mMap.lower_bound(first);
while (iter != end() && getRange(iter).first < last) {
KeyType existing_last = getRange(iter).last;
if (existing_last > last) {
break;
}
iter = mMap.erase(iter);
}
}
// Checks if there is range to the right that touches the given range.
// If so, erases it, extends the given range rightwards, and returns true.
bool mergeRight(Iterator iter) {
Iterator next = iter;
if (++next == end() || getValue(next) != getValue(iter)) {
return false;
}
if (getRange(next).first != getRange(iter).last) {
return false;
}
getRange(iter).last = getRange(next).last;
mMap.erase(next);
return true;
}
// Checks if there is range to the left that touches the given range.
// If so, erases it, extends the given range leftwards, and returns true.
bool mergeLeft(Iterator iter) {
Iterator prev = iter;
if (--prev == end() || getValue(prev) != getValue(iter)) {
return false;
}
if (getRange(prev).last != getRange(iter).first) {
return false;
}
getRange(iter).first = getRange(prev).first;
mMap.erase(prev);
return true;
}
// Private helper that clips one end of an existing range.
Iterator shrink(Iterator iter, KeyType first, KeyType last) {
assert_invariant(first < last);
assert_invariant(getRange(iter).first == first || getRange(iter).last == last);
std::pair<utils::Range<KeyType>, ValueType> value = {{first, last}, iter->second.second};
mMap.erase(iter);
return mMap.insert({first, value}).first;
}
// If the given key is encompassed by an existing range, returns an iterator for that range.
// If no encompassing range exists, returns end().
ConstIterator findRange(KeyType key) const noexcept {
return findRangeT<ConstIterator>(*this, key);
}
// If the given key is encompassed by an existing range, returns an iterator for that range.
// If no encompassing range exists, returns end().
Iterator findRange(KeyType key) noexcept {
return findRangeT<Iterator>(*this, key);
}
// This template method allows us to avoid code duplication for const and non-const variants of
// findRange. C++17 has "std::as_const()" but that would not be helpful here, as we would still
// need to convert a const iterator to a non-const iterator.
template<typename IteratorType, typename SelfType>
static IteratorType findRangeT(SelfType& instance, KeyType key) noexcept {
// Find the first range whose beginning is greater than or equal to the given key.
IteratorType iter = instance.mMap.lower_bound(key);
if (iter != instance.end() && instance.getRange(iter).contains(key)) {
return iter;
}
// If that was the first range, or if the map is empty, return false.
if (iter == instance.begin()) {
return instance.end();
}
// Check the range immediately previous to the one that was found.
return instance.getRange(--iter).contains(key) ? iter : instance.end();
}
// This maps from the start value of each range to the range itself.
Map mMap;
};
} // namespace utils
#endif // TNT_UTILS_RANGEMAP_H

View File

@@ -256,7 +256,7 @@ protected:
private:
// maps an entity to an instance index
tsl::robin_map<Entity, Instance> mInstanceMap;
tsl::robin_map<Entity, Instance, Entity::Hasher> mInstanceMap;
default_random_engine mRng;
};

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_STOPWATCH_H
#define TNT_UTILS_STOPWATCH_H
#include <utils/Log.h>
#include <chrono>
#include <limits>
#include <stdint.h>
namespace utils {
/*
* A very basic Stopwatch class
*/
template<typename Clock = std::chrono::steady_clock>
class Stopwatch {
public:
using duration = typename Clock::duration;
using time_point = typename Clock::time_point;
private:
time_point mStart;
duration mMinLap = std::numeric_limits<duration>::max();
duration mMaxLap{};
std::chrono::duration<double, std::nano> mAvgLap{};
size_t mCount = 0;
const char* mName = nullptr;
public:
// Create a Stopwatch with a name and clock
explicit Stopwatch(const char* name) noexcept: mName(name) {}
// Logs min/avg/max lap time
~Stopwatch() noexcept;
// start the stopwatch
inline void start() noexcept {
mStart = Clock::now();
}
// stop the stopwatch
inline void stop() noexcept {
auto d = Clock::now() - mStart;
mMinLap = std::min(mMinLap, d);
mMaxLap = std::max(mMaxLap, d);
mAvgLap = (mAvgLap * mCount + d) / (mCount + 1);
mCount++;
}
// get the minimum lap time recorded
duration getMinLapTime() const noexcept { return mMinLap; }
// get the maximum lap time recorded
duration getMaxLapTime() const noexcept { return mMaxLap; }
// get the average lap time
duration getAverageLapTime() const noexcept { return mAvgLap; }
};
template<typename Clock>
Stopwatch<Clock>::~Stopwatch() noexcept {
slog.d << "Stopwatch \"" << mName << "\" : ["
<< mMinLap.count() << ", "
<< std::chrono::duration_cast<duration>(mAvgLap).count() << ", "
<< mMaxLap.count() << "] ns" << io::endl;
}
/*
* AutoStopwatch can be used to start and stop a Stopwatch automatically
* when entering and exiting a scope.
*/
template<typename Stopwatch>
class AutoStopwatch {
Stopwatch& stopwatch;
public:
inline explicit AutoStopwatch(Stopwatch& stopwatch) noexcept: stopwatch(stopwatch) {
stopwatch.start();
}
inline ~AutoStopwatch() noexcept { stopwatch.stop(); }
};
} // namespace utils
#endif // TNT_UTILS_STOPWATCH_H

View File

@@ -19,7 +19,6 @@
#include <array> // note: this is safe, see how std::array is used below (inline / private)
#include <cstddef>
#include <functional>
#include <utility>
#include <stddef.h>
@@ -509,7 +508,7 @@ private:
mSize = needed;
}
// this calculate the offset adjusted for all data alignment of a given array
// this calculates the offset adjusted for all data alignment of a given array
static inline size_t getOffset(size_t index, size_t capacity) noexcept {
auto offsets = getOffsets(capacity);
return offsets[index];

View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_SYSTRACE_H
#define TNT_UTILS_SYSTRACE_H
#define SYSTRACE_TAG_NEVER (0)
#define SYSTRACE_TAG_ALWAYS (1<<0)
#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles
#define SYSTRACE_TAG_JOBSYSTEM (1<<2)
#if defined(__ANDROID__)
#include <atomic>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <utils/compiler.h>
/*
* The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined
* before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used.
*/
#ifndef SYSTRACE_TAG
#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS)
#endif
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG)
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
/**
* Creates a Systrace context in the current scope. needed for calling all other systrace
* commands below.
*/
#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___tracer(SYSTRACE_TAG)
// SYSTRACE_NAME traces the beginning and end of the current scope. To trace
// the correct start and end times this macro should be declared first in the
// scope body.
// It also automatically creates a Systrace context
#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name)
// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name.
#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__)
#define SYSTRACE_NAME_BEGIN(name) \
___tracer.traceBegin(SYSTRACE_TAG, name)
#define SYSTRACE_NAME_END() \
___tracer.traceEnd(SYSTRACE_TAG)
/**
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
* contexts, asynchronous events do not need to be nested. The name describes
* the event, and the cookie provides a unique identifier for distinguishing
* simultaneous events. The name and cookie used to begin an event must be
* used to end it.
*/
#define SYSTRACE_ASYNC_BEGIN(name, cookie) \
___tracer.asyncBegin(SYSTRACE_TAG, name, cookie)
/**
* Trace the end of an asynchronous event.
* This should have a corresponding SYSTRACE_ASYNC_BEGIN.
*/
#define SYSTRACE_ASYNC_END(name, cookie) \
___tracer.asyncEnd(SYSTRACE_TAG, name, cookie)
/**
* Traces an integer counter value. name is used to identify the counter.
* This can be used to track how a value changes over time.
*/
#define SYSTRACE_VALUE32(name, val) \
___tracer.value(SYSTRACE_TAG, name, int32_t(val))
#define SYSTRACE_VALUE64(name, val) \
___tracer.value(SYSTRACE_TAG, name, int64_t(val))
// ------------------------------------------------------------------------------------------------
// No user serviceable code below...
// ------------------------------------------------------------------------------------------------
namespace utils {
namespace details {
class Systrace {
public:
enum tags {
NEVER = SYSTRACE_TAG_NEVER,
ALWAYS = SYSTRACE_TAG_ALWAYS,
FILAMENT = SYSTRACE_TAG_FILAMENT,
JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM
// we could define more TAGS here, as we need them.
};
Systrace(uint32_t tag) noexcept {
if (tag) init(tag);
}
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
inline void traceBegin(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginSection(this, name);
}
}
inline void traceEnd(uint32_t tag) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endSection(this);
}
}
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginAsyncSection(this, name, cookie);
}
}
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endAsyncSection(this, name, cookie);
}
}
inline void value(uint32_t tag, const char* name, int32_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
inline void value(uint32_t tag, const char* name, int64_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
private:
friend class ScopedTrace;
// whether tracing is supported at all by the platform
using ATrace_isEnabled_t = bool (*)(void);
using ATrace_beginSection_t = void (*)(const char* sectionName);
using ATrace_endSection_t = void (*)(void);
using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue);
struct GlobalState {
bool isTracingAvailable;
std::atomic<uint32_t> isTracingEnabled;
int markerFd;
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
};
static GlobalState sGlobalState;
// per-instance versions for better performance
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
void init(uint32_t tag) noexcept;
// cached values for faster access, no need to be initialized
bool mIsTracingEnabled;
int mMarkerFd = -1;
pid_t mPid;
static void setup() noexcept;
static void init_once() noexcept;
static bool isTracingEnabled(uint32_t tag) noexcept;
static void begin_body(int fd, int pid, const char* name) noexcept;
static void end_body(int fd, int pid) noexcept;
static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept;
};
// ------------------------------------------------------------------------------------------------
class ScopedTrace {
public:
// we don't inline this because it's relatively heavy due to a global check
ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mTag(tag) {
mTrace.traceBegin(tag, name);
}
inline ~ScopedTrace() noexcept {
mTrace.traceEnd(mTag);
}
inline void value(uint32_t tag, const char* name, int32_t v) noexcept {
mTrace.value(tag, name, v);
}
inline void value(uint32_t tag, const char* name, int64_t v) noexcept {
mTrace.value(tag, name, v);
}
private:
Systrace mTrace;
const uint32_t mTag;
};
} // namespace details
} // namespace utils
// ------------------------------------------------------------------------------------------------
#else // !ANDROID
// ------------------------------------------------------------------------------------------------
#define SYSTRACE_ENABLE()
#define SYSTRACE_DISABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_NAME_BEGIN(name)
#define SYSTRACE_NAME_END()
#define SYSTRACE_CALL()
#define SYSTRACE_ASYNC_BEGIN(name, cookie)
#define SYSTRACE_ASYNC_END(name, cookie)
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#endif // ANDROID
#endif // TNT_UTILS_SYSTRACE_H

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_THERMALMANAGER_H
#define TNT_UTILS_THERMALMANAGER_H
#if defined(__ANDROID__)
#include <utils/android/ThermalManager.h>
#else
#include <utils/generic/ThermalManager.h>
#endif
#endif // TNT_UTILS_THERMALMANAGER_H

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_THREADUTILS_H
#define TNT_UTILS_THREADUTILS_H
#include <thread>
namespace utils {
class ThreadUtils {
public:
static std::thread::id getThreadId() noexcept;
static bool isThisThread(std::thread::id id) noexcept;
};
} // namespace utils
#endif // TNT_UTILS_THREADUTILS_H

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_WORKSTEALINGDEQUEUE_H
#define TNT_UTILS_WORKSTEALINGDEQUEUE_H
#include <atomic>
#include <assert.h>
#include <stddef.h>
namespace utils {
/*
* A templated, lockless, fixed-size work-stealing dequeue
*
*
* top bottom
* v v
* |----|----|----|----|----|----|
* steal() push(), pop()
* any thread main thread
*
*
*/
template <typename TYPE, size_t COUNT>
class WorkStealingDequeue {
static_assert(!(COUNT & (COUNT - 1)), "COUNT must be a power of two");
static constexpr size_t MASK = COUNT - 1;
// mTop and mBottom must be signed integers. We use 64-bits atomics so we don't have
// to worry about wrapping around.
using index_t = int64_t;
std::atomic<index_t> mTop = { 0 }; // written/read in pop()/steal()
std::atomic<index_t> mBottom = { 0 }; // written only in pop(), read in push(), steal()
TYPE mItems[COUNT];
// NOTE: it's not safe to return a reference because getItemAt() can be called
// concurrently and the caller could std::move() the item unsafely.
TYPE getItemAt(index_t index) noexcept { return mItems[index & MASK]; }
void setItemAt(index_t index, TYPE item) noexcept { mItems[index & MASK] = item; }
public:
using value_type = TYPE;
inline void push(TYPE item) noexcept;
inline TYPE pop() noexcept;
inline TYPE steal() noexcept;
size_t getSize() const noexcept { return COUNT; }
// for debugging only...
size_t getCount() const noexcept {
index_t bottom = mBottom.load(std::memory_order_relaxed);
index_t top = mTop.load(std::memory_order_relaxed);
return bottom - top;
}
};
/*
* Adds an item at the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
void WorkStealingDequeue<TYPE, COUNT>::push(TYPE item) noexcept {
// std::memory_order_relaxed is sufficient because this load doesn't acquire anything from
// another thread. mBottom is only written in pop() which cannot be concurrent with push()
index_t bottom = mBottom.load(std::memory_order_relaxed);
setItemAt(bottom, item);
// std::memory_order_release is used because we release the item we just pushed to other
// threads which are calling steal().
mBottom.store(bottom + 1, std::memory_order_release);
}
/*
* Removes an item from the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::pop() noexcept {
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire/release operation:
// - not acquire because mBottom is only written in push() which is not concurrent
// - not release because we're not publishing anything to steal() here
//
// QUESTION: does this prevent mTop load below to be reordered before the "store" part of
// fetch_sub()? Hopefully it does. If not we'd need a full memory barrier.
//
index_t bottom = mBottom.fetch_sub(1, std::memory_order_seq_cst) - 1;
// bottom could be -1 if we tried to pop() from an empty queue. This will be corrected below.
assert( bottom >= -1 );
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
if (top < bottom) {
// Queue isn't empty and it's not the last item, just return it, this is the common case.
return getItemAt(bottom);
}
TYPE item{};
if (top == bottom) {
// we just took the last item
item = getItemAt(bottom);
// Because we know we took the last item, we could be racing with steal() -- the last
// item being both at the top and bottom of the queue.
// We resolve this potential race by also stealing that item from ourselves.
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole our last item from ourself, meaning that a concurrent steal()
// would have failed.
// mTop now equals top + 1, we adjust top to make the queue empty.
top++;
} else {
// failure: mTop was not equal to top, which means the item was stolen under our feet.
// top now equals to mTop. Simply discard the item we just popped.
// The queue is now empty.
item = TYPE();
}
} else {
// We could be here if the item was stolen just before we read mTop, we'll adjust
// mBottom below.
assert(top - bottom == 1);
}
// std::memory_order_relaxed used because we're not publishing any data.
// no concurrent writes to mBottom possible, it's always safe to write mBottom.
mBottom.store(top, std::memory_order_relaxed);
return item;
}
/*
* Steals an item from the TOP of another thread's queue.
*
* This can be called concurrently with steal(), push() or pop()
*
* steal() never fails, either there is an item and it atomically takes it, or there isn't and
* it returns an empty item.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::steal() noexcept {
while (true) {
/*
* Note: A Key component of this algorithm is that mTop is read before mBottom here
* (and observed as such in other threads)
*/
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
// std::memory_order_acquire is needed because we're acquiring items published in push().
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
index_t bottom = mBottom.load(std::memory_order_seq_cst);
if (top >= bottom) {
// queue is empty
return TYPE();
}
// The queue isn't empty
TYPE item(getItemAt(top));
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole an item, just return it.
return item;
}
// failure: the item we just tried to steal was pop()'ed under our feet,
// simply discard it; nothing to do -- it's okay to try again.
}
}
} // namespace utils
#endif // TNT_UTILS_WORKSTEALINGDEQUEUE_H

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_ZIP2ITERATOR_H
#define TNT_UTILS_ZIP2ITERATOR_H
#include <iterator>
#include <type_traits>
#include <utils/compiler.h>
namespace utils {
/*
* A random access iterator that wraps two other random access iterators.
* This mostly exists so that one can sort an array using values from another.
*/
template<typename It1, typename It2>
class Zip2Iterator {
std::pair<It1, It2> mIt;
using Ref1 = typename std::iterator_traits<It1>::reference;
using Ref2 = typename std::iterator_traits<It2>::reference;
using Val1 = typename std::iterator_traits<It1>::value_type;
using Val2 = typename std::iterator_traits<It2>::value_type;
public:
struct Ref : public std::pair<Ref1, Ref2> {
using std::pair<Ref1, Ref2>::pair;
using std::pair<Ref1, Ref2>::operator=;
private:
friend void swap(Ref lhs, Ref rhs) {
using std::swap;
swap(lhs.first, rhs.first);
swap(lhs.second, rhs.second);
}
};
using value_type = std::pair<Val1, Val2>;
using reference = Ref;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
Zip2Iterator() = default;
Zip2Iterator(It1 first, It2 second) : mIt({first, second}) {}
Zip2Iterator(Zip2Iterator const& rhs) noexcept = default;
Zip2Iterator& operator=(Zip2Iterator const& rhs) = default;
reference operator*() const { return { *mIt.first, *mIt.second }; }
reference operator[](size_t n) const { return *(*this + n); }
Zip2Iterator& operator++() {
++mIt.first;
++mIt.second;
return *this;
}
Zip2Iterator& operator--() {
--mIt.first;
--mIt.second;
return *this;
}
// Postfix operator needed by Microsoft C++
const Zip2Iterator operator++(int) {
Zip2Iterator t(*this);
mIt.first++;
mIt.second++;
return t;
}
const Zip2Iterator operator--(int) {
Zip2Iterator t(*this);
mIt.first--;
mIt.second--;
return t;
}
Zip2Iterator& operator+=(size_t v) {
mIt.first += v;
mIt.second += v;
return *this;
}
Zip2Iterator& operator-=(size_t v) {
mIt.first -= v;
mIt.second -= v;
return *this;
}
Zip2Iterator operator+(size_t rhs) const { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator+(size_t rhs) { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator-(size_t rhs) const { return { mIt.first - rhs, mIt.second - rhs }; }
difference_type operator-(Zip2Iterator const& rhs) const { return mIt.first - rhs.mIt.first; }
bool operator==(Zip2Iterator const& rhs) const { return (mIt.first == rhs.mIt.first); }
bool operator!=(Zip2Iterator const& rhs) const { return (mIt.first != rhs.mIt.first); }
bool operator>=(Zip2Iterator const& rhs) const { return (mIt.first >= rhs.mIt.first); }
bool operator> (Zip2Iterator const& rhs) const { return (mIt.first > rhs.mIt.first); }
bool operator<=(Zip2Iterator const& rhs) const { return (mIt.first <= rhs.mIt.first); }
bool operator< (Zip2Iterator const& rhs) const { return (mIt.first < rhs.mIt.first); }
};
} // namespace utils
#endif // TNT_UTILS_ZIP2ITERATOR_H

View File

@@ -19,7 +19,6 @@
#include <utils/compiler.h>
#include <functional> // for std::less
#include <type_traits> // for std::enable_if
#include <limits.h>
@@ -168,74 +167,6 @@ T log2i(T x) noexcept {
return (sizeof(x) * 8 - 1u) - clz(x);
}
/*
* branch-less version of std::lower_bound and std::upper_bound.
* These versions are intended to be fully inlined, which only happens when the size
* of the array is known at compile time. This code also performs better if the
* array is a power-of-two in size.
*
* These code works even if the conditions above are not met, and becomes a less-branches
* algorithm instead of a branch-less one!
*/
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator lower_bound(
RandomAccessIterator first, RandomAccessIterator last, const T& value,
COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(first[len], value) ? difference : 0;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += comp(first[len >>= 1u], value) ? len : 0;
first += comp(first[len >>= 1u], value) ? len : 0;
}
first += comp(*first, value);
return first;
}
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator upper_bound(
RandomAccessIterator first, RandomAccessIterator last,
const T& value, COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(value, first[len]) ? 0 : difference;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += !comp(value, first[len >>= 1u]) ? len : 0;
first += !comp(value, first[len >>= 1u]) ? len : 0;
}
first += !comp(value, *first);
return first;
}
template<typename RandomAccessIterator, typename COMPARE>
inline UTILS_PUBLIC
RandomAccessIterator partition_point(

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_ANDROID_THERMALMANAGER_H
#define TNT_UTILS_ANDROID_THERMALMANAGER_H
#include <utils/compiler.h>
#include <stdint.h>
struct AThermalManager;
namespace utils {
class ThermalManager {
public:
enum class ThermalStatus : int8_t {
ERROR = -1,
NONE,
LIGHT,
MODERATE,
SEVERE,
CRITICAL,
EMERGENCY,
SHUTDOWN
};
ThermalManager();
~ThermalManager();
// Movable
ThermalManager(ThermalManager&& rhs) noexcept;
ThermalManager& operator=(ThermalManager&& rhs) noexcept;
// not copiable
ThermalManager(ThermalManager const& rhs) = delete;
ThermalManager& operator=(ThermalManager const& rhs) = delete;
ThermalStatus getCurrentThermalStatus() const noexcept;
private:
AThermalManager* mThermalManager = nullptr;
};
} // namespace utils
#endif // TNT_UTILS_ANDROID_THERMALMANAGER_H

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 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 TNT_UTILS_APILEVEL_H
#define TNT_UTILS_APILEVEL_H
#include <utils/compiler.h>
namespace utils {
/**
* Returns this platform's API level. On Android this function will return
* the API level as defined by the SDK API level version. If a platform does
* not have an API level, this function returns 0.
*/
UTILS_PUBLIC
int api_level();
} // namespace utils
#endif // TNT_UTILS_APILEVEL_H

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_ARCHITECTURE_H
#define TNT_UTILS_ARCHITECTURE_H
#include <stddef.h>
namespace utils {
constexpr size_t CACHELINE_SIZE = 64;
} // namespace utils
#endif // TNT_UTILS_ARCHITECTURE_H

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_ASHMEM_H
#define TNT_UTILS_ASHMEM_H
#include <stddef.h>
namespace utils {
int ashmem_create_region(const char *name, size_t size);
} // namespace utils
#endif // TNT_UTILS_ASHMEM_H

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_GENERIC_CONDITION_H
#define TNT_UTILS_GENERIC_CONDITION_H
#include <condition_variable>
namespace utils {
class Condition : public std::condition_variable {
public:
using std::condition_variable::condition_variable;
inline void notify_n(size_t n) noexcept {
if (n == 1) {
notify_one();
} else if (n > 1) {
notify_all();
}
}
};
} // namespace utils
#endif // TNT_UTILS_GENERIC_CONDITION_H

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 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 TNT_UTILS_GENERIC_THERMALMANAGER_H
#define TNT_UTILS_GENERIC_THERMALMANAGER_H
#include <utils/compiler.h>
#include <stdint.h>
namespace utils {
class ThermalManager {
public:
enum class ThermalStatus : int8_t {
ERROR = -1,
NONE,
LIGHT,
MODERATE,
SEVERE,
CRITICAL,
EMERGENCY,
SHUTDOWN
};
ThermalManager() = default;
// Movable
ThermalManager(ThermalManager&& rhs) noexcept = default;
ThermalManager& operator=(ThermalManager&& rhs) noexcept = default;
// not copiable
ThermalManager(ThermalManager const& rhs) = delete;
ThermalManager& operator=(ThermalManager const& rhs) = delete;
ThermalStatus getCurrentThermalStatus() const noexcept {
return ThermalStatus::NONE;
}
};
} // namespace utils
#endif // TNT_UTILS_GENERIC_THERMALMANAGER_H

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_LINUX_CONDITION_H
#define TNT_UTILS_LINUX_CONDITION_H
#include <atomic>
#include <chrono>
#include <condition_variable> // for cv_status
#include <limits>
#include <mutex> // for unique_lock
#include <utils/linux/Mutex.h>
#include <time.h>
namespace utils {
/*
* A very simple condition variable class that can be used as an (almost) drop-in replacement
* for std::condition_variable (doesn't have the timed wait() though).
* It is very low overhead as most of it is inlined.
*/
class Condition {
public:
Condition() noexcept = default;
Condition(const Condition&) = delete;
Condition& operator=(const Condition&) = delete;
void notify_all() noexcept {
pulse(std::numeric_limits<int>::max());
}
void notify_one() noexcept {
pulse(1);
}
void notify_n(size_t n) noexcept {
if (n > 0) pulse(n);
}
void wait(std::unique_lock<Mutex>& lock) noexcept {
wait_until(lock.mutex(), false, nullptr);
}
template <class P>
void wait(std::unique_lock<Mutex>& lock, P predicate) {
while (!predicate()) {
wait(lock);
}
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::steady_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), false, &ts);
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::system_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), true, &ts);
}
template<typename C, typename D, typename P>
bool wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<C, D>& timeout_time, P predicate) noexcept {
while (!predicate()) {
if (wait_until(lock, timeout_time) == std::cv_status::timeout) {
return predicate();
}
}
return true;
}
template<typename R, typename Period>
std::cv_status wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time);
}
template<typename R, typename Period, typename P>
bool wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time, P pred) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));
}
private:
std::atomic<uint32_t> mState = { 0 };
void pulse(int threadCount) noexcept;
std::cv_status wait_until(Mutex* lock,
bool realtimeClock, timespec* ts) noexcept;
};
} // namespace utils
#endif // TNT_UTILS_LINUX_CONDITION_H

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2016 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 TNT_UTILS_LINUX_MUTEX_H
#define TNT_UTILS_LINUX_MUTEX_H
#include <atomic>
#include <utils/compiler.h>
namespace utils {
/*
* A very simple mutex class that can be used as an (almost) drop-in replacement
* for std::mutex.
* It is very low overhead as most of it is inlined.
*/
class Mutex {
public:
constexpr Mutex() noexcept = default;
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
void lock() noexcept {
uint32_t old_state = UNLOCKED;
if (UTILS_UNLIKELY(!mState.compare_exchange_strong(old_state,
LOCKED, std::memory_order_acquire, std::memory_order_relaxed))) {
wait();
}
}
void unlock() noexcept {
if (UTILS_UNLIKELY(mState.exchange(UNLOCKED, std::memory_order_release) == LOCKED_CONTENDED)) {
wake();
}
}
private:
enum {
UNLOCKED = 0, LOCKED = 1, LOCKED_CONTENDED = 2
};
std::atomic<uint32_t> mState = { UNLOCKED };
void wait() noexcept;
void wake() noexcept;
};
} // namespace utils
#endif // TNT_UTILS_LINUX_MUTEX_H

View File

@@ -30,15 +30,13 @@
namespace utils {
inline void* aligned_alloc(size_t size, size_t align) noexcept {
// 'align' must be a power of two and a multiple of sizeof(void*)
align = (align < sizeof(void*)) ? sizeof(void*) : align;
assert(align && !(align & align - 1));
assert((align % sizeof(void*)) == 0);
void* p = nullptr;
// must be a power of two and >= sizeof(void*)
while (align < sizeof(void*)) {
align <<= 1u;
}
#if defined(WIN32)
p = ::_aligned_malloc(size, align);
#else

View File

@@ -17,13 +17,14 @@
#ifndef TNT_UTILS_OSTREAM_H
#define TNT_UTILS_OSTREAM_H
#include <string>
#include <utility>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <string>
#include <string_view>
#include <utility>
namespace utils::io {
struct ostream_;
@@ -60,6 +61,9 @@ public:
ostream& operator<<(const char* string) noexcept;
ostream& operator<<(const unsigned char* string) noexcept;
ostream& operator<<(std::string const& s) noexcept;
ostream& operator<<(std::string_view const& s) noexcept;
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
ostream& dec() noexcept;
@@ -110,9 +114,6 @@ private:
const char* getFormat(type t) const noexcept;
};
// handles std::string
inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); }
// handles utils::bitset
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
return o << (void*)uintptr_t(s.getValue());
@@ -131,7 +132,7 @@ inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& endl(ostream& s) noexcept { s << '\n'; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
} // namespace utils::io

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 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 TNT_UTILS_SSTREAM_H
#define TNT_UTILS_SSTREAM_H
#include <utils/ostream.h>
namespace utils::io {
class sstream : public ostream {
public:
ostream& flush() noexcept override;
const char* c_str() const noexcept;
};
} // namespace utils::io
#endif // TNT_UTILS_SSTREAM_H

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2021 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 TNT_UTILS_STRING_H
#define TNT_UTILS_STRING_H
#include <utils/ostream.h>
namespace utils {
float strtof_c(const char* start, char** end);
} // namespace utils
#endif // TNT_UTILS_STRING_H

40
ios/include/utils/trap.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_TRAP_H
#define TNT_UTILS_TRAP_H
#include <csignal>
#if defined(WIN32)
#include <Windows.h>
#include <utils/unwindows.h>
#endif
namespace utils {
// This can be used as a programmatic breakpoint.
inline void debug_trap() noexcept {
#if defined(WIN32)
DebugBreak();
#else
std::raise(SIGINT);
#endif
}
} // namespace utils
#endif // TNT_UTILS_TRAP_H

View File

@@ -0,0 +1,61 @@
/*
* 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.
*/
#ifndef TNT_UTILS_VECTOR_H
#define TNT_UTILS_VECTOR_H
#include <algorithm>
#include <vector>
namespace utils {
/**
* Inserts the specified item in the vector at its sorted position.
*/
template <typename T>
static inline void insert_sorted(std::vector<T>& v, T item) {
auto pos = std::lower_bound(v.begin(), v.end(), item);
v.insert(pos, std::move(item));
}
/**
* Inserts the specified item in the vector at its sorted position.
* The item type must implement the < operator. If the specified
* item is already present in the vector, this method returns without
* inserting the item again.
*
* @return True if the item was inserted at is sorted position, false
* if the item already exists in the vector.
*/
template <typename T>
static inline bool insert_sorted_unique(std::vector<T>& v, T item) {
if (UTILS_LIKELY(v.size() == 0 || v.back() < item)) {
v.push_back(item);
return true;
}
auto pos = std::lower_bound(v.begin(), v.end(), item);
if (UTILS_LIKELY(pos == v.end() || item < *pos)) {
v.insert(pos, std::move(item));
return true;
}
return false;
}
} // end utils namespace
#endif // TNT_UTILS_VECTOR_H

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 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 TNT_UTILS_WIN32_STDTYPES_H
#define TNT_UTILS_WIN32_STDTYPES_H
#if defined(WIN32)
#include <windows.h>
// Copied from linux libc sys/stat.h:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define PATH_MAX (MAX_PATH)
// For getcwd
#include <direct.h>
#define getcwd _getcwd
#endif
#endif // TNT_UTILS_WIN32_STDTYPES_H

View File

@@ -40,7 +40,7 @@ namespace viewer {
*
* 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 asychronous screenshot.
* 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.

View File

@@ -72,6 +72,7 @@ using MultiSampleAntiAliasingOptions = filament::View::MultiSampleAntiAliasingOp
using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions;
using VignetteOptions = filament::View::VignetteOptions;
using VsmShadowOptions = filament::View::VsmShadowOptions;
using GuardBandOptions = filament::View::GuardBandOptions;
using LightManager = filament::LightManager;
// These functions push all editable property values to their respective Filament objects.
@@ -171,6 +172,7 @@ struct ViewSettings {
TemporalAntiAliasingOptions taa;
VignetteOptions vignette;
VsmShadowOptions vsmShadowOptions;
GuardBandOptions guardBand;
// Custom View Options
ColorGradingSettings colorGrading;
@@ -194,7 +196,7 @@ struct LightSettings {
LightManager::ShadowOptions shadowOptions;
SoftShadowOptions softShadowOptions;
float sunlightIntensity = 100000.0f;
math::float3 sunlightDirection = {0.6, -1.0, -0.8};;
math::float3 sunlightDirection = {0.6, -1.0, -0.8};
math::float3 sunlightColor = filament::Color::toLinear<filament::ACCURATE>({ 0.98, 0.92, 0.89});
float iblIntensity = 30000.0f;
float iblRotation = 0.0f;

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef VIEWER_SIMPLEVIEWER_H
#define VIEWER_SIMPLEVIEWER_H
#ifndef VIEWER_VIEWERGUI_H
#define VIEWER_VIEWERGUI_H
#include <filament/Box.h>
#include <filament/DebugRegistry.h>
@@ -26,6 +26,7 @@
#include <gltfio/Animator.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/NodeManager.h>
#include <viewer/Settings.h>
@@ -35,6 +36,7 @@
#include <math/mat4.h>
#include <math/vec3.h>
#include <functional>
#include <vector>
namespace filagui {
@@ -45,15 +47,15 @@ namespace filament {
namespace viewer {
/**
* \class SimpleViewer SimpleViewer.h viewer/SimpleViewer.h
* \brief Manages the state for a simple glTF viewer with imgui controls and a tree view.
* \class ViewerGui ViewerGui.h viewer/ViewerGui.h
* \brief Builds ImGui widgets for a simple glTF viewer and manages the associated state.
*
* This is a utility that can be used across multiple platforms, including web.
*
* \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader
* instead.
*/
class UTILS_PUBLIC SimpleViewer {
class UTILS_PUBLIC ViewerGui {
public:
using Animator = gltfio::Animator;
using FilamentAsset = gltfio::FilamentAsset;
@@ -62,29 +64,39 @@ public:
static constexpr int DEFAULT_SIDEBAR_WIDTH = 350;
/**
* Constructs a SimpleViewer that has a fixed association with the given Filament objects.
* Constructs a ViewerGui that has a fixed association with the given Filament objects.
*
* Upon construction, the simple viewer may create some additional Filament objects (such as
* light sources) that it owns.
*/
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
ViewerGui(filament::Engine* engine, filament::Scene* scene, filament::View* view,
int sidebarWidth = DEFAULT_SIDEBAR_WIDTH);
/**
* Destroys the SimpleViewer and any Filament entities that it owns.
* Destroys the ViewerGui and any Filament entities that it owns.
*/
~SimpleViewer();
~ViewerGui();
/**
* Adds the asset's ready-to-render entities into the scene.
* Sets the viewer's current asset.
*
* The viewer does not claim ownership over the asset or its entities. Clients should use
* AssetLoader and ResourceLoader to load an asset before passing it in.
*
* This method does not add renderables to the scene; see populateScene().
*
* @param asset The asset to view.
* @param instanceToAnimate Optional instance from which to get the animator.
*/
void populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate = nullptr);
void setAsset(FilamentAsset* asset, FilamentInstance* instanceToAnimate = nullptr);
/**
* Adds the asset's ready-to-render entities into the scene.
*
* This is used for asychronous loading. It can be called once per frame to gradually add
* entities into the scene as their textures are loaded.
*/
void populateScene();
/**
* Removes the current asset from the viewer.
@@ -126,7 +138,7 @@ public:
void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio);
/**
* Event-passing methods, useful only when SimpleViewer manages its own instance of ImGuiHelper.
* Event-passing methods, useful only when ViewerGui manages its own instance of ImGuiHelper.
* The key codes used in these methods are just normal ASCII/ANSI codes.
* @{
*/
@@ -218,8 +230,14 @@ public:
int getCurrentCamera() const { return mCurrentCamera; }
private:
using SceneMask = gltfio::NodeManager::SceneMask;
void updateIndirectLight();
bool isRemoteMode() const { return mAsset == nullptr; }
void sceneSelectionUI();
// Immutable properties set from the constructor.
filament::Engine* const mEngine;
filament::Scene* const mScene;
@@ -238,7 +256,6 @@ private:
// Properties that can be changed from the UI.
int mCurrentAnimation = 1; // It is a 1-based index and 0 means not playing animation
int mCurrentVariant = 0;
bool mResetAnimation = true;
bool mEnableWireframe = false;
int mVsmMsaaSamplesLog2 = 1;
Settings mSettings;
@@ -246,10 +263,19 @@ private:
uint32_t mFlags;
utils::Entity mCurrentMorphingEntity;
std::vector<float> mMorphWeights;
SceneMask mVisibleScenes;
bool mShowingRestPose = false;
// 0 is the default "free camera". Additional cameras come from the gltf file (1-based index).
int mCurrentCamera = 0;
// Cross fade animation parameters.
float mCrossFadeDuration = 0.5f; // number of seconds to transition between animations
int mPreviousAnimation = 0; // one-based index of the previous animation
double mCurrentStartTime = 0.0f; // start time of most recent cross-fade (seconds)
double mPreviousStartTime = 0.0f; // start time of previous cross-fade (seconds)
bool mResetAnimation = true; // set when building ImGui widgets, honored in applyAnimation
// Color grading UI state.
float mToneMapPlot[1024];
float mRangePlot[1024 * 3];
@@ -262,4 +288,4 @@ filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffse
} // namespace viewer
} // namespace filament
#endif // VIEWER_SIMPLEVIEWER_H
#endif // VIEWER_VIEWERGUI_H