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:
committed by
Mathias Agopian
parent
d13ccd1c26
commit
1d6aeb5de1
@@ -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
|
||||
|
||||
+17
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user