Add basic support for instanced renderables

We can now specify an "instance count" per renderable during creation. 
This instance count applies to all render primitives in the 
Renderable.

This will simply draw the renderable "instance count" times. A new
method available to vertex shaders, getInstanceIndex(), allows the
material to discriminate the instances and adjust the position/transform
accordingly.

Typically this is used for particle systems.
This commit is contained in:
Mathias Agopian
2022-02-15 13:11:23 -08:00
committed by Mathias Agopian
parent d13ccd1c26
commit 1d6aeb5de1
10 changed files with 76 additions and 9 deletions
@@ -221,6 +221,13 @@ Java_com_google_android_filament_RenderableManager_nBuilderLightChannel(JNIEnv*,
builder->lightChannel(channel, (bool)enable);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderInstances(JNIEnv*, jclass,
jlong nativeBuilder, jint instanceCount) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
builder->instances(instanceCount);
}
// ------------------------------------------------------------------------------------------------
extern "C" JNIEXPORT void JNICALL
@@ -284,6 +284,22 @@ public class RenderableManager {
return this;
}
/**
* 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
* possibly adjust the position or transform.
*
* @param instanceCount the number of instances silently clamped between 1 and 65535.
*/
@NonNull
public Builder instances(@IntRange(from = 1, to = 65535) int instanceCount) {
nBuilderInstances(mNativeBuilder, instanceCount);
return this;
}
/**
* Controls if this renderable casts shadows, false by default.
*
@@ -811,6 +827,7 @@ public class RenderableManager {
private static native void nBuilderMorphing(long nativeBuilder, int targetCount);
private static native void nEnableSkinningBuffers(long nativeBuilder, boolean enabled);
private static native void nBuilderLightChannel(long nativeRenderableManager, int channel, boolean enable);
private static native void nBuilderInstances(long nativeRenderableManager, int instances);
private static native void nSetSkinningBuffer(long nativeObject, int i, long nativeSkinningBuffer, int count, int offset);
private static native int nSetBonesAsMatrices(long nativeObject, int i, Buffer matrices, int remaining, int boneCount, int offset);
@@ -328,6 +328,18 @@ public:
*/
Builder& blendOrder(size_t primitiveIndex, uint16_t order) 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
* possibly adjust the position or transform.
*
* @param instanceCount the number of instances silently clamped between 1 and 65535.
*/
Builder& instances(size_t instanceCount) noexcept;
/**
* Adds the Renderable component to an entity.
*
+4 -1
View File
@@ -291,6 +291,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
auto const* const UTILS_RESTRICT soaPrimitives = soa.data<FScene::PRIMITIVES>();
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
auto const* const UTILS_RESTRICT soaVisibilityMask = soa.data<FScene::VISIBLE_MASK>();
auto const* const UTILS_RESTRICT soaInstanceCount = soa.data<FScene::INSTANCE_COUNT>();
const bool hasShadowing = renderFlags & HAS_SHADOWING;
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
@@ -357,6 +358,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
cmdColor.primitive.index = (uint16_t)i;
cmdColor.primitive.instanceCount = soaInstanceCount[i];
// if we are already a SSR variant, the SRE bit is already set,
// there is no harm setting it again
@@ -371,6 +373,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdDepth.key |= makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
cmdDepth.key |= makeField(distanceBits, DISTANCE_BITS_MASK, DISTANCE_BITS_SHIFT);
cmdDepth.primitive.index = (uint16_t)i;
cmdDepth.primitive.instanceCount = soaInstanceCount[i];
cmdDepth.primitive.materialVariant.setSkinning(
soaVisibility[i].skinning || soaVisibility[i].morphing);
cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
@@ -621,7 +624,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine,
}
}
driver.draw(pipeline, info.primitiveHandle, 1);
driver.draw(pipeline, info.primitiveHandle, info.instanceCount);
}
}
}
+2 -1
View File
@@ -220,8 +220,9 @@ public:
backend::Handle<backend::HwSamplerGroup> morphTargetBuffer; // 4 bytes
backend::RasterState rasterState; // 4 bytes
uint16_t index = 0; // 2 bytes
uint16_t instanceCount; // 2 bytes
Variant materialVariant; // 1 byte
uint8_t reserved[13 - sizeof(void*)] = {}; // 5 bytes (9)
uint8_t reserved[11 - sizeof(void*)] = {}; // 3 bytes (7)
};
static_assert(sizeof(PrimitiveInfo) == 32);
+1
View File
@@ -142,6 +142,7 @@ void FScene::prepare(const mat4& worldOriginTransform, bool shadowReceiversAreCa
worldAABB.center, // WORLD_AABB_CENTER
0, // VISIBLE_MASK
rcm.getChannels(ri), // CHANNELS
rcm.getInstanceCount(ri), // INSTANCE_COUNT
rcm.getLayerMask(ri), // LAYERS
worldAABB.halfExtent, // WORLD_AABB_EXTENT
{}, // PRIMITIVES
@@ -49,6 +49,7 @@ struct RenderableManager::BuilderDetails {
uint8_t mLayerMask = 0x1;
uint8_t mPriority = 0x4;
uint8_t mChannels = 1;
uint16_t mInstanceCount = 1;
bool mCulling : 1;
bool mCastShadows : 1;
bool mReceiveShadows : 1;
@@ -275,6 +276,11 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
return Success;
}
RenderableManager::Builder& RenderableManager::Builder::instances(size_t instanceCount) noexcept {
mImpl->mInstanceCount = clamp((unsigned int)instanceCount, 1u, 65535u);
return *this;
}
// ------------------------------------------------------------------------------------------------
FRenderableManager::FRenderableManager(FEngine& engine) noexcept : mEngine(engine) {
@@ -319,6 +325,7 @@ void FRenderableManager::create(
setSkinning(ci, false);
setMorphing(ci, builder->mMorphTargetCount);
mManager[ci].channels = builder->mChannels;
mManager[ci].instanceCount = builder->mInstanceCount;
const uint32_t boneCount = builder->mSkinningBoneCount;
const uint32_t targetCount = builder->mMorphTargetCount;
+15 -7
View File
@@ -129,6 +129,7 @@ public:
inline uint8_t getLayerMask(Instance instance) const noexcept;
inline uint8_t getPriority(Instance instance) const noexcept;
inline uint8_t getChannels(Instance instance) const noexcept;
inline uint16_t getInstanceCount(Instance instance) const noexcept;
struct SkinningBindingInfo {
backend::Handle<backend::HwBufferObject> handle;
@@ -187,6 +188,7 @@ private:
LAYERS, // user data
MORPH_WEIGHTS, // filament data, UBO storing a pointer to the morph weights information
CHANNELS, // user data
INSTANCE_COUNT, // user data
VISIBILITY, // user data
PRIMITIVES, // user data
BONES, // filament data, UBO storing a pointer to the bones information
@@ -197,6 +199,7 @@ private:
uint8_t, // LAYERS
MorphWeights, // MORPH_WEIGHTS
uint8_t, // CHANNELS
uint16_t, // INSTANCE_COUNT
Visibility, // VISIBILITY
utils::Slice<FRenderPrimitive>, // PRIMITIVES
Bones // BONES
@@ -214,13 +217,14 @@ private:
union {
// this specific usage of union is permitted. All fields are identical
Field<AABB> aabb;
Field<LAYERS> layers;
Field<MORPH_WEIGHTS> morphWeights;
Field<CHANNELS> channels;
Field<VISIBILITY> visibility;
Field<PRIMITIVES> primitives;
Field<BONES> bones;
Field<AABB> aabb;
Field<LAYERS> layers;
Field<MORPH_WEIGHTS> morphWeights;
Field<CHANNELS> channels;
Field<INSTANCE_COUNT> instanceCount;
Field<VISIBILITY> visibility;
Field<PRIMITIVES> primitives;
Field<BONES> bones;
};
};
@@ -343,6 +347,10 @@ uint8_t FRenderableManager::getChannels(Instance instance) const noexcept {
return mManager[instance].channels;
}
uint16_t FRenderableManager::getInstanceCount(Instance instance) const noexcept {
return mManager[instance].instanceCount;
}
Box const& FRenderableManager::getAABB(Instance instance) const noexcept {
return mManager[instance].aabb;
}
+2
View File
@@ -111,6 +111,7 @@ public:
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass
CHANNELS, // 1 | currently light channels only
INSTANCE_COUNT, // 2 | draw instance count
// These are not needed anymore after culling
LAYERS, // 1 | layers
@@ -133,6 +134,7 @@ public:
math::float3, // WORLD_AABB_CENTER
VisibleMaskType, // VISIBLE_MASK
uint8_t, // CHANNELS
uint16_t, // INSTANCE_COUNT
uint8_t, // LAYERS
math::float3, // WORLD_AABB_EXTENT
utils::Slice<FRenderPrimitive>, // PRIMITIVES
+9
View File
@@ -29,6 +29,15 @@ int getVertexIndex() {
#endif
}
/** @public-api */
int getInstanceIndex() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
return gl_InstanceIndex;
#else
return gl_InstanceID;
#endif
}
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
vec3 mulBoneNormal(vec3 n, uint i) {