2
0
Эх сурвалжийг харах

Vertex & index buffer refactoring to unify feature set between graphics APIs, cleanup code, and prepare for Android OpenGL context loss handling.

Lasse Öörni 13 жил өмнө
parent
commit
a4c1924e04
43 өөрчлөгдсөн 888 нэмэгдсэн , 942 устгасан
  1. 20 21
      Engine/Graphics/AnimatedModel.cpp
  2. 1 1
      Engine/Graphics/AnimatedModel.h
  3. 6 12
      Engine/Graphics/Batch.cpp
  4. 16 15
      Engine/Graphics/BillboardSet.cpp
  5. 4 5
      Engine/Graphics/DebugRenderer.cpp
  6. 55 0
      Engine/Graphics/Direct3D9/D3D9Graphics.cpp
  7. 24 0
      Engine/Graphics/Direct3D9/D3D9Graphics.h
  8. 99 65
      Engine/Graphics/Direct3D9/D3D9IndexBuffer.cpp
  9. 19 11
      Engine/Graphics/Direct3D9/D3D9IndexBuffer.h
  10. 8 5
      Engine/Graphics/Direct3D9/D3D9Texture2D.cpp
  11. 8 5
      Engine/Graphics/Direct3D9/D3D9TextureCube.cpp
  12. 96 100
      Engine/Graphics/Direct3D9/D3D9VertexBuffer.cpp
  13. 18 32
      Engine/Graphics/Direct3D9/D3D9VertexBuffer.h
  14. 20 14
      Engine/Graphics/Geometry.cpp
  15. 0 10
      Engine/Graphics/Geometry.h
  16. 0 9
      Engine/Graphics/GraphicsDefs.h
  17. 94 87
      Engine/Graphics/Model.cpp
  18. 12 0
      Engine/Graphics/Model.h
  19. 1 1
      Engine/Graphics/OpenGL/OGLGPUObject.cpp
  20. 2 2
      Engine/Graphics/OpenGL/OGLGPUObject.h
  21. 54 37
      Engine/Graphics/OpenGL/OGLGraphics.cpp
  22. 10 10
      Engine/Graphics/OpenGL/OGLGraphics.h
  23. 61 138
      Engine/Graphics/OpenGL/OGLIndexBuffer.cpp
  24. 21 23
      Engine/Graphics/OpenGL/OGLIndexBuffer.h
  25. 21 0
      Engine/Graphics/OpenGL/OGLRenderSurface.cpp
  26. 2 0
      Engine/Graphics/OpenGL/OGLRenderSurface.h
  27. 11 0
      Engine/Graphics/OpenGL/OGLShaderProgram.cpp
  28. 2 1
      Engine/Graphics/OpenGL/OGLShaderProgram.h
  29. 11 0
      Engine/Graphics/OpenGL/OGLShaderVariation.cpp
  30. 2 0
      Engine/Graphics/OpenGL/OGLShaderVariation.h
  31. 1 0
      Engine/Graphics/OpenGL/OGLTexture.cpp
  32. 4 4
      Engine/Graphics/OpenGL/OGLTexture.h
  33. 10 28
      Engine/Graphics/OpenGL/OGLTexture2D.cpp
  34. 2 2
      Engine/Graphics/OpenGL/OGLTexture2D.h
  35. 11 31
      Engine/Graphics/OpenGL/OGLTextureCube.cpp
  36. 2 2
      Engine/Graphics/OpenGL/OGLTextureCube.h
  37. 57 169
      Engine/Graphics/OpenGL/OGLVertexBuffer.cpp
  38. 22 47
      Engine/Graphics/OpenGL/OGLVertexBuffer.h
  39. 52 34
      Engine/Graphics/Renderer.cpp
  40. 2 0
      Engine/Graphics/Renderer.h
  41. 14 15
      Engine/Graphics/View.cpp
  42. 9 1
      Engine/UI/Font.cpp
  43. 4 5
      Engine/UI/UI.cpp

+ 20 - 21
Engine/Graphics/AnimatedModel.cpp

@@ -909,24 +909,17 @@ void AnimatedModel::CloneGeometries()
     for (unsigned i = 0; i < originalVertexBuffers.Size(); ++i)
     {
         VertexBuffer* original = originalVertexBuffers[i];
-        if (original->HasMorphRange())
+        if (model_->GetMorphRangeCount(i))
         {
             SharedPtr<VertexBuffer> clone(new VertexBuffer(context_));
             clone->SetSize(original->GetVertexCount(), original->GetElementMask(), true);
-            /// \todo Will not work on OpenGL ES
-            void* originalData = original->Lock(0, original->GetVertexCount(), LOCK_READONLY);
-            if (originalData)
-            {
-                clone->SetData(originalData);
-                original->Unlock();
-            }
-            
-            clone->SetMorphRange(original->GetMorphRangeStart(), original->GetMorphRangeCount());
-            clone->SetMorphRangeResetData(original->GetMorphRangeResetData());
+            clone->SetData(original->GetShadowData());
             
             clonedVertexBuffers[original] = clone;
             morphVertexBuffers_[i] = clone;
         }
+        else
+            morphVertexBuffers_[i].Reset();
     }
     
     // Geometries will always be cloned fully. They contain only references to buffer, so they are relatively light
@@ -952,7 +945,6 @@ void AnimatedModel::CloneGeometries()
             clone->SetIndexBuffer(original->GetIndexBuffer());
             clone->SetDrawRange(original->GetPrimitiveType(), original->GetIndexStart(), original->GetIndexCount());
             clone->SetLodDistance(original->GetLodDistance());
-            clone->SetRawData(original->GetRawVertexData(), original->GetRawIndexData());
             
             geometries_[i][j] = clone;
         }
@@ -1069,6 +1061,10 @@ void AnimatedModel::UpdateSkinning()
 
 void AnimatedModel::UpdateMorphs()
 {
+    Graphics* graphics = GetSubsystem<Graphics>();
+    if (!graphics)
+        return;
+    
     if (morphs_.Size())
     {
         // Reset the morph data range from all morphable vertex buffers, then apply morphs
@@ -1077,10 +1073,13 @@ void AnimatedModel::UpdateMorphs()
             VertexBuffer* buffer = morphVertexBuffers_[i];
             if (buffer)
             {
-                void* lockedMorphRange = buffer->LockMorphRange();
-                if (!lockedMorphRange)
-                    continue;
-                buffer->ResetMorphRange(lockedMorphRange);
+                VertexBuffer* originalBuffer = model_->GetVertexBuffers()[i];
+                unsigned morphStart = model_->GetMorphRangeStart(i);
+                unsigned morphCount = model_->GetMorphRangeCount(i);
+                unsigned vertexSize = buffer->GetVertexSize();
+                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)
                 {
@@ -1088,11 +1087,12 @@ void AnimatedModel::UpdateMorphs()
                     {
                         Map<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
                         if (k != morphs_[j].buffers_.End())
-                            ApplyMorph(buffer, lockedMorphRange, k->second_, morphs_[j].weight_);
+                            ApplyMorph(buffer, scratch, morphStart, k->second_, morphs_[j].weight_);
                     }
                 }
                 
-                buffer->Unlock();
+                buffer->SetDataRange(scratch, morphStart, morphCount);
+                graphics->FreeScratchBuffer(scratch);
             }
         }
     }
@@ -1100,17 +1100,16 @@ void AnimatedModel::UpdateMorphs()
     morphsDirty_ = false;
 }
 
-void AnimatedModel::ApplyMorph(VertexBuffer* buffer, void* lockedMorphRange, const VertexBufferMorph& morph, float weight)
+void AnimatedModel::ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsigned morphRangeStart, const VertexBufferMorph& morph, float weight)
 {
     unsigned elementMask = morph.elementMask_;
     unsigned vertexCount = morph.vertexCount_;
     unsigned normalOffset = buffer->GetElementOffset(ELEMENT_NORMAL);
     unsigned tangentOffset = buffer->GetElementOffset(ELEMENT_TANGENT);
-    unsigned morphRangeStart = buffer->GetMorphRangeStart();
     unsigned vertexSize = buffer->GetVertexSize();
     
     unsigned char* srcData = morph.morphData_;
-    unsigned char* destData = (unsigned char*)lockedMorphRange;
+    unsigned char* destData = (unsigned char*)destVertexData;
     
     while (vertexCount--)
     {

+ 1 - 1
Engine/Graphics/AnimatedModel.h

@@ -171,7 +171,7 @@ private:
     /// Reapply all vertex morphs.
     void UpdateMorphs();
     /// Apply a vertex morph.
-    void ApplyMorph(VertexBuffer* buffer, void* lockedMorphRange, const VertexBufferMorph& morph, float weight);
+    void ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsigned morphRangeStart, const VertexBufferMorph& morph, float weight);
     /// Handle model reload finished.
     void HandleModelReloadFinished(StringHash eventType, VariantMap& eventData);
     

+ 6 - 12
Engine/Graphics/Batch.cpp

@@ -633,7 +633,7 @@ void BatchGroup::Draw(Graphics* graphics, Renderer* renderer) const
         vertexBuffers.Push(SharedPtr<VertexBuffer>(instanceBuffer));
         elementMasks.Push(instanceBuffer->GetElementMask());
         
-        // No stream offset support, instancing buffer not pre-filled with transforms: have to lock and fill now
+        // No stream offset support, instancing buffer not pre-filled with transforms: have fill now
         if (startIndex_ == M_MAX_UNSIGNED)
         {
             unsigned startIndex = 0;
@@ -643,19 +643,13 @@ void BatchGroup::Draw(Graphics* graphics, Renderer* renderer) const
                 if (instances > instanceBuffer->GetVertexCount())
                     instances = instanceBuffer->GetVertexCount();
                 
-                // Lock the instance stream buffer and copy the transforms
-                void* data = instanceBuffer->Lock(0, instances, LOCK_DISCARD);
-                if (!data)
-                {
-                    // Remember to remove the instancing buffer and element mask
-                    vertexBuffers.Pop();
-                    elementMasks.Pop();
-                    return;
-                }
-                Matrix3x4* dest = (Matrix3x4*)data;
+                // 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->Unlock();
+                instanceBuffer->SetDataRange(scratch, 0, instances, true);
+                graphics->FreeScratchBuffer(scratch);
                 
                 graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
                 graphics->SetVertexBuffers(vertexBuffers, elementMasks);

+ 16 - 15
Engine/Graphics/BillboardSet.cpp

@@ -126,7 +126,7 @@ void BillboardSet::UpdateBatches(const FrameInfo& frame)
         lodDistance_ = 0.0f;
     
     batches_[0].distance_ = distance_;
-    batches_[0].worldTransform_ = &Matrix3x4::IDENTITY;
+    batches_[0].worldTransform_ = relative_ ? &node_->GetWorldTransform() : &Matrix3x4::IDENTITY;
 }
 
 void BillboardSet::UpdateGeometry(const FrameInfo& frame)
@@ -361,9 +361,9 @@ void BillboardSet::UpdateBufferSize()
         return;
     
     // Indices do not change for a given billboard capacity
-    unsigned short* dest = (unsigned short*)indexBuffer_->Lock(0, numBillboards * 6, LOCK_DISCARD);
-    if (!dest)
-        return;
+    Graphics* graphics = indexBuffer_->GetGraphics();
+    void* scratch = graphics->ReserveScratchBuffer(numBillboards * 6 * sizeof(unsigned short));
+    unsigned short* dest = (unsigned short*)scratch;
     unsigned vertexIndex = 0;
     while (numBillboards--)
     {
@@ -372,7 +372,8 @@ void BillboardSet::UpdateBufferSize()
         
         vertexIndex += 4;
     }
-    indexBuffer_->Unlock();
+    indexBuffer_->SetData(scratch);
+    graphics->FreeScratchBuffer(scratch);
 }
 
 void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
@@ -396,6 +397,7 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     const Matrix3x4& worldTransform = node_->GetWorldTransform();
     Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
     Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE;
+    Vector3 worldScale = worldTransform.Scale();
     
     // First check number of enabled billboards
     for (unsigned i = 0; i < numBillboards; ++i)
@@ -429,16 +431,14 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     if (sorted_)
         Sort(sortedBillboards_.Begin(), sortedBillboards_.End(), CompareBillboards);
     
-    float* dest = (float*)vertexBuffer_->Lock(0, enabledBillboards * 4, LOCK_DISCARD);
-    if (!dest)
-        return;
-    Vector3 worldScale = worldTransform.Scale();
+    Graphics* graphics = vertexBuffer_->GetGraphics();
+    void* scratch = graphics->ReserveScratchBuffer(enabledBillboards * 4 * vertexBuffer_->GetVertexSize());
+    float* dest = (float*)scratch;
     
     for (unsigned i = 0; i < enabledBillboards; ++i)
     {
         Billboard& billboard = *sortedBillboards_[i];
         
-        Vector3 position(billboardTransform * billboard.position_);
         Vector2 size(billboard.size_.x_ * billboardScale.x_, billboard.size_.y_ * billboardScale.y_);
         unsigned color = billboard.color_.ToUInt();
         float angleRad = billboard.rotation_ * M_DEGTORAD;
@@ -449,32 +449,33 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
         rotationMatrix[1][0] = -rotationMatrix[0][1];
         rotationMatrix[1][1] = rotationMatrix[0][0];
         
-        *dest++ = position.x_; *dest++ = position.y_; *dest++ = position.z_;
+        *dest++ = billboard.position_.x_; *dest++ = billboard.position_.y_; *dest++ = billboard.position_.z_;
         *((unsigned*)dest) = color; dest++;
         *dest++ = billboard.uv_.min_.x_; *dest++ = billboard.uv_.max_.y_;
         *dest++ = -size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
         *dest++ = -size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
         
-        *dest++ = position.x_; *dest++ = position.y_; *dest++ = position.z_;
+        *dest++ = billboard.position_.x_; *dest++ = billboard.position_.y_; *dest++ = billboard.position_.z_;
         *((unsigned*)dest) = color; dest++;
         *dest++ = billboard.uv_.max_.x_; *dest++ = billboard.uv_.max_.y_;
         *dest++ = size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
         *dest++ = size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
         
-        *dest++ = position.x_; *dest++ = position.y_; *dest++ = position.z_;
+        *dest++ = billboard.position_.x_; *dest++ = billboard.position_.y_; *dest++ = billboard.position_.z_;
         *((unsigned*)dest) = color; dest++;
         *dest++ = billboard.uv_.max_.x_; *dest++ = billboard.uv_.min_.y_;
         *dest++ = size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
         *dest++ = size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
         
-        *dest++ = position.x_; *dest++ = position.y_; *dest++ = position.z_;
+        *dest++ = billboard.position_.x_; *dest++ = billboard.position_.y_; *dest++ = billboard.position_.z_;
         *((unsigned*)dest) = color; dest++;
         *dest++ = billboard.uv_.min_.x_; *dest++ = billboard.uv_.min_.y_;
         *dest++ = -size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
         *dest++ = -size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
     }
     
-    vertexBuffer_->Unlock();
+    vertexBuffer_->SetDataRange(scratch, 0, enabledBillboards * 4, true);
+    graphics->FreeScratchBuffer(scratch);
 }
 
 void BillboardSet::MarkPositionsDirty()

+ 4 - 5
Engine/Graphics/DebugRenderer.cpp

@@ -368,11 +368,9 @@ void DebugRenderer::Render()
     // Resize the vertex buffer if too small or much too large
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR, true);
-    void* lockedData = vertexBuffer_->Lock(0, numVertices, LOCK_DISCARD);
-    if (!lockedData)
-        return;
     
-    float* dest = (float*)lockedData;
+    void* scratch = graphics->ReserveScratchBuffer(numVertices * vertexBuffer_->GetVertexSize());
+    float* dest = (float*)scratch;
     
     for (unsigned i = 0; i < lines_.Size(); ++i)
     {
@@ -396,7 +394,8 @@ void DebugRenderer::Render()
         *((unsigned*)dest) = line.color_; dest++;
     }
     
-    vertexBuffer_->Unlock();
+    vertexBuffer_->SetDataRange(scratch, 0, numVertices, true);
+    graphics->FreeScratchBuffer(scratch);
     
     graphics->SetBlendMode(BLEND_REPLACE);
     graphics->SetColorWrite(true);

+ 55 - 0
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -40,6 +40,7 @@
 #include "Shader.h"
 #include "ShaderVariation.h"
 #include "Skybox.h"
+#include "StringUtils.h"
 #include "Technique.h"
 #include "Texture2D.h"
 #include "TextureCube.h"
@@ -1764,6 +1765,60 @@ void Graphics::RemoveGPUObject(GPUObject* object)
     gpuObjects_.Erase(gpuObjects_.Find(object));
 }
 
+
+void* Graphics::ReserveScratchBuffer(unsigned size)
+{
+    if (!size)
+        return 0;
+    
+    // First check for a free buffer that is large enough
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ >= size)
+        {
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Then check if a free buffer can be resized
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_)
+        {
+            i->data_ = new unsigned char[size];
+            i->size_ = size;
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Finally allocate a new buffer
+    ScratchBuffer newBuffer;
+    newBuffer.data_ = new unsigned char[size];
+    newBuffer.size_ = size;
+    newBuffer.reserved_ = true;
+    scratchBuffers_.Push(newBuffer);
+    return newBuffer.data_.Get();
+}
+
+void Graphics::FreeScratchBuffer(void* buffer)
+{
+    if (!buffer)
+        return;
+    
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (i->reserved_ && i->data_.Get() == buffer)
+        {
+            i->reserved_ = false;
+            return;
+        }
+    }
+    
+    LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)buffer) + " not found");
+}
+
 unsigned Graphics::GetAlphaFormat()
 {
     return D3DFMT_A8;

+ 24 - 0
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -23,6 +23,7 @@
 
 #pragma once
 
+#include "ArrayPtr.h"
 #include "Color.h"
 #include "HashMap.h"
 #include "Object.h"
@@ -48,6 +49,23 @@ class VertexDeclaration;
 
 struct ShaderParameter;
 
+/// CPU-side scratch buffer for vertex data updates.
+struct ScratchBuffer
+{
+    ScratchBuffer() :
+        size_(0),
+        reserved_(false)
+    {
+    }
+    
+    /// Buffer data.
+    SharedArrayPtr<unsigned char> data_;
+    /// Data size.
+    unsigned size_;
+    /// Reserved flag.
+    bool reserved_;
+};
+
 /// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
 class Graphics : public Object
 {
@@ -297,6 +315,10 @@ public:
     void AddGPUObject(GPUObject* object);
     /// Remove a GPU object. Called by GPUObject.
     void RemoveGPUObject(GPUObject* object);
+    /// Reserve a CPU-side scratch buffer.
+    void* ReserveScratchBuffer(unsigned size);
+    /// Free a CPU-side scratch buffer.
+    void FreeScratchBuffer(void* buffer);
     
     /// Return the API-specific alpha texture format.
     static unsigned GetAlphaFormat();
@@ -385,6 +407,8 @@ private:
     unsigned numBatches_;
     /// GPU objects.
     Vector<GPUObject*> gpuObjects_;
+    /// Scratch buffers.
+    Vector<ScratchBuffer> scratchBuffers_;
     /// Vertex declarations.
     HashMap<unsigned long long, SharedPtr<VertexDeclaration> > vertexDeclarations_;
     /// Shadow map dummy color texture format.

+ 99 - 65
Engine/Graphics/Direct3D9/D3D9IndexBuffer.cpp

@@ -39,7 +39,7 @@ IndexBuffer::IndexBuffer(Context* context) :
     usage_(0),
     indexCount_(0),
     indexSize_(0),
-    locked_(false),
+    shadowed_(false),
     dataLost_(false)
 {
 }
@@ -52,7 +52,10 @@ IndexBuffer::~IndexBuffer()
 void IndexBuffer::OnDeviceLost()
 {
     if (pool_ == D3DPOOL_DEFAULT)
+    {
         Release();
+        dataLost_ = true;
+    }
 }
 
 void IndexBuffer::OnDeviceReset()
@@ -60,7 +63,8 @@ void IndexBuffer::OnDeviceReset()
     if (pool_ == D3DPOOL_DEFAULT)
     {
         Create();
-        dataLost_ = true;
+        if (UpdateToGPU())
+            dataLost_ = false;
     }
 }
 
@@ -71,16 +75,29 @@ void IndexBuffer::Release()
         if (!graphics_)
             return;
         
-        Unlock();
-        
         if (graphics_->GetIndexBuffer() == this)
             graphics_->SetIndexBuffer(0);
         
         ((IDirect3DIndexBuffer9*)object_)->Release();
         object_ = 0;
     }
+}
+
+void IndexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
     
-    fallbackData_.Reset();
+    if (enable != shadowed_)
+    {
+        if (enable && indexSize_ && indexCount_)
+            shadowData_ = new unsigned char[indexCount_ * indexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
 }
 
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
@@ -99,85 +116,79 @@ bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
     indexCount_ = indexCount;
     indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
     
+    if (shadowed_ && indexCount_ && indexSize_)
+        shadowData_ = new unsigned char[indexCount_ * indexSize_];
+    else
+        shadowData_.Reset();
+    
     return Create();
 }
 
 bool IndexBuffer::SetData(const void* data)
 {
-    void* hwData = Lock(0, indexCount_, LOCK_DISCARD);
-    if (!hwData)
+    if (!data)
+    {
+        LOGERROR("Null pointer for index buffer data");
         return false;
+    }
     
-    memcpy(hwData, data, indexCount_ * indexSize_);
-    Unlock();
-    return true;
-}
-
-bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count)
-{
-    if (!count)
-        return true;
-    
-    void* hwData = Lock(start, count, LOCK_NORMAL);
-    if (!hwData)
-        return false;
+    if (object_)
+    {
+        void* hwData = Lock(0, indexCount_, true);
+        if (hwData)
+        {
+            memcpy(hwData, data, indexCount_ * indexSize_);
+            Unlock();
+        }
+    }
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
     
-    memcpy(hwData, data, count * indexSize_);
-    Unlock();
     return true;
 }
 
-void* IndexBuffer::Lock(unsigned start, unsigned count, LockMode mode)
+bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
 {
-    if (!object_ && !fallbackData_)
-        return 0;
-
-    if (locked_)
+    if (start == 0 && count == indexCount_)
+        return SetData(data);
+    
+    if (!data)
     {
-        LOGERROR("Index buffer already locked");
-        return 0;
+        LOGERROR("Null pointer for index buffer data");
+        return false;
     }
-
-    if (!count || start + count > indexCount_)
+    
+    if (start + count > indexCount_)
     {
-        LOGERROR("Illegal range for locking index buffer");
-        return 0;
+        LOGERROR("Illegal range for setting new index buffer data");
+        return false;
     }
     
-    void* hwData = 0;
+    if (!count)
+        return true;
     
     if (object_)
     {
-        DWORD flags = 0;
-        
-        if (mode == LOCK_DISCARD && usage_ & D3DUSAGE_DYNAMIC)
-            flags = D3DLOCK_DISCARD;
-        if (mode == LOCK_NOOVERWRITE && usage_ & D3DUSAGE_DYNAMIC)
-            flags = D3DLOCK_NOOVERWRITE;
-        if (mode == LOCK_READONLY)
-            flags = D3DLOCK_READONLY;
-        
-        if (FAILED(((IDirect3DIndexBuffer9*)object_)->Lock(start * indexSize_, count * indexSize_, &hwData, flags)))
+        void* hwData = Lock(start, count, discard);
+        if (hwData)
         {
-            LOGERROR("Could not lock index buffer");
-            return 0;
+            memcpy(hwData, data, count * indexSize_);
+            Unlock();
         }
     }
-    else
-        hwData = fallbackData_.Get() + start * indexSize_;
     
-    locked_ = true;
-    return hwData;
+    if (shadowData_ && shadowData_.Get() + start * indexSize_ != data)
+        memcpy(shadowData_.Get() + start * indexSize_, data, count * indexSize_);
+    
+    return true;
 }
 
-void IndexBuffer::Unlock()
+bool IndexBuffer::UpdateToGPU()
 {
-    if (locked_)
-    {
-        if (object_)
-            ((IDirect3DIndexBuffer9*)object_)->Unlock();
-        locked_ = false;
-    }
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
 }
 
 void IndexBuffer::ClearDataLost()
@@ -192,8 +203,7 @@ bool IndexBuffer::IsDynamic() const
 
 bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
 {
-    void* data = Lock(start, count, LOCK_READONLY);
-    if (!data)
+    if (!shadowData_)
         return false;
     
     minVertex = M_MAX_UNSIGNED;
@@ -201,7 +211,7 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     
     if (indexSize_ == sizeof(unsigned))
     {
-        unsigned* indices = (unsigned*)data;
+        unsigned* indices = (unsigned*)shadowData_.Get();
         
         for (unsigned i = 0; i < count; ++i)
         {
@@ -213,7 +223,7 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     }
     else
     {
-        unsigned short* indices = (unsigned short*)data;
+        unsigned short* indices = (unsigned short*)shadowData_.Get();
         
         for (unsigned i = 0; i < count; ++i)
         {
@@ -225,8 +235,6 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     }
     
     vertexCount = maxVertex - minVertex + 1;
-    
-    Unlock();
     return true;
 }
 
@@ -252,8 +260,34 @@ bool IndexBuffer::Create()
             return false;
         }
     }
-    else
-        fallbackData_ = new unsigned char[indexCount_ * indexSize_];
     
     return true;
 }
+
+
+void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        DWORD flags = 0;
+        
+        if (discard && usage_ & D3DUSAGE_DYNAMIC)
+            flags = D3DLOCK_DISCARD;
+        
+        if (FAILED(((IDirect3DIndexBuffer9*)object_)->Lock(start * indexSize_, count * indexSize_, &hwData, flags)))
+        {
+            LOGERROR("Could not lock index buffer");
+            return 0;
+        }
+    }
+    
+    return hwData;
+}
+
+void IndexBuffer::Unlock()
+{
+    if (object_)
+        ((IDirect3DIndexBuffer9*)object_)->Unlock();
+}

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

@@ -46,19 +46,21 @@ public:
     /// Release buffer.
     virtual void Release();
     
-    /// %Set buffer size and dynamic mode. Previous data will be lost.
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// %Set size and vertex elements and dynamic mode. Previous data will be lost.
     bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
     /// %Set all data in the buffer.
     bool SetData(const void* data);
-    /// %Set a data range in the buffer.
-    bool SetDataRange(const void* data, unsigned start, unsigned count);
-    /// Lock a data range in the buffer. Return pointer to locked data if successful.
-    void* Lock(unsigned start, unsigned count, LockMode mode);
-    /// Unlock buffer.
-    void Unlock();
+    /// %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);
+    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
+    bool UpdateToGPU();
     /// Clear data lost flag.
     void ClearDataLost();
     
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     bool IsDynamic() const;
     /// Return whether default pool data lost.
@@ -69,13 +71,19 @@ public:
     unsigned GetIndexSize() const { return indexSize_; }
     /// Return used vertex range from index range.
     bool GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
     
 private:
     /// Create buffer.
     bool Create();
+    /// Lock buffer.
+    void* Lock(unsigned start, unsigned count, bool discard);
+    /// Unlock buffer.
+    void Unlock();
     
-    /// Fallback data when operating with a null graphics subsystem.
-    SharedArrayPtr<unsigned char> fallbackData_;
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
     /// Memory pool.
     unsigned pool_;
     /// Usage type.
@@ -84,8 +92,8 @@ private:
     unsigned indexCount_;
     /// Index size.
     unsigned indexSize_;
-    /// Buffer locked flag.
-    bool locked_;
+    /// Shadowed flag.
+    bool shadowed_;
     /// Default pool data lost flag.
     bool dataLost_;
 };

+ 8 - 5
Engine/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -75,21 +75,24 @@ bool Texture2D::Load(Deserializer& source)
 void Texture2D::OnDeviceLost()
 {
     if (pool_ == D3DPOOL_DEFAULT)
+    {
         Release();
+        dataLost_ = true;
+    }
 }
 
 void Texture2D::OnDeviceReset()
 {
     if (pool_ == D3DPOOL_DEFAULT)
     {
-        // If has a file name, reload through the resource cache. Otherwise recreate and mark the data lost
+        // If has a file name, reload through the resource cache. Otherwise just recreate.
         if (!GetName().Trimmed().Empty())
-            GetSubsystem<ResourceCache>()->ReloadResource(this);
-        else
         {
-            Create();
-            dataLost_ = true;
+            if (GetSubsystem<ResourceCache>()->ReloadResource(this))
+                dataLost_ = false;
         }
+        else
+            Create();
     }
 }
 

+ 8 - 5
Engine/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -62,21 +62,24 @@ void TextureCube::RegisterObject(Context* context)
 void TextureCube::OnDeviceLost()
 {
     if (pool_ == D3DPOOL_DEFAULT)
+    {
         Release();
+        dataLost_ = true;
+    }
 }
 
 void TextureCube::OnDeviceReset()
 {
     if (pool_ == D3DPOOL_DEFAULT)
     {
-        // If has a file name, reload through the resource cache. Otherwise recreate and mark the data lost
+        // If has a file name, reload through the resource cache. Otherwise just recreate.
         if (!GetName().Trimmed().Empty())
-            GetSubsystem<ResourceCache>()->ReloadResource(this);
-        else
         {
-            Create();
-            dataLost_ = true;
+            if (GetSubsystem<ResourceCache>()->ReloadResource(this))
+                dataLost_ = false;
         }
+        else
+            Create();
     }
 }
 

+ 96 - 100
Engine/Graphics/Direct3D9/D3D9VertexBuffer.cpp

@@ -72,12 +72,14 @@ VertexBuffer::VertexBuffer(Context* context) :
     usage_(0),
     vertexCount_(0),
     elementMask_(0),
-    morphRangeStart_(0),
-    morphRangeCount_(0),
-    locked_(false),
+    shadowed_(false),
     dataLost_(false)
 {
     UpdateOffsets();
+    
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
 }
 
 VertexBuffer::~VertexBuffer()
@@ -88,7 +90,10 @@ VertexBuffer::~VertexBuffer()
 void VertexBuffer::OnDeviceLost()
 {
     if (pool_ == D3DPOOL_DEFAULT)
+    {
         Release();
+        dataLost_ = true;
+    }
 }
 
 void VertexBuffer::OnDeviceReset()
@@ -96,7 +101,8 @@ void VertexBuffer::OnDeviceReset()
     if (pool_ == D3DPOOL_DEFAULT)
     {
         Create();
-        dataLost_ = true;
+        if (UpdateToGPU())
+            dataLost_ = false;
     }
 }
 
@@ -107,8 +113,6 @@ void VertexBuffer::Release()
         if (!graphics_)
             return;
         
-        Unlock();
-        
         for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
         {
             if (graphics_->GetVertexBuffer(i) == this)
@@ -118,8 +122,23 @@ void VertexBuffer::Release()
         ((IDirect3DVertexBuffer9*)object_)->Release();
         object_ = 0;
     }
+}
+
+void VertexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
     
-    fallbackData_.Reset();
+    if (enable != shadowed_)
+    {
+        if (enable && vertexSize_ && vertexCount_)
+            shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
 }
 
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
@@ -138,129 +157,81 @@ bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dyna
     vertexCount_ = vertexCount;
     elementMask_ = elementMask;
     
-    if (morphRangeStart_ + morphRangeCount_ > vertexCount_)
-    {
-        morphRangeStart_ = 0;
-        morphRangeCount_ = 0;
-    }
-    
     UpdateOffsets();
+    
+    if (shadowed_ && vertexCount_ && vertexSize_)
+        shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+    else
+        shadowData_.Reset();
+    
     return Create();
 }
 
 bool VertexBuffer::SetData(const void* data)
 {
-    void* hwData = Lock(0, vertexCount_, LOCK_DISCARD);
-    if (!hwData)
-        return false;
-    
-    memcpy(hwData, data, vertexCount_ * vertexSize_);
-    Unlock();
-    return true;
-}
-
-bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count)
-{
-    if (!count)
-        return true;
-    
-    void* hwData = Lock(start, count, LOCK_NORMAL);
-    if (!hwData)
+    if (!data)
+    {
+        LOGERROR("Null pointer for vertex buffer data");
         return false;
+    }
     
-    memcpy(hwData, data, count * vertexSize_);
-    Unlock();
-    return true;
-}
-
-bool VertexBuffer::SetMorphRange(unsigned start, unsigned count)
-{
-    if (start + count > vertexCount_)
+    if (object_)
     {
-        LOGERROR("Illegal morph range");
-        return false;
+        void* hwData = Lock(0, vertexCount_, true);
+        if (hwData)
+        {
+            memcpy(hwData, data, vertexCount_ * vertexSize_);
+            Unlock();
+        }
     }
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
     
-    morphRangeStart_ = start;
-    morphRangeCount_ = count;
     return true;
 }
 
-void VertexBuffer::SetMorphRangeResetData(const SharedArrayPtr<unsigned char>& data)
-{
-    morphRangeResetData_ = data;
-}
-
-void* VertexBuffer::Lock(unsigned start, unsigned count, LockMode mode)
+bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
 {
-    if (!object_ && !fallbackData_)
-        return 0;
-
-    if (locked_)
+    if (start == 0 && count == vertexCount_)
+        return SetData(data);
+    
+    if (!data)
     {
-        LOGERROR("Vertex buffer already locked");
-        return 0;
+        LOGERROR("Null pointer for vertex buffer data");
+        return false;
     }
     
-    if (!count || start + count > vertexCount_)
+    if (start + count > vertexCount_)
     {
-        LOGERROR("Illegal range for locking vertex buffer");
-        return 0;
+        LOGERROR("Illegal range for setting new index buffer data");
+        return false;
     }
     
-    void* hwData = 0;
+    if (!count)
+        return true;
     
     if (object_)
     {
-        DWORD flags = 0;
-        
-        if (mode == LOCK_DISCARD && usage_ & D3DUSAGE_DYNAMIC)
-            flags = D3DLOCK_DISCARD;
-        if (mode == LOCK_NOOVERWRITE && usage_ & D3DUSAGE_DYNAMIC)
-            flags = D3DLOCK_NOOVERWRITE;
-        if (mode == LOCK_READONLY)
-            flags = D3DLOCK_READONLY;
-        
-        if (FAILED(((IDirect3DVertexBuffer9*)object_)->Lock(start * vertexSize_, count * vertexSize_, &hwData, flags)))
+        void* hwData = Lock(start, count, discard);
+        if (hwData)
         {
-            LOGERROR("Could not lock vertex buffer");
-            return 0;
+            memcpy(hwData, data, count * vertexSize_);
+            Unlock();
         }
     }
-    else
-        hwData = fallbackData_.Get() + start * vertexSize_;
     
-    locked_ = true;
-    return hwData;
-}
-
-void VertexBuffer::Unlock()
-{
-    if (locked_)
-    {
-        if (object_)
-            ((IDirect3DVertexBuffer9*)object_)->Unlock();
-        locked_ = false;
-    }
-}
-
-void* VertexBuffer::LockMorphRange()
-{
-    if (!HasMorphRange())
-    {
-        LOGERROR("No vertex morph range defined");
-        return 0;
-    }
+    if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data)
+        memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_);
     
-    return Lock(morphRangeStart_, morphRangeCount_, LOCK_DISCARD);
+    return true;
 }
 
-void VertexBuffer::ResetMorphRange(void* lockedMorphRange)
+bool VertexBuffer::UpdateToGPU()
 {
-    if (!lockedMorphRange || !morphRangeResetData_)
-        return;
-    
-    memcpy(lockedMorphRange, morphRangeResetData_.Get(), morphRangeCount_ * vertexSize_);
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
 }
 
 void VertexBuffer::ClearDataLost()
@@ -339,8 +310,33 @@ bool VertexBuffer::Create()
             return false;
         }
     }
-    else
-        fallbackData_ = new unsigned char[vertexCount_ * vertexSize_];
     
     return true;
 }
+
+void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        DWORD flags = 0;
+        
+        if (discard && usage_ & D3DUSAGE_DYNAMIC)
+            flags = D3DLOCK_DISCARD;
+        
+        if (FAILED(((IDirect3DVertexBuffer9*)object_)->Lock(start * vertexSize_, count * vertexSize_, &hwData, flags)))
+        {
+            LOGERROR("Could not lock vertex buffer");
+            return 0;
+        }
+    }
+    
+    return hwData;
+}
+
+void VertexBuffer::Unlock()
+{
+    if (object_)
+        ((IDirect3DVertexBuffer9*)object_)->Unlock();
+}

+ 18 - 32
Engine/Graphics/Direct3D9/D3D9VertexBuffer.h

@@ -45,27 +45,21 @@ public:
     /// Release buffer.
     virtual void Release();
     
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
     /// %Set size and vertex elements and dynamic mode. Previous data will be lost.
     bool SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic = false);
     /// %Set all data in the buffer.
     bool SetData(const void* data);
-    /// %Set a data range in the buffer.
-    bool SetDataRange(const void* data, unsigned first, unsigned count);
-    /// %Set the vertex range to use for morphing.
-    bool SetMorphRange(unsigned start, unsigned count);
-    /// %Set data to be used for resetting the morph vertex range.
-    void SetMorphRangeResetData(const SharedArrayPtr<unsigned char>& data);
-    /// Lock a data range in the buffer. Return pointer to locked data if successful.
-    void* Lock(unsigned start, unsigned count, LockMode mode);
-    /// Unlock buffer.
-    void Unlock();
-    /// Lock the morph vertex range. Return pointer to locked data if successful.
-    void* LockMorphRange();
-    /// Reset the morph vertex range. Needs to be locked first.
-    void ResetMorphRange(void* lockedMorphRange);
+    /// %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);
+    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
+    bool UpdateToGPU();
     /// Clear data lost flag.
     void ClearDataLost();
     
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     bool IsDynamic() const;
     /// Return whether default pool data lost.
@@ -78,16 +72,10 @@ public:
     unsigned GetElementMask() const { return elementMask_; }
     /// Return offset of a specified element within a vertex.
     unsigned GetElementOffset(VertexElement element) const { return elementOffset_[element]; }
-    /// Return morph vertex range start.
-    unsigned GetMorphRangeStart() const { return morphRangeStart_; }
-    /// Return number of vertices in the morph range.
-    unsigned GetMorphRangeCount() const { return morphRangeCount_; }
-    /// Return morph vertex range reset data.
-    const SharedArrayPtr<unsigned char>& GetMorphRangeResetData() { return morphRangeResetData_; }
-    /// Return whether has a morph vertex range defined.
-    bool HasMorphRange() const { return morphRangeCount_ > 0; }
     /// Return buffer hash for building vertex declarations.
     unsigned long long GetBufferHash(unsigned streamIndex, unsigned useMask);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
     
     /// Return vertex size corresponding to a vertex element mask.
     static unsigned GetVertexSize(unsigned elementMask);
@@ -102,11 +90,13 @@ private:
     void UpdateOffsets();
     /// Create buffer.
     bool Create();
+    /// Lock buffer.
+    void* Lock(unsigned start, unsigned count, bool discard);
+    /// Unlock buffer.
+    void Unlock();
     
-    /// Fallback data when operating with a null graphics subsystem.
-    SharedArrayPtr<unsigned char> fallbackData_;
-    /// Morph vertex range reset data.
-    SharedArrayPtr<unsigned char> morphRangeResetData_;
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
     /// Memory pool.
     unsigned pool_;
     /// Usage type.
@@ -119,12 +109,8 @@ private:
     unsigned elementMask_;
     /// Vertex element offsets.
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
-    /// Morph vertex range start.
-    unsigned morphRangeStart_;
-    /// Number of vertices in the morph range.
-    unsigned morphRangeCount_;
-    /// Locked flag.
-    bool locked_;
+    /// Shadowed flag.
+    bool shadowed_;
     /// Default pool data lost flag.
     bool dataLost_;
 };

+ 20 - 14
Engine/Graphics/Geometry.cpp

@@ -154,12 +154,6 @@ void Geometry::SetLodDistance(float distance)
     lodDistance_ = distance;
 }
 
-void Geometry::SetRawData(const SharedArrayPtr<unsigned char>& vertexData, const SharedArrayPtr<unsigned char>& indexData)
-{
-    rawVertexData_ = vertexData;
-    rawIndexData_ = indexData;
-}
-
 void Geometry::Draw(Graphics* graphics)
 {
     graphics->SetIndexBuffer(indexBuffer_);
@@ -196,10 +190,13 @@ unsigned short Geometry::GetBufferHash() const
 
 void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize)
 {
-    if (rawVertexData_)
+    if (positionBufferIndex_ < vertexBuffers_.Size() && vertexBuffers_[positionBufferIndex_])
     {
-        vertexData = rawVertexData_.Get();
-        vertexSize = 3 * sizeof(float);
+        vertexData = vertexBuffers_[positionBufferIndex_]->GetShadowData();
+        if (vertexData)
+            vertexSize = vertexBuffers_[positionBufferIndex_]->GetVertexSize();
+        else
+            vertexSize = 0;
     }
     else
     {
@@ -207,10 +204,13 @@ void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize
         vertexSize = 0;
     }
     
-    if (rawIndexData_ && indexBuffer_)
+    if (indexBuffer_)
     {
-        indexData = rawIndexData_.Get();
-        indexSize = indexBuffer_->GetIndexSize();
+        indexData = indexBuffer_->GetShadowData();
+        if (indexData)
+            indexSize = indexBuffer_->GetIndexSize();
+        else
+            indexSize = 0;
     }
     else
     {
@@ -221,10 +221,16 @@ void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize
 
 float Geometry::GetDistance(const Ray& ray)
 {
-    if (!rawIndexData_ || !rawVertexData_ || !indexBuffer_)
+    const unsigned char* rawVertexData = 0;
+    const unsigned char* rawIndexData = 0;
+    unsigned vertexSize = 0;
+    unsigned indexSize = 0;
+    
+    GetRawData(rawVertexData, vertexSize, rawIndexData, indexSize);
+    if (!rawVertexData || !rawIndexData)
         return M_INFINITY;
     
-    return ray.HitDistance(rawVertexData_.Get(), 3 * sizeof(float), rawIndexData_.Get(), indexBuffer_->GetIndexSize(), indexStart_, indexCount_);
+    return ray.HitDistance(rawVertexData, vertexSize, rawIndexData, indexSize, indexStart_, indexCount_);
 }
 
 void Geometry::GetPositionBufferIndex()

+ 0 - 10
Engine/Graphics/Geometry.h

@@ -55,8 +55,6 @@ public:
     bool SetDrawRange(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount);
     /// %Set the LOD distance.
     void SetLodDistance(float distance);
-    /// %Set vertex and index raw data for CPU access. Vertex raw data should contain positions only.
-    void SetRawData(const SharedArrayPtr<unsigned char>& vertexData, const SharedArrayPtr<unsigned char>& indexData);
     /// Draw.
     void Draw(Graphics* graphics);
     
@@ -88,10 +86,6 @@ public:
     unsigned short GetBufferHash() const;
     /// Return raw vertex and index data for CPU operations, or null pointers if not available.
     void GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize);
-    /// Return the raw vertex data array.
-    const SharedArrayPtr<unsigned char>& GetRawVertexData() const { return rawVertexData_; }
-    /// Return the raw index data array.
-    const SharedArrayPtr<unsigned char>& GetRawIndexData() const { return rawIndexData_; }
     /// Return ray hit distance or infinity if no hit. Requires raw data to be set.
     float GetDistance(const Ray& ray);
     
@@ -119,8 +113,4 @@ private:
     unsigned positionBufferIndex_;
     /// LOD distance.
     float lodDistance_;
-    /// Raw vertex data (positions only) for CPU operations.
-    SharedArrayPtr<unsigned char> rawVertexData_;
-    /// Raw index data for CPU operations.
-    SharedArrayPtr<unsigned char> rawIndexData_;
 };

+ 0 - 9
Engine/Graphics/GraphicsDefs.h

@@ -96,15 +96,6 @@ enum StencilOp
     OP_DECR
 };
 
-/// Buffer lock mode.
-enum LockMode
-{
-    LOCK_NORMAL,
-    LOCK_DISCARD,
-    LOCK_NOOVERWRITE,
-    LOCK_READONLY
-};
-
 /// Vertex elements.
 enum VertexElement
 {

+ 94 - 87
Engine/Graphics/Model.cpp

@@ -37,26 +37,24 @@
 
 #include "DebugNew.h"
 
-unsigned StoreOrLookupVertexBuffer(VertexBuffer* buffer, Vector<VertexBuffer*>& dest)
+unsigned LookupVertexBuffer(VertexBuffer* buffer, const Vector<SharedPtr<VertexBuffer> >& buffers)
 {
-    for (unsigned i = 0; i < dest.Size(); ++i)
+    for (unsigned i = 0; i < buffers.Size(); ++i)
     {
-        if (dest[i] == buffer)
+        if (buffers[i] == buffer)
             return i;
     }
-    dest.Push(buffer);
-    return dest.Size() - 1;
+    return 0;
 }
 
-unsigned StoreOrLookupIndexBuffer(IndexBuffer* buffer, Vector<IndexBuffer*>& dest)
+unsigned LookupIndexBuffer(IndexBuffer* buffer, const Vector<SharedPtr<IndexBuffer> >& buffers)
 {
-    for (unsigned i = 0; i < dest.Size(); ++i)
+    for (unsigned i = 0; i < buffers.Size(); ++i)
     {
-        if (dest[i] == buffer)
+        if (buffers[i] == buffer)
             return i;
     }
-    dest.Push(buffer);
-    return dest.Size() - 1;
+    return 0;
 }
 
 OBJECTTYPESTATIC(Model);
@@ -95,50 +93,26 @@ bool Model::Load(Deserializer& source)
     
     unsigned memoryUse = sizeof(Model);
     
-    Vector<SharedArrayPtr<unsigned char> > rawVertexDatas;
-    Vector<SharedArrayPtr<unsigned char> > rawIndexDatas;
-    
     // Read vertex buffers
     unsigned numVertexBuffers = source.ReadUInt();
+    morphRangeStarts_.Resize(numVertexBuffers);
+    morphRangeCounts_.Resize(numVertexBuffers);
     for (unsigned i = 0; i < numVertexBuffers; ++i)
     {
         unsigned vertexCount = source.ReadUInt();
         unsigned elementMask = source.ReadUInt();
-        unsigned morphStart = source.ReadUInt();
-        unsigned morphCount = source.ReadUInt();
+        morphRangeStarts_[i] = source.ReadUInt();
+        morphRangeCounts_[i] = source.ReadUInt();
         
         SharedPtr<VertexBuffer> buffer(new VertexBuffer(context_));
+        buffer->SetShadowed(true);
         buffer->SetSize(vertexCount, elementMask);
-        buffer->SetMorphRange(morphStart, morphCount);
         
         unsigned vertexSize = buffer->GetVertexSize();
-        SharedArrayPtr<unsigned char> data(new unsigned char[vertexCount * vertexSize]);
-        memoryUse += sizeof(VertexBuffer) + vertexCount * vertexSize;
-
-        source.Read(data.Get(), vertexCount * vertexSize);
-        buffer->SetData(data.Get());
-
-        // If there is a morph range, make a copy of the data so that the morph range can be reset
-        if (morphCount)
-        {
-            SharedArrayPtr<unsigned char> morphResetData(new unsigned char[morphCount * vertexSize]);
-            memcpy(morphResetData.Get(), &data[morphStart * vertexSize], morphCount * vertexSize);
-            buffer->SetMorphRangeResetData(morphResetData);
-            memoryUse += morphCount * vertexSize;
-        }
+        source.Read(buffer->GetShadowData(), vertexCount * vertexSize);
+        buffer->UpdateToGPU();
         
-        // Copy the raw position data for CPU-side operations
-        SharedArrayPtr<unsigned char> rawVertexData(new unsigned char[3 * sizeof(float) * vertexCount]);
-        float* rawDest = (float*)rawVertexData.Get();
-        for (unsigned i = 0; i < vertexCount; ++i)
-        {
-            float* rawSrc = (float*)&data[i * vertexSize];
-            *rawDest++ = *rawSrc++;
-            *rawDest++ = *rawSrc++;
-            *rawDest++ = *rawSrc++;
-        }
-        
-        rawVertexDatas.Push(rawVertexData);
+        memoryUse += sizeof(VertexBuffer) + vertexCount * vertexSize;
         vertexBuffers_.Push(buffer);
     }
 
@@ -148,18 +122,15 @@ bool Model::Load(Deserializer& source)
     {
         unsigned indexCount = source.ReadUInt();
         unsigned indexSize = source.ReadUInt();
-
+        
         SharedPtr<IndexBuffer> buffer(new IndexBuffer(context_));
+        buffer->SetShadowed(true);
         buffer->SetSize(indexCount, indexSize > sizeof(unsigned short));
-
-        // Copy the raw index data for CPU-side operations
-        SharedArrayPtr<unsigned char> data(new unsigned char[indexSize * indexCount]);
+        
+        source.Read(buffer->GetShadowData(), indexCount * indexSize);
+        buffer->UpdateToGPU();
+        
         memoryUse += sizeof(IndexBuffer) + indexCount * indexSize;
-
-        source.Read(data.Get(), indexCount * indexSize);
-        buffer->SetData(data.Get());
-
-        rawIndexDatas.Push(data);
         indexBuffers_.Push(buffer);
     }
     
@@ -204,7 +175,6 @@ bool Model::Load(Deserializer& source)
             geometry->SetIndexBuffer(indexBuffers_[indexBufferRef]);
             geometry->SetDrawRange(type, indexStart, indexCount);
             geometry->SetLodDistance(distance);
-            geometry->SetRawData(rawVertexDatas[vertexBufferRef], rawIndexDatas[indexBufferRef]);
             
             geometryLodLevels.Push(geometry);
             memoryUse += sizeof(Geometry);
@@ -273,52 +243,29 @@ bool Model::Load(Deserializer& source)
 
 bool Model::Save(Serializer& dest)
 {
-    // Build lists of vertex and index buffers used by the geometries
-    Vector<VertexBuffer*> vertexBuffers;
-    Vector<IndexBuffer*> indexBuffers;
-    
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
-        {
-            StoreOrLookupVertexBuffer(geometries_[i][j]->GetVertexBuffer(0), vertexBuffers);
-            StoreOrLookupIndexBuffer(geometries_[i][j]->GetIndexBuffer(), indexBuffers);
-        }
-    }
-    
     // Write ID
     if (!dest.WriteFileID("UMDL"))
         return false;
     
     // Write vertex buffers
-    dest.WriteUInt(vertexBuffers.Size());
-    for (unsigned i = 0; i < vertexBuffers.Size(); ++i)
+    dest.WriteUInt(vertexBuffers_.Size());
+    for (unsigned i = 0; i < vertexBuffers_.Size(); ++i)
     {
-        VertexBuffer* buffer = vertexBuffers[i];
+        VertexBuffer* buffer = vertexBuffers_[i];
         dest.WriteUInt(buffer->GetVertexCount());
         dest.WriteUInt(buffer->GetElementMask());
-        dest.WriteUInt(buffer->GetMorphRangeStart());
-        dest.WriteUInt(buffer->GetMorphRangeCount());
-        /// \todo Will not work on OpenGL ES
-        void* data = buffer->Lock(0, buffer->GetVertexCount(), LOCK_READONLY);
-        if (!data)
-            return false;
-        dest.Write(data, buffer->GetVertexCount() * buffer->GetVertexSize());
-        buffer->Unlock();
+        dest.WriteUInt(morphRangeStarts_[i]);
+        dest.WriteUInt(morphRangeCounts_[i]);
+        dest.Write(buffer->GetShadowData(), buffer->GetVertexCount() * buffer->GetVertexSize());
     }
     // Write index buffers
-    dest.WriteUInt(indexBuffers.Size());
-    for (unsigned i = 0; i < indexBuffers.Size(); ++i)
+    dest.WriteUInt(indexBuffers_.Size());
+    for (unsigned i = 0; i < indexBuffers_.Size(); ++i)
     {
-        IndexBuffer* buffer = indexBuffers[i];
+        IndexBuffer* buffer = indexBuffers_[i];
         dest.WriteUInt(buffer->GetIndexCount());
         dest.WriteUInt(buffer->GetIndexSize());
-        /// \todo Will not work on OpenGL ES
-        void* data = buffer->Lock(0, buffer->GetIndexCount(), LOCK_READONLY);
-        if (!data)
-            return false;
-        dest.Write(data, buffer->GetIndexCount() * buffer->GetIndexSize());
-        buffer->Unlock();
+        dest.Write(buffer->GetShadowData(), buffer->GetIndexCount() * buffer->GetIndexSize());
     }
     // Write geometries
     dest.WriteUInt(geometries_.Size());
@@ -336,8 +283,8 @@ bool Model::Save(Serializer& dest)
             Geometry* geometry = geometries_[i][j];
             dest.WriteFloat(geometry->GetLodDistance());
             dest.WriteUInt(geometry->GetPrimitiveType());
-            dest.WriteUInt(StoreOrLookupVertexBuffer(geometry->GetVertexBuffer(0), vertexBuffers));
-            dest.WriteUInt(StoreOrLookupIndexBuffer(geometry->GetIndexBuffer(), indexBuffers));
+            dest.WriteUInt(LookupVertexBuffer(geometry->GetVertexBuffer(0), vertexBuffers_));
+            dest.WriteUInt(LookupIndexBuffer(geometry->GetIndexBuffer(), indexBuffers_));
             dest.WriteUInt(geometry->GetIndexStart());
             dest.WriteUInt(geometry->GetIndexCount());
         }
@@ -390,6 +337,56 @@ void Model::SetBoundingBox(const BoundingBox& box)
     boundingBox_ = box;
 }
 
+bool Model::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& morphRangeStarts, const PODVector<unsigned>& morphRangeCounts)
+{
+    for (unsigned i = 0; i < buffers.Size(); ++i)
+    {
+        if (!buffers[i])
+        {
+            LOGERROR("Null model vertex buffers specified");
+            return false;
+        }
+        if (!buffers[i]->IsShadowed())
+        {
+            LOGERROR("Model vertex buffers must be shadowed");
+            return false;
+        }
+    }
+    
+    vertexBuffers_ = buffers;
+    morphRangeStarts_.Resize(buffers.Size());
+    morphRangeCounts_.Resize(buffers.Size());
+    
+    // If morph ranges are not specified for buffers, assume to be zero
+    for (unsigned i = 0; i < buffers.Size(); ++i)
+    {
+        morphRangeStarts_[i] = i < morphRangeStarts.Size() ? morphRangeStarts[i] : 0;
+        morphRangeCounts_[i] = i < morphRangeCounts.Size() ? morphRangeCounts[i] : 0;
+    }
+    
+    return true;
+}
+
+bool Model::SetIndexBuffers(const Vector<SharedPtr<IndexBuffer> >& buffers)
+{
+    for (unsigned i = 0; i < buffers.Size(); ++i)
+    {
+        if (!buffers[i])
+        {
+            LOGERROR("Null model index buffers specified");
+            return false;
+        }
+        if (!buffers[i]->IsShadowed())
+        {
+            LOGERROR("Model index buffers must be shadowed");
+            return false;
+        }
+    }
+    
+    indexBuffers_ = buffers;
+    return true;
+}
+
 void Model::SetNumGeometries(unsigned num)
 {
     geometries_.Resize(num);
@@ -491,3 +488,13 @@ const ModelMorph* Model::GetMorph(StringHash nameHash) const
     
     return 0;
 }
+
+unsigned Model::GetMorphRangeStart(unsigned bufferIndex) const
+{
+    return bufferIndex < vertexBuffers_.Size() ? morphRangeStarts_[bufferIndex] : 0;
+}
+
+unsigned Model::GetMorphRangeCount(unsigned bufferIndex) const
+{
+    return bufferIndex < vertexBuffers_.Size() ? morphRangeCounts_[bufferIndex] : 0;
+}

+ 12 - 0
Engine/Graphics/Model.h

@@ -78,6 +78,10 @@ public:
     
     /// %Set bounding box.
     void SetBoundingBox(const BoundingBox& box);
+    /// %Set vertex buffers.
+    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& morphRangeStarts, const PODVector<unsigned>& morphRangeCounts);
+    /// %Set index buffers.
+    bool SetIndexBuffers(const Vector<SharedPtr<IndexBuffer> >& buffers);
     /// %Set number of geometries.
     void SetNumGeometries(unsigned num);
     /// %Set number of LOD levels in a geometry.
@@ -123,6 +127,10 @@ public:
     const ModelMorph* GetMorph(const String& name) const;
     /// Return vertex morph by name hash.
     const ModelMorph* GetMorph(StringHash nameHash) const;
+    /// Return vertex buffer morph range start.
+    unsigned GetMorphRangeStart(unsigned bufferIndex) const;
+    /// Return vertex buffer morph range vertex count.
+    unsigned GetMorphRangeCount(unsigned bufferIndex) const;
     
 private:
     /// Bounding box.
@@ -141,4 +149,8 @@ private:
     PODVector<Vector3> geometryCenters_;
     /// Vertex morphs.
     Vector<ModelMorph> morphs_;
+    /// Vertex buffer morph range start.
+    PODVector<unsigned> morphRangeStarts_;
+    /// Vertex buffer morph range vertex count.
+    PODVector<unsigned> morphRangeCounts_;
 };

+ 1 - 1
Engine/Graphics/OpenGL/OGLGPUObject.cpp

@@ -43,5 +43,5 @@ GPUObject::~GPUObject()
 
 void GPUObject::OnDeviceLost()
 {
-    Release();
+    object_ = 0;
 }

+ 2 - 2
Engine/Graphics/OpenGL/OGLGPUObject.h

@@ -36,9 +36,9 @@ public:
     /// Destruct. Remove from the Graphics.
     virtual ~GPUObject();
     
-    /// OpenGL context will be destroyed. Save data if necessary and release the GPU resource.
+    /// Mark the GPU resource destroyed on context destruction.
     virtual void OnDeviceLost();
-    /// Screen mode change or OpenGL context recreation is complete. Recreate the GPU resource and restore data.
+    /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset() {}
     /// Unconditionally release the GPU resource.
     virtual void Release() {}

+ 54 - 37
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -1684,10 +1684,13 @@ void Graphics::RemoveGPUObject(GPUObject* object)
     gpuObjects_.Erase(gpuObjects_.Find(object));
 }
 
-void* Graphics::ReserveDiscardLockBuffer(unsigned size)
+void* Graphics::ReserveScratchBuffer(unsigned size)
 {
+    if (!size)
+        return 0;
+    
     // First check for a free buffer that is large enough
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
     {
         if (!i->reserved_ && i->size_ >= size)
         {
@@ -1697,7 +1700,7 @@ void* Graphics::ReserveDiscardLockBuffer(unsigned size)
     }
     
     // Then check if a free buffer can be resized
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
     {
         if (!i->reserved_)
         {
@@ -1709,17 +1712,20 @@ void* Graphics::ReserveDiscardLockBuffer(unsigned size)
     }
     
     // Finally allocate a new buffer
-    DiscardLockBuffer newBuffer;
+    ScratchBuffer newBuffer;
     newBuffer.data_ = new unsigned char[size];
     newBuffer.size_ = size;
     newBuffer.reserved_ = true;
-    discardLockBuffers_.Push(newBuffer);
+    scratchBuffers_.Push(newBuffer);
     return newBuffer.data_.Get();
 }
 
-void Graphics::FreeDiscardLockBuffer(void* buffer)
+void Graphics::FreeScratchBuffer(void* buffer)
 {
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    if (!buffer)
+        return;
+    
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
     {
         if (i->reserved_ && i->data_.Get() == buffer)
         {
@@ -1728,7 +1734,7 @@ void Graphics::FreeDiscardLockBuffer(void* buffer)
         }
     }
     
-    LOGWARNING("Reserved discard lock buffer " + ToStringHex((unsigned)buffer) + " not found");
+    LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)buffer) + " not found");
 }
 
 void Graphics::Release(bool clearGPUObjects, bool closeWindow)
@@ -1736,42 +1742,51 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
     if (!impl_->window_)
         return;
     
-    CleanupFramebuffers(true);
-    
-    depthTextures_.Clear();
-    
     if (clearGPUObjects)
     {
-        // Shutting down: release all GPU objects that still exist
+        // Shutting down: release all GPU objects that still exist, then delete the context
         for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
             (*i)->Release();
         gpuObjects_.Clear();
+        
+        if (impl_->context_)
+        {
+            MutexLock lock(GetStaticMutex());
+            SDL_GL_DeleteContext(impl_->context_);
+            impl_->context_ = 0;
+        }
+        
+        CleanupFramebuffers(true);
     }
     else
     {
+        if (impl_->context_)
+        {
+            MutexLock lock(GetStaticMutex());
+            SDL_GL_DeleteContext(impl_->context_);
+            impl_->context_ = 0;
+        }
+        
         // We are not shutting down, but recreating the context: mark GPU objects lost
         for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
             (*i)->OnDeviceLost();
     }
     
+    CleanupFramebuffers(true);
+    depthTextures_.Clear();
+    shaderPrograms_.Clear();
+    
     // When the new context is initialized, it will have default state again
     ResetCachedState();
     ClearParameterSources();
     
+    if (closeWindow)
     {
         MutexLock lock(GetStaticMutex());
         
-        if (impl_->context_)
-        {
-            SDL_GL_DeleteContext(impl_->context_);
-            impl_->context_ = 0;
-        }
-        if (closeWindow)
-        {
-            SDL_ShowCursor(SDL_TRUE);
-            SDL_DestroyWindow(impl_->window_);
-            impl_->window_ = 0;
-        }
+        SDL_ShowCursor(SDL_TRUE);
+        SDL_DestroyWindow(impl_->window_);
+        impl_->window_ = 0;
     }
 }
 
@@ -2128,24 +2143,26 @@ bool Graphics::CheckFramebuffer()
     return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
 }
 
-void Graphics::CleanupFramebuffers(bool deleteAll)
+void Graphics::CleanupFramebuffers(bool contextLost)
 {
-    if (deleteAll && impl_->boundFbo_)
+    if (!contextLost)
     {
-        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
-        impl_->boundFbo_ = 0;
-    }
-    
-    for (Map<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin(); i != impl_->frameBuffers_.End();)
-    {
-        Map<unsigned long long, FrameBufferObject>::Iterator current = i++;
-        if (deleteAll || (current->second_.fbo_ != impl_->boundFbo_ && current->second_.useTimer_.GetMSec(false) >
-            MAX_FRAMEBUFFER_AGE))
+        for (Map<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin(); i != impl_->frameBuffers_.End();)
         {
-            glDeleteFramebuffersEXT(1, &current->second_.fbo_);
-            impl_->frameBuffers_.Erase(current);
+            Map<unsigned long long, FrameBufferObject>::Iterator current = i++;
+            if (current->second_.fbo_ != impl_->boundFbo_ && current->second_.useTimer_.GetMSec(false) >
+                MAX_FRAMEBUFFER_AGE)
+            {
+                glDeleteFramebuffersEXT(1, &current->second_.fbo_);
+                impl_->frameBuffers_.Erase(current);
+            }
         }
     }
+    else
+    {
+        impl_->boundFbo_ = 0;
+        impl_->frameBuffers_.Clear();
+    }
 }
 
 void Graphics::ResetCachedState()

+ 10 - 10
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -52,10 +52,10 @@ typedef Map<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> >
 
 static const unsigned NUM_SCREEN_BUFFERS = 2;
 
-/// CPU-side buffer for VBO discard locking.
-struct DiscardLockBuffer
+/// CPU-side scratch buffer for vertex data updates.
+struct ScratchBuffer
 {
-    DiscardLockBuffer() :
+    ScratchBuffer() :
         size_(0),
         reserved_(false)
     {
@@ -324,10 +324,10 @@ public:
     void AddGPUObject(GPUObject* object);
     /// Remove a GPU object. Called by GPUObject.
     void RemoveGPUObject(GPUObject* object);
-    /// Reserve a CPU side discard locking buffer.
-    void* ReserveDiscardLockBuffer(unsigned size);
-    /// Free a CPU side discard locking buffer.
-    void FreeDiscardLockBuffer(void* buffer);
+    /// Reserve a CPU-side scratch buffer.
+    void* ReserveScratchBuffer(unsigned size);
+    /// Free a CPU-side scratch buffer.
+    void FreeScratchBuffer(void* buffer);
     /// Release/clear GPU objects and optionally close the window.
     void Release(bool clearGPUObjects, bool closeWindow);
     /// Clean up a render surface from all FBOs.
@@ -358,7 +358,7 @@ private:
     /// Check FBO completeness.
     bool CheckFramebuffer();
     /// Cleanup unused and unbound FBO's.
-    void CleanupFramebuffers(bool deleteAll);
+    void CleanupFramebuffers(bool contextLost);
     /// Reset cached rendering state.
     void ResetCachedState();
     /// Initialize texture unit mappings.
@@ -394,8 +394,8 @@ private:
     unsigned numBatches_;
     /// GPU objects.
     Vector<GPUObject*> gpuObjects_;
-    /// Discard lock buffers.
-    Vector<DiscardLockBuffer> discardLockBuffers_;
+    /// Scratch buffers.
+    Vector<ScratchBuffer> scratchBuffers_;
     /// Shadow map depth texture format.
     unsigned shadowMapFormat_;
     /// Shadow map 24-bit depth texture format.

+ 61 - 138
Engine/Graphics/OpenGL/OGLIndexBuffer.cpp

@@ -37,12 +37,14 @@ OBJECTTYPESTATIC(IndexBuffer);
 IndexBuffer::IndexBuffer(Context* context) :
     Object(context),
     GPUObject(GetSubsystem<Graphics>()),
-    discardLockData_(0),
     indexCount_(0),
     indexSize_(0),
     dynamic_(false),
-    locked_(false)
+    dataLost_(false)
 {
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
 }
 
 IndexBuffer::~IndexBuffer()
@@ -52,17 +54,9 @@ IndexBuffer::~IndexBuffer()
 
 void IndexBuffer::OnDeviceLost()
 {
-    if (object_)
-    {
-        void* hwData = Lock(0, indexCount_, LOCK_READONLY);
-        if (hwData)
-        {
-            saveData_ = new unsigned char[indexCount_ * indexSize_];
-            memcpy(saveData_.Get(), hwData, indexCount_ * indexSize_);
-        }
-        Unlock();
-        Release();
-    }
+    GPUObject::OnDeviceLost();
+    
+    dataLost_ = true;
 }
 
 void IndexBuffer::OnDeviceReset()
@@ -70,31 +64,38 @@ void IndexBuffer::OnDeviceReset()
     if (!object_)
     {
         Create();
-        if (saveData_)
-        {
-            SetData(saveData_.Get());
-            saveData_.Reset();
-        }
+        if (UpdateToGPU())
+            dataLost_ = false;
     }
 }
 
 void IndexBuffer::Release()
 {
-    if (object_)
+    if (object_ && graphics_)
     {
-        if (!graphics_)
-            return;
-        
-        Unlock();
-        
         if (graphics_->GetIndexBuffer() == this)
             graphics_->SetIndexBuffer(0);
         
         glDeleteBuffers(1, &object_);
         object_ = 0;
     }
+}
+
+void IndexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
     
-    fallbackData_.Reset();
+    if (enable != shadowed_)
+    {
+        if (enable && indexCount_ && indexSize_)
+            shadowData_ = new unsigned char[indexCount_ * indexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
 }
 
 bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
@@ -103,6 +104,11 @@ bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
     indexCount_ = indexCount;
     indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
     
+    if (shadowed_ && indexCount_ && indexSize_)
+        shadowData_ = new unsigned char[indexCount_ * indexSize_];
+    else
+        shadowData_.Reset();
+    
     return Create();
 }
 
@@ -114,27 +120,24 @@ bool IndexBuffer::SetData(const void* data)
         return false;
     }
     
-    if (locked_)
-        Unlock();
-    
     if (object_)
     {
         graphics_->SetIndexBuffer(0);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
-        return true;
-    }
-    else if (fallbackData_)
-    {
-        memcpy(fallbackData_.Get(), data, indexCount_ * indexSize_);
-        return true;
     }
     
-    return false;
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
+    
+    return true;
 }
 
-bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count)
+bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
 {
+    if (start == 0 && count == indexCount_)
+        return SetData(data);
+    
     if (!data)
     {
         LOGERROR("Null pointer for index buffer data");
@@ -147,123 +150,47 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count)
         return false;
     }
     
-    if (locked_)
-        Unlock();
+    if (!count)
+        return true;
     
     if (object_)
     {
         graphics_->SetIndexBuffer(0);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
-        glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start * indexSize_, count * indexSize_, data);
-        return true;
-    }
-    else if (fallbackData_)
-    {
-        memcpy(fallbackData_.Get() + start * indexSize_, data, count * indexSize_);
-        return true;
-    }
-    
-    return false;
-}
-
-void* IndexBuffer::Lock(unsigned start, unsigned count, LockMode mode)
-{
-    if (!object_ && !fallbackData_)
-        return 0;
-    
-    if (locked_)
-    {
-        LOGERROR("Index buffer already locked");
-        return 0;
-    }
-
-    if (!count || start + count > indexCount_)
-    {
-        LOGERROR("Illegal range for locking index buffer");
-        return 0;
-    }
-    
-    void* hwData = 0;
-    
-    if (object_)
-    {
-        #ifndef GL_ES_VERSION_2_0
-        GLenum glLockMode = GL_WRITE_ONLY;
-        if (mode == LOCK_READONLY)
-            glLockMode = GL_READ_ONLY;
-        else if (mode == LOCK_NORMAL)
-            glLockMode = GL_READ_WRITE;
-        #endif
-        
-        // In discard mode, use a CPU side buffer to avoid stalling
-        if (mode == LOCK_DISCARD)
-        {
-            hwData = discardLockData_ = graphics_->ReserveDiscardLockBuffer(count * indexSize_);
-            discardLockStart_ = start;
-            discardLockCount_ = count;
-        }
+        if (!discard || start != 0)
+            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start * indexSize_, count * indexSize_, data);
         else
-        {
-            #ifndef GL_ES_VERSION_2_0
-            graphics_->SetIndexBuffer(0);
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
-            hwData = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, glLockMode);
-            if (!hwData)
-                return 0;
-            hwData = (unsigned char*)hwData + start * indexSize_;
-            #else
-            LOGERROR("Locking the index buffer without discard not supported on OpenGL ES");
-            #endif
-        }
+            glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * indexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
-    else
-        hwData = fallbackData_.Get() + start * indexSize_;
     
-    locked_ = true;
-    return hwData;
+    if (shadowData_ && shadowData_.Get() + start * indexSize_ != data)
+        memcpy(shadowData_.Get() + start * indexSize_, data, count * indexSize_);
+    
+    return true;
 }
 
-void IndexBuffer::Unlock()
+bool IndexBuffer::UpdateToGPU()
 {
-    if (locked_)
+    if (object_ && shadowData_)
     {
-        locked_ = false;
-        
-        if (object_)
-        {
-            if (!discardLockData_)
-            {
-                #ifndef GL_ES_VERSION_2_0
-                graphics_->SetIndexBuffer(0);
-                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
-                glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
-                #endif
-            }
-            else
-            {
-                if (discardLockCount_ < indexCount_)
-                    SetDataRange(discardLockData_, discardLockStart_, discardLockCount_);
-                else
-                    SetData(discardLockData_);
-                graphics_->FreeDiscardLockBuffer(discardLockData_);
-                discardLockData_ = 0;
-            }
-        }
+        graphics_->SetIndexBuffer(0);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, shadowData_.Get(), dynamic_ ? GL_DYNAMIC_DRAW :
+            GL_STATIC_DRAW);
+        return true;
     }
+    else
+        return false;
 }
 
 void IndexBuffer::ClearDataLost()
 {
+    dataLost_ = false;
 }
 
 bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
 {
-    #ifdef GL_ES_VERSION_2_0
-    return false;
-    #endif
-    
-    void* data = Lock(start, count, LOCK_READONLY);
-    if (!data)
+    if (!shadowData_)
         return false;
     
     minVertex = M_MAX_UNSIGNED;
@@ -271,7 +198,7 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     
     if (indexSize_ == sizeof(unsigned))
     {
-        unsigned* indices = (unsigned*)data;
+        unsigned* indices = (unsigned*)shadowData_.Get();
         
         for (unsigned i = 0; i < count; ++i)
         {
@@ -283,7 +210,7 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     }
     else
     {
-        unsigned short* indices = (unsigned short*)data;
+        unsigned short* indices = (unsigned short*)shadowData_.Get();
         
         for (unsigned i = 0; i < count; ++i)
         {
@@ -295,8 +222,6 @@ bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& m
     }
     
     vertexCount = maxVertex - minVertex + 1;
-    
-    Unlock();
     return true;
 }
 
@@ -318,8 +243,6 @@ bool IndexBuffer::Create()
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
-    else
-        fallbackData_ = new unsigned char[indexCount_ * indexSize_];
     
     return true;
 }

+ 21 - 23
Engine/Graphics/OpenGL/OGLIndexBuffer.h

@@ -39,57 +39,55 @@ public:
     /// Destruct.
     virtual ~IndexBuffer();
     
-    /// Save data and release the buffer.
+    /// Mark the GPU resource destroyed on context destruction.
     virtual void OnDeviceLost();
-    /// Recreate the buffer from saved data.
+    /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     /// Release the buffer.
     virtual void Release();
     
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
     /// Set buffer size and dynamic mode. Previous data will be lost.
     bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
     /// Set all data in the buffer.
     bool SetData(const void* data);
-    /// Set a data range in the buffer.
-    bool SetDataRange(const void* data, unsigned start, unsigned count);
-    /// Lock a data range in the buffer. Return pointer to locked data if successful.
-    void* Lock(unsigned start, unsigned count, LockMode mode);
-    /// Unlock buffer.
-    void Unlock();
-    /// Clear data lost flag. No-op on OpenGL.
+    /// 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);
+    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
+    bool UpdateToGPU();
+    /// Clear data lost flag.
     void ClearDataLost();
     
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     bool IsDynamic() const { return dynamic_; }
-    /// Return whether data is lost. Always false on OpenGL.
+    /// Return whether data is lost due to context loss.
     bool IsDataLost() const { return false; }
     /// Return number of indices.
     unsigned GetIndexCount() const {return indexCount_; }
     /// Return index size.
     unsigned GetIndexSize() const { return indexSize_; }
-    /// Return used vertex range from index range.
+    /// Return used vertex range from index range. Only supported for shadowed buffers.
     bool GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
     
 private:
     /// Create buffer.
     bool Create();
     
-    /// Fallback data when operating with a null graphics subsystem.
-    SharedArrayPtr<unsigned char> fallbackData_;
-    /// Save data when OpenGL context needs to be destroyed and recreated.
-    SharedArrayPtr<unsigned char> saveData_;
-    /// Memory area for discard locking.
-    void* discardLockData_;
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
     /// Number of indices.
     unsigned indexCount_;
     /// Index size.
     unsigned indexSize_;
-    /// Discard lock range start.
-    unsigned discardLockStart_;
-    /// Discard lock vertex count.
-    unsigned discardLockCount_;
+    /// Shadowed flag.
+    bool shadowed_;
     /// Dynamic flag.
     bool dynamic_;
-    /// Buffer locked flag.
-    bool locked_;
+    /// Data lost flag.
+    bool dataLost_;
 };

+ 21 - 0
Engine/Graphics/OpenGL/OGLRenderSurface.cpp

@@ -86,6 +86,27 @@ bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned
     return true;
 }
 
+void RenderSurface::OnDeviceLost()
+{
+    Graphics* graphics = parentTexture_->GetGraphics();
+    if (!graphics)
+        return;
+    
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+    {
+        if (graphics->GetRenderTarget(i) == this)
+            graphics->ResetRenderTarget(i);
+    }
+    
+    if (graphics->GetDepthStencil() == this)
+        graphics->ResetDepthStencil();
+    
+    // Clean up also from non-active FBOs
+    graphics->CleanupRenderSurface(this);
+    
+    renderBuffer_ = 0;
+}
+
 void RenderSurface::Release()
 {
     Graphics* graphics = parentTexture_->GetGraphics();

+ 2 - 0
Engine/Graphics/OpenGL/OGLRenderSurface.h

@@ -50,6 +50,8 @@ public:
     void SetLinkedDepthStencil(RenderSurface* depthStencil);
     /// Create a renderbuffer. Return true if successful.
     bool CreateRenderBuffer(unsigned width, unsigned height, unsigned format);
+    /// Handle device loss.
+    void OnDeviceLost();
     /// Release renderbuffer if any.
     void Release();
     

+ 11 - 0
Engine/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -44,6 +44,17 @@ ShaderProgram::~ShaderProgram()
     Release();
 }
 
+void ShaderProgram::OnDeviceLost()
+{
+    GPUObject::OnDeviceLost();
+    
+    if (graphics_ && graphics_->GetShaderProgram() == this)
+        graphics_->SetShaders(0, 0);
+    
+    linked_ = false;
+    linkerOutput_.Clear();
+}
+
 void ShaderProgram::Release()
 {
     if (object_)

+ 2 - 1
Engine/Graphics/OpenGL/OGLShaderProgram.h

@@ -49,6 +49,8 @@ public:
     /// Destruct.
     ~ShaderProgram();
     
+    /// Mark the GPU resource destroyed on context destruction.
+    virtual void OnDeviceLost();
     /// Release shader program.
     virtual void Release();
     
@@ -83,5 +85,4 @@ private:
     String linkerOutput_;
     /// Linked flag.
     bool linked_;
-    
 };

+ 11 - 0
Engine/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -43,6 +43,17 @@ ShaderVariation::~ShaderVariation()
     Release();
 }
 
+void ShaderVariation::OnDeviceLost()
+{
+    GPUObject::OnDeviceLost();
+    
+    compiled_ = false;
+    compilerOutput_.Clear();
+    
+    if (graphics_)
+        graphics_->CleanupShaderPrograms();
+}
+
 void ShaderVariation::Release()
 {
     if (object_)

+ 2 - 0
Engine/Graphics/OpenGL/OGLShaderVariation.h

@@ -40,6 +40,8 @@ public:
     /// Destruct.
     virtual ~ShaderVariation();
     
+    /// Mark the GPU resource destroyed on context destruction.
+    virtual void OnDeviceLost();
     /// Release the shader.
     virtual void Release();
     

+ 1 - 0
Engine/Graphics/OpenGL/OGLTexture.cpp

@@ -77,6 +77,7 @@ Texture::Texture(Context* context) :
     dynamic_(false),
     shadowCompare_(false),
     parametersDirty_(true),
+    dataLost_(false),
     filterMode_(FILTER_DEFAULT)
 {
     for (int i = 0; i < MAX_COORDS; ++i)

+ 4 - 4
Engine/Graphics/OpenGL/OGLTexture.h

@@ -77,8 +77,8 @@ public:
     int GetWidth() const { return width_; }
     /// Return height.
     int GetHeight() const { return height_; }
-    /// Return whether data is lost. Always false on OpenGL.
-    bool IsDataLost() const { return false; }
+    /// Return whether data is lost due to context loss.
+    bool IsDataLost() const { return dataLost_; }
     /// Return whether parameters are dirty.
     bool GetParametersDirty() const { return parametersDirty_; }
     /// Return filtering mode.
@@ -139,6 +139,8 @@ protected:
     bool shadowCompare_;
     /// Parameters dirty flag.
     bool parametersDirty_;
+    /// Data lost flag.
+    bool dataLost_;
     /// Filtering mode.
     TextureFilterMode filterMode_;
     /// Addressing mode.
@@ -149,6 +151,4 @@ protected:
     Color borderColor_;
     /// Backup texture.
     SharedPtr<Texture> backupTexture_;
-    /// Save data per mip level.
-    Vector<SharedArrayPtr<unsigned char> > savedLevels_;
 };

+ 10 - 28
Engine/Graphics/OpenGL/OGLTexture2D.cpp

@@ -75,42 +75,24 @@ bool Texture2D::Load(Deserializer& source)
 
 void Texture2D::OnDeviceLost()
 {
-    savedLevels_.Clear();
+    GPUObject::OnDeviceLost();
     
-    // Check if save data is supported, in that case save data of each mip level
-    if (GetDataSize(width_, height_))
-    {
-        for (unsigned i = 0; i < levels_; ++i)
-        {
-            int levelWidth = GetLevelWidth(i);
-            int levelHeight = GetLevelHeight(i);
-            SharedArrayPtr<unsigned char> savedLevel(new unsigned char[GetDataSize(levelWidth, levelHeight)]);
-            GetData(i, savedLevel.Get());
-            savedLevels_.Push(savedLevel);
-        }
-    }
+    if (renderSurface_)
+        renderSurface_->OnDeviceLost();
     
-    Release();
+    dataLost_ = true;
 }
 
 void Texture2D::OnDeviceReset()
 {
-    if (!object_)
+    // If has a file name, reload through the resource cache. Otherwise just recreate.
+    if (!GetName().Trimmed().Empty())
     {
-        Create();
-        
-        // Restore texture from save data if it exists
-        if (savedLevels_.Size())
-        {
-            for (unsigned i = 0; i < savedLevels_.Size(); ++i)
-            {
-                int levelWidth = GetLevelWidth(i);
-                int levelHeight = GetLevelHeight(i);
-                SetData(i, 0, 0, levelWidth, levelHeight, savedLevels_[i].Get());
-            }
-            savedLevels_.Clear();
-        }
+        if (GetSubsystem<ResourceCache>()->ReloadResource(this))
+            dataLost_ = false;
     }
+    else
+        Create();
 }
 
 void Texture2D::Release()

+ 2 - 2
Engine/Graphics/OpenGL/OGLTexture2D.h

@@ -43,9 +43,9 @@ public:
     
     /// Load resource. Return true if successful.
     virtual bool Load(Deserializer& source);
-    /// Save data and release the texture.
+    /// Mark the GPU resource destroyed on context destruction.
     virtual void OnDeviceLost();
-    /// Recreate the texture from saved data if necessary and possible.
+    /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     /// Release the texture.
     virtual void Release();

+ 11 - 31
Engine/Graphics/OpenGL/OGLTextureCube.cpp

@@ -62,47 +62,27 @@ void TextureCube::RegisterObject(Context* context)
 
 void TextureCube::OnDeviceLost()
 {
-    savedLevels_.Clear();
+    GPUObject::OnDeviceLost();
     
-    // Check if save data is supported, in that case save data of each face and mip level
-    if (GetDataSize(width_, height_))
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
     {
-        for (unsigned i = 0; i < levels_; ++i)
-        {
-            for (unsigned face = FACE_POSITIVE_X; face < MAX_CUBEMAP_FACES; ++face)
-            {
-                int levelWidth = GetLevelWidth(i);
-                int levelHeight = GetLevelHeight(i);
-                SharedArrayPtr<unsigned char> savedLevel(new unsigned char[GetDataSize(levelWidth, levelHeight)]);
-                GetData((CubeMapFace)face, i, savedLevel.Get());
-                savedLevels_.Push(savedLevel);
-            }
-        }
+        if (renderSurfaces_[i])
+            renderSurfaces_[i]->OnDeviceLost();
     }
     
-    Release();
+    dataLost_ = true;
 }
 
 void TextureCube::OnDeviceReset()
 {
-    if (!object_)
+    // If has a file name, reload through the resource cache. Otherwise just recreate.
+    if (!GetName().Trimmed().Empty())
     {
-        Create();
-        
-        // Restore texture from save data if it exists
-        if (savedLevels_.Size())
-        {
-            for (unsigned i = 0; i < savedLevels_.Size(); ++i)
-            {
-                CubeMapFace face = (CubeMapFace)(i % 6);
-                unsigned level = i / 6;
-                int levelWidth = GetLevelWidth(level);
-                int levelHeight = GetLevelHeight(level);
-                SetData(face, level, 0, 0, levelWidth, levelHeight, savedLevels_[i].Get());
-            }
-            savedLevels_.Clear();
-        }
+        if (GetSubsystem<ResourceCache>()->ReloadResource(this))
+            dataLost_ = false;
     }
+    else
+        Create();
 }
 
 void TextureCube::Release()

+ 2 - 2
Engine/Graphics/OpenGL/OGLTextureCube.h

@@ -45,9 +45,9 @@ public:
     
     /// Load resource. Return true if successful.
     virtual bool Load(Deserializer& source);
-    /// Save data and release the texture.
+    /// Mark the GPU resource destroyed on context destruction.
     virtual void OnDeviceLost();
-    /// Recreate the texture from saved data if necessary and possible.
+    /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     /// Release the texture.
     virtual void Release();

+ 57 - 169
Engine/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -121,15 +121,17 @@ OBJECTTYPESTATIC(VertexBuffer);
 VertexBuffer::VertexBuffer(Context* context) :
     Object(context),
     GPUObject(GetSubsystem<Graphics>()),
-    discardLockData_(0),
     vertexCount_(0),
     elementMask_(0),
-    morphRangeStart_(0),
-    morphRangeCount_(0),
+    shadowed_(false),
     dynamic_(false),
-    locked_(false)
+    dataLost_(false)
 {
     UpdateOffsets();
+    
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
 }
 
 VertexBuffer::~VertexBuffer()
@@ -139,17 +141,9 @@ VertexBuffer::~VertexBuffer()
 
 void VertexBuffer::OnDeviceLost()
 {
-    if (object_)
-    {
-        void* hwData = Lock(0, vertexCount_, LOCK_READONLY);
-        if (hwData)
-        {
-            saveData_ = new unsigned char[vertexCount_ * vertexSize_];
-            memcpy(saveData_.Get(), hwData, vertexCount_ * vertexSize_);
-        }
-        Unlock();
-        Release();
-    }
+    GPUObject::OnDeviceLost();
+    
+    dataLost_ = true;
 }
 
 void VertexBuffer::OnDeviceReset()
@@ -157,23 +151,15 @@ void VertexBuffer::OnDeviceReset()
     if (!object_)
     {
         Create();
-        if (saveData_)
-        {
-            SetData(saveData_.Get());
-            saveData_.Reset();
-        }
+        if (UpdateToGPU())
+            dataLost_ = false;
     }
 }
 
 void VertexBuffer::Release()
 {
-    if (object_)
+    if (object_ && graphics_)
     {
-        if (!graphics_)
-            return;
-        
-        Unlock();
-        
         for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
         {
             if (graphics_->GetVertexBuffer(i) == this)
@@ -183,8 +169,23 @@ void VertexBuffer::Release()
         glDeleteBuffers(1, &object_);
         object_ = 0;
     }
+}
+
+void VertexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
     
-    fallbackData_.Reset();
+    if (enable != shadowed_)
+    {
+        if (enable && vertexSize_ && vertexCount_)
+            shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
 }
 
 bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
@@ -193,13 +194,13 @@ bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dyna
     vertexCount_ = vertexCount;
     elementMask_ = elementMask;
     
-    if (morphRangeStart_ + morphRangeCount_ > vertexCount_)
-    {
-        morphRangeStart_ = 0;
-        morphRangeCount_ = 0;
-    }
-    
     UpdateOffsets();
+    
+    if (shadowed_ && vertexCount_ && vertexSize_)
+        shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+    else
+        shadowData_.Reset();
+    
     return Create();
 }
 
@@ -211,26 +212,23 @@ bool VertexBuffer::SetData(const void* data)
         return false;
     }
     
-    if (locked_)
-        Unlock();
-    
     if (object_)
     {
         glBindBuffer(GL_ARRAY_BUFFER, object_);
         glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
-        return true;
-    }
-    else if (fallbackData_)
-    {
-        memcpy(fallbackData_.Get(), data, vertexCount_ * vertexSize_);
-        return true;
     }
     
-    return false;
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
+    
+    return true;
 }
 
-bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count)
+bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
 {
+    if (start == 0 && count == vertexCount_)
+        return SetData(data);
+    
     if (!data)
     {
         LOGERROR("Null pointer for vertex buffer data");
@@ -243,147 +241,39 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
         return false;
     }
     
-    if (locked_)
-        Unlock();
+    if (!count)
+        return true;
     
     if (object_)
     {
         glBindBuffer(GL_ARRAY_BUFFER, object_);
-        glBufferSubData(GL_ARRAY_BUFFER, start * vertexSize_, count * vertexSize_, data);
-        return true;
-    }
-    else if (fallbackData_)
-    {
-        memcpy(fallbackData_.Get() + start * vertexSize_, data, count * vertexSize_);
-        return true;
+        if (!discard || start != 0)
+            glBufferSubData(GL_ARRAY_BUFFER, start * vertexSize_, count * vertexSize_, data);
+        else
+            glBufferData(GL_ARRAY_BUFFER, count * vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
     
-    return false;
-}
-
-bool VertexBuffer::SetMorphRange(unsigned start, unsigned count)
-{
-    if (start + count > vertexCount_)
-    {
-        LOGERROR("Illegal morph range");
-        return false;
-    }
+    if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data)
+        memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_);
     
-    morphRangeStart_ = start;
-    morphRangeCount_ = count;
     return true;
 }
 
-void VertexBuffer::SetMorphRangeResetData(const SharedArrayPtr<unsigned char>& data)
-{
-    morphRangeResetData_ = data;
-}
-
-void* VertexBuffer::Lock(unsigned start, unsigned count, LockMode mode)
+bool VertexBuffer::UpdateToGPU()
 {
-    if (!object_ && !fallbackData_)
-        return 0;
-    
-    if (locked_)
-    {
-        LOGERROR("Vertex buffer already locked");
-        return 0;
-    }
-    
-    if (!count || start + count > vertexCount_)
-    {
-        LOGERROR("Illegal range for locking vertex buffer");
-        return 0;
-    }
-    
-    void* hwData = 0;
-    
-    if (object_)
+    if (object_ && shadowData_)
     {
-        #ifndef GL_ES_VERSION_2_0
-        GLenum glLockMode = GL_WRITE_ONLY;
-        if (mode == LOCK_READONLY)
-            glLockMode = GL_READ_ONLY;
-        else if (mode == LOCK_NORMAL)
-            glLockMode = GL_READ_WRITE;
-        #endif
-        
-        // In discard mode, use a CPU side buffer to avoid stalling
-        if (mode == LOCK_DISCARD)
-        {
-            hwData = discardLockData_ = graphics_->ReserveDiscardLockBuffer(count * vertexSize_);
-            discardLockStart_ = start;
-            discardLockCount_ = count;
-        }
-        else
-        {
-            #ifndef GL_ES_VERSION_2_0
-            glBindBuffer(GL_ARRAY_BUFFER, object_);
-            hwData = glMapBuffer(GL_ARRAY_BUFFER, glLockMode);
-            if (!hwData)
-                return 0;
-            hwData = (unsigned char*)hwData + start * vertexSize_;
-            #else
-            LOGERROR("Locking the vertex buffer without discard not supported on OpenGL ES");
-            #endif
-        }
+        glBindBuffer(GL_ARRAY_BUFFER, object_);
+        glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, shadowData_.Get(), dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+        return true;
     }
     else
-        hwData = fallbackData_.Get() + start * vertexSize_;
-    
-    locked_ = true;
-    return hwData;
-}
-
-void VertexBuffer::Unlock()
-{
-    if (locked_)
-    {
-        locked_ = false;
-        
-        if (object_)
-        {
-            if (!discardLockData_)
-            {
-                #ifndef GL_ES_VERSION_2_0
-                glBindBuffer(GL_ARRAY_BUFFER, object_);
-                glUnmapBuffer(GL_ARRAY_BUFFER);
-                #endif
-            }
-            else
-            {
-                if (discardLockCount_ < vertexCount_)
-                    SetDataRange(discardLockData_, discardLockStart_, discardLockCount_);
-                else
-                    SetData(discardLockData_);
-                graphics_->FreeDiscardLockBuffer(discardLockData_);
-                discardLockData_ = 0;
-            }
-        }
-    }
-}
-
-void* VertexBuffer::LockMorphRange()
-{
-    if (!HasMorphRange())
-    {
-        LOGERROR("No vertex morph range defined");
-        return 0;
-    }
-    
-    return Lock(morphRangeStart_, morphRangeCount_, LOCK_DISCARD);
-}
-
-void VertexBuffer::ResetMorphRange(void* lockedMorphRange)
-{
-    if (!lockedMorphRange || !morphRangeResetData_)
-        return;
-    
-    memcpy(lockedMorphRange, morphRangeResetData_.Get(), morphRangeCount_ * vertexSize_);
+        return false;
 }
 
 void VertexBuffer::ClearDataLost()
 {
+    dataLost_ = false;
 }
 
 void VertexBuffer::UpdateOffsets()
@@ -431,8 +321,6 @@ bool VertexBuffer::Create()
         glBindBuffer(GL_ARRAY_BUFFER, object_);
         glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
-    else
-        fallbackData_ = new unsigned char[vertexCount_ * vertexSize_];
     
     return true;
 }

+ 22 - 47
Engine/Graphics/OpenGL/OGLVertexBuffer.h

@@ -38,37 +38,31 @@ public:
     /// Destruct.
     virtual ~VertexBuffer();
     
-    /// Save data and release the buffer.
+    /// Mark the GPU resource destroyed on context destruction.
     virtual void OnDeviceLost();
-    /// Recreate the buffer from saved data.
+    /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     /// Release the buffer.
     virtual void Release();
     
-    /// Set size and vertex elements and dynamic mode. Previous data will be lost.
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// %Set size and vertex elements and dynamic mode. Previous data will be lost.
     bool SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic = false);
-    /// Set all data in the buffer.
+    /// %Set all data in the buffer.
     bool SetData(const void* data);
-    /// Set a data range in the buffer.
-    bool SetDataRange(const void* data, unsigned first, unsigned count);
-    /// Set the vertex range to use for morphing.
-    bool SetMorphRange(unsigned start, unsigned count);
-    /// Set data to be used for resetting the morph vertex range.
-    void SetMorphRangeResetData(const SharedArrayPtr<unsigned char>& data);
-    /// Lock a data range in the buffer. Return pointer to locked data if successful.
-    void* Lock(unsigned start, unsigned count, LockMode mode);
-    /// Unlock buffer.
-    void Unlock();
-    /// Lock the morph vertex range. Return pointer to locked data if successful.
-    void* LockMorphRange();
-    /// Reset the morph vertex range. Needs to be locked first.
-    void ResetMorphRange(void* lockedMorphRange);
-    /// Clear data lost flag. No-op on OpenGL.
+    /// %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);
+    /// Update the shadow data to the GPU buffer. Call after manipulating shadow data directly, not necessary with SetData().
+    bool UpdateToGPU();
+    /// Clear data lost flag.
     void ClearDataLost();
     
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
     /// Return whether is dynamic.
     bool IsDynamic() const { return dynamic_; }
-    /// Return whether data is lost. Always false on OpenGL.
+    /// Return whether data is lost due to context loss.
     bool IsDataLost() const { return false; }
     /// Return number of vertices.
     unsigned GetVertexCount() const {return vertexCount_; }
@@ -78,15 +72,8 @@ public:
     unsigned GetElementMask() const { return elementMask_; }
     /// Return offset of a specified element within a vertex.
     unsigned GetElementOffset(VertexElement element) const { return elementOffset_[element]; }
-    /// Return morph vertex range start.
-    unsigned GetMorphRangeStart() const { return morphRangeStart_; }
-    /// Return number of vertices in the morph range.
-    unsigned GetMorphRangeCount() const { return morphRangeCount_; }
-    /// Return morph vertex range reset data.
-    const SharedArrayPtr<unsigned char>& GetMorphRangeResetData() { return morphRangeResetData_; }
-    /// Return whether has a morph vertex range defined.
-    bool HasMorphRange() const { return morphRangeCount_ > 0; }
-    
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
     /// Return vertex size corresponding to a vertex element mask.
     static unsigned GetVertexSize(unsigned elementMask);
     
@@ -107,14 +94,8 @@ private:
     /// Create buffer.
     bool Create();
     
-    /// Fallback data when operating with a null graphics subsystem.
-    SharedArrayPtr<unsigned char> fallbackData_;
-    /// Morph vertex range reset data.
-    SharedArrayPtr<unsigned char> morphRangeResetData_;
-    /// Save data when OpenGL context needs to be destroyed and recreated.
-    SharedArrayPtr<unsigned char> saveData_;
-    /// Memory area for discard locking.
-    void* discardLockData_;
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
     /// Number of vertices.
     unsigned vertexCount_;
     /// Vertex size.
@@ -123,16 +104,10 @@ private:
     unsigned elementMask_;
     /// Vertex element offsets.
     unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
-    /// Morph vertex range start.
-    unsigned morphRangeStart_;
-    /// Number of vertices in the morph range.
-    unsigned morphRangeCount_;
-    /// Discard lock range start.
-    unsigned discardLockStart_;
-    /// Discard lock vertex count.
-    unsigned discardLockCount_;
+    /// Shadowed flag.
+    bool shadowed_;
     /// Dynamic flag.
     bool dynamic_;
-    /// Locked flag.
-    bool locked_;
+    /// Data lost flag.
+    bool dataLost_;
 };

+ 52 - 34
Engine/Graphics/Renderer.cpp

@@ -641,6 +641,10 @@ void Renderer::Render()
     
     PROFILE(RenderViews);
     
+    // If the indirection textures have lost content (OpenGL mode only), restore them now
+    if (faceSelectCubeMap_ && faceSelectCubeMap_->IsDataLost())
+        SetIndirectionTextureData();
+    
     graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
     graphics_->SetTextureAnisotropy(textureAnisotropy_);
     graphics_->ClearParameterSources();
@@ -1656,10 +1660,12 @@ void Renderer::ReloadTextures()
 void Renderer::CreateGeometries()
 {
     SharedPtr<VertexBuffer> dlvb(new VertexBuffer(context_));
+    dlvb->SetShadowed(true);
     dlvb->SetSize(4, MASK_POSITION);
     dlvb->SetData(dirLightVertexData);
     
     SharedPtr<IndexBuffer> dlib(new IndexBuffer(context_));
+    dlib->SetShadowed(true);
     dlib->SetSize(6, false);
     dlib->SetData(dirLightIndexData);
     
@@ -1669,10 +1675,12 @@ void Renderer::CreateGeometries()
     dirLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, dlib->GetIndexCount());
     
     SharedPtr<VertexBuffer> slvb(new VertexBuffer(context_));
+    slvb->SetShadowed(true);
     slvb->SetSize(8, MASK_POSITION);
     slvb->SetData(spotLightVertexData);
     
     SharedPtr<IndexBuffer> slib(new IndexBuffer(context_));
+    slib->SetShadowed(true);
     slib->SetSize(36, false);
     slib->SetData(spotLightIndexData);
     
@@ -1682,10 +1690,12 @@ void Renderer::CreateGeometries()
     spotLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, slib->GetIndexCount());
     
     SharedPtr<VertexBuffer> plvb(new VertexBuffer(context_));
+    plvb->SetShadowed(true);
     plvb->SetSize(24, MASK_POSITION);
     plvb->SetData(pointLightVertexData);
     
     SharedPtr<IndexBuffer> plib(new IndexBuffer(context_));
+    plib->SetShadowed(true);
     plib->SetSize(132, false);
     plib->SetData(pointLightIndexData);
     
@@ -1702,18 +1712,6 @@ void Renderer::CreateGeometries()
         faceSelectCubeMap_->SetSize(1, graphics_->GetRGBAFormat());
         faceSelectCubeMap_->SetFilterMode(FILTER_NEAREST);
         
-        unsigned char data[256 * 256 * 4];
-        
-        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
-        {
-            unsigned axis = i / 2;
-            data[0] = (axis == 0) ? 255 : 0;
-            data[1] = (axis == 1) ? 255 : 0;
-            data[2] = (axis == 2) ? 255 : 0;
-            data[3] = 0;
-            faceSelectCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 1, 1, data);
-        }
-        
         indirectionCubeMap_ = new TextureCube(context_);
         indirectionCubeMap_->SetNumLevels(1);
         indirectionCubeMap_->SetSize(256, graphics_->GetRGBAFormat());
@@ -1722,33 +1720,53 @@ void Renderer::CreateGeometries()
         indirectionCubeMap_->SetAddressMode(COORD_V, ADDRESS_CLAMP);
         indirectionCubeMap_->SetAddressMode(COORD_W, ADDRESS_CLAMP);
         
-        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        SetIndirectionTextureData();
+    }
+    #endif
+}
+
+void Renderer::SetIndirectionTextureData()
+{
+    unsigned char data[256 * 256 * 4];
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        unsigned axis = i / 2;
+        data[0] = (axis == 0) ? 255 : 0;
+        data[1] = (axis == 1) ? 255 : 0;
+        data[2] = (axis == 2) ? 255 : 0;
+        data[3] = 0;
+        faceSelectCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 1, 1, data);
+    }
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        unsigned char faceX = (i & 1) * 255;
+        unsigned char faceY = (i / 2) * 255 / 3;
+        unsigned char* dest = data;
+        for (unsigned y = 0; y < 256; ++y)
         {
-            unsigned char faceX = (i & 1) * 255;
-            unsigned char faceY = (i / 2) * 255 / 3;
-            unsigned char* dest = data;
-            for (unsigned y = 0; y < 256; ++y)
+            for (unsigned x = 0; x < 256; ++x)
             {
-                for (unsigned x = 0; x < 256; ++x)
-                {
-                    #ifdef USE_OPENGL
-                    *dest++ = x;
-                    *dest++ = 255 - y;
-                    *dest++ = faceX;
-                    *dest++ = 255 * 2 / 3 - faceY;
-                    #else
-                    *dest++ = x;
-                    *dest++ = y;
-                    *dest++ = faceX;
-                    *dest++ = faceY;
-                    #endif
-                }
+                #ifdef USE_OPENGL
+                *dest++ = x;
+                *dest++ = 255 - y;
+                *dest++ = faceX;
+                *dest++ = 255 * 2 / 3 - faceY;
+                #else
+                *dest++ = x;
+                *dest++ = y;
+                *dest++ = faceX;
+                *dest++ = faceY;
+                #endif
             }
-            
-            indirectionCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 256, 256, data);
         }
+        
+        indirectionCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 256, 256, data);
     }
-    #endif
+    
+    faceSelectCubeMap_->ClearDataLost();
+    indirectionCubeMap_->ClearDataLost();
 }
 
 void Renderer::CreateInstancingBuffer()

+ 2 - 0
Engine/Graphics/Renderer.h

@@ -345,6 +345,8 @@ private:
     void CreateGeometries();
     /// Create instancing vertex buffer.
     void CreateInstancingBuffer();
+    /// Create point light shadow indirection texture data.
+    void SetIndirectionTextureData();
     /// Prepare for rendering of a new view.
     void PrepareViewRender();
     /// Remove unused occlusion and screen buffers.

+ 14 - 15
Engine/Graphics/View.cpp

@@ -2397,23 +2397,22 @@ void View::PrepareInstancingBuffer()
     {
         VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
         unsigned freeIndex = 0;
-        void* lockedData = instancingBuffer->Lock(0, totalInstances, LOCK_DISCARD);
-        if (lockedData)
+        void* scratch = graphics_->ReserveScratchBuffer(totalInstances * instancingBuffer->GetVertexSize());
+        
+        baseQueue_.SetTransforms(renderer_, scratch, freeIndex);
+        preAlphaQueue_.SetTransforms(renderer_, scratch, freeIndex);
+        if (renderMode_ != RENDER_FORWARD)
+            gbufferQueue_.SetTransforms(renderer_, scratch, freeIndex);
+        
+        for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
         {
-            baseQueue_.SetTransforms(renderer_, lockedData, freeIndex);
-            preAlphaQueue_.SetTransforms(renderer_, lockedData, freeIndex);
-            if (renderMode_ != RENDER_FORWARD)
-                gbufferQueue_.SetTransforms(renderer_, lockedData, freeIndex);
-            
-            for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
-            {
-                for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
-                    i->shadowSplits_[j].shadowBatches_.SetTransforms(renderer_, lockedData, freeIndex);
-                i->litBatches_.SetTransforms(renderer_, lockedData, freeIndex);
-            }
-            
-            instancingBuffer->Unlock();
+            for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
+                i->shadowSplits_[j].shadowBatches_.SetTransforms(renderer_, scratch, freeIndex);
+            i->litBatches_.SetTransforms(renderer_, scratch, freeIndex);
         }
+        
+        instancingBuffer->SetDataRange(scratch, 0, totalInstances, true);
+        graphics_->FreeScratchBuffer(scratch);
     }
 }
 

+ 9 - 1
Engine/UI/Font.cpp

@@ -154,7 +154,15 @@ const FontFace* Font::GetFace(int pointSize)
 {
     HashMap<int, SharedPtr<FontFace> >::ConstIterator i = faces_.Find(pointSize);
     if (i != faces_.End())
-        return i->second_;
+    {
+        if (!i->second_->texture_->IsDataLost())
+            return i->second_;
+        else
+        {
+            // Erase and reload face if texture data lost (OpenGL mode only)
+            faces_.Erase(pointSize);
+        }
+    }
     
     PROFILE(GetFontFace);
     

+ 4 - 5
Engine/UI/UI.cpp

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