Bladeren bron

More complete multi-FBO implementation.

Lasse Öörni 14 jaren geleden
bovenliggende
commit
7ed9a4815d

+ 2197 - 2062
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -1,2062 +1,2197 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// 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 "Precompiled.h"
-#include "AnimatedModel.h"
-#include "Animation.h"
-#include "AnimationController.h"
-#include "BillboardSet.h"
-#include "Camera.h"
-#include "Context.h"
-#include "DebugRenderer.h"
-#include "Graphics.h"
-#include "GraphicsEvents.h"
-#include "GraphicsImpl.h"
-#include "IndexBuffer.h"
-#include "Light.h"
-#include "Log.h"
-#include "Material.h"
-#include "Mutex.h"
-#include "Octree.h"
-#include "ParticleEmitter.h"
-#include "ProcessUtils.h"
-#include "Profiler.h"
-#include "RenderSurface.h"
-#include "Shader.h"
-#include "ShaderProgram.h"
-#include "ShaderVariation.h"
-#include "Skybox.h"
-#include "StringUtils.h"
-#include "Technique.h"
-#include "Texture2D.h"
-#include "TextureCube.h"
-#include "VertexBuffer.h"
-#include "Zone.h"
-
-#include <stdio.h>
-
-#include "DebugNew.h"
-
-static const unsigned glCmpFunc[] =
-{
-    GL_ALWAYS,
-    GL_EQUAL,
-    GL_NOTEQUAL,
-    GL_LESS,
-    GL_LEQUAL,
-    GL_GREATER,
-    GL_GEQUAL
-};
-
-static const unsigned glSrcBlend[] =
-{
-    GL_ONE,
-    GL_ONE,
-    GL_DST_COLOR,
-    GL_SRC_ALPHA,
-    GL_SRC_ALPHA,
-    GL_ONE,
-    GL_ONE_MINUS_DST_ALPHA
-};
-
-static const unsigned glDestBlend[] =
-{
-    GL_ZERO,
-    GL_ONE,
-    GL_ZERO,
-    GL_ONE_MINUS_SRC_ALPHA,
-    GL_ONE,
-    GL_ONE_MINUS_SRC_ALPHA,
-    GL_DST_ALPHA
-};
-
-static const unsigned glStencilOps[] =
-{
-    GL_KEEP,
-    GL_ZERO,
-    GL_REPLACE,
-    GL_INCR_WRAP,
-    GL_DECR_WRAP
-};
-
-static unsigned numInstances = 0;
-
-static const String noParameter;
-
-int CloseCallback(GLFWwindow window)
-{
-    Context* context = GetWindowContext(window);
-    if (context)
-    {
-        Graphics* graphics = context->GetSubsystem<Graphics>();
-        // Do not close the window: GLFW will do it for us
-        if (graphics)
-            graphics->Release(true, false);
-    }
-
-    return GL_TRUE;
-}
-
-OBJECTTYPESTATIC(Graphics);
-
-Graphics::Graphics(Context* context_) :
-    Object(context_),
-    impl_(new GraphicsImpl()),
-    width_(0),
-    height_(0),
-    multiSample_(1),
-    fullscreen_(false),
-    vsync_(false),
-    tripleBuffer_(false),
-    lightPrepassSupport_(false),
-    deferredSupport_(false),
-    hardwareDepthSupport_(false),
-    numPrimitives_(0),
-    numBatches_(0),
-    defaultTextureFilterMode_(FILTER_BILINEAR),
-    shadowMapFormat_(GL_DEPTH_COMPONENT16),
-    hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
-    shaderParameterFrame_(0)
-{
-    ResetCachedState();
-    SetTextureUnitMappings();
-    
-    {
-        MutexLock lock(GetStaticMutex());
-        if (!numInstances)
-            glfwInit();
-        
-        ++numInstances;
-    }
-}
-
-Graphics::~Graphics()
-{
-    Close();
-    
-    delete impl_;
-    impl_ = 0;
-    
-    {
-        MutexLock lock(GetStaticMutex());
-        
-        --numInstances;
-        if (!numInstances)
-            glfwTerminate();
-    }
-}
-
-void Graphics::SetWindowTitle(const String& windowTitle)
-{
-    windowTitle_ = windowTitle;
-    if (impl_->window_)
-        glfwSetWindowTitle(impl_->window_, windowTitle_.CString());
-}
-
-bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool tripleBuffer, int multiSample)
-{
-    PROFILE(SetScreenMode);
-    
-    multiSample = Clamp(multiSample, 1, 16);
-    
-    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ &&
-        vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
-        return true;
-    
-    // If only vsync changes, do not destroy/recreate the context
-    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ &&
-        tripleBuffer == tripleBuffer_ && multiSample == multiSample_ && vsync != vsync_)
-    {
-        glfwSwapInterval(vsync ? 1 : 0);
-        vsync_ = vsync;
-        return true;
-    }
-    
-    // If zero dimensions in windowed mode, set default. If zero in fullscreen, use desktop mode
-    if (!width || !height)
-    {
-        if (!fullscreen)
-        {
-            width = 800;
-            height = 600;
-        }
-        else
-        {
-            GLFWvidmode mode;
-            glfwGetDesktopMode(&mode);
-            width = mode.width;
-            height = mode.height;
-        }
-    }
-    
-    // Close the existing window, mark GPU objects as lost
-    Release(false, true);
-    
-    {
-        // GLFW window parameters and the window list are static, so need to operate under static lock
-        MutexLock lock(GetStaticMutex());
-        
-        glfwOpenWindowHint(GLFW_RED_BITS, 8);
-        glfwOpenWindowHint(GLFW_GREEN_BITS, 8);
-        glfwOpenWindowHint(GLFW_BLUE_BITS, 8);
-        glfwOpenWindowHint(GLFW_ALPHA_BITS, 0);
-        glfwOpenWindowHint(GLFW_DEPTH_BITS, 24);
-        glfwOpenWindowHint(GLFW_STENCIL_BITS, 8);
-        glfwOpenWindowHint(GLFW_WINDOW_RESIZABLE, GL_FALSE);
-        if (multiSample > 1)
-            glfwOpenWindowHint(GLFW_FSAA_SAMPLES, multiSample);
-        else
-            glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 0);
-        
-        impl_->window_ = glfwOpenWindow(width, height, fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOWED, windowTitle_.CString(), 0);
-        if (!impl_->window_)
-        {
-            LOGERROR("Could not open window");
-            return false;
-        }
-        
-        // If OpenGL extensions not yet initialized, initialize now
-        if (!GLeeInitialized())
-            GLeeInit();
-        
-        if (!_GLEE_VERSION_2_0)
-        {
-            LOGERROR("OpenGL 2.0 is required");
-            glfwCloseWindow(impl_->window_);
-            return false;
-        }
-        
-        if (!_GLEE_EXT_framebuffer_object || !_GLEE_EXT_packed_depth_stencil || !_GLEE_EXT_texture_compression_s3tc ||
-            !_GLEE_EXT_texture_filter_anisotropic)
-        {
-            LOGERROR("EXT_framebuffer_object, EXT_packed_depth_stencil, EXT_texture_compression_s3tc and "
-                "EXT_texture_filter_anisotropic OpenGL extensions are required");
-            glfwCloseWindow(impl_->window_);
-            return false;
-        }
-        
-        // Set window close callback
-        glfwSetWindowCloseCallback(CloseCallback);
-        
-        // Associate GLFW window with the execution context
-        SetWindowContext(impl_->window_, context_);
-    }
-    
-    // Set vsync
-    glfwSwapInterval(vsync ? 1 : 0);
-    
-    // Query for system backbuffer depth
-    glGetIntegerv(GL_DEPTH_BITS, &impl_->windowDepthBits_);
-    impl_->depthBits_ = impl_->windowDepthBits_;
-    
-    // Create the FBOs and set the read buffer once (FBO will not be read)
-    glGenFramebuffersEXT(1, &impl_->fbo_);
-    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->fbo_);
-    glReadBuffer(GL_NONE);
-    glGenFramebuffersEXT(1, &impl_->depthOnlyFbo_);
-    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->depthOnlyFbo_);
-    glDrawBuffer(GL_NONE);
-    glReadBuffer(GL_NONE);
-    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
-    impl_->boundFbo_ = 0;
-
-    // Set initial state to match Direct3D
-    glEnable(GL_DEPTH_TEST);
-    SetCullMode(CULL_CCW);
-    SetDepthTest(CMP_LESSEQUAL);
-    
-    glfwGetWindowSize(impl_->window_, &width_, &height_);
-    fullscreen_ = fullscreen;
-    vsync_ = vsync;
-    tripleBuffer_ = tripleBuffer;
-    multiSample_ = multiSample;
-    
-    // Reset rendertargets and viewport for the new screen mode
-    ResetRenderTargets();
-    
-    // Clear the window to black now, because GPU object restore may take time
-    Clear(CLEAR_COLOR);
-    glfwSwapBuffers();
-    
-    // Let GPU objects restore themselves
-    for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
-        (*i)->OnDeviceReset();
-    
-    CheckFeatureSupport();
-    
-    if (multiSample > 1)
-        LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed") +
-        " multisample " + String(multiSample));
-    else
-        LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed"));
-    
-    using namespace ScreenMode;
-    
-    VariantMap eventData;
-    eventData[P_WIDTH] = width_;
-    eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    SendEvent(E_SCREENMODE, eventData);
-    
-    return true;
-}
-
-bool Graphics::SetMode(int width, int height)
-{
-    return SetMode(width, height, fullscreen_, vsync_, tripleBuffer_, multiSample_);
-}
-
-bool Graphics::ToggleFullscreen()
-{
-    return SetMode(width_, height_, !fullscreen_, vsync_, tripleBuffer_, multiSample_);
-}
-
-void Graphics::Close()
-{
-    if (!IsInitialized())
-        return;
-    
-    // Actually close the window
-    Release(true, true);
-}
-
-bool Graphics::TakeScreenShot(Image& destImage)
-{
-    PROFILE(TakeScreenShot);
-    
-    ResetRenderTargets();
-    
-    destImage.SetSize(width_, height_, 3);
-    glReadPixels(0, 0, width_, height_, GL_RGB, GL_UNSIGNED_BYTE, destImage.GetData());
-    
-    return true;
-}
-
-bool Graphics::BeginFrame()
-{
-    PROFILE(BeginRendering);
-
-    if (!IsInitialized())
-        return false;
-
-    // If we should be fullscreen, but are not currently active, do not render
-    if (fullscreen_ && (!glfwGetWindowParam(impl_->window_, GLFW_ACTIVE) || glfwGetWindowParam(impl_->window_, GLFW_ICONIFIED)))
-        return false;
-    
-    // Set default rendertarget and depth buffer
-    ResetRenderTargets();
-    
-    // Cleanup textures from previous frame
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        SetTexture(i, 0);
-    
-    // Enable color and depth write
-    SetColorWrite(true);
-    SetDepthWrite(true);
-    
-    numPrimitives_ = 0;
-    numBatches_ = 0;
-    
-    SendEvent(E_BEGINRENDERING);
-    
-    return true;
-}
-
-void Graphics::EndFrame()
-{
-    PROFILE(EndRendering);
-    
-    if (!IsInitialized())
-        return;
-    
-    SendEvent(E_ENDRENDERING);
-    
-    glfwSwapBuffers();
-}
-
-void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
-{
-    bool oldColorWrite = colorWrite_;
-    bool oldDepthWrite = depthWrite_;
-
-    if (flags & CLEAR_COLOR && !oldColorWrite)
-        SetColorWrite(true);
-    if (flags & CLEAR_DEPTH && !oldDepthWrite)
-        SetDepthWrite(true);
-    if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
-        glStencilMask(M_MAX_UNSIGNED);
-    
-    unsigned glFlags = 0;
-    if (flags & CLEAR_COLOR)
-    {
-        glFlags |= GL_COLOR_BUFFER_BIT;
-        glClearColor(color.r_, color.g_, color.b_, color.a_);
-    }
-    if (flags & CLEAR_DEPTH)
-    {
-        glFlags |= GL_DEPTH_BUFFER_BIT;
-        glClearDepth(depth);
-    }
-    if (flags & CLEAR_STENCIL)
-    {
-        glFlags |= GL_STENCIL_BUFFER_BIT;
-        glClearStencil(stencil);
-    }
-    
-    // If viewport is less than full screen, set a scissor to limit the clear
-    /// \todo Any user-set scissor test will be lost
-    IntVector2 viewSize = GetRenderTargetDimensions();
-    if (viewport_.left_ != 0 || viewport_.top_ != 0 || viewport_.right_ != viewSize.x_ || viewport_.bottom_ != viewSize.y_)
-        SetScissorTest(true, IntRect(0, 0, viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_));
-    else
-        SetScissorTest(false);
-    
-    glClear(glFlags);
-    
-    SetScissorTest(false);
-    SetColorWrite(oldColorWrite);
-    SetDepthWrite(oldDepthWrite);
-    if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
-        glStencilMask(stencilWriteMask_);
-}
-
-bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
-{
-    if (!destination || !destination->GetRenderSurface() || destination->GetWidth() != width_ ||
-        destination->GetHeight() != height_)
-        return false;
-    
-    IntRect vpCopy = viewport;
-    if (vpCopy.right_ <= vpCopy.left_)
-        vpCopy.right_ = vpCopy.left_ + 1;
-    if (vpCopy.bottom_ <= vpCopy.top_)
-        vpCopy.bottom_ = vpCopy.top_ + 1;
-    vpCopy.left_ = Clamp(vpCopy.left_, 0, width_);
-    vpCopy.top_ = Clamp(vpCopy.top_, 0, height_);
-    vpCopy.right_ = Clamp(vpCopy.right_, 0, width_);
-    vpCopy.bottom_ = Clamp(vpCopy.bottom_, 0, height_);
-    
-    // Make sure the FBO is not in use
-    ResetRenderTargets();
-    
-    // Use Direct3D convention with the vertical coordinates ie. 0 is top
-    SetTextureForUpdate(destination);
-    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, vpCopy.left_, height_ - vpCopy.bottom_, vpCopy.left_, height_ - vpCopy.bottom_,
-        vpCopy.right_ - vpCopy.left_, vpCopy.bottom_ - vpCopy.top_);
-    SetTexture(0, 0);
-    
-    return true;
-}
-
-void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
-{
-    if (!vertexCount)
-        return;
-    
-    unsigned primitiveCount = 0;
-    
-    switch (type)
-    {
-    case TRIANGLE_LIST:
-        primitiveCount = vertexCount / 3;
-        glDrawArrays(GL_TRIANGLES, vertexStart, vertexCount);
-        break;
-        
-    case LINE_LIST:
-        primitiveCount = vertexCount / 2;
-        glDrawArrays(GL_LINES, vertexStart, vertexCount);
-        break;
-    }
-    
-    numPrimitives_ += primitiveCount;
-    ++numBatches_;
-}
-
-void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount)
-{
-    if (!indexCount || !indexBuffer_)
-        return;
-    
-    unsigned primitiveCount = 0;
-    unsigned indexSize = indexBuffer_->GetIndexSize();
-    
-    switch (type)
-    {
-    case TRIANGLE_LIST:
-        primitiveCount = indexCount / 3;
-        if (indexSize == sizeof(unsigned short))
-            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize));
-        else
-            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize));
-        break;
-        
-    case LINE_LIST:
-        primitiveCount = indexCount / 2;
-        if (indexSize == sizeof(unsigned short))
-            glDrawElements(GL_LINES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize));
-        else
-            glDrawElements(GL_LINES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize));
-        break;
-    }
-    
-    numPrimitives_ += primitiveCount;
-    ++numBatches_;
-}
-
-void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount)
-{
-}
-
-void Graphics::SetVertexBuffer(VertexBuffer* buffer)
-{
-    Vector<VertexBuffer*> vertexBuffers(1);
-    PODVector<unsigned> elementMasks(1);
-    vertexBuffers[0] = buffer;
-    elementMasks[0] = MASK_DEFAULT;
-    SetVertexBuffers(vertexBuffers, elementMasks);
-}
-
-bool Graphics::SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks,
-    unsigned instanceOffset)
-{
-    if (buffers.Size() > MAX_VERTEX_STREAMS)
-    {
-        LOGERROR("Too many vertex buffers");
-        return false;
-    }
-    if (buffers.Size() != elementMasks.Size())
-    {
-        LOGERROR("Amount of element masks and vertex buffers does not match");
-        return false;
-    }
-    
-    // If no valid shader to determine the attribute bindings, can not set vertex buffers
-    if (!shaderProgram_)
-        return false;
-    const int* attributeLocations = shaderProgram_->GetAttributeLocations();
-    
-    bool changed = false;
-    unsigned newAttributes = 0;
-    
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        VertexBuffer* buffer = 0;
-        unsigned elementMask = 0;
-        
-        if (i < buffers.Size())
-        {
-
-            buffer = buffers[i];
-            elementMask = elementMasks[i];
-            if (elementMask == MASK_DEFAULT && buffer)
-                elementMask = buffers[i]->GetElementMask();
-        }
-        
-        // If buffer and element mask have stayed the same, skip to the next buffer
-        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i])
-            continue;
-        
-        vertexBuffers_[i] = buffer;
-        elementMasks_[i] = elementMask;
-        changed = true;
-        
-        if (!buffer)
-            continue;
-        
-        glBindBuffer(GL_ARRAY_BUFFER, buffer->GetGPUObject());
-        unsigned vertexSize = buffer->GetVertexSize();
-        
-        for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
-        {
-            // If shader does not use the attribute, do not bind it (bandwidth optimization)
-            int attributeIndex = attributeLocations[j];
-            if (attributeIndex < 0)
-                continue;
-            
-            unsigned elementBit = 1 << j;
-            unsigned attributeBit = 1 << attributeIndex;
-            
-            if (elementMask & elementBit)
-            {
-                newAttributes |= attributeBit;
-                
-                // Enable attribute if not enabled yet
-                if ((impl_->enabledAttributes_ & attributeBit) == 0)
-                {
-                    glEnableVertexAttribArray(attributeIndex);
-                    impl_->enabledAttributes_ |= attributeBit;
-                }
-                
-                // Set the attribute pointer
-                glVertexAttribPointer(attributeIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
-                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
-            }
-        }
-    }
-    
-    if (!changed)
-        return true;
-    
-    // Now check which vertex attributes should be disabled
-    unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
-    int disableIndex = 0;
-    while (disableAttributes)
-    {
-        if (disableAttributes & 1)
-        {
-            glDisableVertexAttribArray(disableIndex);
-            impl_->enabledAttributes_ &= ~(1 << disableIndex);
-        }
-        disableAttributes >>= 1;
-        ++disableIndex;
-    }
-    
-    return true;
-}
-
-bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>&
-    elementMasks, unsigned instanceOffset)
-{
-    if (buffers.Size() > MAX_VERTEX_STREAMS)
-    {
-        LOGERROR("Too many vertex buffers");
-        return false;
-    }
-    if (buffers.Size() != elementMasks.Size())
-    {
-        LOGERROR("Amount of element masks and vertex buffers does not match");
-        return false;
-    }
-    
-    // If no valid shader to determine the attribute bindings, can not set vertex buffers
-    if (!shaderProgram_)
-        return false;
-    const int* attributeLocations = shaderProgram_->GetAttributeLocations();
-    
-    bool changed = false;
-    unsigned newAttributes = 0;
-    
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        VertexBuffer* buffer = 0;
-        unsigned elementMask = 0;
-        
-        if (i < buffers.Size())
-        {
-            buffer = buffers[i];
-            elementMask = elementMasks[i];
-            if (elementMask == MASK_DEFAULT && buffer)
-                elementMask = buffers[i]->GetElementMask();
-        }
-        
-        // If buffer and element mask have stayed the same, skip to the next buffer
-        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i])
-            continue;
-        
-        vertexBuffers_[i] = buffer;
-        elementMasks_[i] = elementMask;
-        changed = true;
-        
-        if (!buffer)
-            continue;
-        
-        glBindBuffer(GL_ARRAY_BUFFER, buffer->GetGPUObject());
-        unsigned vertexSize = buffer->GetVertexSize();
-        
-        for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
-        {
-            // If shader does not use the attribute, do not bind it (bandwidth optimization)
-            int attributeIndex = attributeLocations[j];
-            if (attributeIndex < 0)
-                continue;
-            
-            unsigned elementBit = 1 << j;
-            unsigned attributeBit = 1 << attributeIndex;
-            
-            if (elementMask & elementBit)
-            {
-                newAttributes |= attributeBit;
-                
-                // Enable attribute if not enabled yet
-                if ((impl_->enabledAttributes_ & attributeBit) == 0)
-                {
-                    glEnableVertexAttribArray(attributeIndex);
-                    impl_->enabledAttributes_ |= attributeBit;
-                }
-                
-                // Set the attribute pointer
-                glVertexAttribPointer(attributeIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
-                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
-            }
-        }
-    }
-    
-    if (!changed)
-        return true;
-    
-    // Now check which vertex attributes should be disabled
-    unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
-    int disableIndex = 0;
-    while (disableAttributes)
-    {
-        if (disableAttributes & 1)
-        {
-            glDisableVertexAttribArray(disableIndex);
-            impl_->enabledAttributes_ &= ~(1 << disableIndex);
-        }
-        disableAttributes >>= 1;
-        ++disableIndex;
-    }
-    
-    return true;
-}
-
-void Graphics::SetIndexBuffer(IndexBuffer* buffer)
-{
-    if (indexBuffer_ == buffer)
-        return;
-    
-    if (buffer)
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->GetGPUObject());
-    else
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    
-    indexBuffer_ = buffer;
-}
-
-void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
-{
-    if (vs == vertexShader_ && ps == pixelShader_)
-        return;
-    
-    // Compile the shaders now if not yet compiled. If already attempted, do not retry
-    if (vs && !vs->IsCompiled())
-    {
-        if (vs->GetCompilerOutput().Empty())
-        {
-            PROFILE(CompileVertexShader);
-
-            bool success = vs->Create();
-            if (success)
-                LOGDEBUG("Compiled vertex shader " + vs->GetName());
-            else
-            {
-                LOGERROR("Failed to compile vertex shader " + vs->GetName() + ":\n" + vs->GetCompilerOutput());
-                vs = 0;
-            }
-        }
-        else
-            vs = 0;
-    }
-    
-    if (ps && !ps->IsCompiled())
-    {
-        if (ps->GetCompilerOutput().Empty())
-        {
-            PROFILE(CompilePixelShader);
-
-            bool success = ps->Create();
-            if (success)
-                LOGDEBUG("Compiled pixel shader " + ps->GetName());
-            else
-            {
-                LOGERROR("Failed to compile pixel shader " + ps->GetName() + ":\n" + ps->GetCompilerOutput());
-                ps = 0;
-            }
-        }
-        else
-            ps = 0;
-    }
-    
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        vertexBuffers_[i] = 0;
-        elementMasks_[i] = 0;
-    }
-    
-    if (!vs || !ps)
-    {
-        glUseProgram(0);
-        vertexShader_ = 0;
-        pixelShader_ = 0;
-        shaderProgram_ = 0;
-    }
-    else
-    {
-        vertexShader_ = vs;
-        pixelShader_ = ps;
-        
-        Pair<ShaderVariation*, ShaderVariation*> combination(vs, ps);
-        ShaderProgramMap::Iterator i = shaderPrograms_.Find(combination);
-        
-        if (i != shaderPrograms_.End())
-        {
-            // Use the existing linked program
-            if (i->second_->IsLinked())
-            {
-                glUseProgram(i->second_->GetGPUObject());
-                shaderProgram_ = i->second_;
-            }
-            else
-            {
-                glUseProgram(0);
-                shaderProgram_ = 0;
-            }
-        }
-        else
-        {
-            // Link a new combination
-            SharedPtr<ShaderProgram> newProgram(new ShaderProgram(this, vs, ps));
-            if (newProgram->Link())
-            {
-                LOGDEBUG("Linked vertex shader " + vs->GetName() + " and pixel shader " + ps->GetName());
-                // Note: Link() calls glUseProgram() to set the texture sampler uniforms,
-                // so it is not necessary to call it again
-                shaderProgram_ = newProgram;
-            }
-            else
-            {
-                LOGERROR("Failed to link vertex shader " + vs->GetName() + " and pixel shader " + ps->GetName() + ":\n" +
-                    newProgram->GetLinkerOutput());
-                glUseProgram(0);
-                shaderProgram_ = 0;
-            }
-            
-            shaderPrograms_[combination] = newProgram;
-        }
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-        {
-            switch (info->type_)
-            {
-            case GL_FLOAT:
-                glUniform1fv(info->location_, count, data);
-                break;
-                
-            case GL_FLOAT_VEC2:
-                glUniform2fv(info->location_, count / 2, data);
-                break;
-                
-            case GL_FLOAT_VEC3:
-                glUniform3fv(info->location_, count / 3, data);
-                break;
-                
-            case GL_FLOAT_VEC4:
-                glUniform4fv(info->location_, count / 4, data);
-                break;
-                
-            case GL_FLOAT_MAT3:
-                glUniformMatrix3fv(info->location_, count / 9, GL_TRUE, data);
-                break;
-                
-            case GL_FLOAT_MAT4:
-                glUniformMatrix4fv(info->location_, count / 16, GL_TRUE, data);
-                break;
-            }
-        }
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, float value)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-            glUniform1fv(info->location_, 1, &value);
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Color& color)
-{
-    SetShaderParameter(param, color.Data(), 4);
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-            glUniformMatrix3fv(info->location_, 1, GL_TRUE, matrix.Data());
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-        {
-            // Check the uniform type to avoid mismatch
-            switch (info->type_)
-            {
-            case GL_FLOAT:
-                glUniform1fv(info->location_, 1, vector.Data());
-                break;
-                
-            case GL_FLOAT_VEC2:
-                glUniform2fv(info->location_, 1, vector.Data());
-                break;
-                
-            case GL_FLOAT_VEC3:
-                glUniform3fv(info->location_, 1, vector.Data());
-                break;
-            }
-        }
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-            glUniformMatrix4fv(info->location_, 1, GL_TRUE, matrix.Data());
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-        {
-            // Check the uniform type to avoid mismatch
-            switch (info->type_)
-            {
-            case GL_FLOAT:
-                glUniform1fv(info->location_, 1, vector.Data());
-                break;
-                
-            case GL_FLOAT_VEC2:
-                glUniform2fv(info->location_, 1, vector.Data());
-                break;
-                
-            case GL_FLOAT_VEC3:
-                glUniform3fv(info->location_, 1, vector.Data());
-                break;
-                
-            case GL_FLOAT_VEC4:
-                glUniform4fv(info->location_, 1, vector.Data());
-                break;
-            }
-        }
-    }
-}
-
-void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
-{
-    if (shaderProgram_)
-    {
-        const ShaderParameter* info = shaderProgram_->GetParameter(param);
-        if (info)
-        {
-            float data[16];
-            data[0] = matrix.m00_;
-            data[1] = matrix.m01_;
-            data[2] = matrix.m02_;
-            data[3] = matrix.m03_;
-            data[4] = matrix.m10_;
-            data[5] = matrix.m11_;
-            data[6] = matrix.m12_;
-            data[7] = matrix.m13_;
-            data[8] = matrix.m20_;
-            data[9] = matrix.m21_;
-            data[10] = matrix.m22_;
-            data[11] = matrix.m23_;
-            data[12] = 0.0f;
-            data[13] = 0.0f;
-            data[14] = 0.0f;
-            data[15] = 1.0f;
-            
-            glUniformMatrix4fv(info->location_, 1, GL_TRUE, data);
-        }
-    }
-}
-
-bool Graphics::NeedParameterUpdate(StringHash param, const void* source)
-{
-    if (shaderProgram_)
-        return shaderProgram_->NeedParameterUpdate(param, source, shaderParameterFrame_);
-    
-    return false;
-}
-
-bool Graphics::NeedTextureUnit(TextureUnit unit)
-{
-    if (shaderProgram_ && shaderProgram_->HasTextureUnit(unit))
-        return true;
-    
-    return false;
-}
-
-void Graphics::ClearParameterSource(StringHash param)
-{
-    if (shaderProgram_)
-        shaderProgram_->ClearParameterSource(param);
-}
-
-void Graphics::ClearParameterSources()
-{
-    ++shaderParameterFrame_;
-}
-
-void Graphics::ClearTransformSources()
-{
-    if (shaderProgram_)
-    {
-        shaderProgram_->ClearParameterSource(VSP_MODEL);
-        shaderProgram_->ClearParameterSource(VSP_VIEWPROJ);
-    }
-}
-
-void Graphics::CleanupShaderPrograms()
-{
-    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
-    {
-        ShaderProgramMap::Iterator current = i++;
-        ShaderVariation* vs = current->second_->GetVertexShader();
-        ShaderVariation* ps = current->second_->GetPixelShader();
-        
-        if (!vs || !ps || !vs->GetGPUObject() || !ps->GetGPUObject())
-            shaderPrograms_.Erase(current);
-    }
-}
-
-void Graphics::SetTexture(unsigned index, Texture* texture)
-{
-    if (index >= MAX_TEXTURE_UNITS)
-        return;
-    
-    // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
-    if (texture)
-    {
-        if (texture == viewTexture_ || (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture))
-            texture = texture->GetBackupTexture();
-    }
-    
-    if (textures_[index] != texture)
-    {
-        if (impl_->activeTexture_ != index)
-        {
-            glActiveTexture(GL_TEXTURE0 + index);
-            impl_->activeTexture_ = index;
-        }
-        
-        if (texture)
-        {
-            unsigned glType = texture->GetTarget();
-            if (glType != textureTypes_[index])
-            {
-                if (textureTypes_[index])
-                    glDisable(textureTypes_[index]);
-                
-                glEnable(glType);
-                textureTypes_[index] = glType;
-            }
-            
-            glBindTexture(glType, texture->GetGPUObject());
-            
-            if (texture->GetParametersDirty())
-                texture->UpdateParameters();
-        }
-        else
-        {
-            if (textureTypes_[index])
-                glBindTexture(textureTypes_[index], 0);
-        }
-        
-        textures_[index] = texture;
-    }
-    else
-    {
-        if (texture && texture->GetParametersDirty())
-        {
-            if (impl_->activeTexture_ != index)
-            {
-                glActiveTexture(GL_TEXTURE0 + index);
-                impl_->activeTexture_ = index;
-            }
-            
-            glBindTexture(texture->GetTarget(), texture->GetGPUObject());
-            texture->UpdateParameters();
-        }
-    }
-}
-
-void Graphics::SetTextureForUpdate(Texture* texture)
-{
-    if (impl_->activeTexture_ != 0)
-    {
-        glActiveTexture(GL_TEXTURE0);
-        impl_->activeTexture_ = 0;
-    }
-    
-    glBindTexture(texture->GetTarget(), texture->GetGPUObject());
-    textures_[0] = texture;
-}
-
-void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
-{
-    if (mode != defaultTextureFilterMode_)
-    {
-        defaultTextureFilterMode_ = mode;
-        SetTextureParametersDirty();
-    }
-}
-
-void Graphics::SetTextureAnisotropy(unsigned level)
-{
-    if (level != textureAnisotropy_)
-    {
-        textureAnisotropy_ = level;
-        SetTextureParametersDirty();
-    }
-}
-
-void Graphics::SetTextureParametersDirty()
-{
-    for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
-    {
-        Texture* texture = dynamic_cast<Texture*>(*i);
-        if (texture)
-            texture->SetParametersDirty();
-    }
-}
-
-void Graphics::ResetRenderTargets()
-{
-    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-        SetRenderTarget(i, (RenderSurface*)0);
-    SetDepthStencil((RenderSurface*)0);
-    SetViewport(IntRect(0, 0, width_, height_));
-}
-
-void Graphics::ResetRenderTarget(unsigned index)
-{
-    SetRenderTarget(index, (RenderSurface*)0);
-}
-
-void Graphics::ResetDepthStencil()
-{
-    SetDepthStencil((RenderSurface*)0);
-}
-
-void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
-{
-    if (index >= MAX_RENDERTARGETS)
-        return;
-    
-    if (renderTarget != renderTargets_[index])
-    {
-        renderTargets_[index] = renderTarget;
-        
-        // If the rendertarget is also bound as a texture, replace with backup texture or null
-        if (renderTarget)
-        {
-            Texture* parentTexture = renderTarget->GetParentTexture();
-            
-            for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-            {
-                if (textures_[i] == parentTexture)
-                    SetTexture(i, textures_[i]->GetBackupTexture());
-            }
-        }
-    }
-
-    // Note: the rendertargets are actually committed during SetDepthStencil()
-}
-
-void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
-{
-    RenderSurface* renderTarget = 0;
-    if (texture)
-        renderTarget = texture->GetRenderSurface();
-    
-    SetRenderTarget(index, renderTarget);
-}
-
-void Graphics::SetDepthStencil(RenderSurface* depthStencil)
-{
-    depthStencil_ = depthStencil;
-
-    unsigned targetFbo = 0;
-    if (renderTargets_[0])
-        targetFbo = impl_->fbo_;
-    else if (depthStencil_)
-        targetFbo = impl_->depthOnlyFbo_;
-
-    if (impl_->boundFbo_ != targetFbo)
-    {
-        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, targetFbo);
-        impl_->boundFbo_ = targetFbo;
-    }
-
-    // If we are returning to backbuffer rendering, no further operations necessary
-    if (!targetFbo)
-    {
-        impl_->depthBits_ = impl_->windowDepthBits_;
-        return;
-    }
-
-    // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
-    // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
-    if (renderTargets_[0] && !depthStencil_)
-    {
-        int width = renderTargets_[0]->GetWidth();
-        int height = renderTargets_[0]->GetHeight();
-        
-        // Direct3D9 default depth-stencil can not be used when rendertarget is larger than the window.
-        // Check size similarly
-        if (width <= width_ && height <= height_)
-        {
-            int searchKey = (width << 16) | height;
-            HashMap<int, SharedPtr<Texture2D> >::Iterator i = depthTextures_.Find(searchKey);
-            if (i != depthTextures_.End())
-                depthStencil_ = i->second_->GetRenderSurface();
-            else
-            {
-                SharedPtr<Texture2D> newDepthTexture(new Texture2D(context_));
-                newDepthTexture->SetSize(width, height, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
-                depthTextures_[searchKey] = newDepthTexture;
-                depthStencil_ = newDepthTexture->GetRenderSurface();
-            }
-        }
-    }
-    
-    if (targetFbo == impl_->fbo_)
-    {
-        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-        {
-            if (renderTargets_[i])
-            {
-                Texture* texture = renderTargets_[i]->GetParentTexture();
-            
-                // If texture's parameters are dirty, update before attaching
-                if (texture->GetParametersDirty())
-                {
-                    SetTextureForUpdate(texture);
-                    texture->UpdateParameters();
-                    SetTexture(0, 0);
-                }
-                
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, renderTargets_[i]->GetTarget(),
-                    texture->GetGPUObject(), 0);
-            }
-            else
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, 0, 0);
-        }        
-    
-        SetDrawBuffers();
-    }
-      
-    if (depthStencil_)
-    {
-        // Bind either a renderbuffer or a depth texture, depending on what is available
-        Texture* texture = depthStencil_->GetParentTexture();
-        bool hasStencil = texture->GetFormat() == GetDepthStencilFormat();
-        unsigned renderBufferID = depthStencil_->GetRenderBuffer();
-        if (!renderBufferID)
-        {
-            // If texture's parameters are dirty, update before attaching
-            if (texture->GetParametersDirty())
-            {
-                SetTextureForUpdate(texture);
-                texture->UpdateParameters();
-                SetTexture(0, 0);
-            }
-                
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
-            if (hasStencil)
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
-            else
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-                
-            impl_->depthBits_ = texture->GetDepthBits();
-        }
-        else
-        {
-            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
-            if (hasStencil)
-                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
-            else
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-            impl_->depthBits_ = 24;
-        }
-    }
-    else
-    {
-        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);
-        impl_->depthBits_ = impl_->windowDepthBits_;
-    }
-}
-
-void Graphics::SetDepthStencil(Texture2D* texture)
-{
-    RenderSurface* depthStencil = 0;
-    if (texture)
-        depthStencil = texture->GetRenderSurface();
-    
-    SetDepthStencil(depthStencil);
-}
-
-void Graphics::SetViewTexture(Texture* texture)
-{
-    viewTexture_ = texture;
-    
-    if (viewTexture_)
-    {
-        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        {
-            if (textures_[i] == viewTexture_)
-                SetTexture(i, textures_[i]->GetBackupTexture());
-        }
-    }
-}
-
-void Graphics::SetViewport(const IntRect& rect)
-{
-    IntVector2 rtSize = GetRenderTargetDimensions();
-    
-    IntRect rectCopy = rect;
-    
-    if (rectCopy.right_ <= rectCopy.left_)
-        rectCopy.right_ = rectCopy.left_ + 1;
-    if (rectCopy.bottom_ <= rectCopy.top_)
-        rectCopy.bottom_ = rectCopy.top_ + 1;
-    rectCopy.left_ = Clamp(rectCopy.left_, 0, rtSize.x_);
-    rectCopy.top_ = Clamp(rectCopy.top_, 0, rtSize.y_);
-    rectCopy.right_ = Clamp(rectCopy.right_, 0, rtSize.x_);
-    rectCopy.bottom_ = Clamp(rectCopy.bottom_, 0, rtSize.y_);
-    
-    // Use Direct3D convention with the vertical coordinates ie. 0 is top
-    glViewport(rectCopy.left_, rtSize.y_ - rectCopy.bottom_, rectCopy.right_ - rectCopy.left_, rectCopy.bottom_ - rectCopy.top_);
-    viewport_ = rectCopy;
-    
-    // Disable scissor test, needs to be re-enabled by the user
-    SetScissorTest(false);
-}
-
-void Graphics::SetAlphaTest(bool enable, CompareMode mode, float alphaRef)
-{
-    if (enable != alphaTest_)
-    {
-        if (enable)
-            glEnable(GL_ALPHA_TEST);
-        else
-            glDisable(GL_ALPHA_TEST);
-        alphaTest_ = enable;
-    }
-    
-    if (enable)
-    {
-        alphaRef = Clamp(alphaRef, 0.0f, 1.0f);
-        if (mode != alphaTestMode_ || alphaRef != alphaRef_)
-        {
-            glAlphaFunc(glCmpFunc[mode], alphaRef);
-            alphaTestMode_ = mode;
-            alphaRef_ = alphaRef;
-        }
-    }
-}
-
-void Graphics::SetBlendMode(BlendMode mode)
-{
-    if (mode != blendMode_)
-    {
-        if (mode == BLEND_REPLACE)
-            glDisable(GL_BLEND);
-        else
-        {
-            glEnable(GL_BLEND);
-            glBlendFunc(glSrcBlend[mode], glDestBlend[mode]);
-        }
-        
-        blendMode_ = mode;
-    }
-}
-
-void Graphics::SetColorWrite(bool enable)
-{
-    if (enable != colorWrite_)
-    {
-        if (enable)
-            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-        else
-            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-        
-        colorWrite_ = enable;
-    }
-}
-
-void Graphics::SetCullMode(CullMode mode)
-{
-    if (mode != cullMode_)
-    {
-        if (mode == CULL_NONE)
-            glDisable(GL_CULL_FACE);
-        else
-        {
-            // Use Direct3D convention, ie. clockwise vertices define a front face
-            glEnable(GL_CULL_FACE);
-            glCullFace(mode == CULL_CCW ? GL_FRONT : GL_BACK);
-        }
-        
-        cullMode_ = mode;
-    }
-}
-
-void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
-{
-    if (constantBias != constantDepthBias_ || slopeScaledBias != slopeScaledDepthBias_)
-    {
-        if (constantBias != 0.0f || slopeScaledBias != 0.0f)
-        {
-            // Bring the constant bias from Direct3D9 scale to OpenGL (depends on depth buffer bitdepth)
-            // Zero depth bits may be returned if using the packed depth-stencil format. Assume 24bit in that case
-            int depthBits = Min(impl_->depthBits_, 23);
-            if (!depthBits)
-                depthBits = 23;
-            float adjustedConstantBias = constantBias * (float)(1 << (depthBits - 1));
-            float adjustedSlopeScaledBias = slopeScaledBias + 1.0f;
-            
-            glEnable(GL_POLYGON_OFFSET_FILL);
-            glEnable(GL_POLYGON_OFFSET_LINE);
-            glPolygonOffset(adjustedSlopeScaledBias, adjustedConstantBias);
-        }
-        else
-        {
-            glDisable(GL_POLYGON_OFFSET_FILL);
-            glDisable(GL_POLYGON_OFFSET_LINE);
-        }
-        
-        constantDepthBias_ = constantBias;
-        slopeScaledDepthBias_ = slopeScaledBias;
-    }
-}
-
-void Graphics::SetDepthTest(CompareMode mode)
-{
-    if (mode != depthTestMode_)
-    {
-        glDepthFunc(glCmpFunc[mode]);
-        depthTestMode_ = mode;
-    }
-}
-
-void Graphics::SetDepthWrite(bool enable)
-{
-    if (enable != depthWrite_)
-    {
-        glDepthMask(enable ? GL_TRUE : GL_FALSE);
-        depthWrite_ = enable;
-    }
-}
-
-void Graphics::SetFillMode(FillMode mode)
-{
-    if (mode != fillMode_)
-    {
-        glPolygonMode(GL_FRONT_AND_BACK, mode == FILL_SOLID ? GL_FILL : GL_LINE);
-        fillMode_ = mode;
-    }
-}
-
-void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusive)
-{
-    // During some light rendering loops, a full rect is toggled on/off repeatedly.
-    // Disable scissor in that case to reduce state changes
-    if (rect.min_.x_ <= 0.0f && rect.min_.y_ <= 0.0f && rect.max_.x_ >= 1.0f && rect.max_.y_ >= 1.0f)
-        enable = false;
-    
-    if (enable)
-    {
-        IntVector2 rtSize(GetRenderTargetDimensions());
-        IntVector2 viewSize(viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_);
-        IntVector2 viewPos(viewport_.left_, viewport_.top_);
-        IntRect intRect;
-        int expand = borderInclusive ? 1 : 0;
-        
-        intRect.left_ = Clamp((int)((rect.min_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_, 0, rtSize.x_ - 1);
-        intRect.top_ = Clamp((int)((-rect.max_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_, 0, rtSize.y_ - 1);
-        intRect.right_ = Clamp((int)((rect.max_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_ + expand, 0, rtSize.x_);
-        intRect.bottom_ = Clamp((int)((-rect.min_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_ + expand, 0, rtSize.y_);
-        
-        if (intRect.right_ == intRect.left_)
-            intRect.right_++;
-        if (intRect.bottom_ == intRect.top_)
-            intRect.bottom_++;
-        
-        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
-            enable = false;
-        
-        if (enable && scissorRect_ != intRect)
-        {
-            // Use Direct3D convention with the vertical coordinates ie. 0 is top
-            glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.right_ - intRect.left_, intRect.bottom_ - intRect.top_);
-            scissorRect_ = intRect;
-        }
-    }
-    else
-        scissorRect_ = IntRect::ZERO;
-    
-    if (enable != scissorTest_)
-    {
-        if (enable)
-            glEnable(GL_SCISSOR_TEST);
-        else
-            glDisable(GL_SCISSOR_TEST);
-        scissorTest_ = enable;
-    }
-}
-
-void Graphics::SetScissorTest(bool enable, const IntRect& rect)
-{
-    IntVector2 rtSize(GetRenderTargetDimensions());
-    IntVector2 viewSize(viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_);
-    IntVector2 viewPos(viewport_.left_, viewport_.top_);
-    
-    if (enable)
-    {
-        IntRect intRect;
-        intRect.left_ = Clamp(rect.left_ + viewPos.x_, 0, rtSize.x_ - 1);
-        intRect.top_ = Clamp(rect.top_ + viewPos.y_, 0, rtSize.y_ - 1);
-        intRect.right_ = Clamp(rect.right_ + viewPos.x_, 0, rtSize.x_);
-        intRect.bottom_ = Clamp(rect.bottom_ + viewPos.y_, 0, rtSize.y_);
-        
-        if (intRect.right_ == intRect.left_)
-            intRect.right_++;
-        if (intRect.bottom_ == intRect.top_)
-            intRect.bottom_++;
-        
-        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
-            enable = false;
-        
-        if (enable && scissorRect_ != intRect)
-        {
-            // Use Direct3D convention with the vertical coordinates ie. 0 is top
-            glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.right_ - intRect.left_, intRect.bottom_ - intRect.top_);
-            scissorRect_ = intRect;
-        }
-    }
-    else
-        scissorRect_ = IntRect::ZERO;
-    
-    if (enable != scissorTest_)
-    {
-        if (enable)
-            glEnable(GL_SCISSOR_TEST);
-        else
-            glDisable(GL_SCISSOR_TEST);
-        scissorTest_ = enable;
-    }
-}
-
-void Graphics::SetStreamFrequency(unsigned index, unsigned frequency)
-{
-}
-
-void Graphics::ResetStreamFrequencies()
-{
-}
-
-void Graphics::SetStencilTest(bool enable, CompareMode mode, StencilOp pass, StencilOp fail, StencilOp zFail, unsigned stencilRef, unsigned compareMask, unsigned writeMask)
-{
-    if (enable != stencilTest_)
-    {
-        if (enable)
-            glEnable(GL_STENCIL_TEST);
-        else
-            glDisable(GL_STENCIL_TEST);
-        stencilTest_ = enable;
-    }
-    
-    if (enable)
-    {
-        if (mode != stencilTestMode_ || stencilRef != stencilRef_ || compareMask != stencilCompareMask_)
-        {
-            glStencilFunc(glCmpFunc[mode], stencilRef, compareMask);
-            stencilTestMode_ = mode;
-            stencilRef_ = stencilRef;
-            stencilCompareMask_ = compareMask;
-        }
-        if (writeMask != stencilWriteMask_)
-        {
-            glStencilMask(writeMask);
-            stencilWriteMask_ = writeMask;
-        }
-        if (pass != stencilPass_ || fail != stencilFail_ || zFail != stencilZFail_)
-        {
-            glStencilOp(glStencilOps[fail], glStencilOps[zFail], glStencilOps[pass]);
-            stencilPass_ = pass;
-            stencilFail_ = fail;
-            stencilZFail_ = zFail;
-        }
-    }
-}
-
-void Graphics::SetForceSM2(bool enable)
-{
-}
-
-bool Graphics::IsInitialized() const
-{
-    return impl_->window_ != 0;
-}
-
-void* Graphics::GetWindowHandle() const
-{
-    return impl_->window_;
-}
-
-PODVector<IntVector2> Graphics::GetResolutions() const
-{
-    static const unsigned MAX_MODES = 256;
-    GLFWvidmode modes[MAX_MODES];
-    
-    unsigned count = glfwGetVideoModes(modes, MAX_MODES);
-    PODVector<IntVector2> ret;
-    
-    for (unsigned i = 0; i < count; ++i)
-    {
-        int width = modes[i].width;
-        int height  = modes[i].height;
-        
-        // Store mode if unique
-        bool unique = true;
-        for (unsigned j = 0; j < ret.Size(); ++i)
-        {
-            if (ret[j].x_ == width && ret[j].y_ == height)
-            {
-                unique = false;
-                break;
-            }
-        }
-        
-        if (unique)
-            ret.Push(IntVector2(width, height));
-    }
-    
-    return ret;
-}
-
-PODVector<int> Graphics::GetMultiSampleLevels() const
-{
-    PODVector<int> ret;
-    // No multisampling always supported
-    ret.Push(1);
-    /// \todo Implement properly, if possible
-    
-    return ret;
-}
-
-VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
-{
-    return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
-}
-
-TextureUnit Graphics::GetTextureUnit(const String& name)
-{
-    Map<String, TextureUnit>::Iterator i = textureUnits_.Find(name);
-    if (i != textureUnits_.End())
-        return i->second_;
-    else
-        return MAX_TEXTURE_UNITS;
-}
-
-const String& Graphics::GetTextureUnitName(TextureUnit unit)
-{
-    for (Map<String, TextureUnit>::Iterator i = textureUnits_.Begin(); i != textureUnits_.End(); ++i)
-    {
-        if (i->second_ == unit)
-            return i->first_;
-    }
-    return noParameter;
-}
-
-Texture* Graphics::GetTexture(unsigned index) const
-{
-    return index < MAX_TEXTURE_UNITS ? textures_[index] : 0;
-}
-
-RenderSurface* Graphics::GetRenderTarget(unsigned index) const
-{
-    return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
-}
-
-IntVector2 Graphics::GetRenderTargetDimensions() const
-{
-    int width, height;
-    
-    if (renderTargets_[0])
-    {
-        width = renderTargets_[0]->GetWidth();
-        height = renderTargets_[0]->GetHeight();
-    }
-    else if (depthStencil_)
-    {
-        width = depthStencil_->GetWidth();
-        height = depthStencil_->GetHeight();
-    }
-    else
-    {
-        width = width_;
-        height = height_;
-    }
-    
-    return IntVector2(width, height);
-}
-
-void Graphics::AddGPUObject(GPUObject* object)
-{
-    gpuObjects_.Push(object);
-}
-
-void Graphics::RemoveGPUObject(GPUObject* object)
-{
-    Vector<GPUObject*>::Iterator i = gpuObjects_.Find(object);
-    if (i != gpuObjects_.End())
-        gpuObjects_.Erase(i);
-}
-
-void* Graphics::ReserveDiscardLockBuffer(unsigned size)
-{
-    // First check for a free buffer that is large enough
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
-    {
-        if (!i->reserved_ && i->size_ >= size)
-        {
-            i->reserved_ = true;
-            return i->data_.Get();
-        }
-    }
-    
-    // Then check if a free buffer can be resized
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
-    {
-        if (!i->reserved_)
-        {
-            i->data_ = new unsigned char[size];
-            i->size_ = size;
-            i->reserved_ = true;
-            return i->data_.Get();
-        }
-    }
-    
-    // Finally allocate a new buffer
-    DiscardLockBuffer newBuffer;
-    newBuffer.data_ = new unsigned char[size];
-    newBuffer.size_ = size;
-    newBuffer.reserved_ = true;
-    discardLockBuffers_.Push(newBuffer);
-    return newBuffer.data_.Get();
-}
-
-void Graphics::FreeDiscardLockBuffer(void* buffer)
-{
-    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
-    {
-        if (i->reserved_ && i->data_.Get() == buffer)
-        {
-            i->reserved_ = false;
-            return;
-        }
-    }
-    
-    LOGWARNING("Reserved discard lock buffer " + ToStringHex((unsigned)buffer) + " not found");
-}
-
-void Graphics::Release(bool clearGPUObjects, bool closeWindow)
-{
-    if (!impl_->window_)
-        return;
-    
-    depthTextures_.Clear();
-    
-    if (clearGPUObjects)
-    {
-        // Shutting down: release all GPU objects that still exist
-        for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
-            (*i)->Release();
-        gpuObjects_.Clear();
-    }
-    else
-    {
-        // 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();
-    }
-    
-    if (impl_->fbo_)
-    {
-        glDeleteFramebuffersEXT(1, &impl_->fbo_);
-        glDeleteFramebuffersEXT(1, &impl_->depthOnlyFbo_);
-        impl_->fbo_ = 0;
-        impl_->depthOnlyFbo_ = 0;
-    }
-    
-    // When the new context is initialized, it will have default state again
-    ResetCachedState();
-    ClearParameterSources();
-    
-    {
-        MutexLock lock(GetStaticMutex());
-        
-        SetWindowContext(impl_->window_, 0);
-        // If in close callback, GLFW will close the window for us, so skip it
-        if (closeWindow)
-            glfwCloseWindow(impl_->window_);
-        impl_->window_ = 0;
-    }
-}
-
-unsigned Graphics::GetAlphaFormat()
-{
-    return GL_ALPHA;
-}
-
-unsigned Graphics::GetLuminanceFormat()
-{
-    return GL_LUMINANCE;
-}
-
-unsigned Graphics::GetLuminanceAlphaFormat()
-{
-    return GL_LUMINANCE_ALPHA;
-}
-
-unsigned Graphics::GetRGBFormat()
-{
-    return GL_RGB;
-}
-
-unsigned Graphics::GetRGBAFormat()
-{
-    return GL_RGBA;
-}
-
-unsigned Graphics::GetFloatFormat()
-{
-    return GL_LUMINANCE32F_ARB;
-}
-
-unsigned Graphics::GetLinearDepthFormat()
-{
-    // OpenGL FBO specs state that color attachments must have the same format; therefore must encode linear depth to RGBA
-    // manually if not using a readable hardware depth texture
-    return GL_RGBA;
-}
-
-unsigned Graphics::GetDepthStencilFormat()
-{
-    return GL_DEPTH24_STENCIL8_EXT;
-}
-
-void Graphics::CheckFeatureSupport()
-{
-    // Check supported features: light pre-pass, deferred rendering and hardware depth texture
-    lightPrepassSupport_ = false;
-    deferredSupport_ = false;
-    hardwareDepthSupport_ = false;
-
-    int numSupportedRTs = 1;
-    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
-
-    // For now hardware depth texture is only tested for on NVIDIA hardware because of visual artifacts and slowdown on ATI
-    String vendorString = String((const char*)glGetString(GL_VENDOR)).ToUpper();
-    if (vendorString.Find("NVIDIA") != String::NPOS)
-    {
-        SharedPtr<Texture2D> depthTexture(new Texture2D(context_));
-        
-        hardwareDepthSupport_ = true;
-        // Note: Texture2D::SetSize() requires hardwareDepthSupport_ == true to create a texture instead of a renderbuffer
-        depthTexture->SetSize(256, 256, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
-        SetDepthStencil(depthTexture);
-        
-        // If hardware depth textures work, this means also light pre-pass is automatically supported
-        if (CheckFramebuffer())
-        {
-            lightPrepassSupport_ = true;
-            if (numSupportedRTs >= 3)
-                deferredSupport_ = true;
-        }
-        else
-            hardwareDepthSupport_ = false;
-        
-        ResetDepthStencil();
-    }
-    
-    if (!hardwareDepthSupport_)
-    {
-        // If hardware depth is not supported, must support 2 rendertargets for light pre-pass, and 4 for deferred
-        if (numSupportedRTs >= 2)
-            lightPrepassSupport_ = true;
-        if (numSupportedRTs >= 4)
-            deferredSupport_ = true;
-    }
-}
-
-void Graphics::SetDrawBuffers()
-{
-    // 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 == impl_->drawBuffers_)
-        return;
-    
-    // Check for no color rendertargets (depth rendering only)
-    if (!newDrawBuffers)
-        glDrawBuffer(GL_NONE);
-    else
-    {
-        int drawBufferIds[4];
-        unsigned drawBufferCount = 0;
-        
-        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-        {
-            if (renderTargets_[i])
-                drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0_EXT + i;
-        }
-        glDrawBuffers(drawBufferCount, (const GLenum*)drawBufferIds);
-    }
-    
-    impl_->drawBuffers_ = newDrawBuffers;
-}
-
-bool Graphics::CheckFramebuffer()
-{
-    return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
-}
-
-void Graphics::ResetCachedState()
-{
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        vertexBuffers_[i] = 0;
-        elementMasks_[i] = 0;
-    }
-    
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-    {
-        textures_[i] = 0;
-        textureTypes_[i] = 0;
-    }
-    
-    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-        renderTargets_[i] = 0;
-    
-    depthStencil_ = 0;
-    viewTexture_ = 0;
-    viewport_ = IntRect(0, 0, 0, 0);
-    indexBuffer_ = 0;
-    vertexShader_ = 0;
-    pixelShader_ = 0;
-    shaderProgram_ = 0;
-    blendMode_ = BLEND_REPLACE;
-    alphaTest_ = false;
-    alphaTestMode_ = CMP_ALWAYS;
-    alphaRef_ = 0.0f;
-    textureAnisotropy_ = 1;
-    colorWrite_ = true;
-    cullMode_ = CULL_NONE;
-    constantDepthBias_ = 0.0f;
-    slopeScaledDepthBias_ = 0.0f;
-    depthTestMode_ = CMP_ALWAYS;
-    depthWrite_ = true;
-    fillMode_ = FILL_SOLID;
-    scissorTest_ = false;
-    scissorRect_ = IntRect::ZERO;
-    stencilTest_ = false;
-    stencilTestMode_ = CMP_ALWAYS;
-    stencilPass_ = OP_KEEP;
-    stencilFail_ = OP_KEEP;
-    stencilZFail_ = OP_KEEP;
-    stencilRef_ = 0;
-    stencilCompareMask_ = M_MAX_UNSIGNED;
-    stencilWriteMask_ = M_MAX_UNSIGNED;
-    
-    impl_->activeTexture_ = 0;
-    impl_->drawBuffers_ = M_MAX_UNSIGNED;
-    impl_->enabledAttributes_ = 0;
-}
-
-void Graphics::SetTextureUnitMappings()
-{
-    textureUnits_["DiffMap"] = TU_DIFFUSE;
-    textureUnits_["DiffCubeMap"] = TU_DIFFUSE;
-    textureUnits_["NormalMap"] = TU_NORMAL;
-    textureUnits_["EmissiveMap"] = TU_EMISSIVE;
-    textureUnits_["DetailMap"] = TU_DETAIL;
-    textureUnits_["EnvironmentMap"] = TU_ENVIRONMENT;
-    textureUnits_["EnvironmentCubeMap"] = TU_ENVIRONMENT;
-    textureUnits_["LightRampMap"] = TU_LIGHTRAMP;
-    textureUnits_["LightSpotMap"] = TU_LIGHTSHAPE;
-    textureUnits_["LightCubeMap"]  = TU_LIGHTSHAPE;
-    textureUnits_["ShadowMap"] = TU_SHADOWMAP;
-    textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
-    textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
-    textureUnits_["AlbedoBuffer"] = TU_ALBEDOBUFFER;
-    textureUnits_["NormalBuffer"] = TU_NORMALBUFFER;
-    textureUnits_["DepthBuffer"] = TU_DEPTHBUFFER;
-    textureUnits_["LightBuffer"] = TU_LIGHTBUFFER;
-}
-
-void RegisterGraphicsLibrary(Context* context)
-{
-    Animation::RegisterObject(context);
-    Material::RegisterObject(context);
-    Model::RegisterObject(context);
-    Shader::RegisterObject(context);
-    Technique::RegisterObject(context);
-    Texture2D::RegisterObject(context);
-    TextureCube::RegisterObject(context);
-    Camera::RegisterObject(context);
-    Drawable::RegisterObject(context);
-    Light::RegisterObject(context);
-    StaticModel::RegisterObject(context);
-    Skybox::RegisterObject(context);
-    AnimatedModel::RegisterObject(context);
-    AnimationController::RegisterObject(context);
-    BillboardSet::RegisterObject(context);
-    ParticleEmitter::RegisterObject(context);
-    DebugRenderer::RegisterObject(context);
-    Octree::RegisterObject(context);
-    Zone::RegisterObject(context);
-}
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// 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 "Precompiled.h"
+#include "AnimatedModel.h"
+#include "Animation.h"
+#include "AnimationController.h"
+#include "BillboardSet.h"
+#include "Camera.h"
+#include "Context.h"
+#include "DebugRenderer.h"
+#include "Graphics.h"
+#include "GraphicsEvents.h"
+#include "GraphicsImpl.h"
+#include "IndexBuffer.h"
+#include "Light.h"
+#include "Log.h"
+#include "Material.h"
+#include "Mutex.h"
+#include "Octree.h"
+#include "ParticleEmitter.h"
+#include "ProcessUtils.h"
+#include "Profiler.h"
+#include "RenderSurface.h"
+#include "Shader.h"
+#include "ShaderProgram.h"
+#include "ShaderVariation.h"
+#include "Skybox.h"
+#include "StringUtils.h"
+#include "Technique.h"
+#include "Texture2D.h"
+#include "TextureCube.h"
+#include "VertexBuffer.h"
+#include "Zone.h"
+
+#include <stdio.h>
+
+#include "DebugNew.h"
+
+static const unsigned glCmpFunc[] =
+{
+    GL_ALWAYS,
+    GL_EQUAL,
+    GL_NOTEQUAL,
+    GL_LESS,
+    GL_LEQUAL,
+    GL_GREATER,
+    GL_GEQUAL
+};
+
+static const unsigned glSrcBlend[] =
+{
+    GL_ONE,
+    GL_ONE,
+    GL_DST_COLOR,
+    GL_SRC_ALPHA,
+    GL_SRC_ALPHA,
+    GL_ONE,
+    GL_ONE_MINUS_DST_ALPHA
+};
+
+static const unsigned glDestBlend[] =
+{
+    GL_ZERO,
+    GL_ONE,
+    GL_ZERO,
+    GL_ONE_MINUS_SRC_ALPHA,
+    GL_ONE,
+    GL_ONE_MINUS_SRC_ALPHA,
+    GL_DST_ALPHA
+};
+
+static const unsigned glStencilOps[] =
+{
+    GL_KEEP,
+    GL_ZERO,
+    GL_REPLACE,
+    GL_INCR_WRAP,
+    GL_DECR_WRAP
+};
+
+static unsigned numInstances = 0;
+
+static const String noParameter;
+
+static const unsigned MAX_FRAMEBUFFER_AGE = 2000;
+
+int CloseCallback(GLFWwindow window)
+{
+    Context* context = GetWindowContext(window);
+    if (context)
+    {
+        Graphics* graphics = context->GetSubsystem<Graphics>();
+        // Do not close the window: GLFW will do it for us
+        if (graphics)
+            graphics->Release(true, false);
+    }
+
+    return GL_TRUE;
+}
+
+OBJECTTYPESTATIC(Graphics);
+
+Graphics::Graphics(Context* context_) :
+    Object(context_),
+    impl_(new GraphicsImpl()),
+    width_(0),
+    height_(0),
+    multiSample_(1),
+    fullscreen_(false),
+    vsync_(false),
+    tripleBuffer_(false),
+    lightPrepassSupport_(false),
+    deferredSupport_(false),
+    hardwareDepthSupport_(false),
+    numPrimitives_(0),
+    numBatches_(0),
+    defaultTextureFilterMode_(FILTER_BILINEAR),
+    shadowMapFormat_(GL_DEPTH_COMPONENT16),
+    hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
+    shaderParameterFrame_(0)
+{
+    ResetCachedState();
+    SetTextureUnitMappings();
+    
+    {
+        MutexLock lock(GetStaticMutex());
+        if (!numInstances)
+            glfwInit();
+        
+        ++numInstances;
+    }
+}
+
+Graphics::~Graphics()
+{
+    Close();
+    
+    delete impl_;
+    impl_ = 0;
+    
+    {
+        MutexLock lock(GetStaticMutex());
+        
+        --numInstances;
+        if (!numInstances)
+            glfwTerminate();
+    }
+}
+
+void Graphics::SetWindowTitle(const String& windowTitle)
+{
+    windowTitle_ = windowTitle;
+    if (impl_->window_)
+        glfwSetWindowTitle(impl_->window_, windowTitle_.CString());
+}
+
+bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool tripleBuffer, int multiSample)
+{
+    PROFILE(SetScreenMode);
+    
+    multiSample = Clamp(multiSample, 1, 16);
+    
+    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ &&
+        vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
+        return true;
+    
+    // If only vsync changes, do not destroy/recreate the context
+    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ &&
+        tripleBuffer == tripleBuffer_ && multiSample == multiSample_ && vsync != vsync_)
+    {
+        glfwSwapInterval(vsync ? 1 : 0);
+        vsync_ = vsync;
+        return true;
+    }
+    
+    // If zero dimensions in windowed mode, set default. If zero in fullscreen, use desktop mode
+    if (!width || !height)
+    {
+        if (!fullscreen)
+        {
+            width = 800;
+            height = 600;
+        }
+        else
+        {
+            GLFWvidmode mode;
+            glfwGetDesktopMode(&mode);
+            width = mode.width;
+            height = mode.height;
+        }
+    }
+    
+    // Close the existing window, mark GPU objects as lost
+    Release(false, true);
+    
+    {
+        // GLFW window parameters and the window list are static, so need to operate under static lock
+        MutexLock lock(GetStaticMutex());
+        
+        glfwOpenWindowHint(GLFW_RED_BITS, 8);
+        glfwOpenWindowHint(GLFW_GREEN_BITS, 8);
+        glfwOpenWindowHint(GLFW_BLUE_BITS, 8);
+        glfwOpenWindowHint(GLFW_ALPHA_BITS, 0);
+        glfwOpenWindowHint(GLFW_DEPTH_BITS, 24);
+        glfwOpenWindowHint(GLFW_STENCIL_BITS, 8);
+        glfwOpenWindowHint(GLFW_WINDOW_RESIZABLE, GL_FALSE);
+        if (multiSample > 1)
+            glfwOpenWindowHint(GLFW_FSAA_SAMPLES, multiSample);
+        else
+            glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 0);
+        
+        impl_->window_ = glfwOpenWindow(width, height, fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOWED, windowTitle_.CString(), 0);
+        if (!impl_->window_)
+        {
+            LOGERROR("Could not open window");
+            return false;
+        }
+        
+        // If OpenGL extensions not yet initialized, initialize now
+        if (!GLeeInitialized())
+            GLeeInit();
+        
+        if (!_GLEE_VERSION_2_0)
+        {
+            LOGERROR("OpenGL 2.0 is required");
+            glfwCloseWindow(impl_->window_);
+            return false;
+        }
+        
+        if (!_GLEE_EXT_framebuffer_object || !_GLEE_EXT_packed_depth_stencil || !_GLEE_EXT_texture_compression_s3tc ||
+            !_GLEE_EXT_texture_filter_anisotropic)
+        {
+            LOGERROR("EXT_framebuffer_object, EXT_packed_depth_stencil, EXT_texture_compression_s3tc and "
+                "EXT_texture_filter_anisotropic OpenGL extensions are required");
+            glfwCloseWindow(impl_->window_);
+            return false;
+        }
+        
+        // Set window close callback
+        glfwSetWindowCloseCallback(CloseCallback);
+        
+        // Associate GLFW window with the execution context
+        SetWindowContext(impl_->window_, context_);
+    }
+    
+    // Set vsync
+    glfwSwapInterval(vsync ? 1 : 0);
+    
+    // Query for system backbuffer depth
+    glGetIntegerv(GL_DEPTH_BITS, &impl_->windowDepthBits_);
+    impl_->depthBits_ = impl_->windowDepthBits_;
+    
+    // Set initial state to match Direct3D
+    glEnable(GL_DEPTH_TEST);
+    SetCullMode(CULL_CCW);
+    SetDepthTest(CMP_LESSEQUAL);
+    
+    glfwGetWindowSize(impl_->window_, &width_, &height_);
+    fullscreen_ = fullscreen;
+    vsync_ = vsync;
+    tripleBuffer_ = tripleBuffer;
+    multiSample_ = multiSample;
+    
+    // Reset rendertargets and viewport for the new screen mode
+    ResetRenderTargets();
+    
+    // Clear the window to black now, because GPU object restore may take time
+    Clear(CLEAR_COLOR);
+    glfwSwapBuffers();
+    
+    // Let GPU objects restore themselves
+    for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+        (*i)->OnDeviceReset();
+    
+    CheckFeatureSupport();
+    
+    if (multiSample > 1)
+        LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed") +
+        " multisample " + String(multiSample));
+    else
+        LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed"));
+    
+    using namespace ScreenMode;
+    
+    VariantMap eventData;
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = fullscreen_;
+    SendEvent(E_SCREENMODE, eventData);
+    
+    return true;
+}
+
+bool Graphics::SetMode(int width, int height)
+{
+    return SetMode(width, height, fullscreen_, vsync_, tripleBuffer_, multiSample_);
+}
+
+bool Graphics::ToggleFullscreen()
+{
+    return SetMode(width_, height_, !fullscreen_, vsync_, tripleBuffer_, multiSample_);
+}
+
+void Graphics::Close()
+{
+    if (!IsInitialized())
+        return;
+    
+    // Actually close the window
+    Release(true, true);
+}
+
+bool Graphics::TakeScreenShot(Image& destImage)
+{
+    PROFILE(TakeScreenShot);
+    
+    ResetRenderTargets();
+    
+    destImage.SetSize(width_, height_, 3);
+    glReadPixels(0, 0, width_, height_, GL_RGB, GL_UNSIGNED_BYTE, destImage.GetData());
+    
+    return true;
+}
+
+bool Graphics::BeginFrame()
+{
+    PROFILE(BeginRendering);
+
+    if (!IsInitialized())
+        return false;
+
+    // If we should be fullscreen, but are not currently active, do not render
+    if (fullscreen_ && (!glfwGetWindowParam(impl_->window_, GLFW_ACTIVE) || glfwGetWindowParam(impl_->window_, GLFW_ICONIFIED)))
+        return false;
+    
+    // Set default rendertarget and depth buffer
+    ResetRenderTargets();
+    
+    // Cleanup textures from previous frame
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        SetTexture(i, 0);
+    
+    // Enable color and depth write
+    SetColorWrite(true);
+    SetDepthWrite(true);
+    
+    numPrimitives_ = 0;
+    numBatches_ = 0;
+    
+    SendEvent(E_BEGINRENDERING);
+    
+    return true;
+}
+
+void Graphics::EndFrame()
+{
+    PROFILE(EndRendering);
+    
+    if (!IsInitialized())
+        return;
+    
+    SendEvent(E_ENDRENDERING);
+    
+    glfwSwapBuffers();
+    
+    // Clean up FBO's that have not been used for a long time
+    CleanupFramebuffers(false);
+}
+
+void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
+{
+    if (impl_->fboDirty_)
+        CommitFramebuffer();
+    
+    bool oldColorWrite = colorWrite_;
+    bool oldDepthWrite = depthWrite_;
+
+    if (flags & CLEAR_COLOR && !oldColorWrite)
+        SetColorWrite(true);
+    if (flags & CLEAR_DEPTH && !oldDepthWrite)
+        SetDepthWrite(true);
+    if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
+        glStencilMask(M_MAX_UNSIGNED);
+    
+    unsigned glFlags = 0;
+    if (flags & CLEAR_COLOR)
+    {
+        glFlags |= GL_COLOR_BUFFER_BIT;
+        glClearColor(color.r_, color.g_, color.b_, color.a_);
+    }
+    if (flags & CLEAR_DEPTH)
+    {
+        glFlags |= GL_DEPTH_BUFFER_BIT;
+        glClearDepth(depth);
+    }
+    if (flags & CLEAR_STENCIL)
+    {
+        glFlags |= GL_STENCIL_BUFFER_BIT;
+        glClearStencil(stencil);
+    }
+    
+    // If viewport is less than full screen, set a scissor to limit the clear
+    /// \todo Any user-set scissor test will be lost
+    IntVector2 viewSize = GetRenderTargetDimensions();
+    if (viewport_.left_ != 0 || viewport_.top_ != 0 || viewport_.right_ != viewSize.x_ || viewport_.bottom_ != viewSize.y_)
+        SetScissorTest(true, IntRect(0, 0, viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_));
+    else
+        SetScissorTest(false);
+    
+    glClear(glFlags);
+    
+    SetScissorTest(false);
+    SetColorWrite(oldColorWrite);
+    SetDepthWrite(oldDepthWrite);
+    if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
+        glStencilMask(stencilWriteMask_);
+}
+
+bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
+{
+    if (!destination || !destination->GetRenderSurface() || destination->GetWidth() != width_ ||
+        destination->GetHeight() != height_)
+        return false;
+    
+    IntRect vpCopy = viewport;
+    if (vpCopy.right_ <= vpCopy.left_)
+        vpCopy.right_ = vpCopy.left_ + 1;
+    if (vpCopy.bottom_ <= vpCopy.top_)
+        vpCopy.bottom_ = vpCopy.top_ + 1;
+    vpCopy.left_ = Clamp(vpCopy.left_, 0, width_);
+    vpCopy.top_ = Clamp(vpCopy.top_, 0, height_);
+    vpCopy.right_ = Clamp(vpCopy.right_, 0, width_);
+    vpCopy.bottom_ = Clamp(vpCopy.bottom_, 0, height_);
+    
+    // Make sure the FBO is not in use
+    ResetRenderTargets();
+    
+    // Use Direct3D convention with the vertical coordinates ie. 0 is top
+    SetTextureForUpdate(destination);
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, vpCopy.left_, height_ - vpCopy.bottom_, vpCopy.left_, height_ - vpCopy.bottom_,
+        vpCopy.right_ - vpCopy.left_, vpCopy.bottom_ - vpCopy.top_);
+    SetTexture(0, 0);
+    
+    return true;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
+{
+    if (!vertexCount)
+        return;
+    
+    if (impl_->fboDirty_)
+        CommitFramebuffer();
+    
+    unsigned primitiveCount = 0;
+    
+    switch (type)
+    {
+    case TRIANGLE_LIST:
+        primitiveCount = vertexCount / 3;
+        glDrawArrays(GL_TRIANGLES, vertexStart, vertexCount);
+        break;
+        
+    case LINE_LIST:
+        primitiveCount = vertexCount / 2;
+        glDrawArrays(GL_LINES, vertexStart, vertexCount);
+        break;
+    }
+    
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount)
+{
+    if (!indexCount || !indexBuffer_)
+        return;
+    
+    if (impl_->fboDirty_)
+        CommitFramebuffer();
+    
+    unsigned primitiveCount = 0;
+    unsigned indexSize = indexBuffer_->GetIndexSize();
+    
+    switch (type)
+    {
+    case TRIANGLE_LIST:
+        primitiveCount = indexCount / 3;
+        if (indexSize == sizeof(unsigned short))
+            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize));
+        else
+            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize));
+        break;
+        
+    case LINE_LIST:
+        primitiveCount = indexCount / 2;
+        if (indexSize == sizeof(unsigned short))
+            glDrawElements(GL_LINES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize));
+        else
+            glDrawElements(GL_LINES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize));
+        break;
+    }
+    
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount)
+{
+}
+
+void Graphics::SetVertexBuffer(VertexBuffer* buffer)
+{
+    Vector<VertexBuffer*> vertexBuffers(1);
+    PODVector<unsigned> elementMasks(1);
+    vertexBuffers[0] = buffer;
+    elementMasks[0] = MASK_DEFAULT;
+    SetVertexBuffers(vertexBuffers, elementMasks);
+}
+
+bool Graphics::SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks,
+    unsigned instanceOffset)
+{
+    if (buffers.Size() > MAX_VERTEX_STREAMS)
+    {
+        LOGERROR("Too many vertex buffers");
+        return false;
+    }
+    if (buffers.Size() != elementMasks.Size())
+    {
+        LOGERROR("Amount of element masks and vertex buffers does not match");
+        return false;
+    }
+    
+    // If no valid shader to determine the attribute bindings, can not set vertex buffers
+    if (!shaderProgram_)
+        return false;
+    const int* attributeLocations = shaderProgram_->GetAttributeLocations();
+    
+    bool changed = false;
+    unsigned newAttributes = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        VertexBuffer* buffer = 0;
+        unsigned elementMask = 0;
+        
+        if (i < buffers.Size())
+        {
+            buffer = buffers[i];
+            elementMask = elementMasks[i];
+            if (elementMask == MASK_DEFAULT && buffer)
+                elementMask = buffers[i]->GetElementMask();
+        }
+        
+        // If buffer and element mask have stayed the same, skip to the next buffer
+        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i])
+            continue;
+        
+        vertexBuffers_[i] = buffer;
+        elementMasks_[i] = elementMask;
+        changed = true;
+        
+        if (!buffer)
+            continue;
+        
+        glBindBuffer(GL_ARRAY_BUFFER, buffer->GetGPUObject());
+        unsigned vertexSize = buffer->GetVertexSize();
+        
+        for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+        {
+            // If shader does not use the attribute, do not bind it (bandwidth optimization)
+            int attributeIndex = attributeLocations[j];
+            if (attributeIndex < 0)
+                continue;
+            
+            unsigned elementBit = 1 << j;
+            unsigned attributeBit = 1 << attributeIndex;
+            
+            if (elementMask & elementBit)
+            {
+                newAttributes |= attributeBit;
+                
+                // Enable attribute if not enabled yet
+                if ((impl_->enabledAttributes_ & attributeBit) == 0)
+                {
+                    glEnableVertexAttribArray(attributeIndex);
+                    impl_->enabledAttributes_ |= attributeBit;
+                }
+                
+                // Set the attribute pointer
+                glVertexAttribPointer(attributeIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
+                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
+            }
+        }
+    }
+    
+    if (!changed)
+        return true;
+    
+    // Now check which vertex attributes should be disabled
+    unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
+    int disableIndex = 0;
+    while (disableAttributes)
+    {
+        if (disableAttributes & 1)
+        {
+            glDisableVertexAttribArray(disableIndex);
+            impl_->enabledAttributes_ &= ~(1 << disableIndex);
+        }
+        disableAttributes >>= 1;
+        ++disableIndex;
+    }
+    
+    return true;
+}
+
+bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>&
+    elementMasks, unsigned instanceOffset)
+{
+    if (buffers.Size() > MAX_VERTEX_STREAMS)
+    {
+        LOGERROR("Too many vertex buffers");
+        return false;
+    }
+    if (buffers.Size() != elementMasks.Size())
+    {
+        LOGERROR("Amount of element masks and vertex buffers does not match");
+        return false;
+    }
+    
+    // If no valid shader to determine the attribute bindings, can not set vertex buffers
+    if (!shaderProgram_)
+        return false;
+    const int* attributeLocations = shaderProgram_->GetAttributeLocations();
+    
+    bool changed = false;
+    unsigned newAttributes = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        VertexBuffer* buffer = 0;
+        unsigned elementMask = 0;
+        
+        if (i < buffers.Size())
+        {
+            buffer = buffers[i];
+            elementMask = elementMasks[i];
+            if (elementMask == MASK_DEFAULT && buffer)
+                elementMask = buffers[i]->GetElementMask();
+        }
+        
+        // If buffer and element mask have stayed the same, skip to the next buffer
+        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i])
+            continue;
+        
+        vertexBuffers_[i] = buffer;
+        elementMasks_[i] = elementMask;
+        changed = true;
+        
+        if (!buffer)
+            continue;
+        
+        glBindBuffer(GL_ARRAY_BUFFER, buffer->GetGPUObject());
+        unsigned vertexSize = buffer->GetVertexSize();
+        
+        for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+        {
+            // If shader does not use the attribute, do not bind it (bandwidth optimization)
+            int attributeIndex = attributeLocations[j];
+            if (attributeIndex < 0)
+                continue;
+            
+            unsigned elementBit = 1 << j;
+            unsigned attributeBit = 1 << attributeIndex;
+            
+            if (elementMask & elementBit)
+            {
+                newAttributes |= attributeBit;
+                
+                // Enable attribute if not enabled yet
+                if ((impl_->enabledAttributes_ & attributeBit) == 0)
+                {
+                    glEnableVertexAttribArray(attributeIndex);
+                    impl_->enabledAttributes_ |= attributeBit;
+                }
+                
+                // Set the attribute pointer
+                glVertexAttribPointer(attributeIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
+                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
+            }
+        }
+    }
+    
+    if (!changed)
+        return true;
+    
+    // Now check which vertex attributes should be disabled
+    unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
+    int disableIndex = 0;
+    while (disableAttributes)
+    {
+        if (disableAttributes & 1)
+        {
+            glDisableVertexAttribArray(disableIndex);
+            impl_->enabledAttributes_ &= ~(1 << disableIndex);
+        }
+        disableAttributes >>= 1;
+        ++disableIndex;
+    }
+    
+    return true;
+}
+
+void Graphics::SetIndexBuffer(IndexBuffer* buffer)
+{
+    if (indexBuffer_ == buffer)
+        return;
+    
+    if (buffer)
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->GetGPUObject());
+    else
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    
+    indexBuffer_ = buffer;
+}
+
+void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
+{
+    if (vs == vertexShader_ && ps == pixelShader_)
+        return;
+    
+    // Compile the shaders now if not yet compiled. If already attempted, do not retry
+    if (vs && !vs->IsCompiled())
+    {
+        if (vs->GetCompilerOutput().Empty())
+        {
+            PROFILE(CompileVertexShader);
+
+            bool success = vs->Create();
+            if (success)
+                LOGDEBUG("Compiled vertex shader " + vs->GetName());
+            else
+            {
+                LOGERROR("Failed to compile vertex shader " + vs->GetName() + ":\n" + vs->GetCompilerOutput());
+                vs = 0;
+            }
+        }
+        else
+            vs = 0;
+    }
+    
+    if (ps && !ps->IsCompiled())
+    {
+        if (ps->GetCompilerOutput().Empty())
+        {
+            PROFILE(CompilePixelShader);
+
+            bool success = ps->Create();
+            if (success)
+                LOGDEBUG("Compiled pixel shader " + ps->GetName());
+            else
+            {
+                LOGERROR("Failed to compile pixel shader " + ps->GetName() + ":\n" + ps->GetCompilerOutput());
+                ps = 0;
+            }
+        }
+        else
+            ps = 0;
+    }
+    
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        elementMasks_[i] = 0;
+    }
+    
+    if (!vs || !ps)
+    {
+        glUseProgram(0);
+        vertexShader_ = 0;
+        pixelShader_ = 0;
+        shaderProgram_ = 0;
+    }
+    else
+    {
+        vertexShader_ = vs;
+        pixelShader_ = ps;
+        
+        Pair<ShaderVariation*, ShaderVariation*> combination(vs, ps);
+        ShaderProgramMap::Iterator i = shaderPrograms_.Find(combination);
+        
+        if (i != shaderPrograms_.End())
+        {
+            // Use the existing linked program
+            if (i->second_->IsLinked())
+            {
+                glUseProgram(i->second_->GetGPUObject());
+                shaderProgram_ = i->second_;
+            }
+            else
+            {
+                glUseProgram(0);
+                shaderProgram_ = 0;
+            }
+        }
+        else
+        {
+            // Link a new combination
+            SharedPtr<ShaderProgram> newProgram(new ShaderProgram(this, vs, ps));
+            if (newProgram->Link())
+            {
+                LOGDEBUG("Linked vertex shader " + vs->GetName() + " and pixel shader " + ps->GetName());
+                // Note: Link() calls glUseProgram() to set the texture sampler uniforms,
+                // so it is not necessary to call it again
+                shaderProgram_ = newProgram;
+            }
+            else
+            {
+                LOGERROR("Failed to link vertex shader " + vs->GetName() + " and pixel shader " + ps->GetName() + ":\n" +
+                    newProgram->GetLinkerOutput());
+                glUseProgram(0);
+                shaderProgram_ = 0;
+            }
+            
+            shaderPrograms_[combination] = newProgram;
+        }
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+        {
+            switch (info->type_)
+            {
+            case GL_FLOAT:
+                glUniform1fv(info->location_, count, data);
+                break;
+                
+            case GL_FLOAT_VEC2:
+                glUniform2fv(info->location_, count / 2, data);
+                break;
+                
+            case GL_FLOAT_VEC3:
+                glUniform3fv(info->location_, count / 3, data);
+                break;
+                
+            case GL_FLOAT_VEC4:
+                glUniform4fv(info->location_, count / 4, data);
+                break;
+                
+            case GL_FLOAT_MAT3:
+                glUniformMatrix3fv(info->location_, count / 9, GL_TRUE, data);
+                break;
+                
+            case GL_FLOAT_MAT4:
+                glUniformMatrix4fv(info->location_, count / 16, GL_TRUE, data);
+                break;
+            }
+        }
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, float value)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+            glUniform1fv(info->location_, 1, &value);
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Color& color)
+{
+    SetShaderParameter(param, color.Data(), 4);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+            glUniformMatrix3fv(info->location_, 1, GL_TRUE, matrix.Data());
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+        {
+            // Check the uniform type to avoid mismatch
+            switch (info->type_)
+            {
+            case GL_FLOAT:
+                glUniform1fv(info->location_, 1, vector.Data());
+                break;
+                
+            case GL_FLOAT_VEC2:
+                glUniform2fv(info->location_, 1, vector.Data());
+                break;
+                
+            case GL_FLOAT_VEC3:
+                glUniform3fv(info->location_, 1, vector.Data());
+                break;
+            }
+        }
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+            glUniformMatrix4fv(info->location_, 1, GL_TRUE, matrix.Data());
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+        {
+            // Check the uniform type to avoid mismatch
+            switch (info->type_)
+            {
+            case GL_FLOAT:
+                glUniform1fv(info->location_, 1, vector.Data());
+                break;
+                
+            case GL_FLOAT_VEC2:
+                glUniform2fv(info->location_, 1, vector.Data());
+                break;
+                
+            case GL_FLOAT_VEC3:
+                glUniform3fv(info->location_, 1, vector.Data());
+                break;
+                
+            case GL_FLOAT_VEC4:
+                glUniform4fv(info->location_, 1, vector.Data());
+                break;
+            }
+        }
+    }
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
+{
+    if (shaderProgram_)
+    {
+        const ShaderParameter* info = shaderProgram_->GetParameter(param);
+        if (info)
+        {
+            float data[16];
+            data[0] = matrix.m00_;
+            data[1] = matrix.m01_;
+            data[2] = matrix.m02_;
+            data[3] = matrix.m03_;
+            data[4] = matrix.m10_;
+            data[5] = matrix.m11_;
+            data[6] = matrix.m12_;
+            data[7] = matrix.m13_;
+            data[8] = matrix.m20_;
+            data[9] = matrix.m21_;
+            data[10] = matrix.m22_;
+            data[11] = matrix.m23_;
+            data[12] = 0.0f;
+            data[13] = 0.0f;
+            data[14] = 0.0f;
+            data[15] = 1.0f;
+            
+            glUniformMatrix4fv(info->location_, 1, GL_TRUE, data);
+        }
+    }
+}
+
+bool Graphics::NeedParameterUpdate(StringHash param, const void* source)
+{
+    if (shaderProgram_)
+        return shaderProgram_->NeedParameterUpdate(param, source, shaderParameterFrame_);
+    
+    return false;
+}
+
+bool Graphics::NeedTextureUnit(TextureUnit unit)
+{
+    if (shaderProgram_ && shaderProgram_->HasTextureUnit(unit))
+        return true;
+    
+    return false;
+}
+
+void Graphics::ClearParameterSource(StringHash param)
+{
+    if (shaderProgram_)
+        shaderProgram_->ClearParameterSource(param);
+}
+
+void Graphics::ClearParameterSources()
+{
+    ++shaderParameterFrame_;
+}
+
+void Graphics::ClearTransformSources()
+{
+    if (shaderProgram_)
+    {
+        shaderProgram_->ClearParameterSource(VSP_MODEL);
+        shaderProgram_->ClearParameterSource(VSP_VIEWPROJ);
+    }
+}
+
+void Graphics::CleanupShaderPrograms()
+{
+    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
+    {
+        ShaderProgramMap::Iterator current = i++;
+        ShaderVariation* vs = current->second_->GetVertexShader();
+        ShaderVariation* ps = current->second_->GetPixelShader();
+        
+        if (!vs || !ps || !vs->GetGPUObject() || !ps->GetGPUObject())
+            shaderPrograms_.Erase(current);
+    }
+}
+
+void Graphics::SetTexture(unsigned index, Texture* texture)
+{
+    if (index >= MAX_TEXTURE_UNITS)
+        return;
+    
+    // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
+    if (texture)
+    {
+        if (texture == viewTexture_ || (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture))
+            texture = texture->GetBackupTexture();
+    }
+    
+    if (textures_[index] != texture)
+    {
+        if (impl_->activeTexture_ != index)
+        {
+            glActiveTexture(GL_TEXTURE0 + index);
+            impl_->activeTexture_ = index;
+        }
+        
+        if (texture)
+        {
+            unsigned glType = texture->GetTarget();
+            if (glType != textureTypes_[index])
+            {
+                if (textureTypes_[index])
+                    glDisable(textureTypes_[index]);
+                
+                glEnable(glType);
+                textureTypes_[index] = glType;
+            }
+            
+            glBindTexture(glType, texture->GetGPUObject());
+            
+            if (texture->GetParametersDirty())
+                texture->UpdateParameters();
+        }
+        else
+        {
+            if (textureTypes_[index])
+                glBindTexture(textureTypes_[index], 0);
+        }
+        
+        textures_[index] = texture;
+    }
+    else
+    {
+        if (texture && texture->GetParametersDirty())
+        {
+            if (impl_->activeTexture_ != index)
+            {
+                glActiveTexture(GL_TEXTURE0 + index);
+                impl_->activeTexture_ = index;
+            }
+            
+            glBindTexture(texture->GetTarget(), texture->GetGPUObject());
+            texture->UpdateParameters();
+        }
+    }
+}
+
+void Graphics::SetTextureForUpdate(Texture* texture)
+{
+    if (impl_->activeTexture_ != 0)
+    {
+        glActiveTexture(GL_TEXTURE0);
+        impl_->activeTexture_ = 0;
+    }
+    
+    glBindTexture(texture->GetTarget(), texture->GetGPUObject());
+    textures_[0] = texture;
+}
+
+void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
+{
+    if (mode != defaultTextureFilterMode_)
+    {
+        defaultTextureFilterMode_ = mode;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureAnisotropy(unsigned level)
+{
+    if (level != textureAnisotropy_)
+    {
+        textureAnisotropy_ = level;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureParametersDirty()
+{
+    for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+    {
+        Texture* texture = dynamic_cast<Texture*>(*i);
+        if (texture)
+            texture->SetParametersDirty();
+    }
+}
+
+void Graphics::ResetRenderTargets()
+{
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        SetRenderTarget(i, (RenderSurface*)0);
+    SetDepthStencil((RenderSurface*)0);
+    SetViewport(IntRect(0, 0, width_, height_));
+}
+
+void Graphics::ResetRenderTarget(unsigned index)
+{
+    SetRenderTarget(index, (RenderSurface*)0);
+}
+
+void Graphics::ResetDepthStencil()
+{
+    SetDepthStencil((RenderSurface*)0);
+}
+
+void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
+{
+    if (index >= MAX_RENDERTARGETS)
+        return;
+    
+    if (renderTarget != renderTargets_[index])
+    {
+        renderTargets_[index] = renderTarget;
+        
+        // If the rendertarget is also bound as a texture, replace with backup texture or null
+        if (renderTarget)
+        {
+            Texture* parentTexture = renderTarget->GetParentTexture();
+            
+            for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+            {
+                if (textures_[i] == parentTexture)
+                    SetTexture(i, textures_[i]->GetBackupTexture());
+            }
+        }
+        
+        impl_->fboDirty_ = true;
+    }
+}
+
+void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
+{
+    RenderSurface* renderTarget = 0;
+    if (texture)
+        renderTarget = texture->GetRenderSurface();
+    
+    SetRenderTarget(index, renderTarget);
+}
+
+void Graphics::SetDepthStencil(RenderSurface* depthStencil)
+{
+    // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
+    // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
+    if (renderTargets_[0] && !depthStencil)
+    {
+        int width = renderTargets_[0]->GetWidth();
+        int height = renderTargets_[0]->GetHeight();
+        
+        // Direct3D9 default depth-stencil can not be used when rendertarget is larger than the window.
+        // Check size similarly
+        if (width <= width_ && height <= height_)
+        {
+            int searchKey = (width << 16) | height;
+            HashMap<int, SharedPtr<Texture2D> >::Iterator i = depthTextures_.Find(searchKey);
+            if (i != depthTextures_.End())
+                depthStencil = i->second_->GetRenderSurface();
+            else
+            {
+                SharedPtr<Texture2D> newDepthTexture(new Texture2D(context_));
+                newDepthTexture->SetSize(width, height, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
+                depthTextures_[searchKey] = newDepthTexture;
+                depthStencil = newDepthTexture->GetRenderSurface();
+            }
+        }
+    }
+    
+    if (depthStencil != depthStencil_)
+    {
+        depthStencil_ = depthStencil;
+        impl_->fboDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthStencil(Texture2D* texture)
+{
+    RenderSurface* depthStencil = 0;
+    if (texture)
+        depthStencil = texture->GetRenderSurface();
+    
+    SetDepthStencil(depthStencil);
+}
+
+void Graphics::SetViewTexture(Texture* texture)
+{
+    viewTexture_ = texture;
+    
+    if (viewTexture_)
+    {
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (textures_[i] == viewTexture_)
+                SetTexture(i, textures_[i]->GetBackupTexture());
+        }
+    }
+}
+
+void Graphics::SetViewport(const IntRect& rect)
+{
+    if (impl_->fboDirty_)
+        CommitFramebuffer();
+    
+    IntVector2 rtSize = GetRenderTargetDimensions();
+    
+    IntRect rectCopy = rect;
+    
+    if (rectCopy.right_ <= rectCopy.left_)
+        rectCopy.right_ = rectCopy.left_ + 1;
+    if (rectCopy.bottom_ <= rectCopy.top_)
+        rectCopy.bottom_ = rectCopy.top_ + 1;
+    rectCopy.left_ = Clamp(rectCopy.left_, 0, rtSize.x_);
+    rectCopy.top_ = Clamp(rectCopy.top_, 0, rtSize.y_);
+    rectCopy.right_ = Clamp(rectCopy.right_, 0, rtSize.x_);
+    rectCopy.bottom_ = Clamp(rectCopy.bottom_, 0, rtSize.y_);
+    
+    // Use Direct3D convention with the vertical coordinates ie. 0 is top
+    glViewport(rectCopy.left_, rtSize.y_ - rectCopy.bottom_, rectCopy.right_ - rectCopy.left_, rectCopy.bottom_ - rectCopy.top_);
+    viewport_ = rectCopy;
+    
+    // Disable scissor test, needs to be re-enabled by the user
+    SetScissorTest(false);
+}
+
+void Graphics::SetAlphaTest(bool enable, CompareMode mode, float alphaRef)
+{
+    if (enable != alphaTest_)
+    {
+        if (enable)
+            glEnable(GL_ALPHA_TEST);
+        else
+            glDisable(GL_ALPHA_TEST);
+        alphaTest_ = enable;
+    }
+    
+    if (enable)
+    {
+        alphaRef = Clamp(alphaRef, 0.0f, 1.0f);
+        if (mode != alphaTestMode_ || alphaRef != alphaRef_)
+        {
+            glAlphaFunc(glCmpFunc[mode], alphaRef);
+            alphaTestMode_ = mode;
+            alphaRef_ = alphaRef;
+        }
+    }
+}
+
+void Graphics::SetBlendMode(BlendMode mode)
+{
+    if (mode != blendMode_)
+    {
+        if (mode == BLEND_REPLACE)
+            glDisable(GL_BLEND);
+        else
+        {
+            glEnable(GL_BLEND);
+            glBlendFunc(glSrcBlend[mode], glDestBlend[mode]);
+        }
+        
+        blendMode_ = mode;
+    }
+}
+
+void Graphics::SetColorWrite(bool enable)
+{
+    if (enable != colorWrite_)
+    {
+        if (enable)
+            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+        else
+            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+        
+        colorWrite_ = enable;
+    }
+}
+
+void Graphics::SetCullMode(CullMode mode)
+{
+    if (mode != cullMode_)
+    {
+        if (mode == CULL_NONE)
+            glDisable(GL_CULL_FACE);
+        else
+        {
+            // Use Direct3D convention, ie. clockwise vertices define a front face
+            glEnable(GL_CULL_FACE);
+            glCullFace(mode == CULL_CCW ? GL_FRONT : GL_BACK);
+        }
+        
+        cullMode_ = mode;
+    }
+}
+
+void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
+{
+    if (constantBias != constantDepthBias_ || slopeScaledBias != slopeScaledDepthBias_)
+    {
+        if (constantBias != 0.0f || slopeScaledBias != 0.0f)
+        {
+            // Bring the constant bias from Direct3D9 scale to OpenGL (depends on depth buffer bitdepth)
+            // Zero depth bits may be returned if using the packed depth-stencil format. Assume 24bit in that case
+            int depthBits = Min(impl_->depthBits_, 23);
+            if (!depthBits)
+                depthBits = 23;
+            float adjustedConstantBias = constantBias * (float)(1 << (depthBits - 1));
+            float adjustedSlopeScaledBias = slopeScaledBias + 1.0f;
+            
+            glEnable(GL_POLYGON_OFFSET_FILL);
+            glEnable(GL_POLYGON_OFFSET_LINE);
+            glPolygonOffset(adjustedSlopeScaledBias, adjustedConstantBias);
+        }
+        else
+        {
+            glDisable(GL_POLYGON_OFFSET_FILL);
+            glDisable(GL_POLYGON_OFFSET_LINE);
+        }
+        
+        constantDepthBias_ = constantBias;
+        slopeScaledDepthBias_ = slopeScaledBias;
+    }
+}
+
+void Graphics::SetDepthTest(CompareMode mode)
+{
+    if (mode != depthTestMode_)
+    {
+        glDepthFunc(glCmpFunc[mode]);
+        depthTestMode_ = mode;
+    }
+}
+
+void Graphics::SetDepthWrite(bool enable)
+{
+    if (enable != depthWrite_)
+    {
+        glDepthMask(enable ? GL_TRUE : GL_FALSE);
+        depthWrite_ = enable;
+    }
+}
+
+void Graphics::SetFillMode(FillMode mode)
+{
+    if (mode != fillMode_)
+    {
+        glPolygonMode(GL_FRONT_AND_BACK, mode == FILL_SOLID ? GL_FILL : GL_LINE);
+        fillMode_ = mode;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusive)
+{
+    // During some light rendering loops, a full rect is toggled on/off repeatedly.
+    // Disable scissor in that case to reduce state changes
+    if (rect.min_.x_ <= 0.0f && rect.min_.y_ <= 0.0f && rect.max_.x_ >= 1.0f && rect.max_.y_ >= 1.0f)
+        enable = false;
+    
+    if (enable)
+    {
+        IntVector2 rtSize(GetRenderTargetDimensions());
+        IntVector2 viewSize(viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_);
+        IntVector2 viewPos(viewport_.left_, viewport_.top_);
+        IntRect intRect;
+        int expand = borderInclusive ? 1 : 0;
+        
+        intRect.left_ = Clamp((int)((rect.min_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp((int)((-rect.max_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp((int)((rect.max_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_ + expand, 0, rtSize.x_);
+        intRect.bottom_ = Clamp((int)((-rect.min_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_ + expand, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && scissorRect_ != intRect)
+        {
+            // Use Direct3D convention with the vertical coordinates ie. 0 is top
+            glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.right_ - intRect.left_, intRect.bottom_ - intRect.top_);
+            scissorRect_ = intRect;
+        }
+    }
+    else
+        scissorRect_ = IntRect::ZERO;
+    
+    if (enable != scissorTest_)
+    {
+        if (enable)
+            glEnable(GL_SCISSOR_TEST);
+        else
+            glDisable(GL_SCISSOR_TEST);
+        scissorTest_ = enable;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const IntRect& rect)
+{
+    IntVector2 rtSize(GetRenderTargetDimensions());
+    IntVector2 viewSize(viewport_.right_ - viewport_.left_, viewport_.bottom_ - viewport_.top_);
+    IntVector2 viewPos(viewport_.left_, viewport_.top_);
+    
+    if (enable)
+    {
+        IntRect intRect;
+        intRect.left_ = Clamp(rect.left_ + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp(rect.top_ + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp(rect.right_ + viewPos.x_, 0, rtSize.x_);
+        intRect.bottom_ = Clamp(rect.bottom_ + viewPos.y_, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && scissorRect_ != intRect)
+        {
+            // Use Direct3D convention with the vertical coordinates ie. 0 is top
+            glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.right_ - intRect.left_, intRect.bottom_ - intRect.top_);
+            scissorRect_ = intRect;
+        }
+    }
+    else
+        scissorRect_ = IntRect::ZERO;
+    
+    if (enable != scissorTest_)
+    {
+        if (enable)
+            glEnable(GL_SCISSOR_TEST);
+        else
+            glDisable(GL_SCISSOR_TEST);
+        scissorTest_ = enable;
+    }
+}
+
+void Graphics::SetStreamFrequency(unsigned index, unsigned frequency)
+{
+}
+
+void Graphics::ResetStreamFrequencies()
+{
+}
+
+void Graphics::SetStencilTest(bool enable, CompareMode mode, StencilOp pass, StencilOp fail, StencilOp zFail, unsigned stencilRef, unsigned compareMask, unsigned writeMask)
+{
+    if (enable != stencilTest_)
+    {
+        if (enable)
+            glEnable(GL_STENCIL_TEST);
+        else
+            glDisable(GL_STENCIL_TEST);
+        stencilTest_ = enable;
+    }
+    
+    if (enable)
+    {
+        if (mode != stencilTestMode_ || stencilRef != stencilRef_ || compareMask != stencilCompareMask_)
+        {
+            glStencilFunc(glCmpFunc[mode], stencilRef, compareMask);
+            stencilTestMode_ = mode;
+            stencilRef_ = stencilRef;
+            stencilCompareMask_ = compareMask;
+        }
+        if (writeMask != stencilWriteMask_)
+        {
+            glStencilMask(writeMask);
+            stencilWriteMask_ = writeMask;
+        }
+        if (pass != stencilPass_ || fail != stencilFail_ || zFail != stencilZFail_)
+        {
+            glStencilOp(glStencilOps[fail], glStencilOps[zFail], glStencilOps[pass]);
+            stencilPass_ = pass;
+            stencilFail_ = fail;
+            stencilZFail_ = zFail;
+        }
+    }
+}
+
+void Graphics::SetForceSM2(bool enable)
+{
+}
+
+bool Graphics::IsInitialized() const
+{
+    return impl_->window_ != 0;
+}
+
+void* Graphics::GetWindowHandle() const
+{
+    return impl_->window_;
+}
+
+PODVector<IntVector2> Graphics::GetResolutions() const
+{
+    static const unsigned MAX_MODES = 256;
+    GLFWvidmode modes[MAX_MODES];
+    
+    unsigned count = glfwGetVideoModes(modes, MAX_MODES);
+    PODVector<IntVector2> ret;
+    
+    for (unsigned i = 0; i < count; ++i)
+    {
+        int width = modes[i].width;
+        int height  = modes[i].height;
+        
+        // Store mode if unique
+        bool unique = true;
+        for (unsigned j = 0; j < ret.Size(); ++i)
+        {
+            if (ret[j].x_ == width && ret[j].y_ == height)
+            {
+                unique = false;
+                break;
+            }
+        }
+        
+        if (unique)
+            ret.Push(IntVector2(width, height));
+    }
+    
+    return ret;
+}
+
+PODVector<int> Graphics::GetMultiSampleLevels() const
+{
+    PODVector<int> ret;
+    // No multisampling always supported
+    ret.Push(1);
+    /// \todo Implement properly, if possible
+    
+    return ret;
+}
+
+VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
+{
+    return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
+}
+
+TextureUnit Graphics::GetTextureUnit(const String& name)
+{
+    Map<String, TextureUnit>::Iterator i = textureUnits_.Find(name);
+    if (i != textureUnits_.End())
+        return i->second_;
+    else
+        return MAX_TEXTURE_UNITS;
+}
+
+const String& Graphics::GetTextureUnitName(TextureUnit unit)
+{
+    for (Map<String, TextureUnit>::Iterator i = textureUnits_.Begin(); i != textureUnits_.End(); ++i)
+    {
+        if (i->second_ == unit)
+            return i->first_;
+    }
+    return noParameter;
+}
+
+Texture* Graphics::GetTexture(unsigned index) const
+{
+    return index < MAX_TEXTURE_UNITS ? textures_[index] : 0;
+}
+
+RenderSurface* Graphics::GetRenderTarget(unsigned index) const
+{
+    return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
+}
+
+IntVector2 Graphics::GetRenderTargetDimensions() const
+{
+    int width, height;
+    
+    if (renderTargets_[0])
+    {
+        width = renderTargets_[0]->GetWidth();
+        height = renderTargets_[0]->GetHeight();
+    }
+    else if (depthStencil_)
+    {
+        width = depthStencil_->GetWidth();
+        height = depthStencil_->GetHeight();
+    }
+    else
+    {
+        width = width_;
+        height = height_;
+    }
+    
+    return IntVector2(width, height);
+}
+
+void Graphics::AddGPUObject(GPUObject* object)
+{
+    gpuObjects_.Push(object);
+}
+
+void Graphics::RemoveGPUObject(GPUObject* object)
+{
+    Vector<GPUObject*>::Iterator i = gpuObjects_.Find(object);
+    if (i != gpuObjects_.End())
+        gpuObjects_.Erase(i);
+}
+
+void* Graphics::ReserveDiscardLockBuffer(unsigned size)
+{
+    // First check for a free buffer that is large enough
+    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ >= size)
+        {
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Then check if a free buffer can be resized
+    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    {
+        if (!i->reserved_)
+        {
+            i->data_ = new unsigned char[size];
+            i->size_ = size;
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Finally allocate a new buffer
+    DiscardLockBuffer newBuffer;
+    newBuffer.data_ = new unsigned char[size];
+    newBuffer.size_ = size;
+    newBuffer.reserved_ = true;
+    discardLockBuffers_.Push(newBuffer);
+    return newBuffer.data_.Get();
+}
+
+void Graphics::FreeDiscardLockBuffer(void* buffer)
+{
+    for (Vector<DiscardLockBuffer>::Iterator i = discardLockBuffers_.Begin(); i != discardLockBuffers_.End(); ++i)
+    {
+        if (i->reserved_ && i->data_.Get() == buffer)
+        {
+            i->reserved_ = false;
+            return;
+        }
+    }
+    
+    LOGWARNING("Reserved discard lock buffer " + ToStringHex((unsigned)buffer) + " not found");
+}
+
+void Graphics::Release(bool clearGPUObjects, bool closeWindow)
+{
+    if (!impl_->window_)
+        return;
+    
+    CleanupFramebuffers(true);
+    
+    depthTextures_.Clear();
+    
+    if (clearGPUObjects)
+    {
+        // Shutting down: release all GPU objects that still exist
+        for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+            (*i)->Release();
+        gpuObjects_.Clear();
+    }
+    else
+    {
+        // 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();
+    }
+    
+    // When the new context is initialized, it will have default state again
+    ResetCachedState();
+    ClearParameterSources();
+    
+    {
+        MutexLock lock(GetStaticMutex());
+        
+        SetWindowContext(impl_->window_, 0);
+        // If in close callback, GLFW will close the window for us, so skip it
+        if (closeWindow)
+            glfwCloseWindow(impl_->window_);
+        impl_->window_ = 0;
+    }
+}
+
+void Graphics::CleanupRenderSurface(RenderSurface* surface)
+{
+    if (!surface)
+        return;
+    
+    unsigned currentFbo = impl_->boundFbo_;
+    
+    // Go through all FBOs and clean up the surface from them
+    for (Map<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_);
+}
+
+unsigned Graphics::GetAlphaFormat()
+{
+    return GL_ALPHA;
+}
+
+unsigned Graphics::GetLuminanceFormat()
+{
+    return GL_LUMINANCE;
+}
+
+unsigned Graphics::GetLuminanceAlphaFormat()
+{
+    return GL_LUMINANCE_ALPHA;
+}
+
+unsigned Graphics::GetRGBFormat()
+{
+    return GL_RGB;
+}
+
+unsigned Graphics::GetRGBAFormat()
+{
+    return GL_RGBA;
+}
+
+unsigned Graphics::GetFloatFormat()
+{
+    return GL_LUMINANCE32F_ARB;
+}
+
+unsigned Graphics::GetLinearDepthFormat()
+{
+    // OpenGL FBO specs state that color attachments must have the same format; therefore must encode linear depth to RGBA
+    // manually if not using a readable hardware depth texture
+    return GL_RGBA;
+}
+
+unsigned Graphics::GetDepthStencilFormat()
+{
+    return GL_DEPTH24_STENCIL8_EXT;
+}
+
+void Graphics::CheckFeatureSupport()
+{
+    // Check supported features: light pre-pass, deferred rendering and hardware depth texture
+    lightPrepassSupport_ = false;
+    deferredSupport_ = false;
+    hardwareDepthSupport_ = false;
+
+    int numSupportedRTs = 1;
+    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
+
+    // For now hardware depth texture is only tested for on NVIDIA hardware because of visual artifacts and slowdown on ATI
+    String vendorString = String((const char*)glGetString(GL_VENDOR)).ToUpper();
+    if (vendorString.Find("NVIDIA") != String::NPOS)
+    {
+        SharedPtr<Texture2D> depthTexture(new Texture2D(context_));
+        
+        hardwareDepthSupport_ = true;
+        // Note: Texture2D::SetSize() requires hardwareDepthSupport_ == true to create a texture instead of a renderbuffer
+        depthTexture->SetSize(256, 256, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
+        SetDepthStencil(depthTexture);
+        
+        // If hardware depth textures work, this means also light pre-pass is automatically supported
+        if (CheckFramebuffer())
+        {
+            lightPrepassSupport_ = true;
+            if (numSupportedRTs >= 3)
+                deferredSupport_ = true;
+        }
+        else
+            hardwareDepthSupport_ = false;
+        
+        ResetDepthStencil();
+    }
+    
+    if (!hardwareDepthSupport_)
+    {
+        // If hardware depth is not supported, must support 2 rendertargets for light pre-pass, and 4 for deferred
+        if (numSupportedRTs >= 2)
+            lightPrepassSupport_ = true;
+        if (numSupportedRTs >= 4)
+            deferredSupport_ = true;
+    }
+}
+
+void Graphics::CommitFramebuffer()
+{
+    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)
+    {
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        {
+            if (renderTargets_[i])
+            {
+                noFbo = false;
+                break;
+            }
+        }
+    }
+    
+    if (noFbo)
+    {
+        if (impl_->boundFbo_)
+        {
+            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+            impl_->boundFbo_ = 0;
+        }
+        
+        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();
+    
+    unsigned long long fboKey = (rtSize.x_ << 16 | rtSize.y_) | (((unsigned long long)format) << 32);
+    
+    Map<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Find(fboKey);
+    if (i == impl_->frameBuffers_.End())
+    {
+        FrameBufferObject newFbo;
+        glGenFramebuffersEXT(1, &newFbo.fbo_);
+        i = impl_->frameBuffers_.Insert(MakePair(fboKey, newFbo));
+    }
+    
+    i->second_.useTimer_.Reset();
+    
+    if (impl_->boundFbo_ != i->second_.fbo_)
+    {
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, i->second_.fbo_);
+        impl_->boundFbo_ = i->second_.fbo_;
+    }
+    
+    // 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;
+    }
+    
+    if (newDrawBuffers != i->second_.drawBuffers_)
+    {
+        // Check for no color rendertargets (depth rendering only)
+        if (!newDrawBuffers)
+            glDrawBuffer(GL_NONE);
+        else
+        {
+            int drawBufferIds[4];
+            unsigned drawBufferCount = 0;
+            
+            for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+            {
+                if (renderTargets_[i])
+                    drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0_EXT + i;
+            }
+            glDrawBuffers(drawBufferCount, (const GLenum*)drawBufferIds);
+        }
+        
+        i->second_.drawBuffers_ = newDrawBuffers;
+    }
+    
+    for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
+    {
+        if (renderTargets_[j])
+        {
+            Texture* texture = renderTargets_[j]->GetParentTexture();
+            
+            // 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])
+            {
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j, renderTargets_[j]->GetTarget(),
+                    texture->GetGPUObject(), 0);
+                i->second_.colorAttachments_[j] = renderTargets_[j];
+            }
+        }
+        else
+        {
+            if (i->second_.colorAttachments_[j])
+            {
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j, GL_TEXTURE_2D, 0, 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();
+        bool hasStencil = texture->GetFormat() == GetDepthStencilFormat();
+        unsigned renderBufferID = depthStencil_->GetRenderBuffer();
+        if (!renderBufferID)
+        {
+            // If texture's parameters are dirty, update before attaching
+            if (texture->GetParametersDirty())
+            {
+                SetTextureForUpdate(texture);
+                texture->UpdateParameters();
+                SetTexture(0, 0);
+            }
+            
+            if (i->second_.depthAttachment_ != depthStencil_)
+            {
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
+                if (hasStencil)
+                {
+                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D,
+                        texture->GetGPUObject(), 0);
+                }
+                else
+                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+                
+                i->second_.depthAttachment_ = depthStencil_;
+            }
+            
+            impl_->depthBits_ = texture->GetDepthBits();
+        }
+        else
+        {
+            if (i->second_.depthAttachment_ != depthStencil_)
+            {
+                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
+                if (hasStencil)
+                {
+                    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT,
+                        renderBufferID);
+                }
+                else
+                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+                
+                i->second_.depthAttachment_ = depthStencil_;
+            }
+            
+            impl_->depthBits_ = 24;
+        }
+    }
+    else
+    {
+        if (i->second_.depthAttachment_)
+        {
+            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;
+            impl_->depthBits_ = impl_->windowDepthBits_;
+        }
+    }
+}
+
+bool Graphics::CheckFramebuffer()
+{
+    return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
+}
+
+void Graphics::CleanupFramebuffers(bool deleteAll)
+{
+    if (deleteAll && impl_->boundFbo_)
+    {
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+        impl_->boundFbo_ = 0;
+    }
+    
+    for (Map<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin(); i != impl_->frameBuffers_.End();)
+    {
+        Map<unsigned long long, FrameBufferObject>::Iterator current = i++;
+        if (deleteAll || (current->second_.fbo_ != impl_->boundFbo_ && current->second_.useTimer_.GetMSec(false) > MAX_FRAMEBUFFER_AGE))
+        {
+            glDeleteFramebuffersEXT(1, &current->second_.fbo_);
+            impl_->frameBuffers_.Erase(current);
+        }
+    }
+}
+
+void Graphics::ResetCachedState()
+{
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        elementMasks_[i] = 0;
+    }
+    
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        textures_[i] = 0;
+        textureTypes_[i] = 0;
+    }
+    
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        renderTargets_[i] = 0;
+    
+    depthStencil_ = 0;
+    viewTexture_ = 0;
+    viewport_ = IntRect(0, 0, 0, 0);
+    indexBuffer_ = 0;
+    vertexShader_ = 0;
+    pixelShader_ = 0;
+    shaderProgram_ = 0;
+    blendMode_ = BLEND_REPLACE;
+    alphaTest_ = false;
+    alphaTestMode_ = CMP_ALWAYS;
+    alphaRef_ = 0.0f;
+    textureAnisotropy_ = 1;
+    colorWrite_ = true;
+    cullMode_ = CULL_NONE;
+    constantDepthBias_ = 0.0f;
+    slopeScaledDepthBias_ = 0.0f;
+    depthTestMode_ = CMP_ALWAYS;
+    depthWrite_ = true;
+    fillMode_ = FILL_SOLID;
+    scissorTest_ = false;
+    scissorRect_ = IntRect::ZERO;
+    stencilTest_ = false;
+    stencilTestMode_ = CMP_ALWAYS;
+    stencilPass_ = OP_KEEP;
+    stencilFail_ = OP_KEEP;
+    stencilZFail_ = OP_KEEP;
+    stencilRef_ = 0;
+    stencilCompareMask_ = M_MAX_UNSIGNED;
+    stencilWriteMask_ = M_MAX_UNSIGNED;
+    
+    impl_->activeTexture_ = 0;
+    impl_->enabledAttributes_ = 0;
+}
+
+void Graphics::SetTextureUnitMappings()
+{
+    textureUnits_["DiffMap"] = TU_DIFFUSE;
+    textureUnits_["DiffCubeMap"] = TU_DIFFUSE;
+    textureUnits_["NormalMap"] = TU_NORMAL;
+    textureUnits_["EmissiveMap"] = TU_EMISSIVE;
+    textureUnits_["DetailMap"] = TU_DETAIL;
+    textureUnits_["EnvironmentMap"] = TU_ENVIRONMENT;
+    textureUnits_["EnvironmentCubeMap"] = TU_ENVIRONMENT;
+    textureUnits_["LightRampMap"] = TU_LIGHTRAMP;
+    textureUnits_["LightSpotMap"] = TU_LIGHTSHAPE;
+    textureUnits_["LightCubeMap"]  = TU_LIGHTSHAPE;
+    textureUnits_["ShadowMap"] = TU_SHADOWMAP;
+    textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
+    textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
+    textureUnits_["AlbedoBuffer"] = TU_ALBEDOBUFFER;
+    textureUnits_["NormalBuffer"] = TU_NORMALBUFFER;
+    textureUnits_["DepthBuffer"] = TU_DEPTHBUFFER;
+    textureUnits_["LightBuffer"] = TU_LIGHTBUFFER;
+}
+
+void RegisterGraphicsLibrary(Context* context)
+{
+    Animation::RegisterObject(context);
+    Material::RegisterObject(context);
+    Model::RegisterObject(context);
+    Shader::RegisterObject(context);
+    Technique::RegisterObject(context);
+    Texture2D::RegisterObject(context);
+    TextureCube::RegisterObject(context);
+    Camera::RegisterObject(context);
+    Drawable::RegisterObject(context);
+    Light::RegisterObject(context);
+    StaticModel::RegisterObject(context);
+    Skybox::RegisterObject(context);
+    AnimatedModel::RegisterObject(context);
+    AnimationController::RegisterObject(context);
+    BillboardSet::RegisterObject(context);
+    ParticleEmitter::RegisterObject(context);
+    DebugRenderer::RegisterObject(context);
+    Octree::RegisterObject(context);
+    Zone::RegisterObject(context);
+}

+ 490 - 486
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -1,486 +1,490 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// 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 "ArrayPtr.h"
-#include "Color.h"
-#include "GraphicsDefs.h"
-#include "HashMap.h"
-#include "Matrix3x4.h"
-#include "Object.h"
-#include "Rect.h"
-
-class Image;
-class IndexBuffer;
-class Matrix3;
-class Matrix4;
-class Matrix3x4;
-class GPUObject;
-class GraphicsImpl;
-class RenderSurface;
-class ShaderProgram;
-class ShaderVariation;
-class Texture;
-class Texture2D;
-class TextureCube;
-class Vector3;
-class Vector4;
-class VertexBuffer;
-
-typedef Map<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
-
-static const unsigned NUM_SCREEN_BUFFERS = 2;
-
-/// CPU-side buffer for VBO discard locking.
-struct DiscardLockBuffer
-{
-    DiscardLockBuffer() :
-        size_(0),
-        reserved_(false)
-    {
-    }
-    
-    /// Buffer data.
-    SharedArrayPtr<unsigned char> data_;
-    /// Data size.
-    unsigned size_;
-    /// Reserved flag.
-    bool reserved_;
-};
-
-/// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
-class Graphics : public Object
-{
-    OBJECT(Graphics);
-    
-public:
-    /// Construct.
-    Graphics(Context* context_);
-    /// Destruct. Release the OpenGL context and close the window.
-    virtual ~Graphics();
-    
-    /// Set window title.
-    void SetWindowTitle(const String& windowTitle);
-    /// Set screen mode. Return true if successful.
-    bool SetMode(int width, int height, bool fullscreen, bool vsync, bool tripleBuffer, int multiSample);
-    /// Set screen resolution only. Return true if successful.
-    bool SetMode(int width, int height);
-    /// Toggle between full screen and windowed mode.
-    bool ToggleFullscreen();
-    /// Close the window.
-    void Close();
-    /// Take a screenshot.
-    bool TakeScreenShot(Image& destImage);
-    /// Begin frame rendering. Return true if device available and can render.
-    bool BeginFrame();
-    /// End frame rendering and swap buffers.
-    void EndFrame();
-    /// Clear any or all of rendertarget, depth buffer and stencil buffer.
-    void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
-    /// Resolve multisampled backbuffer to a texture rendertarget.
-    bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
-    /// Draw non-indexed geometry.
-    void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
-    /// Draw indexed geometry.
-    void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
-    /// Draw indexed, instanced geometry. No-op on OpenGL.
-    void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
-    /// Set vertex buffer.
-    void SetVertexBuffer(VertexBuffer* buffer);
-    /// Set multiple vertex buffers.
-    bool SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
-    /// Set multiple vertex buffers.
-    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
-    /// Set index buffer.
-    void SetIndexBuffer(IndexBuffer* buffer);
-    /// Set shaders.
-    void SetShaders(ShaderVariation* vs, ShaderVariation* ps);
-    /// Set shader float constants.
-    void SetShaderParameter(StringHash param, const float* data, unsigned count);
-    /// Set shader float constant.
-    void SetShaderParameter(StringHash param, float value);
-    /// Set shader color constant.
-    void SetShaderParameter(StringHash param, const Color& color);
-    /// Set shader 3x3 matrix constant.
-    void SetShaderParameter(StringHash param, const Matrix3& matrix);
-    /// Set shader 3D vector constant.
-    void SetShaderParameter(StringHash param, const Vector3& vector);
-    /// Set shader 4x4 matrix constant.
-    void SetShaderParameter(StringHash param, const Matrix4& matrix);
-    /// Set shader 4D vector constant.
-    void SetShaderParameter(StringHash param, const Vector4& vector);
-    /// Set shader 4x3 matrix constant.
-    void SetShaderParameter(StringHash param, const Matrix3x4& matrix);
-    /// Check whether a shader parameter in the currently set shaders needs update.
-    bool NeedParameterUpdate(StringHash param, const void* source);
-    /// Check whether the current pixel shader uses a texture unit.
-    bool NeedTextureUnit(TextureUnit unit);
-    /// Clear remembered shader parameter source.
-    void ClearParameterSource(StringHash param);
-    /// Clear remembered shader parameter sources.
-    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.
-    void SetTextureForUpdate(Texture* texture);
-    /// Set default texture filtering mode.
-    void SetDefaultTextureFilterMode(TextureFilterMode mode);
-    /// Set texture anisotropy.
-    void SetTextureAnisotropy(unsigned level);
-    /// Dirty texture parameters of all textures (when global settings change.)
-    void SetTextureParametersDirty();
-    /// Reset all rendertargets, depth-stencil surface and viewport.
-    void ResetRenderTargets();
-    /// Reset specific rendertarget.
-    void ResetRenderTarget(unsigned index);
-    /// Reset depth-stencil surface.
-    void ResetDepthStencil();
-    /// Set rendertarget.
-    void SetRenderTarget(unsigned index, RenderSurface* renderTarget);
-    /// Set rendertarget.
-    void SetRenderTarget(unsigned index, Texture2D* texture);
-    /// Set depth-stencil surface.
-    void SetDepthStencil(RenderSurface* depthStencil);
-    /// Set depth-stencil surface.
-    void SetDepthStencil(Texture2D* texture);
-    /// %Set view texture (deferred rendering final output rendertarget) to prevent it from being sampled.
-    void SetViewTexture(Texture* texture);
-    /// Set viewport.
-    void SetViewport(const IntRect& rect);
-    /// Set alpha test.
-    void SetAlphaTest(bool enable, CompareMode mode = CMP_ALWAYS, float alphaRef = 0.5f);
-    /// Set blending mode.
-    void SetBlendMode(BlendMode mode);
-    /// Set color write on/off.
-    void SetColorWrite(bool enable);
-    /// Set hardware culling mode.
-    void SetCullMode(CullMode mode);
-    /// Set depth bias.
-    void SetDepthBias(float constantBias, float slopeScaledBias);
-    /// Set depth compare.
-    void SetDepthTest(CompareMode mode);
-    /// Set depth write on/off.
-    void SetDepthWrite(bool enable);
-    /// Set polygon fill mode.
-    void SetFillMode(FillMode mode);
-    /// Set scissor test.
-    void SetScissorTest(bool enable, const Rect& rect = Rect::FULL, bool borderInclusive = true);
-    /// Set scissor test.
-    void SetScissorTest(bool enable, const IntRect& rect);
-    /// Set stencil test.
-    void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
-    /// Set vertex buffer stream frequency. No-op on OpenGL.
-    void SetStreamFrequency(unsigned index, unsigned frequency);
-    /// Reset stream frequencies. No-op on OpenGL.
-    void ResetStreamFrequencies();
-    /// Set force Shader Model 2 flag. No effect on OpenGL.
-    void SetForceSM2(bool enable);
-
-    /// Return whether rendering initialized.
-    bool IsInitialized() const;
-    /// Return graphics implementation, which holds the actual API-specific resources.
-    GraphicsImpl* GetImpl() const { return impl_; }
-    /// Return window title.
-    const String& GetWindowTitle() const { return windowTitle_; }
-    /// Return window width.
-    int GetWidth() const { return width_; }
-    /// Return window height.
-    int GetHeight() const { return height_; }
-    /// Return multisample mode (1 = no multisampling.)
-    int GetMultiSample() const { return multiSample_; }
-    /// Return whether window is fullscreen.
-    bool GetFullscreen() const { return fullscreen_; }
-    /// Return whether vertical sync is on.
-    bool GetVSync() const { return vsync_; }
-    /// Return whether triple buffering is enabled.
-    bool GetTripleBuffer() const { return tripleBuffer_; }
-    /// Return whether device is lost, and can not yet render. Always false on OpenGL.
-    bool IsDeviceLost() const { return false; }
-    /// Return window handle.
-    void* GetWindowHandle() const;
-    /// Return number of primitives drawn this frame.
-    unsigned GetNumPrimitives() const { return numPrimitives_; }
-    /// Return number of batches drawn this frame.
-    unsigned GetNumBatches() const { return numBatches_; }
-    /// Return dummy color texture format for shadow maps. Is always 0 on OpenGL.
-    unsigned GetDummyColorFormat() const { return 0; }
-    /// Return shadow map depth texture format, or 0 if not supported.
-    unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
-    /// Return 24-bit shadow map depth texture format, or 0 if not supported.
-    unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
-    /// Return whether Shader Model 3 is supported. Always false on OpenGL.
-    bool GetSM3Support() const { return false; }
-    /// Return whether light pre-pass rendering is supported.
-    bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
-    /// Return whether deferred rendering is supported.
-    bool GetDeferredSupport() const { return deferredSupport_; }
-    /// Return whether hardware depth texture is supported.
-    bool GetHardwareDepthSupport() const { return hardwareDepthSupport_; }
-    /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
-    bool GetHardwareShadowSupport() const { return true; }
-    /// Return whether 24-bit shadow maps are supported. Assume true on OpenGL.
-    bool GetHiresShadowSupport() const { return true; }
-    /// Return whether stream offset is supported. Always false on OpenGL.
-    bool GetStreamOffsetSupport() const { return false; }
-    /// Return supported fullscreen resolutions.
-    PODVector<IntVector2> GetResolutions() const;
-    /// Return supported multisampling levels.
-    PODVector<int> GetMultiSampleLevels() const;
-    /// Return vertex buffer by index.
-    VertexBuffer* GetVertexBuffer(unsigned index) const;
-    /// Return index buffer.
-    IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
-    /// Return vertex shader.
-    ShaderVariation* GetVertexShader() const { return vertexShader_; }
-    /// Return pixel shader.
-    ShaderVariation* GetPixelShader() const { return pixelShader_; }
-    /// Return shader program.
-    ShaderProgram* GetShaderProgram() const { return shaderProgram_; }
-    /// Return texture unit index by name.
-    TextureUnit GetTextureUnit(const String& name);
-    /// Return texture unit name by index.
-    const String& GetTextureUnitName(TextureUnit unit);
-    /// Return texture by texture unit index.
-    Texture* GetTexture(unsigned index) const;
-    /// Return default texture filtering mode.
-    TextureFilterMode GetDefaultTextureFilterMode() const { return defaultTextureFilterMode_; }
-    /// Return rendertarget by index.
-    RenderSurface* GetRenderTarget(unsigned index) const;
-    /// Return depth-stencil surface.
-    RenderSurface* GetDepthStencil() const { return depthStencil_; }
-    /// Return readable depth-stencil texture. Not created automatically on OpenGL.
-    Texture2D* GetDepthTexture() const { return 0; }
-    /// Return the viewport coordinates.
-    IntRect GetViewport() const { return viewport_; }
-    /// Return whether alpha testing is enabled.
-    bool GetAlphaTest() const { return alphaTest_; }
-    /// Return alpha test compare mode.
-    CompareMode GetAlphaTestMode() const { return alphaTestMode_; }
-    /// Return texture anisotropy.
-    unsigned GetTextureAnisotropy() const { return textureAnisotropy_; }
-    /// Return alpha test reference value.
-    float GetAlphaRef() const { return alphaRef_; }
-    /// Return blending mode.
-    BlendMode GetBlendMode() const { return blendMode_; }
-    /// Return whether color write is enabled.
-    bool GetColorWrite() const { return colorWrite_; }
-    /// Return hardware culling mode.
-    CullMode GetCullMode() const { return cullMode_; }
-    /// Return depth constant bias.
-    float GetDepthConstantBias() const { return constantDepthBias_; }
-    /// Return depth slope scaled bias.
-    float GetDepthSlopeScaledBias() const { return slopeScaledDepthBias_; }
-    /// Return depth compare mode.
-    CompareMode GetDepthTest() const { return depthTestMode_; }
-    /// Return whether depth write is enabled.
-    bool GetDepthWrite() const { return depthWrite_; }
-    /// Return polygon fill mode.
-    FillMode GetFillMode() const { return fillMode_; }
-    /// Return whether stencil test is enabled.
-    bool GetStencilTest() const { return stencilTest_; }
-    /// Return whether scissor test is enabled.
-    bool GetScissorTest() const { return scissorTest_; }
-    /// Return scissor rectangle coordinates.
-    const IntRect& GetScissorRect() const { return scissorRect_; }
-    /// Return stencil compare mode.
-    CompareMode GetStencilTestMode() const { return stencilTestMode_; }
-    /// Return stencil operation to do if stencil test passes.
-    StencilOp GetStencilPass() const { return stencilPass_; }
-    /// Return stencil operation to do if stencil test fails.
-    StencilOp GetStencilFail() const { return stencilFail_; }
-    /// Return stencil operation to do if depth compare fails.
-    StencilOp GetStencilZFail() const { return stencilZFail_; }
-    /// Return stencil reference value.
-    unsigned GetStencilRef() const { return stencilRef_; }
-    /// Return stencil compare bitmask.
-    unsigned GetStencilCompareMask() const { return stencilCompareMask_; }
-    /// Return stencil write bitmask.
-    unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
-    /// Return stream frequency by vertex buffer index. Always returns 0 on OpenGL.
-    unsigned GetStreamFrequency(unsigned index) const { return 0; }
-    /// Return rendertarget width and height.
-    IntVector2 GetRenderTargetDimensions() const;
-    /// Return force Shader Model 2 flag. Always false on OpenGL.
-    bool GetForceSM2() const { return false; }
-
-    /// Add a GPU object to keep track of. Called by GPUObject.
-    void AddGPUObject(GPUObject* object);
-    /// Remove a GPU object. Called by GPUObject.
-    void RemoveGPUObject(GPUObject* object);
-    /// Reserve a CPU side discard locking buffer.
-    void* ReserveDiscardLockBuffer(unsigned size);
-    /// Free a CPU side discard locking buffer.
-    void FreeDiscardLockBuffer(void* buffer);
-    /// Release/clear GPU objects and optionally close the window.
-    void Release(bool clearGPUObjects, bool closeWindow);
-    
-    /// Return the API-specific alpha texture format.
-    static unsigned GetAlphaFormat();
-    /// Return the API-specific luminance texture format.
-    static unsigned GetLuminanceFormat();
-    /// Return the API-specific luminance alpha texture format.
-    static unsigned GetLuminanceAlphaFormat();
-    /// Return the API-specific RGB texture format.
-    static unsigned GetRGBFormat();
-    /// Return the API-specific RGBA texture format.
-    static unsigned GetRGBAFormat();
-    /// Return the API-specific single channel float texture format.
-    static unsigned GetFloatFormat();
-    /// Return the API-specific linear depth texture format.
-    static unsigned GetLinearDepthFormat();
-    /// Return the API-specific hardware depth-stencil texture format.
-    static unsigned GetDepthStencilFormat();
-    
-private:
-    /// Check supported rendering features.
-    void CheckFeatureSupport();
-    /// Set draw buffer(s) on the FBO.
-    void SetDrawBuffers();
-    /// Check FBO completeness.
-    bool CheckFramebuffer();
-    /// Reset cached rendering state.
-    void ResetCachedState();
-    /// Initialize texture unit mappings.
-    void SetTextureUnitMappings();
-    
-    /// Implementation.
-    GraphicsImpl* impl_;
-    /// Window title.
-    String windowTitle_;
-    /// Window width.
-    int width_;
-    /// Window height.
-    int height_;
-    /// Multisampling mode.
-    int multiSample_;
-    /// Fullscreen flag.
-    bool fullscreen_;
-    /// Vertical sync flag.
-    bool vsync_;
-    /// Triple buffering flag.
-    bool tripleBuffer_;
-    /// Light prepass support flag.
-    bool lightPrepassSupport_;
-    /// Deferred rendering support flag.
-    bool deferredSupport_;
-    /// Hardware depth support flag.
-    bool hardwareDepthSupport_;
-    /// Number of primitives this frame.
-    unsigned numPrimitives_;
-    /// Number of batches this frame.
-    unsigned numBatches_;
-    /// GPU objects.
-    Vector<GPUObject*> gpuObjects_;
-    /// Discard lock buffers.
-    Vector<DiscardLockBuffer> discardLockBuffers_;
-    /// Shadow map depth texture format.
-    unsigned shadowMapFormat_;
-    /// Shadow map 24-bit depth texture format.
-    unsigned hiresShadowMapFormat_;
-    /// Vertex buffers in use.
-    VertexBuffer* vertexBuffers_[MAX_VERTEX_STREAMS];
-    /// Element mask in use.
-    unsigned elementMasks_[MAX_VERTEX_STREAMS];
-    /// Index buffer in use.
-    IndexBuffer* indexBuffer_;
-    /// Vertex shader in use.
-    ShaderVariation* vertexShader_;
-    /// Pixel shader in use.
-    ShaderVariation* pixelShader_;
-    /// Shader program in use.
-    ShaderProgram* shaderProgram_;
-    /// Linked shader programs.
-    ShaderProgramMap shaderPrograms_;
-    /// Shader parameters global frame number.
-    unsigned shaderParameterFrame_;
-    /// Textures in use.
-    Texture* textures_[MAX_TEXTURE_UNITS];
-    /// OpenGL texture types in use.
-    unsigned textureTypes_[MAX_TEXTURE_UNITS];
-    /// Texture unit mappings.
-    Map<String, TextureUnit> textureUnits_;
-    /// Rendertargets in use.
-    RenderSurface* renderTargets_[MAX_RENDERTARGETS];
-    /// Depth-stencil surface in use.
-    RenderSurface* depthStencil_;
-    /// View texture.
-    Texture* viewTexture_;
-    /// Viewport coordinates.
-    IntRect viewport_;
-    /// Alpha test enable flag.
-    bool alphaTest_;
-    /// Alpha test compare mode.
-    CompareMode alphaTestMode_;
-    /// Alpha test reference value.
-    float alphaRef_;
-    /// Texture anisotropy level.
-    unsigned textureAnisotropy_;
-    /// Blending mode.
-    BlendMode blendMode_;
-    /// Color write enable.
-    bool colorWrite_;
-    /// Hardware culling mode.
-    CullMode cullMode_;
-    /// Depth constant bias.
-    float constantDepthBias_;
-    /// Depth slope scaled bias.
-    float slopeScaledDepthBias_;
-    /// Depth compare mode.
-    CompareMode depthTestMode_;
-    /// Depth write enable flag.
-    bool depthWrite_;
-    /// Polygon fill mode.
-    FillMode fillMode_;
-    /// Scissor test rectangle.
-    IntRect scissorRect_;
-    /// Scissor test enable flag.
-    bool scissorTest_;
-    /// Stencil test compare mode.
-    CompareMode stencilTestMode_;
-    /// Stencil operation on pass.
-    StencilOp stencilPass_;
-    /// Stencil operation on fail.
-    StencilOp stencilFail_;
-    /// Stencil operation on depth fail.
-    StencilOp stencilZFail_;
-    /// Stencil test enable flag.
-    bool stencilTest_;
-    /// Stencil test reference value.
-    unsigned stencilRef_;
-    /// Stencil compare bitmask.
-    unsigned stencilCompareMask_;
-    /// Stencil write bitmask.
-    unsigned stencilWriteMask_;
-    /// Default texture filtering mode.
-    TextureFilterMode defaultTextureFilterMode_;
-    /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.
-    HashMap<int, SharedPtr<Texture2D> > depthTextures_;
-};
-
-/// Register Graphics library objects.
-void RegisterGraphicsLibrary(Context* context_);
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// 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 "ArrayPtr.h"
+#include "Color.h"
+#include "GraphicsDefs.h"
+#include "HashMap.h"
+#include "Matrix3x4.h"
+#include "Object.h"
+#include "Rect.h"
+
+class Image;
+class IndexBuffer;
+class Matrix3;
+class Matrix4;
+class Matrix3x4;
+class GPUObject;
+class GraphicsImpl;
+class RenderSurface;
+class ShaderProgram;
+class ShaderVariation;
+class Texture;
+class Texture2D;
+class TextureCube;
+class Vector3;
+class Vector4;
+class VertexBuffer;
+
+typedef Map<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
+
+static const unsigned NUM_SCREEN_BUFFERS = 2;
+
+/// CPU-side buffer for VBO discard locking.
+struct DiscardLockBuffer
+{
+    DiscardLockBuffer() :
+        size_(0),
+        reserved_(false)
+    {
+    }
+    
+    /// Buffer data.
+    SharedArrayPtr<unsigned char> data_;
+    /// Data size.
+    unsigned size_;
+    /// Reserved flag.
+    bool reserved_;
+};
+
+/// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
+class Graphics : public Object
+{
+    OBJECT(Graphics);
+    
+public:
+    /// Construct.
+    Graphics(Context* context_);
+    /// Destruct. Release the OpenGL context and close the window.
+    virtual ~Graphics();
+    
+    /// Set window title.
+    void SetWindowTitle(const String& windowTitle);
+    /// Set screen mode. Return true if successful.
+    bool SetMode(int width, int height, bool fullscreen, bool vsync, bool tripleBuffer, int multiSample);
+    /// Set screen resolution only. Return true if successful.
+    bool SetMode(int width, int height);
+    /// Toggle between full screen and windowed mode.
+    bool ToggleFullscreen();
+    /// Close the window.
+    void Close();
+    /// Take a screenshot.
+    bool TakeScreenShot(Image& destImage);
+    /// Begin frame rendering. Return true if device available and can render.
+    bool BeginFrame();
+    /// End frame rendering and swap buffers.
+    void EndFrame();
+    /// Clear any or all of rendertarget, depth buffer and stencil buffer.
+    void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
+    /// Resolve multisampled backbuffer to a texture rendertarget.
+    bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
+    /// Draw non-indexed geometry.
+    void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
+    /// Draw indexed geometry.
+    void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
+    /// Draw indexed, instanced geometry. No-op on OpenGL.
+    void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
+    /// Set vertex buffer.
+    void SetVertexBuffer(VertexBuffer* buffer);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set index buffer.
+    void SetIndexBuffer(IndexBuffer* buffer);
+    /// Set shaders.
+    void SetShaders(ShaderVariation* vs, ShaderVariation* ps);
+    /// Set shader float constants.
+    void SetShaderParameter(StringHash param, const float* data, unsigned count);
+    /// Set shader float constant.
+    void SetShaderParameter(StringHash param, float value);
+    /// Set shader color constant.
+    void SetShaderParameter(StringHash param, const Color& color);
+    /// Set shader 3x3 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3& matrix);
+    /// Set shader 3D vector constant.
+    void SetShaderParameter(StringHash param, const Vector3& vector);
+    /// Set shader 4x4 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix4& matrix);
+    /// Set shader 4D vector constant.
+    void SetShaderParameter(StringHash param, const Vector4& vector);
+    /// Set shader 4x3 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3x4& matrix);
+    /// Check whether a shader parameter in the currently set shaders needs update.
+    bool NeedParameterUpdate(StringHash param, const void* source);
+    /// Check whether the current pixel shader uses a texture unit.
+    bool NeedTextureUnit(TextureUnit unit);
+    /// Clear remembered shader parameter source.
+    void ClearParameterSource(StringHash param);
+    /// Clear remembered shader parameter sources.
+    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.
+    void SetTextureForUpdate(Texture* texture);
+    /// Set default texture filtering mode.
+    void SetDefaultTextureFilterMode(TextureFilterMode mode);
+    /// Set texture anisotropy.
+    void SetTextureAnisotropy(unsigned level);
+    /// Dirty texture parameters of all textures (when global settings change.)
+    void SetTextureParametersDirty();
+    /// Reset all rendertargets, depth-stencil surface and viewport.
+    void ResetRenderTargets();
+    /// Reset specific rendertarget.
+    void ResetRenderTarget(unsigned index);
+    /// Reset depth-stencil surface.
+    void ResetDepthStencil();
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, RenderSurface* renderTarget);
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, Texture2D* texture);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(RenderSurface* depthStencil);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(Texture2D* texture);
+    /// %Set view texture (deferred rendering final output rendertarget) to prevent it from being sampled.
+    void SetViewTexture(Texture* texture);
+    /// Set viewport.
+    void SetViewport(const IntRect& rect);
+    /// Set alpha test.
+    void SetAlphaTest(bool enable, CompareMode mode = CMP_ALWAYS, float alphaRef = 0.5f);
+    /// Set blending mode.
+    void SetBlendMode(BlendMode mode);
+    /// Set color write on/off.
+    void SetColorWrite(bool enable);
+    /// Set hardware culling mode.
+    void SetCullMode(CullMode mode);
+    /// Set depth bias.
+    void SetDepthBias(float constantBias, float slopeScaledBias);
+    /// Set depth compare.
+    void SetDepthTest(CompareMode mode);
+    /// Set depth write on/off.
+    void SetDepthWrite(bool enable);
+    /// Set polygon fill mode.
+    void SetFillMode(FillMode mode);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const Rect& rect = Rect::FULL, bool borderInclusive = true);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const IntRect& rect);
+    /// Set stencil test.
+    void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
+    /// Set vertex buffer stream frequency. No-op on OpenGL.
+    void SetStreamFrequency(unsigned index, unsigned frequency);
+    /// Reset stream frequencies. No-op on OpenGL.
+    void ResetStreamFrequencies();
+    /// Set force Shader Model 2 flag. No effect on OpenGL.
+    void SetForceSM2(bool enable);
+
+    /// Return whether rendering initialized.
+    bool IsInitialized() const;
+    /// Return graphics implementation, which holds the actual API-specific resources.
+    GraphicsImpl* GetImpl() const { return impl_; }
+    /// Return window title.
+    const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return window width.
+    int GetWidth() const { return width_; }
+    /// Return window height.
+    int GetHeight() const { return height_; }
+    /// Return multisample mode (1 = no multisampling.)
+    int GetMultiSample() const { return multiSample_; }
+    /// Return whether window is fullscreen.
+    bool GetFullscreen() const { return fullscreen_; }
+    /// Return whether vertical sync is on.
+    bool GetVSync() const { return vsync_; }
+    /// Return whether triple buffering is enabled.
+    bool GetTripleBuffer() const { return tripleBuffer_; }
+    /// Return whether device is lost, and can not yet render. Always false on OpenGL.
+    bool IsDeviceLost() const { return false; }
+    /// Return window handle.
+    void* GetWindowHandle() const;
+    /// Return number of primitives drawn this frame.
+    unsigned GetNumPrimitives() const { return numPrimitives_; }
+    /// Return number of batches drawn this frame.
+    unsigned GetNumBatches() const { return numBatches_; }
+    /// Return dummy color texture format for shadow maps. Is always 0 on OpenGL.
+    unsigned GetDummyColorFormat() const { return 0; }
+    /// Return shadow map depth texture format, or 0 if not supported.
+    unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
+    /// Return 24-bit shadow map depth texture format, or 0 if not supported.
+    unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
+    /// Return whether Shader Model 3 is supported. Always false on OpenGL.
+    bool GetSM3Support() const { return false; }
+    /// Return whether light pre-pass rendering is supported.
+    bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
+    /// Return whether deferred rendering is supported.
+    bool GetDeferredSupport() const { return deferredSupport_; }
+    /// Return whether hardware depth texture is supported.
+    bool GetHardwareDepthSupport() const { return hardwareDepthSupport_; }
+    /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
+    bool GetHardwareShadowSupport() const { return true; }
+    /// Return whether 24-bit shadow maps are supported. Assume true on OpenGL.
+    bool GetHiresShadowSupport() const { return true; }
+    /// Return whether stream offset is supported. Always false on OpenGL.
+    bool GetStreamOffsetSupport() const { return false; }
+    /// Return supported fullscreen resolutions.
+    PODVector<IntVector2> GetResolutions() const;
+    /// Return supported multisampling levels.
+    PODVector<int> GetMultiSampleLevels() const;
+    /// Return vertex buffer by index.
+    VertexBuffer* GetVertexBuffer(unsigned index) const;
+    /// Return index buffer.
+    IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
+    /// Return vertex shader.
+    ShaderVariation* GetVertexShader() const { return vertexShader_; }
+    /// Return pixel shader.
+    ShaderVariation* GetPixelShader() const { return pixelShader_; }
+    /// Return shader program.
+    ShaderProgram* GetShaderProgram() const { return shaderProgram_; }
+    /// Return texture unit index by name.
+    TextureUnit GetTextureUnit(const String& name);
+    /// Return texture unit name by index.
+    const String& GetTextureUnitName(TextureUnit unit);
+    /// Return texture by texture unit index.
+    Texture* GetTexture(unsigned index) const;
+    /// Return default texture filtering mode.
+    TextureFilterMode GetDefaultTextureFilterMode() const { return defaultTextureFilterMode_; }
+    /// Return rendertarget by index.
+    RenderSurface* GetRenderTarget(unsigned index) const;
+    /// Return depth-stencil surface.
+    RenderSurface* GetDepthStencil() const { return depthStencil_; }
+    /// Return readable depth-stencil texture. Not created automatically on OpenGL.
+    Texture2D* GetDepthTexture() const { return 0; }
+    /// Return the viewport coordinates.
+    IntRect GetViewport() const { return viewport_; }
+    /// Return whether alpha testing is enabled.
+    bool GetAlphaTest() const { return alphaTest_; }
+    /// Return alpha test compare mode.
+    CompareMode GetAlphaTestMode() const { return alphaTestMode_; }
+    /// Return texture anisotropy.
+    unsigned GetTextureAnisotropy() const { return textureAnisotropy_; }
+    /// Return alpha test reference value.
+    float GetAlphaRef() const { return alphaRef_; }
+    /// Return blending mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
+    /// Return whether color write is enabled.
+    bool GetColorWrite() const { return colorWrite_; }
+    /// Return hardware culling mode.
+    CullMode GetCullMode() const { return cullMode_; }
+    /// Return depth constant bias.
+    float GetDepthConstantBias() const { return constantDepthBias_; }
+    /// Return depth slope scaled bias.
+    float GetDepthSlopeScaledBias() const { return slopeScaledDepthBias_; }
+    /// Return depth compare mode.
+    CompareMode GetDepthTest() const { return depthTestMode_; }
+    /// Return whether depth write is enabled.
+    bool GetDepthWrite() const { return depthWrite_; }
+    /// Return polygon fill mode.
+    FillMode GetFillMode() const { return fillMode_; }
+    /// Return whether stencil test is enabled.
+    bool GetStencilTest() const { return stencilTest_; }
+    /// Return whether scissor test is enabled.
+    bool GetScissorTest() const { return scissorTest_; }
+    /// Return scissor rectangle coordinates.
+    const IntRect& GetScissorRect() const { return scissorRect_; }
+    /// Return stencil compare mode.
+    CompareMode GetStencilTestMode() const { return stencilTestMode_; }
+    /// Return stencil operation to do if stencil test passes.
+    StencilOp GetStencilPass() const { return stencilPass_; }
+    /// Return stencil operation to do if stencil test fails.
+    StencilOp GetStencilFail() const { return stencilFail_; }
+    /// Return stencil operation to do if depth compare fails.
+    StencilOp GetStencilZFail() const { return stencilZFail_; }
+    /// Return stencil reference value.
+    unsigned GetStencilRef() const { return stencilRef_; }
+    /// Return stencil compare bitmask.
+    unsigned GetStencilCompareMask() const { return stencilCompareMask_; }
+    /// Return stencil write bitmask.
+    unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
+    /// Return stream frequency by vertex buffer index. Always returns 0 on OpenGL.
+    unsigned GetStreamFrequency(unsigned index) const { return 0; }
+    /// Return rendertarget width and height.
+    IntVector2 GetRenderTargetDimensions() const;
+    /// Return force Shader Model 2 flag. Always false on OpenGL.
+    bool GetForceSM2() const { return false; }
+
+    /// Add a GPU object to keep track of. Called by GPUObject.
+    void AddGPUObject(GPUObject* object);
+    /// Remove a GPU object. Called by GPUObject.
+    void RemoveGPUObject(GPUObject* object);
+    /// Reserve a CPU side discard locking buffer.
+    void* ReserveDiscardLockBuffer(unsigned size);
+    /// Free a CPU side discard locking buffer.
+    void FreeDiscardLockBuffer(void* buffer);
+    /// Release/clear GPU objects and optionally close the window.
+    void Release(bool clearGPUObjects, bool closeWindow);
+    /// Clean up a render surface from all FBOs.
+    void CleanupRenderSurface(RenderSurface* surface);
+    
+    /// Return the API-specific alpha texture format.
+    static unsigned GetAlphaFormat();
+    /// Return the API-specific luminance texture format.
+    static unsigned GetLuminanceFormat();
+    /// Return the API-specific luminance alpha texture format.
+    static unsigned GetLuminanceAlphaFormat();
+    /// Return the API-specific RGB texture format.
+    static unsigned GetRGBFormat();
+    /// Return the API-specific RGBA texture format.
+    static unsigned GetRGBAFormat();
+    /// Return the API-specific single channel float texture format.
+    static unsigned GetFloatFormat();
+    /// Return the API-specific linear depth texture format.
+    static unsigned GetLinearDepthFormat();
+    /// Return the API-specific hardware depth-stencil texture format.
+    static unsigned GetDepthStencilFormat();
+    
+private:
+    /// Check supported rendering features.
+    void CheckFeatureSupport();
+    /// Select FBO and commit changes.
+    void CommitFramebuffer();
+    /// Check FBO completeness.
+    bool CheckFramebuffer();
+    /// Cleanup unused and unbound FBO's.
+    void CleanupFramebuffers(bool deleteAll);
+    /// Reset cached rendering state.
+    void ResetCachedState();
+    /// Initialize texture unit mappings.
+    void SetTextureUnitMappings();
+    
+    /// Implementation.
+    GraphicsImpl* impl_;
+    /// Window title.
+    String windowTitle_;
+    /// Window width.
+    int width_;
+    /// Window height.
+    int height_;
+    /// Multisampling mode.
+    int multiSample_;
+    /// Fullscreen flag.
+    bool fullscreen_;
+    /// Vertical sync flag.
+    bool vsync_;
+    /// Triple buffering flag.
+    bool tripleBuffer_;
+    /// Light prepass support flag.
+    bool lightPrepassSupport_;
+    /// Deferred rendering support flag.
+    bool deferredSupport_;
+    /// Hardware depth support flag.
+    bool hardwareDepthSupport_;
+    /// Number of primitives this frame.
+    unsigned numPrimitives_;
+    /// Number of batches this frame.
+    unsigned numBatches_;
+    /// GPU objects.
+    Vector<GPUObject*> gpuObjects_;
+    /// Discard lock buffers.
+    Vector<DiscardLockBuffer> discardLockBuffers_;
+    /// Shadow map depth texture format.
+    unsigned shadowMapFormat_;
+    /// Shadow map 24-bit depth texture format.
+    unsigned hiresShadowMapFormat_;
+    /// Vertex buffers in use.
+    VertexBuffer* vertexBuffers_[MAX_VERTEX_STREAMS];
+    /// Element mask in use.
+    unsigned elementMasks_[MAX_VERTEX_STREAMS];
+    /// Index buffer in use.
+    IndexBuffer* indexBuffer_;
+    /// Vertex shader in use.
+    ShaderVariation* vertexShader_;
+    /// Pixel shader in use.
+    ShaderVariation* pixelShader_;
+    /// Shader program in use.
+    ShaderProgram* shaderProgram_;
+    /// Linked shader programs.
+    ShaderProgramMap shaderPrograms_;
+    /// Shader parameters global frame number.
+    unsigned shaderParameterFrame_;
+    /// Textures in use.
+    Texture* textures_[MAX_TEXTURE_UNITS];
+    /// OpenGL texture types in use.
+    unsigned textureTypes_[MAX_TEXTURE_UNITS];
+    /// Texture unit mappings.
+    Map<String, TextureUnit> textureUnits_;
+    /// Rendertargets in use.
+    RenderSurface* renderTargets_[MAX_RENDERTARGETS];
+    /// Depth-stencil surface in use.
+    RenderSurface* depthStencil_;
+    /// View texture.
+    Texture* viewTexture_;
+    /// Viewport coordinates.
+    IntRect viewport_;
+    /// Alpha test enable flag.
+    bool alphaTest_;
+    /// Alpha test compare mode.
+    CompareMode alphaTestMode_;
+    /// Alpha test reference value.
+    float alphaRef_;
+    /// Texture anisotropy level.
+    unsigned textureAnisotropy_;
+    /// Blending mode.
+    BlendMode blendMode_;
+    /// Color write enable.
+    bool colorWrite_;
+    /// Hardware culling mode.
+    CullMode cullMode_;
+    /// Depth constant bias.
+    float constantDepthBias_;
+    /// Depth slope scaled bias.
+    float slopeScaledDepthBias_;
+    /// Depth compare mode.
+    CompareMode depthTestMode_;
+    /// Depth write enable flag.
+    bool depthWrite_;
+    /// Polygon fill mode.
+    FillMode fillMode_;
+    /// Scissor test rectangle.
+    IntRect scissorRect_;
+    /// Scissor test enable flag.
+    bool scissorTest_;
+    /// Stencil test compare mode.
+    CompareMode stencilTestMode_;
+    /// Stencil operation on pass.
+    StencilOp stencilPass_;
+    /// Stencil operation on fail.
+    StencilOp stencilFail_;
+    /// Stencil operation on depth fail.
+    StencilOp stencilZFail_;
+    /// Stencil test enable flag.
+    bool stencilTest_;
+    /// Stencil test reference value.
+    unsigned stencilRef_;
+    /// Stencil compare bitmask.
+    unsigned stencilCompareMask_;
+    /// Stencil write bitmask.
+    unsigned stencilWriteMask_;
+    /// Default texture filtering mode.
+    TextureFilterMode defaultTextureFilterMode_;
+    /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.
+    HashMap<int, SharedPtr<Texture2D> > depthTextures_;
+};
+
+/// Register Graphics library objects.
+void RegisterGraphicsLibrary(Context* context_);

+ 65 - 67
Engine/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -1,67 +1,65 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// 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 "Precompiled.h"
-#include "Context.h"
-#include "Graphics.h"
-#include "GraphicsEvents.h"
-#include "GraphicsImpl.h"
-#include "Mutex.h"
-#include "ProcessUtils.h"
-
-static HashMap<GLFWwindow, Context*> windowContexts;
-
-GraphicsImpl::GraphicsImpl() :
-    window_(0),
-    activeTexture_(0),
-    drawBuffers_(M_MAX_UNSIGNED),
-    fbo_(0),
-    depthOnlyFbo_(0),
-    boundFbo_(0),
-    enabledAttributes_(0),
-    pixelFormat_(0),
-    depthBits_(0),
-    windowDepthBits_(0)
-{
-}
-
-void SetWindowContext(GLFWwindow window, Context* context)
-{
-    MutexLock lock(GetStaticMutex());
-    
-    if (context)
-        windowContexts[window] = context;
-    else
-        windowContexts.Erase(context);
-}
-
-Context* GetWindowContext(GLFWwindow window)
-{
-    MutexLock lock(GetStaticMutex());
-    
-    HashMap<GLFWwindow, Context*>::ConstIterator i = windowContexts.Find(window);
-    if (i != windowContexts.End())
-        return i->second_;
-    else
-        return 0;
-}
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// 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 "Precompiled.h"
+#include "Context.h"
+#include "Graphics.h"
+#include "GraphicsEvents.h"
+#include "GraphicsImpl.h"
+#include "Mutex.h"
+#include "ProcessUtils.h"
+
+static HashMap<GLFWwindow, Context*> windowContexts;
+
+GraphicsImpl::GraphicsImpl() :
+    window_(0),
+    activeTexture_(0),
+    enabledAttributes_(0),
+    boundFbo_(0),
+    pixelFormat_(0),
+    depthBits_(0),
+    windowDepthBits_(0),
+    fboDirty_(false)
+{
+}
+
+void SetWindowContext(GLFWwindow window, Context* context)
+{
+    MutexLock lock(GetStaticMutex());
+    
+    if (context)
+        windowContexts[window] = context;
+    else
+        windowContexts.Erase(context);
+}
+
+Context* GetWindowContext(GLFWwindow window)
+{
+    MutexLock lock(GetStaticMutex());
+    
+    HashMap<GLFWwindow, Context*>::ConstIterator i = windowContexts.Find(window);
+    if (i != windowContexts.End())
+        return i->second_;
+    else
+        return 0;
+}

+ 95 - 69
Engine/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -1,69 +1,95 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// 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 "Color.h"
-#include "Map.h"
-
-#include <GLee.h>
-#include <GL/glfw3.h>
-
-class Context;
-
-/// %Graphics subsystem implementation. Holds API-specific objects.
-class GraphicsImpl
-{
-    friend class Graphics;
-    
-public:
-    /// Construct.
-    GraphicsImpl();
-    
-private:
-    /// GLFW window.
-    GLFWwindow window_;
-    /// Active texture unit.
-    unsigned activeTexture_;
-    /// Active draw buffers.
-    unsigned drawBuffers_;
-    /// Frame buffer object. Reused whenever rendering to a texture.
-    unsigned fbo_;
-    /// Frame buffer object for depth-only rendering.
-    unsigned depthOnlyFbo_;
-    /// Currently bound frame buffer object.
-    unsigned boundFbo_;
-    /// Vertex attributes in use.
-    unsigned enabledAttributes_;
-    /// Current pixel format.
-    int pixelFormat_;
-    /// Current depth bits.
-    int depthBits_;
-    /// Backbuffer depth bits.
-    int windowDepthBits_;
-};
-
-/// Store execution context specific to the GLFW window
-void SetWindowContext(GLFWwindow window, Context* context);
-/// Return execution context specific to the GLFW window
-Context* GetWindowContext(GLFWwindow window);
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// 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 "Color.h"
+#include "Map.h"
+#include "Timer.h"
+
+#include <GLee.h>
+#include <GL/glfw3.h>
+
+class Context;
+
+/// Cached state of a frame buffer object
+struct FrameBufferObject
+{
+    FrameBufferObject() :
+        fbo_(0),
+        readBuffers_(M_MAX_UNSIGNED),
+        drawBuffers_(M_MAX_UNSIGNED),
+        depthAttachment_(0)
+    {
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+            colorAttachments_[i] = 0;
+    }
+    
+    /// Frame buffer handle.
+    unsigned fbo_;
+    /// Bound color attachment textures.
+    RenderSurface* colorAttachments_[MAX_RENDERTARGETS];
+    /// Bound depth/stencil attachment.
+    RenderSurface* depthAttachment_;
+    /// Read buffer bits.
+    unsigned readBuffers_;
+    /// Draw buffer bits.
+    unsigned drawBuffers_;
+    /// Use timer for cleaning up.
+    Timer useTimer_;
+};
+
+/// %Graphics subsystem implementation. Holds API-specific objects.
+class GraphicsImpl
+{
+    friend class Graphics;
+    
+public:
+    /// Construct.
+    GraphicsImpl();
+    
+private:
+    /// GLFW window.
+    GLFWwindow window_;
+    /// Active texture unit.
+    unsigned activeTexture_;
+    /// Vertex attributes in use.
+    unsigned enabledAttributes_;
+    /// Currently bound frame buffer object.
+    unsigned boundFbo_;
+    /// Current pixel format.
+    int pixelFormat_;
+    /// Current depth bits.
+    int depthBits_;
+    /// Backbuffer depth bits.
+    int windowDepthBits_;
+    /// Map for FBO's per resolution and format.
+    Map<unsigned long long, FrameBufferObject> frameBuffers_;
+    /// Need FBO commit flag.
+    bool fboDirty_;
+};
+
+/// Store execution context specific to the GLFW window
+void SetWindowContext(GLFWwindow window, Context* context);
+/// Return execution context specific to the GLFW window
+Context* GetWindowContext(GLFWwindow window);

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

@@ -93,6 +93,9 @@ void RenderSurface::Release()
     if (graphics->GetDepthStencil() == this)
     if (graphics->GetDepthStencil() == this)
         graphics->ResetDepthStencil();
         graphics->ResetDepthStencil();
     
     
+    // Clean up also from non-active FBOs
+    graphics->CleanupRenderSurface(this);
+    
     if (renderBuffer_)
     if (renderBuffer_)
     {
     {
         glDeleteRenderbuffersEXT(1, &renderBuffer_);
         glDeleteRenderbuffersEXT(1, &renderBuffer_);