Просмотр исходного кода

Restored public locking API for vertex & index buffers. On OpenGL locking is always emulated using either shadow or scratch data.

Lasse Öörni 13 лет назад
Родитель
Сommit
153106519e

+ 29 - 23
Engine/Graphics/AnimatedModel.cpp

@@ -1093,43 +1093,49 @@ void AnimatedModel::UpdateMorphs()
                 
                 
                 if (!buffer->IsDataLost())
                 if (!buffer->IsDataLost())
                 {
                 {
-                    void* scratch = graphics->ReserveScratchBuffer(morphCount * vertexSize);
-                    // Reset morph range by copying data from the original vertex buffer
-                    memcpy(scratch, originalBuffer->GetShadowData() + morphStart * vertexSize, morphCount * vertexSize);
-                    
-                    for (unsigned j = 0; j < morphs_.Size(); ++j)
+                    void* dest = buffer->Lock(morphStart, morphCount);
+                    if (dest)
                     {
                     {
-                        if (morphs_[j].weight_ > 0.0f)
+                        // Reset morph range by copying data from the original vertex buffer
+                        memcpy(dest, originalBuffer->GetShadowData() + morphStart * vertexSize, morphCount * vertexSize);
+                        
+                        for (unsigned j = 0; j < morphs_.Size(); ++j)
                         {
                         {
-                            Map<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
-                            if (k != morphs_[j].buffers_.End())
-                                ApplyMorph(buffer, scratch, morphStart, k->second_, morphs_[j].weight_);
+                            if (morphs_[j].weight_ > 0.0f)
+                            {
+                                Map<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
+                                if (k != morphs_[j].buffers_.End())
+                                    ApplyMorph(buffer, dest, morphStart, k->second_, morphs_[j].weight_);
+                            }
                         }
                         }
+                        
+                        buffer->Unlock();
                     }
                     }
-                    
-                    buffer->SetDataRange(scratch, morphStart, morphCount);
-                    graphics->FreeScratchBuffer(scratch);
                 }
                 }
                 else
                 else
                 {
                 {
                     // Data is lost, need to copy whole original buffer
                     // Data is lost, need to copy whole original buffer
-                    void* scratch = graphics->ReserveScratchBuffer(buffer->GetVertexCount() * vertexSize);
-                    memcpy(scratch, originalBuffer->GetShadowData(), buffer->GetVertexCount() * vertexSize);
-                    
-                    void* morphScratch = (void*)(((unsigned char*)scratch) + morphStart * vertexSize);
-                    for (unsigned j = 0; j < morphs_.Size(); ++j)
+                    unsigned vertexCount = buffer->GetVertexCount();
+                    void* dest = buffer->Lock(0, vertexCount, true);
+                    if (dest)
                     {
                     {
-                        if (morphs_[j].weight_ > 0.0f)
+                        memcpy(dest, originalBuffer->GetShadowData(), vertexCount * vertexSize);
+                        
+                        dest = ((unsigned char*)dest) + morphStart * vertexSize;
+                        for (unsigned j = 0; j < morphs_.Size(); ++j)
                         {
                         {
-                            Map<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
-                            if (k != morphs_[j].buffers_.End())
-                                ApplyMorph(buffer, morphScratch, morphStart, k->second_, morphs_[j].weight_);
+                            if (morphs_[j].weight_ > 0.0f)
+                            {
+                                Map<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
+                                if (k != morphs_[j].buffers_.End())
+                                    ApplyMorph(buffer, dest, morphStart, k->second_, morphs_[j].weight_);
+                            }
                         }
                         }
+                        
+                        buffer->Unlock();
                     }
                     }
                     
                     
-                    buffer->SetData(scratch);
                     buffer->ClearDataLost();
                     buffer->ClearDataLost();
-                    graphics->FreeScratchBuffer(scratch);
                 }
                 }
             }
             }
         }
         }

+ 12 - 11
Engine/Graphics/Batch.cpp

@@ -644,17 +644,18 @@ void BatchGroup::Draw(Graphics* graphics, Renderer* renderer) const
                     instances = instanceBuffer->GetVertexCount();
                     instances = instanceBuffer->GetVertexCount();
                 
                 
                 // Copy the transforms
                 // Copy the transforms
-                void* scratch = graphics->ReserveScratchBuffer(instances * instanceBuffer->GetVertexSize());
-                Matrix3x4* dest = (Matrix3x4*)scratch;
-                for (unsigned i = 0; i < instances; ++i)
-                    dest[i] = *instances_[i + startIndex].worldTransform_;
-                instanceBuffer->SetDataRange(scratch, 0, instances, true);
-                graphics->FreeScratchBuffer(scratch);
-                
-                graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
-                graphics->SetVertexBuffers(vertexBuffers, elementMasks);
-                graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
-                    geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances);
+                Matrix3x4* dest = (Matrix3x4*)instanceBuffer->Lock(0, instances, true);
+                if (dest)
+                {
+                    for (unsigned i = 0; i < instances; ++i)
+                        dest[i] = *instances_[i + startIndex].worldTransform_;
+                    instanceBuffer->Unlock();
+                    
+                    graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
+                    graphics->SetVertexBuffers(vertexBuffers, elementMasks);
+                    graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
+                        geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances);
+                }
                 
                 
                 startIndex += instances;
                 startIndex += instances;
             }
             }

+ 10 - 9
Engine/Graphics/BillboardSet.cpp

@@ -353,9 +353,10 @@ void BillboardSet::UpdateBufferSize()
         return;
         return;
     
     
     // Indices do not change for a given billboard capacity
     // Indices do not change for a given billboard capacity
-    Graphics* graphics = indexBuffer_->GetGraphics();
-    void* scratch = graphics->ReserveScratchBuffer(numBillboards * 6 * sizeof(unsigned short));
-    unsigned short* dest = (unsigned short*)scratch;
+    unsigned short* dest = (unsigned short*)indexBuffer_->Lock(0, numBillboards * 6, true);
+    if (!dest)
+        return;
+    
     unsigned vertexIndex = 0;
     unsigned vertexIndex = 0;
     while (numBillboards--)
     while (numBillboards--)
     {
     {
@@ -364,8 +365,8 @@ void BillboardSet::UpdateBufferSize()
         
         
         vertexIndex += 4;
         vertexIndex += 4;
     }
     }
-    indexBuffer_->SetData(scratch);
-    graphics->FreeScratchBuffer(scratch);
+    
+    indexBuffer_->Unlock();
 }
 }
 
 
 void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
 void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
@@ -424,8 +425,9 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
         Sort(sortedBillboards_.Begin(), sortedBillboards_.End(), CompareBillboards);
         Sort(sortedBillboards_.Begin(), sortedBillboards_.End(), CompareBillboards);
     
     
     Graphics* graphics = vertexBuffer_->GetGraphics();
     Graphics* graphics = vertexBuffer_->GetGraphics();
-    void* scratch = graphics->ReserveScratchBuffer(enabledBillboards * 4 * vertexBuffer_->GetVertexSize());
-    float* dest = (float*)scratch;
+    float* dest = (float*)vertexBuffer_->Lock(0, enabledBillboards * 4, true);
+    if (!dest)
+        return;
     
     
     for (unsigned i = 0; i < enabledBillboards; ++i)
     for (unsigned i = 0; i < enabledBillboards; ++i)
     {
     {
@@ -466,8 +468,7 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
         *dest++ = -size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
         *dest++ = -size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
     }
     }
     
     
-    vertexBuffer_->SetDataRange(scratch, 0, enabledBillboards * 4, true);
-    graphics->FreeScratchBuffer(scratch);
+    vertexBuffer_->Unlock();
 }
 }
 
 
 void BillboardSet::MarkPositionsDirty()
 void BillboardSet::MarkPositionsDirty()

+ 4 - 4
Engine/Graphics/DebugRenderer.cpp

@@ -369,8 +369,9 @@ void DebugRenderer::Render()
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR, true);
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR, true);
     
     
-    void* scratch = graphics->ReserveScratchBuffer(numVertices * vertexBuffer_->GetVertexSize());
-    float* dest = (float*)scratch;
+    float* dest = (float*)vertexBuffer_->Lock(0, numVertices, true);
+    if (!dest)
+        return;
     
     
     for (unsigned i = 0; i < lines_.Size(); ++i)
     for (unsigned i = 0; i < lines_.Size(); ++i)
     {
     {
@@ -394,8 +395,7 @@ void DebugRenderer::Render()
         *((unsigned*)dest) = line.color_; dest++;
         *((unsigned*)dest) = line.color_; dest++;
     }
     }
     
     
-    vertexBuffer_->SetDataRange(scratch, 0, numVertices, true);
-    graphics->FreeScratchBuffer(scratch);
+    vertexBuffer_->Unlock();
     
     
     graphics->SetBlendMode(BLEND_REPLACE);
     graphics->SetBlendMode(BLEND_REPLACE);
     graphics->SetColorWrite(true);
     graphics->SetColorWrite(true);

+ 110 - 17
Engine/Graphics/Direct3D9/D3D9IndexBuffer.cpp

@@ -35,13 +35,18 @@ OBJECTTYPESTATIC(IndexBuffer);
 IndexBuffer::IndexBuffer(Context* context) :
 IndexBuffer::IndexBuffer(Context* context) :
     Object(context),
     Object(context),
     GPUObject(GetSubsystem<Graphics>()),
     GPUObject(GetSubsystem<Graphics>()),
-    pool_(D3DPOOL_MANAGED),
-    usage_(0),
     indexCount_(0),
     indexCount_(0),
     indexSize_(0),
     indexSize_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
     shadowed_(false),
     shadowed_(false),
     dataLost_(false)
     dataLost_(false)
 {
 {
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
 }
 }
 
 
 IndexBuffer::~IndexBuffer()
 IndexBuffer::~IndexBuffer()
@@ -70,6 +75,8 @@ void IndexBuffer::OnDeviceReset()
 
 
 void IndexBuffer::Release()
 void IndexBuffer::Release()
 {
 {
+    Unlock();
+    
     if (object_)
     if (object_)
     {
     {
         if (!graphics_)
         if (!graphics_)
@@ -91,7 +98,7 @@ void IndexBuffer::SetShadowed(bool enable)
     
     
     if (enable != shadowed_)
     if (enable != shadowed_)
     {
     {
-        if (enable && indexSize_ && indexCount_)
+        if (enable && indexCount_ && indexSize_)
             shadowData_ = new unsigned char[indexCount_ * indexSize_];
             shadowData_ = new unsigned char[indexCount_ * indexSize_];
         else
         else
             shadowData_.Reset();
             shadowData_.Reset();
@@ -102,6 +109,8 @@ void IndexBuffer::SetShadowed(bool enable)
 
 
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
 {
 {
+    Unlock();
+    
     if (dynamic)
     if (dynamic)
     {
     {
         pool_ = D3DPOOL_DEFAULT;
         pool_ = D3DPOOL_DEFAULT;
@@ -132,15 +141,22 @@ bool IndexBuffer::SetData(const void* data)
         return false;
         return false;
     }
     }
     
     
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
     if (object_)
     if (object_)
     {
     {
-        void* hwData = Lock(0, indexCount_, true);
+        void* hwData = MapBuffer(0, indexCount_, true);
         if (hwData)
         if (hwData)
         {
         {
             memcpy(hwData, data, indexCount_ * indexSize_);
             memcpy(hwData, data, indexCount_ * indexSize_);
-            Unlock();
+            UnmapBuffer();
         }
         }
     }
     }
+    
     if (shadowData_ && data != shadowData_.Get())
     if (shadowData_ && data != shadowData_.Get())
         memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
         memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
     
     
@@ -158,6 +174,12 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
         return false;
         return false;
     }
     }
     
     
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
     if (start + count > indexCount_)
     if (start + count > indexCount_)
     {
     {
         LOGERROR("Illegal range for setting new index buffer data");
         LOGERROR("Illegal range for setting new index buffer data");
@@ -169,11 +191,11 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
     
     
     if (object_)
     if (object_)
     {
     {
-        void* hwData = Lock(start, count, discard);
+        void* hwData = MapBuffer(start, count, discard);
         if (hwData)
         if (hwData)
         {
         {
             memcpy(hwData, data, count * indexSize_);
             memcpy(hwData, data, count * indexSize_);
-            Unlock();
+            UnmapBuffer();
         }
         }
     }
     }
     
     
@@ -183,12 +205,71 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
     return true;
     return true;
 }
 }
 
 
-bool IndexBuffer::UpdateToGPU()
+void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
 {
 {
-    if (object_ && shadowData_)
-        return SetData(shadowData_.Get());
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Index buffer already locked");
+        return 0;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not lock index buffer");
+        return 0;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for locking index buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * indexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * indexSize_);
+        return lockScratchData_;
+    }
     else
     else
-        return false;
+        return 0;
+}
+
+void IndexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * indexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
 }
 }
 
 
 void IndexBuffer::ClearDataLost()
 void IndexBuffer::ClearDataLost()
@@ -204,7 +285,10 @@ bool IndexBuffer::IsDynamic() const
 bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
 bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
 {
 {
     if (!shadowData_)
     if (!shadowData_)
+    {
+        LOGERROR("Used vertex range can only be queried from an index buffer with shadow data");
         return false;
         return false;
+    }
     
     
     minVertex = M_MAX_UNSIGNED;
     minVertex = M_MAX_UNSIGNED;
     unsigned maxVertex = 0;
     unsigned maxVertex = 0;
@@ -264,8 +348,15 @@ bool IndexBuffer::Create()
     return true;
     return true;
 }
 }
 
 
+bool IndexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
 
 
-void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
+void* IndexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
 {
 {
     void* hwData = 0;
     void* hwData = 0;
     
     
@@ -277,17 +368,19 @@ void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
             flags = D3DLOCK_DISCARD;
             flags = D3DLOCK_DISCARD;
         
         
         if (FAILED(((IDirect3DIndexBuffer9*)object_)->Lock(start * indexSize_, count * indexSize_, &hwData, flags)))
         if (FAILED(((IDirect3DIndexBuffer9*)object_)->Lock(start * indexSize_, count * indexSize_, &hwData, flags)))
-        {
             LOGERROR("Could not lock index buffer");
             LOGERROR("Could not lock index buffer");
-            return 0;
-        }
+        else
+            lockState_ = LOCK_HARDWARE;
     }
     }
     
     
     return hwData;
     return hwData;
 }
 }
 
 
-void IndexBuffer::Unlock()
+void IndexBuffer::UnmapBuffer()
 {
 {
-    if (object_)
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
         ((IDirect3DIndexBuffer9*)object_)->Unlock();
         ((IDirect3DIndexBuffer9*)object_)->Unlock();
+        lockState_ = LOCK_NONE;
+    }
 }
 }

+ 25 - 11
Engine/Graphics/Direct3D9/D3D9IndexBuffer.h

@@ -54,8 +54,10 @@ public:
     bool SetData(const void* data);
     bool SetData(const void* data);
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
-    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
-    bool UpdateToGPU();
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
     /// Clear data lost flag.
     /// Clear data lost flag.
     void ClearDataLost();
     void ClearDataLost();
     
     
@@ -63,8 +65,10 @@ public:
     bool IsShadowed() const { return shadowed_; }
     bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     /// Return whether is dynamic.
     bool IsDynamic() const;
     bool IsDynamic() const;
-    /// Return whether default pool data lost.
+    /// Return whether default pool data is lost.
     bool IsDataLost() const { return dataLost_; }
     bool IsDataLost() const { return dataLost_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
     /// Return number of indices.
     /// Return number of indices.
     unsigned GetIndexCount() const {return indexCount_; }
     unsigned GetIndexCount() const {return indexCount_; }
     /// Return index size.
     /// Return index size.
@@ -77,21 +81,31 @@ public:
 private:
 private:
     /// Create buffer.
     /// Create buffer.
     bool Create();
     bool Create();
-    /// Lock buffer.
-    void* Lock(unsigned start, unsigned count, bool discard);
-    /// Unlock buffer.
-    void Unlock();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
     
     
     /// Shadow data.
     /// Shadow data.
     SharedArrayPtr<unsigned char> shadowData_;
     SharedArrayPtr<unsigned char> shadowData_;
-    /// Memory pool.
-    unsigned pool_;
-    /// Usage type.
-    unsigned usage_;
     /// Number of indices.
     /// Number of indices.
     unsigned indexCount_;
     unsigned indexCount_;
     /// Index size.
     /// Index size.
     unsigned indexSize_;
     unsigned indexSize_;
+    /// Memory pool.
+    unsigned pool_;
+    /// Usage type.
+    unsigned usage_;
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
     /// Shadowed flag.
     /// Shadowed flag.
     bool shadowed_;
     bool shadowed_;
     /// Default pool data lost flag.
     /// Default pool data lost flag.

+ 107 - 17
Engine/Graphics/Direct3D9/D3D9VertexBuffer.cpp

@@ -68,10 +68,14 @@ OBJECTTYPESTATIC(VertexBuffer);
 VertexBuffer::VertexBuffer(Context* context) :
 VertexBuffer::VertexBuffer(Context* context) :
     Object(context),
     Object(context),
     GPUObject(GetSubsystem<Graphics>()),
     GPUObject(GetSubsystem<Graphics>()),
-    pool_(D3DPOOL_MANAGED),
-    usage_(0),
     vertexCount_(0),
     vertexCount_(0),
     elementMask_(0),
     elementMask_(0),
+    pool_(D3DPOOL_MANAGED),
+    usage_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
     shadowed_(false),
     shadowed_(false),
     dataLost_(false)
     dataLost_(false)
 {
 {
@@ -108,6 +112,8 @@ void VertexBuffer::OnDeviceReset()
 
 
 void VertexBuffer::Release()
 void VertexBuffer::Release()
 {
 {
+    Unlock();
+    
     if (object_)
     if (object_)
     {
     {
         if (!graphics_)
         if (!graphics_)
@@ -143,6 +149,8 @@ void VertexBuffer::SetShadowed(bool enable)
 
 
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
 {
 {
+    Unlock();
+    
     if (dynamic)
     if (dynamic)
     {
     {
         pool_ = D3DPOOL_DEFAULT;
         pool_ = D3DPOOL_DEFAULT;
@@ -175,15 +183,22 @@ bool VertexBuffer::SetData(const void* data)
         return false;
         return false;
     }
     }
     
     
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
     if (object_)
     if (object_)
     {
     {
-        void* hwData = Lock(0, vertexCount_, true);
+        void* hwData = MapBuffer(0, vertexCount_, true);
         if (hwData)
         if (hwData)
         {
         {
             memcpy(hwData, data, vertexCount_ * vertexSize_);
             memcpy(hwData, data, vertexCount_ * vertexSize_);
-            Unlock();
+            UnmapBuffer();
         }
         }
     }
     }
+    
     if (shadowData_ && data != shadowData_.Get())
     if (shadowData_ && data != shadowData_.Get())
         memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
         memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
     
     
@@ -201,9 +216,15 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
         return false;
         return false;
     }
     }
     
     
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
     if (start + count > vertexCount_)
     if (start + count > vertexCount_)
     {
     {
-        LOGERROR("Illegal range for setting new index buffer data");
+        LOGERROR("Illegal range for setting new vertex buffer data");
         return false;
         return false;
     }
     }
     
     
@@ -212,11 +233,11 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
     
     
     if (object_)
     if (object_)
     {
     {
-        void* hwData = Lock(start, count, discard);
+        void* hwData = MapBuffer(start, count, discard);
         if (hwData)
         if (hwData)
         {
         {
             memcpy(hwData, data, count * vertexSize_);
             memcpy(hwData, data, count * vertexSize_);
-            Unlock();
+            UnmapBuffer();
         }
         }
     }
     }
     
     
@@ -226,12 +247,71 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
     return true;
     return true;
 }
 }
 
 
-bool VertexBuffer::UpdateToGPU()
+void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
 {
 {
-    if (object_ && shadowData_)
-        return SetData(shadowData_.Get());
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Vertex buffer already locked");
+        return 0;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not lock vertex buffer");
+        return 0;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for locking vertex buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * vertexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * vertexSize_);
+        return lockScratchData_;
+    }
     else
     else
-        return false;
+        return 0;
+}
+
+void VertexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * vertexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
 }
 }
 
 
 void VertexBuffer::ClearDataLost()
 void VertexBuffer::ClearDataLost()
@@ -314,7 +394,15 @@ bool VertexBuffer::Create()
     return true;
     return true;
 }
 }
 
 
-void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
+bool VertexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
+
+void* VertexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
 {
 {
     void* hwData = 0;
     void* hwData = 0;
     
     
@@ -326,17 +414,19 @@ void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
             flags = D3DLOCK_DISCARD;
             flags = D3DLOCK_DISCARD;
         
         
         if (FAILED(((IDirect3DVertexBuffer9*)object_)->Lock(start * vertexSize_, count * vertexSize_, &hwData, flags)))
         if (FAILED(((IDirect3DVertexBuffer9*)object_)->Lock(start * vertexSize_, count * vertexSize_, &hwData, flags)))
-        {
             LOGERROR("Could not lock vertex buffer");
             LOGERROR("Could not lock vertex buffer");
-            return 0;
-        }
+        else
+            lockState_ = LOCK_HARDWARE;
     }
     }
     
     
     return hwData;
     return hwData;
 }
 }
 
 
-void VertexBuffer::Unlock()
+void VertexBuffer::UnmapBuffer()
 {
 {
-    if (object_)
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
         ((IDirect3DVertexBuffer9*)object_)->Unlock();
         ((IDirect3DVertexBuffer9*)object_)->Unlock();
+        lockState_ = LOCK_NONE;
+    }
 }
 }

+ 26 - 12
Engine/Graphics/Direct3D9/D3D9VertexBuffer.h

@@ -53,8 +53,10 @@ public:
     bool SetData(const void* data);
     bool SetData(const void* data);
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
-    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
-    bool UpdateToGPU();
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
     /// Clear data lost flag.
     /// Clear data lost flag.
     void ClearDataLost();
     void ClearDataLost();
     
     
@@ -62,8 +64,10 @@ public:
     bool IsShadowed() const { return shadowed_; }
     bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     /// Return whether is dynamic.
     bool IsDynamic() const;
     bool IsDynamic() const;
-    /// Return whether default pool data lost.
+    /// Return whether default pool data is lost.
     bool IsDataLost() const { return dataLost_; }
     bool IsDataLost() const { return dataLost_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
     /// Return number of vertices.
     /// Return number of vertices.
     unsigned GetVertexCount() const {return vertexCount_; }
     unsigned GetVertexCount() const {return vertexCount_; }
     /// Return vertex size.
     /// Return vertex size.
@@ -90,25 +94,35 @@ private:
     void UpdateOffsets();
     void UpdateOffsets();
     /// Create buffer.
     /// Create buffer.
     bool Create();
     bool Create();
-    /// Lock buffer.
-    void* Lock(unsigned start, unsigned count, bool discard);
-    /// Unlock buffer.
-    void Unlock();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
     
     
     /// Shadow data.
     /// Shadow data.
     SharedArrayPtr<unsigned char> shadowData_;
     SharedArrayPtr<unsigned char> shadowData_;
-    /// Memory pool.
-    unsigned pool_;
-    /// Usage type.
-    unsigned usage_;
     /// Number of vertices.
     /// Number of vertices.
     unsigned vertexCount_;
     unsigned vertexCount_;
     /// Vertex size.
     /// Vertex size.
     unsigned vertexSize_;
     unsigned vertexSize_;
     /// Vertex element bitmask.
     /// Vertex element bitmask.
     unsigned elementMask_;
     unsigned elementMask_;
-    /// Vertex element offsets.
+   /// Vertex element offsets.
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
+    /// Memory pool.
+    unsigned pool_;
+    /// Usage type.
+    unsigned usage_;
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
     /// Shadowed flag.
     /// Shadowed flag.
     bool shadowed_;
     bool shadowed_;
     /// Default pool data lost flag.
     /// Default pool data lost flag.

+ 9 - 0
Engine/Graphics/GraphicsDefs.h

@@ -96,6 +96,15 @@ enum StencilOp
     OP_DECR
     OP_DECR
 };
 };
 
 
+/// Vertex/index buffer lock state.
+enum LockState
+{
+    LOCK_NONE = 0,
+    LOCK_HARDWARE,
+    LOCK_SHADOW,
+    LOCK_SCRATCH
+};
+
 /// Vertex elements.
 /// Vertex elements.
 enum VertexElement
 enum VertexElement
 {
 {

+ 6 - 4
Engine/Graphics/Model.cpp

@@ -108,9 +108,10 @@ bool Model::Load(Deserializer& source)
         buffer->SetShadowed(true);
         buffer->SetShadowed(true);
         buffer->SetSize(vertexCount, elementMask);
         buffer->SetSize(vertexCount, elementMask);
         
         
+        void* dest = buffer->Lock(0, vertexCount);
         unsigned vertexSize = buffer->GetVertexSize();
         unsigned vertexSize = buffer->GetVertexSize();
-        source.Read(buffer->GetShadowData(), vertexCount * vertexSize);
-        buffer->UpdateToGPU();
+        source.Read(dest, vertexCount * vertexSize);
+        buffer->Unlock();
         
         
         memoryUse += sizeof(VertexBuffer) + vertexCount * vertexSize;
         memoryUse += sizeof(VertexBuffer) + vertexCount * vertexSize;
         vertexBuffers_.Push(buffer);
         vertexBuffers_.Push(buffer);
@@ -127,8 +128,9 @@ bool Model::Load(Deserializer& source)
         buffer->SetShadowed(true);
         buffer->SetShadowed(true);
         buffer->SetSize(indexCount, indexSize > sizeof(unsigned short));
         buffer->SetSize(indexCount, indexSize > sizeof(unsigned short));
         
         
-        source.Read(buffer->GetShadowData(), indexCount * indexSize);
-        buffer->UpdateToGPU();
+        void* dest = buffer->Lock(0, indexCount);
+        source.Read(dest, indexCount * indexSize);
+        buffer->Unlock();
         
         
         memoryUse += sizeof(IndexBuffer) + indexCount * indexSize;
         memoryUse += sizeof(IndexBuffer) + indexCount * indexSize;
         indexBuffers_.Push(buffer);
         indexBuffers_.Push(buffer);

+ 89 - 5
Engine/Graphics/OpenGL/OGLIndexBuffer.cpp

@@ -39,6 +39,11 @@ IndexBuffer::IndexBuffer(Context* context) :
     GPUObject(GetSubsystem<Graphics>()),
     GPUObject(GetSubsystem<Graphics>()),
     indexCount_(0),
     indexCount_(0),
     indexSize_(0),
     indexSize_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
+    shadowed_(false),
     dynamic_(false),
     dynamic_(false),
     dataLost_(false)
     dataLost_(false)
 {
 {
@@ -71,8 +76,13 @@ void IndexBuffer::OnDeviceReset()
 
 
 void IndexBuffer::Release()
 void IndexBuffer::Release()
 {
 {
-    if (object_ && graphics_)
+    Unlock();
+    
+    if (object_)
     {
     {
+        if (!graphics_)
+            return;
+        
         if (graphics_->GetIndexBuffer() == this)
         if (graphics_->GetIndexBuffer() == this)
             graphics_->SetIndexBuffer(0);
             graphics_->SetIndexBuffer(0);
         
         
@@ -100,6 +110,8 @@ void IndexBuffer::SetShadowed(bool enable)
 
 
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
 {
 {
+    Unlock();
+    
     dynamic_ = dynamic;
     dynamic_ = dynamic;
     indexCount_ = indexCount;
     indexCount_ = indexCount;
     indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
     indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
@@ -120,6 +132,12 @@ bool IndexBuffer::SetData(const void* data)
         return false;
         return false;
     }
     }
     
     
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
     if (object_)
     if (object_)
     {
     {
         graphics_->SetIndexBuffer(0);
         graphics_->SetIndexBuffer(0);
@@ -144,6 +162,12 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
         return false;
         return false;
     }
     }
     
     
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
     if (start + count > indexCount_)
     if (start + count > indexCount_)
     {
     {
         LOGERROR("Illegal range for setting new index buffer data");
         LOGERROR("Illegal range for setting new index buffer data");
@@ -169,12 +193,64 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
     return true;
     return true;
 }
 }
 
 
-bool IndexBuffer::UpdateToGPU()
+void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
 {
 {
-    if (object_ && shadowData_)
-        return SetData(shadowData_.Get());
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Index buffer already locked");
+        return 0;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not lock index buffer");
+        return 0;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for locking index buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * indexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * indexSize_);
+        return lockScratchData_;
+    }
     else
     else
-        return false;
+        return 0;
+}
+
+void IndexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * indexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
 }
 }
 
 
 void IndexBuffer::ClearDataLost()
 void IndexBuffer::ClearDataLost()
@@ -240,3 +316,11 @@ bool IndexBuffer::Create()
     
     
     return true;
     return true;
 }
 }
+
+bool IndexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}

+ 19 - 5
Engine/Graphics/OpenGL/OGLIndexBuffer.h

@@ -48,14 +48,16 @@ public:
     
     
     /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
     /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
     void SetShadowed(bool enable);
     void SetShadowed(bool enable);
-    /// Set buffer size and dynamic mode. Previous data will be lost.
+    /// %Set buffer size and dynamic mode. Previous data will be lost.
     bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
     bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
-    /// Set all data in the buffer.
+    /// %Set all data in the buffer.
     bool SetData(const void* data);
     bool SetData(const void* data);
-    /// Set a data range in the buffer. Optionally discard data outside the range.
+    /// %Set a data range in the buffer. Optionally discard data outside the range.
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
-    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
-    bool UpdateToGPU();
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
     /// Clear data lost flag.
     /// Clear data lost flag.
     void ClearDataLost();
     void ClearDataLost();
     
     
@@ -65,6 +67,8 @@ public:
     bool IsDynamic() const { return dynamic_; }
     bool IsDynamic() const { return dynamic_; }
     /// Return whether data is lost due to context loss.
     /// Return whether data is lost due to context loss.
     bool IsDataLost() const { return false; }
     bool IsDataLost() const { return false; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
     /// Return number of indices.
     /// Return number of indices.
     unsigned GetIndexCount() const {return indexCount_; }
     unsigned GetIndexCount() const {return indexCount_; }
     /// Return index size.
     /// Return index size.
@@ -77,6 +81,8 @@ public:
 private:
 private:
     /// Create buffer.
     /// Create buffer.
     bool Create();
     bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
     
     
     /// Shadow data.
     /// Shadow data.
     SharedArrayPtr<unsigned char> shadowData_;
     SharedArrayPtr<unsigned char> shadowData_;
@@ -84,6 +90,14 @@ private:
     unsigned indexCount_;
     unsigned indexCount_;
     /// Index size.
     /// Index size.
     unsigned indexSize_;
     unsigned indexSize_;
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
     /// Shadowed flag.
     /// Shadowed flag.
     bool shadowed_;
     bool shadowed_;
     /// Dynamic flag.
     /// Dynamic flag.

+ 89 - 6
Engine/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -123,6 +123,10 @@ VertexBuffer::VertexBuffer(Context* context) :
     GPUObject(GetSubsystem<Graphics>()),
     GPUObject(GetSubsystem<Graphics>()),
     vertexCount_(0),
     vertexCount_(0),
     elementMask_(0),
     elementMask_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
     shadowed_(false),
     shadowed_(false),
     dynamic_(false),
     dynamic_(false),
     dataLost_(false)
     dataLost_(false)
@@ -158,8 +162,13 @@ void VertexBuffer::OnDeviceReset()
 
 
 void VertexBuffer::Release()
 void VertexBuffer::Release()
 {
 {
-    if (object_ && graphics_)
+    Unlock();
+    
+    if (object_)
     {
     {
+        if (!graphics_)
+            return;
+        
         for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
         for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
         {
         {
             if (graphics_->GetVertexBuffer(i) == this)
             if (graphics_->GetVertexBuffer(i) == this)
@@ -190,6 +199,8 @@ void VertexBuffer::SetShadowed(bool enable)
 
 
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
 {
 {
+    Unlock();
+    
     dynamic_ = dynamic;
     dynamic_ = dynamic;
     vertexCount_ = vertexCount;
     vertexCount_ = vertexCount;
     elementMask_ = elementMask;
     elementMask_ = elementMask;
@@ -212,6 +223,12 @@ bool VertexBuffer::SetData(const void* data)
         return false;
         return false;
     }
     }
     
     
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
     if (object_)
     if (object_)
     {
     {
         glBindBuffer(GL_ARRAY_BUFFER, object_);
         glBindBuffer(GL_ARRAY_BUFFER, object_);
@@ -235,9 +252,15 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
         return false;
         return false;
     }
     }
     
     
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
     if (start + count > vertexCount_)
     if (start + count > vertexCount_)
     {
     {
-        LOGERROR("Illegal range for setting new index buffer data");
+        LOGERROR("Illegal range for setting new vertex buffer data");
         return false;
         return false;
     }
     }
     
     
@@ -259,12 +282,64 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
     return true;
     return true;
 }
 }
 
 
-bool VertexBuffer::UpdateToGPU()
+void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
 {
 {
-    if (object_ && shadowData_)
-        return SetData(shadowData_.Get());
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Vertex buffer already locked");
+        return 0;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not lock vertex buffer");
+        return 0;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for locking vertex buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * vertexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * vertexSize_);
+        return lockScratchData_;
+    }
     else
     else
-        return false;
+        return 0;
+}
+
+void VertexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * vertexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
 }
 }
 
 
 void VertexBuffer::ClearDataLost()
 void VertexBuffer::ClearDataLost()
@@ -320,3 +395,11 @@ bool VertexBuffer::Create()
     
     
     return true;
     return true;
 }
 }
+
+bool VertexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}

+ 16 - 2
Engine/Graphics/OpenGL/OGLVertexBuffer.h

@@ -53,8 +53,10 @@ public:
     bool SetData(const void* data);
     bool SetData(const void* data);
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     /// %Set a data range in the buffer. Optionally discard data outside the range.
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
     bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
-    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
-    bool UpdateToGPU();
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
     /// Clear data lost flag.
     /// Clear data lost flag.
     void ClearDataLost();
     void ClearDataLost();
     
     
@@ -64,6 +66,8 @@ public:
     bool IsDynamic() const { return dynamic_; }
     bool IsDynamic() const { return dynamic_; }
     /// Return whether data is lost due to context loss.
     /// Return whether data is lost due to context loss.
     bool IsDataLost() const { return false; }
     bool IsDataLost() const { return false; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
     /// Return number of vertices.
     /// Return number of vertices.
     unsigned GetVertexCount() const {return vertexCount_; }
     unsigned GetVertexCount() const {return vertexCount_; }
     /// Return vertex size.
     /// Return vertex size.
@@ -93,6 +97,8 @@ private:
     void UpdateOffsets();
     void UpdateOffsets();
     /// Create buffer.
     /// Create buffer.
     bool Create();
     bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
     
     
     /// Shadow data.
     /// Shadow data.
     SharedArrayPtr<unsigned char> shadowData_;
     SharedArrayPtr<unsigned char> shadowData_;
@@ -104,6 +110,14 @@ private:
     unsigned elementMask_;
     unsigned elementMask_;
     /// Vertex element offsets.
     /// Vertex element offsets.
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
     /// Shadowed flag.
     /// Shadowed flag.
     bool shadowed_;
     bool shadowed_;
     /// Dynamic flag.
     /// Dynamic flag.

+ 9 - 8
Engine/Graphics/View.cpp

@@ -2397,22 +2397,23 @@ void View::PrepareInstancingBuffer()
     {
     {
         VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
         VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
         unsigned freeIndex = 0;
         unsigned freeIndex = 0;
-        void* scratch = graphics_->ReserveScratchBuffer(totalInstances * instancingBuffer->GetVertexSize());
+        void* dest = instancingBuffer->Lock(0, totalInstances, true);
+        if (!dest)
+            return;
         
         
-        baseQueue_.SetTransforms(renderer_, scratch, freeIndex);
-        preAlphaQueue_.SetTransforms(renderer_, scratch, freeIndex);
+        baseQueue_.SetTransforms(renderer_, dest, freeIndex);
+        preAlphaQueue_.SetTransforms(renderer_, dest, freeIndex);
         if (renderMode_ != RENDER_FORWARD)
         if (renderMode_ != RENDER_FORWARD)
-            gbufferQueue_.SetTransforms(renderer_, scratch, freeIndex);
+            gbufferQueue_.SetTransforms(renderer_, dest, 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(renderer_, scratch, freeIndex);
-            i->litBatches_.SetTransforms(renderer_, scratch, freeIndex);
+                i->shadowSplits_[j].shadowBatches_.SetTransforms(renderer_, dest, freeIndex);
+            i->litBatches_.SetTransforms(renderer_, dest, freeIndex);
         }
         }
         
         
-        instancingBuffer->SetDataRange(scratch, 0, totalInstances, true);
-        graphics_->FreeScratchBuffer(scratch);
+        instancingBuffer->Unlock();
     }
     }
 }
 }
 
 

+ 5 - 4
Engine/UI/UI.cpp

@@ -259,13 +259,14 @@ void UI::Render()
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
     
     
     unsigned vertexSize = vertexBuffer_->GetVertexSize();
     unsigned vertexSize = vertexBuffer_->GetVertexSize();
-    void* scratch = graphics_->ReserveScratchBuffer(numVertices * vertexSize);
+    void* dest = vertexBuffer_->Lock(0, numVertices, true);
+    if (!dest)
+        return;
     
     
     for (unsigned i = 0; i < batches_.Size(); ++i)
     for (unsigned i = 0; i < batches_.Size(); ++i)
-        batches_[i].UpdateGeometry(graphics_, ((unsigned char*)scratch) + batches_[i].quadStart_ * vertexSize * 6);
+        batches_[i].UpdateGeometry(graphics_, ((unsigned char*)dest) + batches_[i].quadStart_ * vertexSize * 6);
     
     
-    vertexBuffer_->SetDataRange(scratch, 0, numVertices, true);
-    graphics_->FreeScratchBuffer(scratch);
+    vertexBuffer_->Unlock();
     
     
     Vector2 scale(2.0f, -2.0f);
     Vector2 scale(2.0f, -2.0f);
     Vector2 offset(-1.0f, 1.0f);
     Vector2 offset(-1.0f, 1.0f);