Bläddra i källkod

Use a vertex buffer for UI rendering instead of several immediate mode draw operations. The vertex buffer is locked and updated once per frame, instead of several smaller locks.

Lasse Öörni 14 år sedan
förälder
incheckning
71d2e019e6
5 ändrade filer med 82 tillägg och 53 borttagningar
  1. 1 1
      Docs/Reference.dox
  2. 51 10
      Engine/UI/UI.cpp
  3. 3 0
      Engine/UI/UI.h
  4. 25 40
      Engine/UI/UIBatch.cpp
  5. 2 2
      Engine/UI/UIBatch.h

+ 1 - 1
Docs/Reference.dox

@@ -367,7 +367,7 @@ Graphics implements the low-level functionality:
 - Handling lost device
 - Performing primitive rendering operations
 
-It also provides a low-performance, immediate-like interface for manually defining small amounts of geometry to be rendered. This interface is used for rendering the debug geometry and the user interface.
+It also provides a low-performance, immediate-like interface for manually defining small amounts of geometry to be rendered. This interface is used for rendering the debug geometry.
 
 Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling level are all set at once by calling Graphics's \ref Graphics::SetMode "SetMode()" function.
 

+ 51 - 10
Engine/UI/UI.cpp

@@ -46,6 +46,7 @@
 #include "Texture2D.h"
 #include "UI.h"
 #include "UIEvents.h"
+#include "VertexBuffer.h"
 #include "Window.h"
 
 #include "Sort.h"
@@ -220,12 +221,37 @@ void UI::RenderUpdate()
     if (!rootElement_ || !graphics_ || graphics_->IsDeviceLost())
         return;
     
-    PROFILE(GetUIBatches);
+    {
+        PROFILE(GetUIBatches);
+        
+        // Get batches & quads from the UI elements
+        batches_.Clear();
+        quads_.Clear();
+        const IntVector2& rootSize = rootElement_->GetSize();
+        GetBatches(rootElement_, IntRect(0, 0, rootSize.x_, rootSize.y_));
+    }
     
-    batches_.Clear();
-    quads_.Clear();
-    const IntVector2& rootSize = rootElement_->GetSize();
-    GetBatches(rootElement_, IntRect(0, 0, rootSize.x_, rootSize.y_));
+    {
+        PROFILE(UpdateUIGeometry);
+        
+        // Update quad geometry into the vertex buffer
+        unsigned numVertices = quads_.Size() * 6;
+        if (numVertices)
+        {
+            if (vertexBuffer_->GetVertexCount() < numVertices)
+                vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
+        
+            unsigned vertexSize = vertexBuffer_->GetVertexSize();
+            unsigned char* lockedData = (unsigned char*)vertexBuffer_->Lock(0, numVertices, LOCK_DISCARD);
+        
+            if (lockedData)
+            {
+                for (unsigned i = 0; i < batches_.Size(); ++i)
+                    batches_[i].UpdateGeometry(graphics_, lockedData + 6 * batches_[i].quadStart_ * vertexSize);
+                vertexBuffer_->Unlock();
+            }
+        }
+    }
     
     // If no drag, reset cursor shape for next frame
     if (cursor_ && !dragElement_)
@@ -259,6 +285,7 @@ void UI::Render()
     graphics_->SetDepthWrite(false);
     graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetStencilTest(false);
+    graphics_->SetVertexBuffer(vertexBuffer_);
     
     ShaderVariation* ps = 0;
     ShaderVariation* vs = 0;
@@ -267,18 +294,21 @@ void UI::Render()
     
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
-        // Choose shaders here so that UIBatch does not need to look up shaders each time
-        if (!batches_[i].texture_)
+        const UIBatch& batch = batches_[i];
+        if (!batch.quadCount_)
+            continue;
+        
+        if (!batch.texture_)
         {
             ps = noTexturePS_;
             vs = noTextureVS_;
         }
         else
         {
-            // If texture contains only an alpha channel, interpret it as alpha (for fonts)
+            // If texture contains only an alpha channel, use alpha shader (for fonts)
             vs = diffTextureVS_;
             
-            if (batches_[i].texture_->GetFormat() == alphaFormat)
+            if (batch.texture_->GetFormat() == alphaFormat)
                 ps = alphaTexturePS_;
             else
                 ps = diffTexturePS_;
@@ -292,7 +322,16 @@ void UI::Render()
         if (graphics_->NeedParameterUpdate(PSP_MATDIFFCOLOR, this))
             graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
         
-        batches_[i].Draw(graphics_);
+        // Use alpha test if not alpha blending
+        if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
+            graphics_->SetAlphaTest(true, CMP_GREATEREQUAL, 0.5f);
+        else
+            graphics_->SetAlphaTest(false);
+        
+        graphics_->SetBlendMode(batch.blendMode_);
+        graphics_->SetScissorTest(true, batch.scissor_);
+        graphics_->SetTexture(0, batch.texture_);
+        graphics_->Draw(TRIANGLE_LIST, batch.quadStart_ * 6, batch.quadCount_ * 6);
     }
 }
 
@@ -433,6 +472,8 @@ void UI::Initialize()
         alphaTexturePS_ = basicPS->GetVariation("AlphaVCol");
     }
     
+    vertexBuffer_ = new VertexBuffer(context_);
+    
     LOGINFO("Initialized user interface");
     initialized_ = true;
 }

+ 3 - 0
Engine/UI/UI.h

@@ -31,6 +31,7 @@ class Graphics;
 class ResourceCache;
 class UIBatch;
 class UIElement;
+class VertexBuffer;
 class XMLElement;
 class XMLFile;
 
@@ -139,6 +140,8 @@ private:
     PODVector<UIBatch> batches_;
     /// UI rendering quads.
     PODVector<UIQuad> quads_;
+    /// UI vertex buffer.
+    SharedPtr<VertexBuffer> vertexBuffer_;
     /// Clipboard text.
     String clipBoard_;
     /// Mouse buttons held down.

+ 25 - 40
Engine/UI/UIBatch.cpp

@@ -165,21 +165,11 @@ bool UIBatch::Merge(const UIBatch& batch)
     return true;
 }
 
-void UIBatch::Draw(Graphics* graphics) const
+void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
 {
-    if (!quads_ || !quadCount_)
+    if (!quadCount_)
         return;
     
-    // Use alpha test if not alpha blending
-    if (blendMode_ != BLEND_ALPHA && blendMode_ != BLEND_ADDALPHA && blendMode_ != BLEND_PREMULALPHA)
-        graphics->SetAlphaTest(true, CMP_GREATEREQUAL, 0.5f);
-    else
-        graphics->SetAlphaTest(false);
-    
-    graphics->SetBlendMode(blendMode_);
-    graphics->SetScissorTest(true, scissor_);
-    graphics->SetTexture(0, texture_);
-    
     #ifdef USE_OPENGL
     Vector2 posAdjust(Vector2::ZERO);
     #else
@@ -187,18 +177,15 @@ void UIBatch::Draw(Graphics* graphics) const
     #endif
     Vector2 invScreenSize(1.0f / (float)graphics->GetWidth(), 1.0f / (float)graphics->GetHeight());
     
-    const PODVector<UIQuad>& quads = *quads_;
+    float* dest = (float*)lockedData;
     
     if (texture_)
     {
         Vector2 invTextureSize(1.0f / (float)texture_->GetWidth(), 1.0f / (float)texture_->GetHeight());
         
-        graphics->BeginImmediate(TRIANGLE_LIST, quadCount_ * 6, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1);
-        float* dest = (float*)graphics->GetImmediateDataPtr();
-        
-        for (unsigned i = quadStart_; i < quadStart_ + quadCount_; ++i)
+        for (unsigned i = 0; i < quadCount_; ++i)
         {
-            const UIQuad& quad = quads[i];
+            const UIQuad& quad = quads_->At(quadStart_ + i);
             Vector2 topLeft, bottomRight, topLeftUV, bottomRightUV;
             
             topLeft = (Vector2((float)quad.left_, (float)quad.top_) - posAdjust) * invScreenSize;
@@ -207,66 +194,64 @@ void UIBatch::Draw(Graphics* graphics) const
             bottomRightUV = Vector2((float)quad.rightUV_, (float)quad.bottomUV_) * invTextureSize;
             
             *dest++ = topLeft.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].topLeftColor_; dest++;
+            *((unsigned*)dest) = quad.topLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = topLeftUV.y_;
             
             *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].topRightColor_; dest++;
+            *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             
             *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomLeftColor_; dest++;
+            *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
             
             *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-
-            *((unsigned*)dest) = quads[i].topRightColor_; dest++;
+            *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             
             *dest++ = bottomRight.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomRightColor_; dest++;
+            *((unsigned*)dest) = quad.bottomRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = bottomRightUV.y_;
             
             *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomLeftColor_; dest++;
+            *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
         }
-        
-        graphics->EndImmediate();
     }
     else
     {
-        graphics->BeginImmediate(TRIANGLE_LIST, quadCount_ * 6, MASK_POSITION | MASK_COLOR);
-        float* dest = (float*)graphics->GetImmediateDataPtr();
-        
-        for (unsigned i = quadStart_; i < quadStart_ + quadCount_; ++i)
+        for (unsigned i = 0; i < quadCount_; ++i)
         {
-            const UIQuad& quad = quads[i];
+            const UIQuad& quad = quads_->At(quadStart_ + i);
             Vector2 topLeft, bottomRight, topLeftUV, bottomRightUV;
             
             topLeft = (Vector2((float)quad.left_, (float)quad.top_) - posAdjust) * invScreenSize;
             bottomRight = (Vector2((float)quad.right_, (float)quad.bottom_) - posAdjust) * invScreenSize;
             
             *dest++ = topLeft.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].topLeftColor_; dest++;
+            *((unsigned*)dest) = quad.topLeftColor_; dest++;
+            dest += 2; // Jump over unused UV coordinates
             
             *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].topRightColor_; dest++;
+            *((unsigned*)dest) = quad.topRightColor_; dest++;
+            dest += 2;
             
             *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomLeftColor_; dest++;
+            *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
+            dest += 2;
             
             *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].topRightColor_; dest++;
+            *((unsigned*)dest) = quad.topRightColor_; dest++;
+            dest += 2;
             
             *dest++ = bottomRight.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomRightColor_; dest++;
+            *((unsigned*)dest) = quad.bottomRightColor_; dest++;
+            dest += 2;
             
             *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
-            *((unsigned*)dest) = quads[i].bottomLeftColor_; dest++;
+            *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
+            dest += 2;
         }
-        
-        graphics->EndImmediate();
     }
 }
 

+ 2 - 2
Engine/UI/UIBatch.h

@@ -85,8 +85,8 @@ public:
     void AddQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth, int texHeight, const Color& color);
     /// Merge with another batch.
     bool Merge(const UIBatch& batch);
-    /// Draw.
-    void Draw(Graphics* graphics) const;
+    /// Update the vertex data.
+    void UpdateGeometry(Graphics* graphics, void* lockedData);
     
     /// Add or merge a batch.
     static void AddOrMerge(const UIBatch& batch, PODVector<UIBatch>& batches);