Ver código fonte

Add a ShaderProgram class also for D3D9, which holds the combined shader parameters. Cleaned up ShaderProgram handling on OpenGL. Optimized away repeated assignment of constant values from D3D9 SetShaderParameter().

Lasse Öörni 10 anos atrás
pai
commit
6e6c39c5a8

+ 85 - 109
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -43,6 +43,7 @@
 #include "../../Resource/ResourceCache.h"
 #include "../../Graphics/Shader.h"
 #include "../../Graphics/ShaderPrecache.h"
+#include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
 #include "../../Graphics/Skybox.h"
 #include "../../Graphics/StaticModelGroup.h"
@@ -272,7 +273,7 @@ Graphics::Graphics(Context* context) :
     numBatches_(0),
     maxScratchBufferRequest_(0),
     defaultTextureFilterMode_(FILTER_TRILINEAR),
-    currentShaderParameters_(0),
+    shaderProgram_(0),
     shaderPath_("Shaders/HLSL/"),
     shaderExtension_(".hlsl"),
     orientations_("LandscapeLeft LandscapeRight"),
@@ -1167,29 +1168,17 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
     if (vertexShader_ && pixelShader_)
     {
         Pair<ShaderVariation*, ShaderVariation*> key = MakePair(vertexShader_, pixelShader_);
-        HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > >::Iterator i =
-            shaderParameters_.Find(key);
-        if (i != shaderParameters_.End())
-            currentShaderParameters_ = &i->second_;
+        ShaderProgramMap::Iterator i = shaderPrograms_.Find(key);
+        if (i != shaderPrograms_.End())
+            shaderProgram_ = i->second_.Get();
         else
         {
-            HashMap<StringHash, Pair<ShaderType, unsigned> >& parameters = shaderParameters_[key];
-
-            const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader_->GetParameters();
-            for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
-                parameters[i->first_] = MakePair(i->second_.type_, i->second_.register_);
-
-            const HashMap<StringHash, ShaderParameter>& psParams = pixelShader_->GetParameters();
-            for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
-                parameters[i->first_] = MakePair(i->second_.type_, i->second_.register_);
-
-            // Optimize shader parameter lookup by rehashing to next power of two
-            parameters.Rehash(NextPowerOfTwo(parameters.Size()));
-            currentShaderParameters_ = &parameters;
+            ShaderProgram* newProgram = shaderPrograms_[key] = new ShaderProgram(vertexShader_, pixelShader_);
+            shaderProgram_ = newProgram;
         }
     }
     else
-        currentShaderParameters_ = 0;
+        shaderProgram_ = 0;
 
     // Store shader combination if shader dumping in progress
     if (shaderPrecache_)
@@ -1198,161 +1187,148 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, data, count / 4);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, data, count / 4);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, data, count / 4);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, data, count / 4);
 }
 
 void Graphics::SetShaderParameter(StringHash param, float value)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
-    
-    data[0] = value;
-    data[1] = 0.0f;
-    data[2] = 0.0f;
-    data[3] = 0.0f;
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = value;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, bool value)
 {
     /// \todo Bool constants possibly have no effect on Direct3D9
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
 
     BOOL data = value;
 
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantB(i->second_.second_, &data, 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantB(i->second_.register_, &data, 1);
     else
-        impl_->device_->SetPixelShaderConstantB(i->second_.second_, &data, 1);
+        impl_->device_->SetPixelShaderConstantB(i->second_.register_, &data, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Color& color)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, color.Data(), 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, color.Data(), 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, color.Data(), 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, color.Data(), 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = vector.x_;
+    data.y_ = vector.y_;
     
-    data[0] = vector.x_;
-    data[1] = vector.y_;
-    data[2] = 0.0f;
-    data[3] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[12];
-    
-    data[0] = matrix.m00_;
-    data[1] = matrix.m01_;
-    data[2] = matrix.m02_;
-    data[3] = 0.0f;
-    data[4] = matrix.m10_;
-    data[5] = matrix.m11_;
-    data[6] = matrix.m12_;
-    data[7] = 0.0f;
-    data[8] = matrix.m20_;
-    data[9] = matrix.m21_;
-    data[10] = matrix.m22_;
-    data[11] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 3);
+    static Matrix3x4 data(Matrix3x4::ZERO);
+    data.m00_ = matrix.m00_;
+    data.m01_ = matrix.m01_;
+    data.m02_ = matrix.m02_;
+    data.m10_ = matrix.m10_;
+    data.m11_ = matrix.m11_;
+    data.m12_ = matrix.m12_;
+    data.m20_ = matrix.m20_;
+    data.m21_ = matrix.m21_;
+    data.m22_ = matrix.m22_;
+    
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.m00_, 3);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 3);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.m00_, 3);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
-    
-    data[0] = vector.x_;
-    data[1] = vector.y_;
-    data[2] = vector.z_;
-    data[3] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = vector.x_;
+    data.y_ = vector.y_;
+    data.z_ = vector.z_;
+
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, matrix.Data(), 4);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, matrix.Data(), 4);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, matrix.Data(), 4);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, matrix.Data(), 4);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, vector.Data(), 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, vector.Data(), 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, vector.Data(), 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, vector.Data(), 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, matrix.Data(), 3);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, matrix.Data(), 3);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, matrix.Data(), 3);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, matrix.Data(), 3);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Variant& value)
@@ -1414,7 +1390,7 @@ bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* sourc
 
 bool Graphics::HasShaderParameter(StringHash param)
 {
-    return currentShaderParameters_ && currentShaderParameters_->Find(param) != currentShaderParameters_->End();
+    return shaderProgram_ && shaderProgram_->parameters_.Find(param) != shaderProgram_->parameters_.End();
 }
 
 bool Graphics::HasTextureUnit(TextureUnit unit)
@@ -2294,19 +2270,19 @@ void Graphics::CleanupScratchBuffers()
     maxScratchBufferRequest_ = 0;
 }
 
-void Graphics::CleanupShaderParameters(ShaderVariation* variation)
+void Graphics::CleanupShaderPrograms(ShaderVariation* variation)
 {
-    for (HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > >::Iterator i =
-        shaderParameters_.Begin(); i != shaderParameters_.End();)
+    for (ShaderProgramMap::Iterator i =
+        shaderPrograms_.Begin(); i != shaderPrograms_.End();)
     {
         if (i->first_.first_ == variation || i->first_.second_ == variation)
-            i = shaderParameters_.Erase(i);
+            i = shaderPrograms_.Erase(i);
         else
             ++i;
     }
 
     if (vertexShader_ == variation || pixelShader_ == variation)
-        currentShaderParameters_ = 0;
+        shaderProgram_ = 0;
 }
 
 unsigned Graphics::GetAlphaFormat()

+ 9 - 6
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.h

@@ -43,6 +43,7 @@ class GraphicsImpl;
 class RenderSurface;
 class Shader;
 class ShaderPrecache;
+class ShaderProgram;
 class ShaderVariation;
 class Texture;
 class Texture2D;
@@ -54,6 +55,8 @@ class VertexDeclaration;
 
 struct ShaderParameter;
 
+typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
+
 /// CPU-side scratch buffer for vertex data updates.
 struct ScratchBuffer
 {
@@ -376,8 +379,8 @@ public:
     void FreeScratchBuffer(void* buffer);
     /// Clean up too large scratch buffers.
     void CleanupScratchBuffers();
-    /// Clean up shader parameters when a shader variation is released or destroyed.
-    void CleanupShaderParameters(ShaderVariation* variation);
+    /// Clean up shader programs when a shader variation is released or destroyed.
+    void CleanupShaderPrograms(ShaderVariation* variation);
 
     /// Return the API-specific alpha texture format.
     static unsigned GetAlphaFormat();
@@ -578,10 +581,10 @@ private:
     bool drawAntialiased_;
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
-    /// Shader parameters for all vertex/pixel shader combinations.
-    HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > > shaderParameters_;
-    /// Current active shader parameters.
-    HashMap<StringHash, Pair<ShaderType, unsigned> >* currentShaderParameters_;
+    /// Shader programs.
+    ShaderProgramMap shaderPrograms_;
+    /// Shader program in use.
+    ShaderProgram* shaderProgram_;
     /// Remembered shader parameter sources.
     const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
     /// Base directory for shaders.

+ 54 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9ShaderProgram.h

@@ -0,0 +1,54 @@
+//
+// 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 "../../Container/HashMap.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+/// Combined information for a combination of vertex and pixel shaders.
+class ShaderProgram : public RefCounted
+{
+public:
+    /// Construct.
+    ShaderProgram(ShaderVariation* vertexShader, ShaderVariation* pixelShader)
+    {
+        const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
+        parameters_[i->first_] = i->second_;
+
+        const HashMap<StringHash, ShaderParameter>& psParams = pixelShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
+            parameters_[i->first_] = i->second_;
+    
+        // Optimize shader parameter lookup by rehashing to next power of two
+        parameters_.Rehash(NextPowerOfTwo(parameters_.Size()));
+    }
+
+    /// Combined parameters from the vertex and pixel shader.
+    HashMap<StringHash, ShaderParameter> parameters_;
+};
+
+}

+ 1 - 1
Source/Urho3D/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -110,7 +110,7 @@ void ShaderVariation::Release()
         if (!graphics_)
             return;
         
-        graphics_->CleanupShaderParameters(this);
+        graphics_->CleanupShaderPrograms(this);
 
         if (type_ == VS)
         {

+ 71 - 75
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -248,7 +248,6 @@ Graphics::Graphics(Context* context_) :
     dummyColorFormat_(0),
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
-    releasingGPUObjects_(false),
     defaultTextureFilterMode_(FILTER_TRILINEAR),
     shaderPath_("Shaders/GLSL/"),
     shaderExtension_(".glsl"),
@@ -1432,26 +1431,6 @@ void Graphics::ClearTransformSources()
     shaderParameterSources_[SP_OBJECTTRANSFORM] = (const void*)M_MAX_UNSIGNED;
 }
 
-void Graphics::CleanupShaderPrograms()
-{
-    // Ignore individual call from ShaderVariation instance when Graphics subsystem is in process of
-    // releasing all GPU objects or recreating GPU objects due device lost, because the Graphics subsystem
-    // will eventually erase all the shader programs afterward as part of the release process.
-    if (releasingGPUObjects_)
-        return;
-    
-    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
-    {
-        ShaderVariation* vs = i->second_->GetVertexShader();
-        ShaderVariation* ps = i->second_->GetPixelShader();
-        
-        if (!vs || !ps || !vs->GetGPUObject() || !ps->GetGPUObject())
-            i = shaderPrograms_.Erase(i);
-        else
-            ++i;
-    }
-}
-
 void Graphics::SetTexture(unsigned index, Texture* texture)
 {
     if (index >= MAX_TEXTURE_UNITS)
@@ -2294,19 +2273,82 @@ void Graphics::CleanupScratchBuffers()
     maxScratchBufferRequest_ = 0;
 }
 
+void Graphics::CleanupRenderSurface(RenderSurface* surface)
+{
+    if (!surface)
+        return;
+
+    // Flush pending FBO changes first if any
+    CommitFramebuffer();
+
+    unsigned currentFbo = impl_->boundFbo_;
+
+    // Go through all FBOs and clean up the surface from them
+    for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
+        i != impl_->frameBuffers_.End(); ++i)
+    {
+        for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
+        {
+            if (i->second_.colorAttachments_[j] == surface)
+            {
+                if (currentFbo != i->second_.fbo_)
+                {
+                    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, i->second_.fbo_);
+                    currentFbo = i->second_.fbo_;
+                }
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j, GL_TEXTURE_2D, 0, 0);
+                i->second_.colorAttachments_[j] = 0;
+                // Mark drawbuffer bits to need recalculation
+                i->second_.drawBuffers_ = M_MAX_UNSIGNED;
+            }
+        }
+        if (i->second_.depthAttachment_ == surface)
+        {
+            if (currentFbo != i->second_.fbo_)
+            {
+                glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, i->second_.fbo_);
+                currentFbo = i->second_.fbo_;
+            }
+            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+            i->second_.depthAttachment_ = 0;
+        }
+    }
+
+    // Restore previously bound FBO now if needed
+    if (currentFbo != impl_->boundFbo_)
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->boundFbo_);
+}
+
+void Graphics::CleanupShaderPrograms(ShaderVariation* variation)
+{
+    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
+    {
+        if (i->second_->GetVertexShader() == variation || i->second_->GetPixelShader() == variation)
+            i = shaderPrograms_.Erase(i);
+        else
+            ++i;
+    }
+
+    if (vertexShader_ == variation || pixelShader_ == variation)
+        shaderProgram_ = 0;
+}
+
+
 void Graphics::Release(bool clearGPUObjects, bool closeWindow)
 {
     if (!impl_->window_)
         return;
     
-    releasingGPUObjects_ = true;
-    
     {
         MutexLock lock(gpuObjectMutex_);
         
         if (clearGPUObjects)
         {
             // Shutting down: release all GPU objects that still exist
+            // Shader programs are also GPU objects; clear them first to avoid list modification during iteration
+            shaderPrograms_.Clear();
+
             for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
                 (*i)->Release();
             gpuObjects_.Clear();
@@ -2316,17 +2358,18 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
             // 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();
+                
+            // In this case clear shader programs last so that they do not attempt to delete their OpenGL program
+            // from a context that may no longer exist
+            shaderPrograms_.Clear();
 
             SendEvent(E_DEVICELOST);
         }
     }
-    
-    releasingGPUObjects_ = false;
-    
+
     CleanupFramebuffers(true);
     depthTextures_.Clear();
-    shaderPrograms_.Clear();
-    
+
     // End fullscreen mode first to counteract transition and getting stuck problems on OS X
     #if defined(__APPLE__) && !defined(IOS)
     if (closeWindow && fullscreen_ && !externalWindow_)
@@ -2415,53 +2458,6 @@ void Graphics::Minimize()
     SDL_MinimizeWindow(impl_->window_);
 }
 
-void Graphics::CleanupRenderSurface(RenderSurface* surface)
-{
-    if (!surface)
-        return;
-    
-    // Flush pending FBO changes first if any
-    CommitFramebuffer();
-    
-    unsigned currentFbo = impl_->boundFbo_;
-    
-    // Go through all FBOs and clean up the surface from them
-    for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
-        i != impl_->frameBuffers_.End(); ++i)
-    {
-        for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
-        {
-            if (i->second_.colorAttachments_[j] == surface)
-            {
-                if (currentFbo != i->second_.fbo_)
-                {
-                    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, i->second_.fbo_);
-                    currentFbo = i->second_.fbo_;
-                }
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j, GL_TEXTURE_2D, 0, 0);
-                i->second_.colorAttachments_[j] = 0;
-                // Mark drawbuffer bits to need recalculation
-                i->second_.drawBuffers_ = M_MAX_UNSIGNED;
-            }
-        }
-        if (i->second_.depthAttachment_ == surface)
-        {
-            if (currentFbo != i->second_.fbo_)
-            {
-                glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, i->second_.fbo_);
-                currentFbo = i->second_.fbo_;
-            }
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-            i->second_.depthAttachment_ = 0;
-        }
-    }
-    
-    // Restore previously bound FBO now if needed
-    if (currentFbo != impl_->boundFbo_)
-        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->boundFbo_);
-}
-
 void Graphics::MarkFBODirty()
 {
     impl_->fboDirty_ = true;

+ 4 - 6
Source/Urho3D/Graphics/OpenGL/OGLGraphics.h

@@ -165,8 +165,6 @@ public:
     void ClearParameterSources();
     /// Clear remembered transform shader parameter sources.
     void ClearTransformSources();
-    /// Clean up unused shader programs.
-    void CleanupShaderPrograms();
     /// Set texture.
     void SetTexture(unsigned index, Texture* texture);
     /// Bind texture unit 0 for update. Called by Texture.
@@ -383,6 +381,10 @@ public:
     void FreeScratchBuffer(void* buffer);
     /// Clean up too large scratch buffers.
     void CleanupScratchBuffers();
+    /// Clean up a render surface from all FBOs.
+    void CleanupRenderSurface(RenderSurface* surface);
+    /// Clean up shader programs when a shader variation is released or destroyed.
+    void CleanupShaderPrograms(ShaderVariation* variation);
     /// Release/clear GPU objects and optionally close the window.
     void Release(bool clearGPUObjects, bool closeWindow);
     /// Restore GPU objects and reinitialize state. Requires an open window.
@@ -391,8 +393,6 @@ public:
     void Maximize();
     /// Minimize the Window.
     void Minimize();
-    /// Clean up a render surface from all FBOs.
-    void CleanupRenderSurface(RenderSurface* surface);
     /// Mark the FBO needing an update.
     void MarkFBODirty();
     
@@ -581,8 +581,6 @@ private:
     bool useClipPlane_;
     /// Draw antialiased mode flag.
     bool drawAntialiased_;
-    /// Releasing GPU objects flag.
-    bool releasingGPUObjects_;
     /// Last used instance data offset.
     unsigned lastInstanceOffset_;
     /// Default texture filtering mode.

+ 0 - 1
Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -50,7 +50,6 @@ void ShaderProgram::OnDeviceLost()
     
     if (graphics_ && graphics_->GetShaderProgram() == this)
         graphics_->SetShaders(0, 0);
-    
 
     linkerOutput_.Clear();
 }

+ 1 - 4
Source/Urho3D/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -49,9 +49,6 @@ void ShaderVariation::OnDeviceLost()
     GPUObject::OnDeviceLost();
 
     compilerOutput_.Clear();
-    
-    if (graphics_)
-        graphics_->CleanupShaderPrograms();
 }
 
 void ShaderVariation::Release()
@@ -78,7 +75,7 @@ void ShaderVariation::Release()
         }
         
         object_ = 0;
-        graphics_->CleanupShaderPrograms();
+        graphics_->CleanupShaderPrograms(this);
     }
     
     compilerOutput_.Clear();

+ 5 - 1
Source/Urho3D/Graphics/ShaderProgram.h

@@ -22,6 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLShaderProgram.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11ShaderProgram.h"
+#else
+#include "Direct3D9/D3D9ShaderProgram.h"
 #endif