Browse Source

OpenGL 3 constant buffer support.

Lasse Öörni 10 years ago
parent
commit
0e5a05afc7

+ 3 - 1
Source/Urho3D/Graphics/ConstantBuffer.h

@@ -22,6 +22,8 @@
 
 
 #pragma once
 #pragma once
 
 
-#if defined(URHO3D_D3D11)
+#if defined(URHO3D_OPENGL)
+#include "OpenGL/OGLConstantBuffer.h"
+#elif defined(URHO3D_D3D11)
 #include "Direct3D11/D3D11ConstantBuffer.h"
 #include "Direct3D11/D3D11ConstantBuffer.h"
 #endif
 #endif

+ 139 - 0
Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.cpp

@@ -0,0 +1,139 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/ConstantBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+
+ConstantBuffer::ConstantBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>())
+{
+}
+
+ConstantBuffer::~ConstantBuffer()
+{
+    Release();
+}
+
+void ConstantBuffer::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+
+        #ifndef GL_ES_VERSION_2_0
+        glDeleteBuffers(1, &object_);
+        #endif
+        object_ = 0;
+    }
+
+    shadowData_.Reset();
+    size_ = 0;
+}
+
+void ConstantBuffer::OnDeviceReset()
+{
+    if (size_)
+        SetSize(size_); // Recreate
+}
+
+bool ConstantBuffer::SetSize(unsigned size)
+{
+    Release();
+
+    if (!size)
+    {
+        LOGERROR("Can not create zero-sized constant buffer");
+        return false;
+    }
+
+    // Round up to next 16 bytes
+    size += 15;
+    size &= 0xfffffff0;
+
+    size_ = size;
+    dirty_ = false;
+    shadowData_ = new unsigned char[size_];
+    memset(shadowData_.Get(), 0, size_);
+
+    if (graphics_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        glGenBuffers(1, &object_);
+        glBindBuffer(GL_UNIFORM_BUFFER, object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+    }
+
+    return true;
+}
+
+void ConstantBuffer::SetParameter(unsigned offset, unsigned size, const void* data)
+{
+    if (offset + size > size_)
+        return; // Would overflow the buffer
+
+    memcpy(&shadowData_[offset], data, size);
+    dirty_ = true;
+}
+
+void ConstantBuffer::SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data)
+{
+    if (offset + rows * 4 * sizeof(float) > size_)
+        return; // Would overflow the buffer
+
+    float* dest = (float*)&shadowData_[offset];
+    const float* src = (const float*)data;
+
+    while (rows--)
+    {
+        *dest++ = *src++;
+        *dest++ = *src++;
+        *dest++ = *src++;
+        ++dest; // Skip over the w coordinate
+    }
+
+    dirty_ = true;
+}
+
+void ConstantBuffer::Apply()
+{
+    if (dirty_ && object_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        glBindBuffer(GL_UNIFORM_BUFFER, object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+        dirty_ = false;
+    }
+}
+
+}

+ 75 - 0
Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.h

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+#include "../../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Hardware constant buffer.
+class URHO3D_API ConstantBuffer : public Object, public GPUObject
+{
+    OBJECT(ConstantBuffer);
+    
+public:
+    /// Construct.
+    ConstantBuffer(Context* context);
+    /// Destruct.
+    virtual ~ConstantBuffer();
+    
+    /// Recreate the GPU resource and restore data if applicable.
+    virtual void OnDeviceReset();
+    /// Release the buffer.
+    virtual void Release();
+    
+    /// Set size and create GPU-side buffer. Return true on success.
+    bool SetSize(unsigned size);
+    /// Set a generic parameter and mark buffer dirty.
+    void SetParameter(unsigned offset, unsigned size, const void* data);
+    /// Set a Vector3 array parameter and mark buffer dirty.
+    void SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data);
+    /// Apply to GPU.
+    void Apply();
+
+    /// Return size.
+    unsigned GetSize() const { return size_; }
+    /// Return whether has unapplied data.
+    bool IsDirty() const { return dirty_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Buffer byte size.
+    unsigned size_;
+    /// Dirty flag.
+    bool dirty_;
+};
+
+}

+ 296 - 176
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -25,6 +25,7 @@
 #include "../../Graphics/AnimationController.h"
 #include "../../Graphics/AnimationController.h"
 #include "../../Graphics/BillboardSet.h"
 #include "../../Graphics/BillboardSet.h"
 #include "../../Graphics/Camera.h"
 #include "../../Graphics/Camera.h"
+#include "../../Graphics/ConstantBuffer.h"
 #include "../../Core/Context.h"
 #include "../../Core/Context.h"
 #include "../../Graphics/CustomGeometry.h"
 #include "../../Graphics/CustomGeometry.h"
 #include "../../Graphics/DebugRenderer.h"
 #include "../../Graphics/DebugRenderer.h"
@@ -754,8 +755,7 @@ void Graphics::EndFrame()
 
 
 void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
 void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
 {
 {
-    if (impl_->fboDirty_)
-        CommitFramebuffer();
+    PrepareDraw();
     
     
     #ifdef GL_ES_VERSION_2_0
     #ifdef GL_ES_VERSION_2_0
     flags &= ~CLEAR_STENCIL;
     flags &= ~CLEAR_STENCIL;
@@ -838,8 +838,7 @@ void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCou
     if (!vertexCount)
     if (!vertexCount)
         return;
         return;
     
     
-    if (impl_->fboDirty_)
-        CommitFramebuffer();
+    PrepareDraw();
     
     
     unsigned primitiveCount;
     unsigned primitiveCount;
     GLenum glPrimitiveType;
     GLenum glPrimitiveType;
@@ -856,8 +855,7 @@ void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount
     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObject())
     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObject())
         return;
         return;
     
     
-    if (impl_->fboDirty_)
-        CommitFramebuffer();
+    PrepareDraw();
     
     
     unsigned indexSize = indexBuffer_->GetIndexSize();
     unsigned indexSize = indexBuffer_->GetIndexSize();
     unsigned primitiveCount;
     unsigned primitiveCount;
@@ -879,8 +877,7 @@ void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned i
     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObject() || !instancingSupport_)
     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObject() || !instancingSupport_)
         return;
         return;
     
     
-    if (impl_->fboDirty_)
-        CommitFramebuffer();
+    PrepareDraw();
     
     
     unsigned indexSize = indexBuffer_->GetIndexSize();
     unsigned indexSize = indexBuffer_->GetIndexSize();
     unsigned primitiveCount;
     unsigned primitiveCount;
@@ -1127,8 +1124,6 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
     if (vs == vertexShader_ && ps == pixelShader_)
     if (vs == vertexShader_ && ps == pixelShader_)
         return;
         return;
     
     
-    ClearParameterSources();
-
     // Compile the shaders now if not yet compiled. If already attempted, do not retry
     // Compile the shaders now if not yet compiled. If already attempted, do not retry
     if (vs && !vs->GetGPUObject())
     if (vs && !vs->GetGPUObject())
     {
     {
@@ -1222,9 +1217,33 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
         }
         }
     }
     }
     
     
-    // Update the clip plane uniform on GL3
-    if (gl3Support)
+    // Update the clip plane uniform on GL3, and set constant buffers
+    bool clearAllParameterGroups = true;
+
+    #ifndef GL_ES_VERSION_2_0
+    if (gl3Support && shaderProgram_)
+    {
+        // When using OpenGL 3, only clear the parameter groups that change their buffer binding
+        clearAllParameterGroups = false;
+
+        const SharedPtr<ConstantBuffer>* constantBuffers = shaderProgram_->GetConstantBuffers();
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS * 2; ++i)
+        {
+            ConstantBuffer* buffer = constantBuffers[i].Get();
+            if (buffer != currentConstantBuffers_[i])
+            {
+                glBindBufferBase(GL_UNIFORM_BUFFER, i, buffer->GetGPUObject());
+                shaderParameterSources_[i % MAX_SHADER_PARAMETER_GROUPS] = (const void*)M_MAX_UNSIGNED;
+                currentConstantBuffers_[i] = buffer;
+            }
+        }
+
         SetShaderParameter(VSP_CLIPPLANE, useClipPlane_ ? clipPlane_ : Vector4(0.0f, 0.0f, 0.0f, 1.0f));
         SetShaderParameter(VSP_CLIPPLANE, useClipPlane_ ? clipPlane_ : Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+    }
+    #endif
+    
+    if (clearAllParameterGroups)
+        ClearParameterSources();
 
 
     // Store shader combination if shader dumping in progress
     // Store shader combination if shader dumping in progress
     if (shaderPrecache_)
     if (shaderPrecache_)
@@ -1238,6 +1257,15 @@ void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
         {
         {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, count * sizeof(float), data);
+                return;
+            }
+
             switch (info->type_)
             switch (info->type_)
             {
             {
             case GL_FLOAT:
             case GL_FLOAT:
@@ -1274,7 +1302,18 @@ void Graphics::SetShaderParameter(StringHash param, float value)
     {
     {
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
+        {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(float), &value);
+                return;
+            }
+
             glUniform1fv(info->location_, 1, &value);
             glUniform1fv(info->location_, 1, &value);
+        }
     }
     }
 }
 }
 
 
@@ -1290,6 +1329,15 @@ void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
         {
         {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(Vector2), &vector);
+                return;
+            }
+
             // Check the uniform type to avoid mismatch
             // Check the uniform type to avoid mismatch
             switch (info->type_)
             switch (info->type_)
             {
             {
@@ -1311,7 +1359,18 @@ void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
     {
     {
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
+        {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetVector3ArrayParameter(info->location_, 3, &matrix);
+                return;
+            }
+
             glUniformMatrix3fv(info->location_, 1, GL_FALSE, matrix.Data());
             glUniformMatrix3fv(info->location_, 1, GL_FALSE, matrix.Data());
+        }
     }
     }
 }
 }
 
 
@@ -1322,6 +1381,15 @@ void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
         {
         {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(Vector3), &vector);
+                return;
+            }
+
             // Check the uniform type to avoid mismatch
             // Check the uniform type to avoid mismatch
             switch (info->type_)
             switch (info->type_)
             {
             {
@@ -1347,7 +1415,18 @@ void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
     {
     {
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
+        {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(Matrix4), &matrix);
+                return;
+            }
+
             glUniformMatrix4fv(info->location_, 1, GL_FALSE, matrix.Data());
             glUniformMatrix4fv(info->location_, 1, GL_FALSE, matrix.Data());
+        }
     }
     }
 }
 }
 
 
@@ -1358,6 +1437,15 @@ void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         const ShaderParameter* info = shaderProgram_->GetParameter(param);
         if (info)
         if (info)
         {
         {
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(Vector4), &vector);
+                return;
+            }
+
             // Check the uniform type to avoid mismatch
             // Check the uniform type to avoid mismatch
             switch (info->type_)
             switch (info->type_)
             {
             {
@@ -1403,6 +1491,15 @@ void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
             fullMatrix.m22_ = matrix.m22_;
             fullMatrix.m22_ = matrix.m22_;
             fullMatrix.m23_ = matrix.m23_;
             fullMatrix.m23_ = matrix.m23_;
 
 
+            if (info->bufferPtr_)
+            {
+                ConstantBuffer* buffer = info->bufferPtr_;
+                if (!buffer->IsDirty())
+                    dirtyConstantBuffers_.Push(buffer);
+                buffer->SetParameter(info->location_, sizeof(Matrix4), &fullMatrix);
+                return;
+            }
+
             glUniformMatrix4fv(info->location_, 1, GL_FALSE, fullMatrix.Data());
             glUniformMatrix4fv(info->location_, 1, GL_FALSE, fullMatrix.Data());
         }
         }
     }
     }
@@ -1696,8 +1793,7 @@ void Graphics::SetDepthStencil(Texture2D* texture)
 
 
 void Graphics::SetViewport(const IntRect& rect)
 void Graphics::SetViewport(const IntRect& rect)
 {
 {
-    if (impl_->fboDirty_)
-        CommitFramebuffer();
+    PrepareDraw();
     
     
     IntVector2 rtSize = GetRenderTargetDimensions();
     IntVector2 rtSize = GetRenderTargetDimensions();
     
     
@@ -2330,7 +2426,7 @@ void Graphics::CleanupRenderSurface(RenderSurface* surface)
         return;
         return;
 
 
     // Flush pending FBO changes first if any
     // Flush pending FBO changes first if any
-    CommitFramebuffer();
+    PrepareDraw();
 
 
     unsigned currentFbo = impl_->boundFbo_;
     unsigned currentFbo = impl_->boundFbo_;
 
 
@@ -2385,6 +2481,17 @@ void Graphics::CleanupShaderPrograms(ShaderVariation* variation)
         shaderProgram_ = 0;
         shaderProgram_ = 0;
 }
 }
 
 
+ConstantBuffer* Graphics::GetOrCreateConstantBuffer(unsigned bindingIndex, unsigned size)
+{
+    unsigned key = (bindingIndex << 16) | size;
+    HashMap<unsigned, SharedPtr<ConstantBuffer> >::Iterator i = constantBuffers_.Find(key);
+    if (i == constantBuffers_.End())
+    {
+        i = constantBuffers_.Insert(MakePair(key, SharedPtr<ConstantBuffer>(new ConstantBuffer(context_))));
+        i->second_->SetSize(size);
+    }
+    return i->second_.Get();
+}
 
 
 void Graphics::Release(bool clearGPUObjects, bool closeWindow)
 void Graphics::Release(bool clearGPUObjects, bool closeWindow)
 {
 {
@@ -2628,11 +2735,11 @@ unsigned Graphics::GetFloat32Format()
 
 
 unsigned Graphics::GetLinearDepthFormat()
 unsigned Graphics::GetLinearDepthFormat()
 {
 {
-#ifndef GL_ES_VERSION_2_0
+    #ifndef GL_ES_VERSION_2_0
     // OpenGL 3 can use different color attachment formats
     // OpenGL 3 can use different color attachment formats
     if (gl3Support)
     if (gl3Support)
         return GL_R32F;
         return GL_R32F;
-#endif
+    #endif
     // OpenGL 2 requires color attachments to have the same format, therefore encode deferred depth to RGBA manually
     // OpenGL 2 requires color attachments to have the same format, therefore encode deferred depth to RGBA manually
     // if not using a readable hardware depth texture
     // if not using a readable hardware depth texture
     return GL_RGBA;
     return GL_RGBA;
@@ -2771,215 +2878,224 @@ void Graphics::CheckFeatureSupport(String& extensions)
     #endif
     #endif
 }
 }
 
 
-void Graphics::CommitFramebuffer()
+void Graphics::PrepareDraw()
 {
 {
-    if (!impl_->fboDirty_)
-        return;
-    
-    impl_->fboDirty_ = false;
-    
-    // First check if no framebuffer is needed. In that case simply return to backbuffer rendering
-    bool noFbo = !depthStencil_;
-    if (noFbo)
+    #ifndef GL_ES_VERSION_2_0
+    if (gl3Support)
     {
     {
-        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        for (PODVector<ConstantBuffer*>::Iterator i = dirtyConstantBuffers_.Begin(); i != dirtyConstantBuffers_.End(); ++i)
+            (*i)->Apply();
+        dirtyConstantBuffers_.Clear();
+    }
+    #endif
+
+    if (impl_->fboDirty_)
+    {
+        impl_->fboDirty_ = false;
+    
+        // First check if no framebuffer is needed. In that case simply return to backbuffer rendering
+        bool noFbo = !depthStencil_;
+        if (noFbo)
         {
         {
-            if (renderTargets_[i])
+            for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
             {
             {
-                noFbo = false;
-                break;
+                if (renderTargets_[i])
+                {
+                    noFbo = false;
+                    break;
+                }
             }
             }
         }
         }
-    }
     
     
-    if (noFbo)
-    {
-        if (impl_->boundFbo_ != impl_->systemFbo_)
+        if (noFbo)
         {
         {
-            BindFramebuffer(impl_->systemFbo_);
-            impl_->boundFbo_ = impl_->systemFbo_;
-        }
+            if (impl_->boundFbo_ != impl_->systemFbo_)
+            {
+                BindFramebuffer(impl_->systemFbo_);
+                impl_->boundFbo_ = impl_->systemFbo_;
+            }
         
         
-        #ifndef GL_ES_VERSION_2_0
-        // Disable/enable sRGB write
-        if (sRGBWriteSupport_)
-        {
-            bool sRGBWrite = sRGB_;
-            if (sRGBWrite != impl_->sRGBWrite_)
+            #ifndef GL_ES_VERSION_2_0
+            // Disable/enable sRGB write
+            if (sRGBWriteSupport_)
             {
             {
-                if (sRGBWrite)
-                    glEnable(GL_FRAMEBUFFER_SRGB_EXT);
-                else
-                    glDisable(GL_FRAMEBUFFER_SRGB_EXT);
-                impl_->sRGBWrite_ = sRGBWrite;
+                bool sRGBWrite = sRGB_;
+                if (sRGBWrite != impl_->sRGBWrite_)
+                {
+                    if (sRGBWrite)
+                        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
+                    else
+                        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
+                    impl_->sRGBWrite_ = sRGBWrite;
+                }
             }
             }
-        }
-        #endif
+            #endif
         
         
-        return;
-    }
+            return;
+        }
     
     
-    // Search for a new framebuffer based on format & size, or create new
-    IntVector2 rtSize = Graphics::GetRenderTargetDimensions();
-    unsigned format = 0;
-    if (renderTargets_[0])
-        format = renderTargets_[0]->GetParentTexture()->GetFormat();
-    else if (depthStencil_)
-        format = depthStencil_->GetParentTexture()->GetFormat();
+        // Search for a new framebuffer based on format & size, or create new
+        IntVector2 rtSize = Graphics::GetRenderTargetDimensions();
+        unsigned format = 0;
+        if (renderTargets_[0])
+            format = renderTargets_[0]->GetParentTexture()->GetFormat();
+        else if (depthStencil_)
+            format = depthStencil_->GetParentTexture()->GetFormat();
     
     
-    unsigned long long fboKey = (rtSize.x_ << 16 | rtSize.y_) | (((unsigned long long)format) << 32);
+        unsigned long long fboKey = (rtSize.x_ << 16 | rtSize.y_) | (((unsigned long long)format) << 32);
     
     
-    HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Find(fboKey);
-    if (i == impl_->frameBuffers_.End())
-    {
-        FrameBufferObject newFbo;
-        newFbo.fbo_ = CreateFramebuffer();
-        i = impl_->frameBuffers_.Insert(MakePair(fboKey, newFbo));
-    }
+        HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Find(fboKey);
+        if (i == impl_->frameBuffers_.End())
+        {
+            FrameBufferObject newFbo;
+            newFbo.fbo_ = CreateFramebuffer();
+            i = impl_->frameBuffers_.Insert(MakePair(fboKey, newFbo));
+        }
     
     
-    i->second_.useTimer_.Reset();
+        i->second_.useTimer_.Reset();
     
     
-    if (impl_->boundFbo_ != i->second_.fbo_)
-    {
-        BindFramebuffer(i->second_.fbo_);
-        impl_->boundFbo_ = i->second_.fbo_;
-    }
+        if (impl_->boundFbo_ != i->second_.fbo_)
+        {
+            BindFramebuffer(i->second_.fbo_);
+            impl_->boundFbo_ = i->second_.fbo_;
+        }
     
     
-    #ifndef GL_ES_VERSION_2_0
-    // Setup readbuffers & drawbuffers if needed
-    if (i->second_.readBuffers_ != GL_NONE)
-    {
-        glReadBuffer(GL_NONE);
-        i->second_.readBuffers_ = GL_NONE;
-    }
+        #ifndef GL_ES_VERSION_2_0
+        // Setup readbuffers & drawbuffers if needed
+        if (i->second_.readBuffers_ != GL_NONE)
+        {
+            glReadBuffer(GL_NONE);
+            i->second_.readBuffers_ = GL_NONE;
+        }
     
     
-    // Calculate the bit combination of non-zero color rendertargets to first check if the combination changed
-    unsigned newDrawBuffers = 0;
-    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-    {
-        if (renderTargets_[i])
-            newDrawBuffers |= 1 << i;
-    }
+        // Calculate the bit combination of non-zero color rendertargets to first check if the combination changed
+        unsigned newDrawBuffers = 0;
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        {
+            if (renderTargets_[i])
+                newDrawBuffers |= 1 << i;
+        }
     
     
-    if (newDrawBuffers != i->second_.drawBuffers_)
-    {
-        // Check for no color rendertargets (depth rendering only)
-        if (!newDrawBuffers)
-            glDrawBuffer(GL_NONE);
-        else
+        if (newDrawBuffers != i->second_.drawBuffers_)
         {
         {
-            int drawBufferIds[MAX_RENDERTARGETS];
-            unsigned drawBufferCount = 0;
-            
-            for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+            // Check for no color rendertargets (depth rendering only)
+            if (!newDrawBuffers)
+                glDrawBuffer(GL_NONE);
+            else
             {
             {
-                if (renderTargets_[i])
+                int drawBufferIds[MAX_RENDERTARGETS];
+                unsigned drawBufferCount = 0;
+            
+                for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
                 {
                 {
-                    if (!gl3Support)
-                        drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0_EXT + i;
-                    else
-                        drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0 + i;
+                    if (renderTargets_[i])
+                    {
+                        if (!gl3Support)
+                            drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0_EXT + i;
+                        else
+                            drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0 + i;
+                    }
                 }
                 }
+                glDrawBuffers(drawBufferCount, (const GLenum*)drawBufferIds);
             }
             }
-            glDrawBuffers(drawBufferCount, (const GLenum*)drawBufferIds);
-        }
         
         
-        i->second_.drawBuffers_ = newDrawBuffers;
-    }
-    #endif
+            i->second_.drawBuffers_ = newDrawBuffers;
+        }
+        #endif
     
     
-    for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
-    {
-        if (renderTargets_[j])
+        for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
         {
         {
-            Texture* texture = renderTargets_[j]->GetParentTexture();
-            
-            // If texture's parameters are dirty, update before attaching
-            if (texture->GetParametersDirty())
+            if (renderTargets_[j])
             {
             {
-                SetTextureForUpdate(texture);
-                texture->UpdateParameters();
-                SetTexture(0, 0);
-            }
+                Texture* texture = renderTargets_[j]->GetParentTexture();
             
             
-            if (i->second_.colorAttachments_[j] != renderTargets_[j])
-            {
-                BindColorAttachment(j, renderTargets_[j]->GetTarget(), texture->GetGPUObject());
-                i->second_.colorAttachments_[j] = renderTargets_[j];
+                // If texture's parameters are dirty, update before attaching
+                if (texture->GetParametersDirty())
+                {
+                    SetTextureForUpdate(texture);
+                    texture->UpdateParameters();
+                    SetTexture(0, 0);
+                }
+            
+                if (i->second_.colorAttachments_[j] != renderTargets_[j])
+                {
+                    BindColorAttachment(j, renderTargets_[j]->GetTarget(), texture->GetGPUObject());
+                    i->second_.colorAttachments_[j] = renderTargets_[j];
+                }
             }
             }
-        }
-        else
-        {
-            if (i->second_.colorAttachments_[j])
+            else
             {
             {
-                BindColorAttachment(j, GL_TEXTURE_2D, 0);
-                i->second_.colorAttachments_[j] = 0;
+                if (i->second_.colorAttachments_[j])
+                {
+                    BindColorAttachment(j, GL_TEXTURE_2D, 0);
+                    i->second_.colorAttachments_[j] = 0;
+                }
             }
             }
         }
         }
-    }
     
     
-    if (depthStencil_)
-    {
-        // Bind either a renderbuffer or a depth texture, depending on what is available
-        Texture* texture = depthStencil_->GetParentTexture();
-        #ifndef GL_ES_VERSION_2_0
-        bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_EXT;
-        #else
-        bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_OES;
-        #endif
-        unsigned renderBufferID = depthStencil_->GetRenderBuffer();
-        if (!renderBufferID)
+        if (depthStencil_)
         {
         {
-            // If texture's parameters are dirty, update before attaching
-            if (texture->GetParametersDirty())
+            // Bind either a renderbuffer or a depth texture, depending on what is available
+            Texture* texture = depthStencil_->GetParentTexture();
+            #ifndef GL_ES_VERSION_2_0
+            bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_EXT;
+            #else
+            bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_OES;
+            #endif
+            unsigned renderBufferID = depthStencil_->GetRenderBuffer();
+            if (!renderBufferID)
             {
             {
-                SetTextureForUpdate(texture);
-                texture->UpdateParameters();
-                SetTexture(0, 0);
-            }
+                // If texture's parameters are dirty, update before attaching
+                if (texture->GetParametersDirty())
+                {
+                    SetTextureForUpdate(texture);
+                    texture->UpdateParameters();
+                    SetTexture(0, 0);
+                }
 
 
-            if (i->second_.depthAttachment_ != depthStencil_)
+                if (i->second_.depthAttachment_ != depthStencil_)
+                {
+                    BindDepthAttachment(texture->GetGPUObject(), false);
+                    BindStencilAttachment(hasStencil ? texture->GetGPUObject() : 0, false);
+                    i->second_.depthAttachment_ = depthStencil_;
+                }
+            }
+            else
             {
             {
-                BindDepthAttachment(texture->GetGPUObject(), false);
-                BindStencilAttachment(hasStencil ? texture->GetGPUObject() : 0, false);
-                i->second_.depthAttachment_ = depthStencil_;
+                if (i->second_.depthAttachment_ != depthStencil_)
+                {
+                    BindDepthAttachment(renderBufferID, true);
+                    BindStencilAttachment(hasStencil ? renderBufferID : 0, true);
+                    i->second_.depthAttachment_ = depthStencil_;
+                }
             }
             }
         }
         }
         else
         else
         {
         {
-            if (i->second_.depthAttachment_ != depthStencil_)
+            if (i->second_.depthAttachment_)
             {
             {
-                BindDepthAttachment(renderBufferID, true);
-                BindStencilAttachment(hasStencil ? renderBufferID : 0, true);
-                i->second_.depthAttachment_ = depthStencil_;
+                BindDepthAttachment(0, false);
+                BindStencilAttachment(0, false);
+                i->second_.depthAttachment_ = 0;
             }
             }
         }
         }
-    }
-    else
-    {
-        if (i->second_.depthAttachment_)
-        {
-            BindDepthAttachment(0, false);
-            BindStencilAttachment(0, false);
-            i->second_.depthAttachment_ = 0;
-        }
-    }
     
     
-    #ifndef GL_ES_VERSION_2_0
-    // Disable/enable sRGB write
-    if (sRGBWriteSupport_)
-    {
-        bool sRGBWrite = renderTargets_[0] ? renderTargets_[0]->GetParentTexture()->GetSRGB() : sRGB_;
-        if (sRGBWrite != impl_->sRGBWrite_)
+        #ifndef GL_ES_VERSION_2_0
+        // Disable/enable sRGB write
+        if (sRGBWriteSupport_)
         {
         {
-            if (sRGBWrite)
-                glEnable(GL_FRAMEBUFFER_SRGB_EXT);
-            else
-                glDisable(GL_FRAMEBUFFER_SRGB_EXT);
-            impl_->sRGBWrite_ = sRGBWrite;
+            bool sRGBWrite = renderTargets_[0] ? renderTargets_[0]->GetParentTexture()->GetSRGB() : sRGB_;
+            if (sRGBWrite != impl_->sRGBWrite_)
+            {
+                if (sRGBWrite)
+                    glEnable(GL_FRAMEBUFFER_SRGB_EXT);
+                else
+                    glDisable(GL_FRAMEBUFFER_SRGB_EXT);
+                impl_->sRGBWrite_ = sRGBWrite;
+            }
         }
         }
+        #endif
     }
     }
-    #endif
 }
 }
 
 
 void Graphics::CleanupFramebuffers(bool force)
 void Graphics::CleanupFramebuffers(bool force)
@@ -3063,6 +3179,10 @@ void Graphics::ResetCachedState()
         SetDepthTest(CMP_LESSEQUAL);
         SetDepthTest(CMP_LESSEQUAL);
         SetDepthWrite(true);
         SetDepthWrite(true);
     }
     }
+
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS * 2; ++i)
+        currentConstantBuffers_[i] = 0;
+    dirtyConstantBuffers_.Clear();
 }
 }
 
 
 void Graphics::SetTextureUnitMappings()
 void Graphics::SetTextureUnitMappings()

+ 11 - 2
Source/Urho3D/Graphics/OpenGL/OGLGraphics.h

@@ -34,6 +34,7 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+class ConstantBuffer;
 class File;
 class File;
 class Image;
 class Image;
 class IndexBuffer;
 class IndexBuffer;
@@ -385,6 +386,8 @@ public:
     void CleanupRenderSurface(RenderSurface* surface);
     void CleanupRenderSurface(RenderSurface* surface);
     /// Clean up shader programs when a shader variation is released or destroyed.
     /// Clean up shader programs when a shader variation is released or destroyed.
     void CleanupShaderPrograms(ShaderVariation* variation);
     void CleanupShaderPrograms(ShaderVariation* variation);
+    /// Reserve a constant buffer.
+    ConstantBuffer* GetOrCreateConstantBuffer(unsigned bindingIndex, unsigned size);
     /// Release/clear GPU objects and optionally close the window.
     /// Release/clear GPU objects and optionally close the window.
     void Release(bool clearGPUObjects, bool closeWindow);
     void Release(bool clearGPUObjects, bool closeWindow);
     /// Restore GPU objects and reinitialize state. Requires an open window.
     /// Restore GPU objects and reinitialize state. Requires an open window.
@@ -440,8 +443,8 @@ private:
     void CreateWindowIcon();
     void CreateWindowIcon();
     /// Check supported rendering features.
     /// Check supported rendering features.
     void CheckFeatureSupport(String& extensions);
     void CheckFeatureSupport(String& extensions);
-    /// Select FBO and commit changes.
-    void CommitFramebuffer();
+    /// Prepare for draw call. Update constant buffers and setup the FBO.
+    void PrepareDraw();
     /// Cleanup unused and unbound FBO's.
     /// Cleanup unused and unbound FBO's.
     void CleanupFramebuffers(bool force = false);
     void CleanupFramebuffers(bool force = false);
     /// Reset cached rendering state.
     /// Reset cached rendering state.
@@ -549,6 +552,12 @@ private:
     unsigned textureTypes_[MAX_TEXTURE_UNITS];
     unsigned textureTypes_[MAX_TEXTURE_UNITS];
     /// Texture unit mappings.
     /// Texture unit mappings.
     HashMap<String, TextureUnit> textureUnits_;
     HashMap<String, TextureUnit> textureUnits_;
+    /// All constant buffers.
+    HashMap<unsigned, SharedPtr<ConstantBuffer> > constantBuffers_;
+    /// Currently bound constant buffers.
+    ConstantBuffer* currentConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
+    /// Dirty constant buffers.
+    PODVector<ConstantBuffer*> dirtyConstantBuffers_;
     /// Rendertargets in use.
     /// Rendertargets in use.
     RenderSurface* renderTargets_[MAX_RENDERTARGETS];
     RenderSurface* renderTargets_[MAX_RENDERTARGETS];
     /// Depth-stencil surface in use.
     /// Depth-stencil surface in use.

+ 105 - 8
Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -20,16 +20,28 @@
 // THE SOFTWARE.
 // THE SOFTWARE.
 //
 //
 
 
+#include "../../Graphics/ConstantBuffer.h"
 #include "../../Graphics/Graphics.h"
 #include "../../Graphics/Graphics.h"
 #include "../../Graphics/GraphicsImpl.h"
 #include "../../Graphics/GraphicsImpl.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
 #include "../../Graphics/ShaderVariation.h"
+#include "../../IO/Log.h"
 
 
 #include "../../DebugNew.h"
 #include "../../DebugNew.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+const char* shaderParameterGroups[] = {
+    "frame",
+    "camera",
+    "zone",
+    "light",
+    "material",
+    "object",
+    "custom"
+};
+
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
     GPUObject(graphics),
     GPUObject(graphics),
     vertexShader_(vertexShader),
     vertexShader_(vertexShader),
@@ -75,6 +87,8 @@ void ShaderProgram::Release()
         
         
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
             useTextureUnit_[i] = false;
             useTextureUnit_[i] = false;
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+            constantBuffers_[i].Reset();
     }
     }
 }
 }
 
 
@@ -139,6 +153,75 @@ bool ShaderProgram::Link()
     glUseProgram(object_);
     glUseProgram(object_);
     glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &uniformCount);
     glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &uniformCount);
     
     
+    // Check for constant buffers
+    #ifndef GL_ES_VERSION_2_0
+    HashMap<unsigned, unsigned> blockToBinding;
+
+    if (Graphics::GetGL3Support())
+    {
+        int numUniformBlocks = 0;
+
+        glGetProgramiv(object_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
+        for (int i = 0; i < numUniformBlocks; ++i)
+        {
+            int nameLength;
+            glGetActiveUniformBlockName(object_, i, MAX_PARAMETER_NAME_LENGTH, &nameLength, uniformName);
+
+            String name(uniformName, nameLength);
+
+            unsigned blockIndex = glGetUniformBlockIndex(object_, name.CString());
+            unsigned group = M_MAX_UNSIGNED;
+
+            // Try to recognize the use of the buffer from its name
+            for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j)
+            {
+                if (name.Contains(shaderParameterGroups[j], false))
+                {
+                    group = j;
+                    break;
+                }
+            }
+
+            // If name is not recognized, search for a digit in the name and use that as the group index
+            if (group == M_MAX_UNSIGNED)
+            {
+                for (unsigned j = 1; j < name.Length(); ++j)
+                {
+                    if (name[j] >= '0' && name[j] <= '5')
+                    {
+                        group = name[j] - '0';
+                        break;
+                    }
+                }
+            }
+
+            if (group >= MAX_SHADER_PARAMETER_GROUPS)
+            {
+                LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() +
+                    " " + pixelShader_->GetFullName());
+                continue;
+            }
+
+            // Find total constant buffer data size
+            int dataSize;
+            glGetActiveUniformBlockiv(object_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
+            if (!dataSize)
+                return 0;
+
+            unsigned bindingIndex = group;
+            // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings
+            // from that point onward
+            if (name.Contains("PS", false))
+                bindingIndex += MAX_SHADER_PARAMETER_GROUPS;
+
+            glUniformBlockBinding(object_, blockIndex, bindingIndex);
+            blockToBinding[blockIndex] = bindingIndex;
+
+            constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(bindingIndex, dataSize);
+        }
+    }
+    #endif
+
     // Check for shader parameters and texture units
     // Check for shader parameters and texture units
     for (int i = 0; i < uniformCount; ++i)
     for (int i = 0; i < uniformCount; ++i)
     {
     {
@@ -148,10 +231,6 @@ bool ShaderProgram::Link()
         glGetActiveUniform(object_, i, MAX_PARAMETER_NAME_LENGTH, 0, &count, &type, uniformName);
         glGetActiveUniform(object_, i, MAX_PARAMETER_NAME_LENGTH, 0, &count, &type, uniformName);
         int location = glGetUniformLocation(object_, uniformName);
         int location = glGetUniformLocation(object_, uniformName);
         
         
-        // Skip inbuilt or disabled uniforms
-        if (location < 0)
-            continue;
-        
         // Check for array index included in the name and strip it
         // Check for array index included in the name and strip it
         String name(uniformName);
         String name(uniformName);
         unsigned index = name.Find('[');
         unsigned index = name.Find('[');
@@ -166,14 +245,32 @@ bool ShaderProgram::Link()
         
         
         if (name[0] == 'c')
         if (name[0] == 'c')
         {
         {
-            // Store the constant uniform mapping
+            // Store constant uniform
             String paramName = name.Substring(1);
             String paramName = name.Substring(1);
             ShaderParameter newParam;
             ShaderParameter newParam;
-            newParam.location_ = location;
             newParam.type_ = type;
             newParam.type_ = type;
-            shaderParameters_[StringHash(paramName)] = newParam;
+            newParam.location_ = location;
+
+            #ifndef GL_ES_VERSION_2_0
+            // If running OpenGL 3, the uniform may be inside a constant buffer
+            if (newParam.location_ < 0 && Graphics::GetGL3Support())
+            {
+                int blockIndex, blockOffset;
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset);
+                if (blockIndex >= 0)
+                {
+                    newParam.location_ = blockOffset;
+                    newParam.buffer_ = blockToBinding[blockIndex];
+                    newParam.bufferPtr_ = constantBuffers_[newParam.buffer_];
+                }
+            }
+            #endif
+
+            if (newParam.location_ >= 0)
+                shaderParameters_[StringHash(paramName)] = newParam;
         }
         }
-        else if (name[0] == 's')
+        else if (location >= 0 && name[0] == 's')
         {
         {
             // Set the samplers here so that they do not have to be set later
             // Set the samplers here so that they do not have to be set later
             int unit = graphics_->GetTextureUnit(name.Substring(1));
             int unit = graphics_->GetTextureUnit(name.Substring(1));

+ 17 - 1
Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.h

@@ -30,16 +30,28 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+class ConstantBuffer;
 class Graphics;
 class Graphics;
 class ShaderVariation;
 class ShaderVariation;
 
 
 /// %Shader parameter definition.
 /// %Shader parameter definition.
 struct ShaderParameter
 struct ShaderParameter
 {
 {
-    /// Uniform location.
+    /// Construct with defaults.
+    ShaderParameter() :
+        buffer_(M_MAX_UNSIGNED),
+        bufferPtr_(0)
+    {
+    }
+
+    /// Uniform location or byte offset in constant buffer.
     int location_;
     int location_;
     /// Element type.
     /// Element type.
     unsigned type_;
     unsigned type_;
+    /// Constant buffer binding index. M_MAX_UNSIGNED if is a free-standing uniform.
+    unsigned buffer_;
+    /// Constant buffer pointer.
+    ConstantBuffer* bufferPtr_;
 };
 };
 
 
 /// Linked shader program on the GPU.
 /// Linked shader program on the GPU.
@@ -71,6 +83,8 @@ public:
     const ShaderParameter* GetParameter(StringHash param) const;
     const ShaderParameter* GetParameter(StringHash param) const;
     /// Return linker output.
     /// Return linker output.
     const String& GetLinkerOutput() const { return linkerOutput_; }
     const String& GetLinkerOutput() const { return linkerOutput_; }
+    /// Return all constant buffers.
+    const SharedPtr<ConstantBuffer>* GetConstantBuffers() const { return &constantBuffers_[0]; }
     
     
 private:
 private:
     /// Vertex shader.
     /// Vertex shader.
@@ -81,6 +95,8 @@ private:
     HashMap<StringHash, ShaderParameter> shaderParameters_;
     HashMap<StringHash, ShaderParameter> shaderParameters_;
     /// Texture unit use.
     /// Texture unit use.
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
+    /// Constant buffers by binding index.
+    SharedPtr<ConstantBuffer> constantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
     /// Shader link error string.
     /// Shader link error string.
     String linkerOutput_;
     String linkerOutput_;
 };
 };

+ 2 - 0
bin/CoreData/Shaders/GLSL/Transform.glsl

@@ -74,6 +74,7 @@ float GetDepth(vec4 clipPos)
     return dot(clipPos.zw, cDepthMode.zw);
     return dot(clipPos.zw, cDepthMode.zw);
 }
 }
 
 
+#ifdef BILLBOARD
 vec3 GetBillboardPos(vec4 iPos, vec2 iSize, mat4 modelMatrix)
 vec3 GetBillboardPos(vec4 iPos, vec2 iSize, mat4 modelMatrix)
 {
 {
     return (iPos * modelMatrix).xyz + vec3(iSize.x, iSize.y, 0.0) * cBillboardRot;
     return (iPos * modelMatrix).xyz + vec3(iSize.x, iSize.y, 0.0) * cBillboardRot;
@@ -83,6 +84,7 @@ vec3 GetBillboardNormal()
 {
 {
     return vec3(-cBillboardRot[0][2], -cBillboardRot[1][2], -cBillboardRot[2][2]);
     return vec3(-cBillboardRot[0][2], -cBillboardRot[1][2], -cBillboardRot[2][2]);
 }
 }
+#endif
 
 
 #if defined(SKINNED)
 #if defined(SKINNED)
     #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
     #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)

+ 125 - 3
bin/CoreData/Shaders/GLSL/Uniforms.glsl

@@ -1,4 +1,10 @@
+#ifndef GL3
+
+// OpenGL 2 uniforms (no constant buffers)
+
 #ifdef COMPILEVS
 #ifdef COMPILEVS
+          
+// Vertex shader uniforms
 uniform vec3 cAmbientStartColor;
 uniform vec3 cAmbientStartColor;
 uniform vec3 cAmbientEndColor;
 uniform vec3 cAmbientEndColor;
 uniform mat3 cBillboardRot;
 uniform mat3 cBillboardRot;
@@ -33,12 +39,11 @@ uniform mat4 cZone;
 #ifdef NUMVERTEXLIGHTS
 #ifdef NUMVERTEXLIGHTS
     uniform vec4 cVertexLights[4*3];
     uniform vec4 cVertexLights[4*3];
 #endif
 #endif
-#ifdef GL3
-    uniform vec4 cClipPlane;
-#endif
 #endif
 #endif
 
 
 #ifdef COMPILEPS
 #ifdef COMPILEPS
+
+// Fragment shader uniforms
 #ifdef GL_ES
 #ifdef GL_ES
     precision mediump float;
     precision mediump float;
 #endif
 #endif
@@ -66,4 +71,121 @@ uniform vec2 cShadowIntensity;
 uniform vec2 cShadowMapInvSize;
 uniform vec2 cShadowMapInvSize;
 uniform vec4 cShadowSplits;
 uniform vec4 cShadowSplits;
 uniform mat4 cLightMatricesPS[4];
 uniform mat4 cLightMatricesPS[4];
+
+#endif
+
+#else
+
+// OpenGL 3 uniforms (using constant buffers)
+
+#ifdef COMPILEVS
+
+layout(std140) uniform FrameVS
+{
+    float cDeltaTime;
+    float cElapsedTime;
+};
+
+layout(std140) uniform CameraVS
+{
+    vec3 cCameraPos;
+    mat3 cCameraRot;
+    float cNearClip;
+    float cFarClip;
+    vec4 cDepthMode;
+    vec3 cFrustumSize;
+    vec4 cGBufferOffsets;
+    mat4 cViewProj;
+    vec4 cClipPlane;
+};
+
+layout(std140) uniform ZoneVS
+{
+    vec3 cAmbientStartColor;
+    vec3 cAmbientEndColor;
+    mat4 cZone;
+};
+
+layout(std140) uniform LightVS
+{
+    vec3 cLightDir;
+    vec4 cLightPos;
+#ifdef NUMVERTEXLIGHTS
+    vec4 cVertexLights[4 * 3];
+#else
+    mat4 cLightMatrices[4];
+#endif
+};
+
+#ifndef CUSTOM_MATERIAL_CBUFFER
+layout(std140) uniform MaterialVS
+{
+    vec4 cUOffset;
+    vec4 cVOffset;
+};
+#endif
+
+layout(std140) uniform ObjectVS
+{
+    mat4 cModel;
+#ifdef BILLBOARD
+    mat3 cBillboardRot;
+#endif
+#ifdef SKINNED
+    uniform vec4 cSkinMatrices[64*3];
+#endif
+};
+
+#endif
+
+#ifdef COMPILEPS
+
+// Pixel shader uniforms
+layout(std140) uniform FramePS
+{
+    float cDeltaTimePS;
+    float cElapsedTimePS;
+};
+
+layout(std140) uniform CameraPS
+{
+    vec3 cCameraPosPS;
+    vec4 cDepthReconstruct;
+    vec2 cGBufferInvSize;
+    float cNearClipPS;
+    float cFarClipPS;
+};
+
+layout(std140) uniform ZonePS
+{
+    vec3 cAmbientColor;
+    vec4 cFogParams;
+    vec3 cFogColor;
+};
+
+layout(std140) uniform LightPS
+{
+    vec4 cLightColor;
+    vec4 cLightPosPS;
+    vec3 cLightDirPS;
+    vec4 cShadowCubeAdjust;
+    vec4 cShadowDepthFade;
+    vec2 cShadowIntensity;
+    vec2 cShadowMapInvSize;
+    vec4 cShadowSplits;
+    mat4 cLightMatricesPS[4];
+};
+
+#ifndef CUSTOM_MATERIAL_CBUFFER
+layout(std140) uniform MaterialPS
+{
+    vec4 cMatDiffColor;
+    vec3 cMatEmissiveColor;
+    vec3 cMatEnvMapColor;
+    vec4 cMatSpecColor;
+};
+#endif
+
+#endif
+
 #endif
 #endif