Browse Source

Add support of custom per-instance data in batches.

Eugene Kozlov 9 years ago
parent
commit
c92894d250

+ 2 - 0
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -1861,6 +1861,8 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_minInstances(int)", asMETHOD(Renderer, SetMinInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_minInstances(int)", asMETHOD(Renderer, SetMinInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_minInstances() const", asMETHOD(Renderer, GetMinInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_minInstances() const", asMETHOD(Renderer, GetMinInstances), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_numExtraInstancingBufferElements(int)", asMETHOD(Renderer, SetNumExtraInstancingBufferElements), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "int get_numExtraInstancingBufferElements() const", asMETHOD(Renderer, GetNumExtraInstancingBufferElements), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxSortedInstances(int)", asMETHOD(Renderer, SetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxSortedInstances(int)", asMETHOD(Renderer, SetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxSortedInstances() const", asMETHOD(Renderer, GetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxSortedInstances() const", asMETHOD(Renderer, GetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);

+ 13 - 6
Source/Urho3D/Graphics/Batch.cpp

@@ -635,18 +635,25 @@ void Batch::Draw(View* view, Camera* camera, bool allowDepthWrite) const
     }
     }
 }
 }
 
 
-void BatchGroup::SetTransforms(void* lockedData, unsigned& freeIndex)
+void BatchGroup::SetInstancingData(void* lockedData, unsigned stride, unsigned& freeIndex)
 {
 {
     // Do not use up buffer space if not going to draw as instanced
     // Do not use up buffer space if not going to draw as instanced
     if (geometryType_ != GEOM_INSTANCED)
     if (geometryType_ != GEOM_INSTANCED)
         return;
         return;
 
 
     startIndex_ = freeIndex;
     startIndex_ = freeIndex;
-    Matrix3x4* dest = (Matrix3x4*)lockedData;
-    dest += freeIndex;
+    unsigned char* buffer = static_cast<unsigned char*>(lockedData) + startIndex_ * stride;
 
 
     for (unsigned i = 0; i < instances_.Size(); ++i)
     for (unsigned i = 0; i < instances_.Size(); ++i)
-        *dest++ = *instances_[i].worldTransform_;
+    {
+        const InstanceData& instance = instances_[i];
+
+        memcpy(buffer, instance.worldTransform_, sizeof(Matrix3x4));
+        if (instance.instancingData_)
+            memcpy(buffer + sizeof(Matrix3x4), instance.instancingData_, stride - sizeof(Matrix3x4));
+
+        buffer += stride;
+    }
 
 
     freeIndex += instances_.Size();
     freeIndex += instances_.Size();
 }
 }
@@ -825,10 +832,10 @@ void BatchQueue::SortFrontToBack2Pass(PODVector<Batch*>& batches)
 #endif
 #endif
 }
 }
 
 
-void BatchQueue::SetTransforms(void* lockedData, unsigned& freeIndex)
+void BatchQueue::SetInstancingData(void* lockedData, unsigned stride, unsigned& freeIndex)
 {
 {
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
-        i->second_.SetTransforms(lockedData, freeIndex);
+        i->second_.SetInstancingData(lockedData, stride, freeIndex);
 }
 }
 
 
 void BatchQueue::Draw(View* view, Camera* camera, bool markToStencil, bool usingLightOptimization, bool allowDepthWrite) const
 void BatchQueue::Draw(View* view, Camera* camera, bool markToStencil, bool usingLightOptimization, bool allowDepthWrite) const

+ 15 - 7
Source/Urho3D/Graphics/Batch.h

@@ -65,6 +65,7 @@ struct Batch
         material_(rhs.material_),
         material_(rhs.material_),
         worldTransform_(rhs.worldTransform_),
         worldTransform_(rhs.worldTransform_),
         numWorldTransforms_(rhs.numWorldTransforms_),
         numWorldTransforms_(rhs.numWorldTransforms_),
+        instancingData_(rhs.instancingData_),
         lightQueue_(0),
         lightQueue_(0),
         geometryType_(rhs.geometryType_)
         geometryType_(rhs.geometryType_)
     {
     {
@@ -76,6 +77,7 @@ struct Batch
     void Prepare(View* view, Camera* camera, bool setModelTransform, bool allowDepthWrite) const;
     void Prepare(View* view, Camera* camera, bool setModelTransform, bool allowDepthWrite) const;
     /// Prepare and draw.
     /// Prepare and draw.
     void Draw(View* view, Camera* camera, bool allowDepthWrite) const;
     void Draw(View* view, Camera* camera, bool allowDepthWrite) const;
+
     /// State sorting key.
     /// State sorting key.
     unsigned long long sortKey_;
     unsigned long long sortKey_;
     /// Distance from camera.
     /// Distance from camera.
@@ -84,7 +86,7 @@ struct Batch
     unsigned char renderOrder_;
     unsigned char renderOrder_;
     /// 8-bit light mask for stencil marking in deferred rendering.
     /// 8-bit light mask for stencil marking in deferred rendering.
     unsigned char lightMask_;
     unsigned char lightMask_;
-   /// Base batch flag. This tells to draw the object fully without light optimizations.
+    /// Base batch flag. This tells to draw the object fully without light optimizations.
     bool isBase_;
     bool isBase_;
     /// Geometry.
     /// Geometry.
     Geometry* geometry_;
     Geometry* geometry_;
@@ -94,6 +96,8 @@ struct Batch
     const Matrix3x4* worldTransform_;
     const Matrix3x4* worldTransform_;
     /// Number of world transforms.
     /// Number of world transforms.
     unsigned numWorldTransforms_;
     unsigned numWorldTransforms_;
+    /// Per-instance data. If not null, must contain enough data to fill instancing buffer.
+    void* instancingData_;
     /// Zone.
     /// Zone.
     Zone* zone_;
     Zone* zone_;
     /// Light properties.
     /// Light properties.
@@ -116,15 +120,18 @@ struct InstanceData
     {
     {
     }
     }
 
 
-    /// Construct with transform and distance.
-    InstanceData(const Matrix3x4* worldTransform, float distance) :
+    /// Construct with transform, instancing data and distance.
+    InstanceData(const Matrix3x4* worldTransform, const void* instancingData, float distance) :
         worldTransform_(worldTransform),
         worldTransform_(worldTransform),
+        instancingData_(instancingData),
         distance_(distance)
         distance_(distance)
     {
     {
     }
     }
 
 
     /// World transform.
     /// World transform.
     const Matrix3x4* worldTransform_;
     const Matrix3x4* worldTransform_;
+    /// Instancing data buffer.
+    const void* instancingData_;
     /// Distance from camera.
     /// Distance from camera.
     float distance_;
     float distance_;
 };
 };
@@ -155,6 +162,7 @@ struct BatchGroup : public Batch
     {
     {
         InstanceData newInstance;
         InstanceData newInstance;
         newInstance.distance_ = batch.distance_;
         newInstance.distance_ = batch.distance_;
+        newInstance.instancingData_ = batch.instancingData_;
 
 
         for (unsigned i = 0; i < batch.numWorldTransforms_; ++i)
         for (unsigned i = 0; i < batch.numWorldTransforms_; ++i)
         {
         {
@@ -163,8 +171,8 @@ struct BatchGroup : public Batch
         }
         }
     }
     }
 
 
-    /// Pre-set the instance transforms. Buffer must be big enough to hold all transforms.
-    void SetTransforms(void* lockedData, unsigned& freeIndex);
+    /// Pre-set the instance data. Buffer must be big enough to hold all data.
+    void SetInstancingData(void* lockedData, unsigned stride, unsigned& freeIndex);
     /// Prepare and draw.
     /// Prepare and draw.
     void Draw(View* view, Camera* camera, bool allowDepthWrite) const;
     void Draw(View* view, Camera* camera, bool allowDepthWrite) const;
 
 
@@ -236,8 +244,8 @@ public:
     void SortFrontToBack();
     void SortFrontToBack();
     /// Sort batches front to back while also maintaining state sorting.
     /// Sort batches front to back while also maintaining state sorting.
     void SortFrontToBack2Pass(PODVector<Batch*>& batches);
     void SortFrontToBack2Pass(PODVector<Batch*>& batches);
-    /// Pre-set instance transforms of all groups. The vertex buffer must be big enough to hold all transforms.
-    void SetTransforms(void* lockedData, unsigned& freeIndex);
+    /// Pre-set instance data of all groups. The vertex buffer must be big enough to hold all data.
+    void SetInstancingData(void* lockedData, unsigned stride, unsigned& freeIndex);
     /// Draw.
     /// Draw.
     void Draw(View* view, Camera* camera, bool markToStencil, bool usingLightOptimization, bool allowDepthWrite) const;
     void Draw(View* view, Camera* camera, bool markToStencil, bool usingLightOptimization, bool allowDepthWrite) const;
     /// Return the combined amount of instances.
     /// Return the combined amount of instances.

+ 2 - 0
Source/Urho3D/Graphics/Drawable.cpp

@@ -52,6 +52,7 @@ SourceBatch::SourceBatch() :
     geometry_(0),
     geometry_(0),
     worldTransform_(&Matrix3x4::IDENTITY),
     worldTransform_(&Matrix3x4::IDENTITY),
     numWorldTransforms_(1),
     numWorldTransforms_(1),
+    instancingData_((void*)0),
     geometryType_(GEOM_STATIC)
     geometryType_(GEOM_STATIC)
 {
 {
 }
 }
@@ -72,6 +73,7 @@ SourceBatch& SourceBatch::operator =(const SourceBatch& rhs)
     material_ = rhs.material_;
     material_ = rhs.material_;
     worldTransform_ = rhs.worldTransform_;
     worldTransform_ = rhs.worldTransform_;
     numWorldTransforms_ = rhs.numWorldTransforms_;
     numWorldTransforms_ = rhs.numWorldTransforms_;
+    instancingData_ = rhs.instancingData_;
     geometryType_ = rhs.geometryType_;
     geometryType_ = rhs.geometryType_;
 
 
     return *this;
     return *this;

+ 2 - 0
Source/Urho3D/Graphics/Drawable.h

@@ -97,6 +97,8 @@ struct URHO3D_API SourceBatch
     const Matrix3x4* worldTransform_;
     const Matrix3x4* worldTransform_;
     /// Number of world transforms.
     /// Number of world transforms.
     unsigned numWorldTransforms_;
     unsigned numWorldTransforms_;
+    /// Per-instance data. If not null, must contain enough data to fill instancing buffer.
+    void* instancingData_;
     /// %Geometry type.
     /// %Geometry type.
     GeometryType geometryType_;
     GeometryType geometryType_;
 };
 };

+ 29 - 5
Source/Urho3D/Graphics/Renderer.cpp

@@ -248,9 +248,21 @@ static const char* heightFogVariations[] =
     "HEIGHTFOG "
     "HEIGHTFOG "
 };
 };
 
 
-static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3;
 static const unsigned MAX_BUFFER_AGE = 1000;
 static const unsigned MAX_BUFFER_AGE = 1000;
 
 
+static const int MAX_EXTRA_INSTANCING_BUFFER_ELEMENTS = 4;
+
+inline PODVector<VertexElement> CreateInstancingBufferElements(unsigned numExtraElements)
+{
+    static const unsigned NUM_INSTANCEMATRIX_ELEMENTS = 3;
+    static const unsigned FIRST_UNUSED_TEXCOORD = 4;
+
+    PODVector<VertexElement> elements;
+    for (unsigned i = 0; i < NUM_INSTANCEMATRIX_ELEMENTS + numExtraElements; ++i)
+        elements.Push(VertexElement(TYPE_VECTOR4, SEM_TEXCOORD, FIRST_UNUSED_TEXCOORD + i, true));
+    return elements;
+}
+
 Renderer::Renderer(Context* context) :
 Renderer::Renderer(Context* context) :
     Object(context),
     Object(context),
     defaultZone_(new Zone(context)),
     defaultZone_(new Zone(context)),
@@ -281,6 +293,7 @@ Renderer::Renderer(Context* context) :
     drawShadows_(true),
     drawShadows_(true),
     reuseShadowMaps_(true),
     reuseShadowMaps_(true),
     dynamicInstancing_(true),
     dynamicInstancing_(true),
+    numExtraInstancingBufferElements_(0),
     threadedOcclusion_(false),
     threadedOcclusion_(false),
     shadersDirty_(true),
     shadersDirty_(true),
     initialized_(false),
     initialized_(false),
@@ -473,9 +486,18 @@ void Renderer::SetDynamicInstancing(bool enable)
     dynamicInstancing_ = enable;
     dynamicInstancing_ = enable;
 }
 }
 
 
+void Renderer::SetNumExtraInstancingBufferElements(int elements)
+{
+    if (numExtraInstancingBufferElements_ != elements)
+    {
+        numExtraInstancingBufferElements_ = Clamp(elements, 0, MAX_EXTRA_INSTANCING_BUFFER_ELEMENTS);
+        CreateInstancingBuffer();
+    }
+}
+
 void Renderer::SetMinInstances(int instances)
 void Renderer::SetMinInstances(int instances)
 {
 {
-    minInstances_ = Max(instances, 2);
+    minInstances_ = Max(instances, 1);
 }
 }
 
 
 void Renderer::SetMaxSortedInstances(int instances)
 void Renderer::SetMaxSortedInstances(int instances)
@@ -1327,11 +1349,12 @@ bool Renderer::ResizeInstancingBuffer(unsigned numInstances)
     while (newSize < numInstances)
     while (newSize < numInstances)
         newSize <<= 1;
         newSize <<= 1;
 
 
-    if (!instancingBuffer_->SetSize(newSize, INSTANCING_BUFFER_MASK, true))
+    const PODVector<VertexElement> instancingBufferElements = CreateInstancingBufferElements(numExtraInstancingBufferElements_);
+    if (!instancingBuffer_->SetSize(newSize, instancingBufferElements, true))
     {
     {
         URHO3D_LOGERROR("Failed to resize instancing buffer to " + String(newSize));
         URHO3D_LOGERROR("Failed to resize instancing buffer to " + String(newSize));
         // If failed, try to restore the old size
         // If failed, try to restore the old size
-        instancingBuffer_->SetSize(oldSize, INSTANCING_BUFFER_MASK, true);
+        instancingBuffer_->SetSize(oldSize, instancingBufferElements, true);
         return false;
         return false;
     }
     }
 
 
@@ -1850,7 +1873,8 @@ void Renderer::CreateInstancingBuffer()
     }
     }
 
 
     instancingBuffer_ = new VertexBuffer(context_);
     instancingBuffer_ = new VertexBuffer(context_);
-    if (!instancingBuffer_->SetSize(INSTANCING_BUFFER_DEFAULT_SIZE, INSTANCING_BUFFER_MASK, true))
+    const PODVector<VertexElement> instancingBufferElements = CreateInstancingBufferElements(numExtraInstancingBufferElements_);
+    if (!instancingBuffer_->SetSize(INSTANCING_BUFFER_DEFAULT_SIZE, instancingBufferElements, true))
     {
     {
         instancingBuffer_.Reset();
         instancingBuffer_.Reset();
         dynamicInstancing_ = false;
         dynamicInstancing_ = false;

+ 7 - 0
Source/Urho3D/Graphics/Renderer.h

@@ -220,6 +220,8 @@ public:
     void SetMaxShadowMaps(int shadowMaps);
     void SetMaxShadowMaps(int shadowMaps);
     /// Set dynamic instancing on/off. When on (default), drawables using the same static-type geometry and material will be automatically combined to an instanced draw call.
     /// Set dynamic instancing on/off. When on (default), drawables using the same static-type geometry and material will be automatically combined to an instanced draw call.
     void SetDynamicInstancing(bool enable);
     void SetDynamicInstancing(bool enable);
+    /// Set number of extra instancing buffer elements. Default is 0. Extra 4-vectors are available through TEXCOORD7 and further.
+    void SetNumExtraInstancingBufferElements(int elements);
     /// Set minimum number of instances required in a batch group to render as instanced.
     /// Set minimum number of instances required in a batch group to render as instanced.
     void SetMinInstances(int instances);
     void SetMinInstances(int instances);
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
@@ -296,6 +298,9 @@ public:
     /// Return whether dynamic instancing is in use.
     /// Return whether dynamic instancing is in use.
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
 
 
+    /// Return number of extra instancing buffer elements.
+    int GetNumExtraInstancingBufferElements() const { return numExtraInstancingBufferElements_; };
+
     /// Return minimum number of instances required in a batch group to render as instanced.
     /// Return minimum number of instances required in a batch group to render as instanced.
     int GetMinInstances() const { return minInstances_; }
     int GetMinInstances() const { return minInstances_; }
 
 
@@ -580,6 +585,8 @@ private:
     bool reuseShadowMaps_;
     bool reuseShadowMaps_;
     /// Dynamic instancing flag.
     /// Dynamic instancing flag.
     bool dynamicInstancing_;
     bool dynamicInstancing_;
+    /// Number of extra instancing data elements.
+    int numExtraInstancingBufferElements_;
     /// Threaded occlusion rendering flag.
     /// Threaded occlusion rendering flag.
     bool threadedOcclusion_;
     bool threadedOcclusion_;
     /// Shaders need reloading flag.
     /// Shaders need reloading flag.

+ 5 - 4
Source/Urho3D/Graphics/View.cpp

@@ -2898,15 +2898,16 @@ void View::PrepareInstancingBuffer()
     if (!dest)
     if (!dest)
         return;
         return;
 
 
+    const unsigned stride = instancingBuffer->GetVertexSize();
     for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
     for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
-        i->second_.SetTransforms(dest, freeIndex);
+        i->second_.SetInstancingData(dest, stride, freeIndex);
 
 
     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
     {
     {
         for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
         for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
-            i->shadowSplits_[j].shadowBatches_.SetTransforms(dest, freeIndex);
-        i->litBaseBatches_.SetTransforms(dest, freeIndex);
-        i->litBatches_.SetTransforms(dest, freeIndex);
+            i->shadowSplits_[j].shadowBatches_.SetInstancingData(dest, stride, freeIndex);
+        i->litBaseBatches_.SetInstancingData(dest, stride, freeIndex);
+        i->litBatches_.SetInstancingData(dest, stride, freeIndex);
     }
     }
 
 
     instancingBuffer->Unlock();
     instancingBuffer->Unlock();

+ 3 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg

@@ -21,6 +21,7 @@ class Renderer
     void SetReuseShadowMaps(bool enable);
     void SetReuseShadowMaps(bool enable);
     void SetMaxShadowMaps(int shadowMaps);
     void SetMaxShadowMaps(int shadowMaps);
     void SetDynamicInstancing(bool enable);
     void SetDynamicInstancing(bool enable);
+    void SetNumExtraInstancingBufferElements(int elements);
     void SetMinInstances(int instances);
     void SetMinInstances(int instances);
     void SetMaxSortedInstances(int instances);
     void SetMaxSortedInstances(int instances);
     void SetMaxOccluderTriangles(int triangles);
     void SetMaxOccluderTriangles(int triangles);
@@ -50,6 +51,7 @@ class Renderer
     bool GetReuseShadowMaps() const;
     bool GetReuseShadowMaps() const;
     int GetMaxShadowMaps() const;
     int GetMaxShadowMaps() const;
     bool GetDynamicInstancing() const;
     bool GetDynamicInstancing() const;
+    int GetNumExtraInstancingBufferElements() const;
     int GetMinInstances() const;
     int GetMinInstances() const;
     int GetMaxSortedInstances() const;
     int GetMaxSortedInstances() const;
     int GetMaxOccluderTriangles() const;
     int GetMaxOccluderTriangles() const;
@@ -89,6 +91,7 @@ class Renderer
     tolua_property__get_set bool reuseShadowMaps;
     tolua_property__get_set bool reuseShadowMaps;
     tolua_property__get_set int maxShadowMaps;
     tolua_property__get_set int maxShadowMaps;
     tolua_property__get_set bool dynamicInstancing;
     tolua_property__get_set bool dynamicInstancing;
+    tolua_property__get_set int numExtraInstancingBufferElements;
     tolua_property__get_set int minInstances;
     tolua_property__get_set int minInstances;
     tolua_property__get_set int maxSortedInstances;
     tolua_property__get_set int maxSortedInstances;
     tolua_property__get_set int maxOccluderTriangles;
     tolua_property__get_set int maxOccluderTriangles;

+ 1 - 1
Source/Urho3D/Math/StringHash.h

@@ -76,7 +76,7 @@ public:
         return *this;
         return *this;
     }
     }
 
 
-    // Test for equality with another hash.
+    /// Test for equality with another hash.
     bool operator ==(const StringHash& rhs) const { return value_ == rhs.value_; }
     bool operator ==(const StringHash& rhs) const { return value_ == rhs.value_; }
 
 
     /// Test for inequality with another hash.
     /// Test for inequality with another hash.