Browse Source

Started work on GLES2 shadow mapping.

Lasse Öörni 13 years ago
parent
commit
a8c622f330

+ 0 - 1
Engine/Engine/GraphicsAPI.cpp

@@ -289,7 +289,6 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterEnumValue("TextureUnit", "TU_NORMAL", TU_NORMAL);
     engine->RegisterEnumValue("TextureUnit", "TU_SPECULAR", TU_SPECULAR);
     engine->RegisterEnumValue("TextureUnit", "TU_EMISSIVE", TU_EMISSIVE);
-    engine->RegisterEnumValue("TextureUnit", "TU_DETAIL", TU_DETAIL);
     engine->RegisterEnumValue("TextureUnit", "TU_ENVIRONMENT", TU_ENVIRONMENT);
     engine->RegisterEnumValue("TextureUnit", "MAX_MATERIAL_TEXTURE_UNITS", MAX_MATERIAL_TEXTURE_UNITS);
     

+ 0 - 2
Engine/Graphics/Batch.cpp

@@ -550,8 +550,6 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
             graphics->SetTexture(TU_NORMAL, textures[TU_NORMAL]);
         if (graphics->HasTextureUnit(TU_SPECULAR))
             graphics->SetTexture(TU_NORMAL, textures[TU_SPECULAR]);
-        if (graphics->HasTextureUnit(TU_DETAIL))
-            graphics->SetTexture(TU_DETAIL, textures[TU_DETAIL]);
         if (graphics->HasTextureUnit(TU_ENVIRONMENT))
             graphics->SetTexture(TU_ENVIRONMENT, textures[TU_ENVIRONMENT]);
     }

+ 9 - 10
Engine/Graphics/GraphicsDefs.h

@@ -246,16 +246,15 @@ enum TextureUnit
     TU_EMISSIVE = 2,
     TU_DEPTHBUFFER = 2,
     TU_SPECULAR = 3,
-    TU_DETAIL = 4,
-    TU_ENVIRONMENT = 5,
-    MAX_MATERIAL_TEXTURE_UNITS = 6,
-    TU_LIGHTRAMP = 6,
-    TU_LIGHTBUFFER = 6,
-    TU_LIGHTSHAPE = 7,
-    TU_SHADOWMAP = 8,
-    TU_FACESELECT = 9,
-    TU_INDIRECTION = 10,
-    MAX_TEXTURE_UNITS = 16
+    TU_ENVIRONMENT = 4,
+    MAX_MATERIAL_TEXTURE_UNITS = 5,
+    TU_LIGHTRAMP = 5,
+    TU_LIGHTBUFFER = 5,
+    TU_LIGHTSHAPE = 6,
+    TU_SHADOWMAP = 7,
+    TU_FACESELECT = 8,
+    TU_INDIRECTION = 9,
+    MAX_TEXTURE_UNITS = 10
 };
 
 /// Shader parameter groups for determining need to update.

+ 2257 - 2245
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -1,2245 +1,2257 @@
-//
-// 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 "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"
-
-#ifdef GL_ES_VERSION_2_0
-#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
-#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER
-#define GL_RENDERBUFFER_EXT GL_RENDERBUFFER
-#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
-#define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT
-#define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT
-#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE
-#define glClearDepth glClearDepthf
-#define glBindFramebufferEXT glBindFramebuffer
-#define glFramebufferTexture2DEXT glFramebufferTexture2D
-#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
-#define glGenFramebuffersEXT glGenFramebuffers
-#define glDeleteFramebuffersEXT glDeleteFramebuffers
-#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
-#endif
-
-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;
-
-OBJECTTYPESTATIC(Graphics);
-
-bool CheckExtension(const String& name)
-{
-    String extensions((const char*)glGetString(GL_EXTENSIONS));
-    return extensions.Find(name) != String::NPOS;
-}
-
-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),
-    compressedTextureSupport_(false),
-    numPrimitives_(0),
-    numBatches_(0),
-    defaultTextureFilterMode_(FILTER_BILINEAR),
-    shadowMapFormat_(GL_DEPTH_COMPONENT16),
-    hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
-    shaderParameterFrame_(0)
-{
-    ResetCachedState();
-    SetTextureUnitMappings();
-    
-    // If first instance in this process, initialize SDL under static mutex. Note that Graphics subsystem will also be in charge
-    // of shutting down SDL as a whole, so it should be the last SDL-using subsystem (Audio and Input also use SDL) alive
-    {
-        MutexLock lock(GetStaticMutex());
-        
-        if (!numInstances)
-            SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
-        ++numInstances;
-    }
-}
-
-Graphics::~Graphics()
-{
-    Close();
-    
-    delete impl_;
-    impl_ = 0;
-    
-    // If last instance in this process, shut down SDL under static mutex
-    {
-        MutexLock lock(GetStaticMutex());
-        
-        --numInstances;
-        if (!numInstances)
-            SDL_Quit();
-    }
-}
-
-void Graphics::SetWindowTitle(const String& windowTitle)
-{
-    windowTitle_ = windowTitle;
-    if (impl_->window_)
-        SDL_SetWindowTitle(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_)
-    {
-        SDL_GL_SetSwapInterval(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
-        {
-            SDL_DisplayMode mode;
-            SDL_GetDesktopDisplayMode(0, &mode);
-            width = mode.w;
-            height = mode.h;
-        }
-    }
-    
-    // Close the existing window and OpenGL context, mark GPU objects as lost
-    Release(false, true);
-    
-    {
-        // SDL window parameters are static, so need to operate under static lock
-        MutexLock lock(GetStaticMutex());
-        
-        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
-        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
-        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
-        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
-        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
-        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
-        
-        if (multiSample > 1)
-        {
-            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
-            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multiSample);
-        }
-        else
-        {
-            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
-            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
-        }
-        
-        unsigned flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
-        int x = fullscreen ? 0 : SDL_WINDOWPOS_UNDEFINED;
-        int y = fullscreen ? 0 : SDL_WINDOWPOS_UNDEFINED;
-        if (fullscreen)
-            flags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
-        
-        impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), x, y, width, height, flags);
-        if (!impl_->window_)
-        {
-            LOGERROR("Could not open window");
-            return false;
-        }
-        
-        impl_->context_ = SDL_GL_CreateContext(impl_->window_);
-        if (!impl_->context_)
-        {
-            LOGERROR("Could not create OpenGL context");
-            return false;
-        }
-        
-        // If OpenGL extensions not yet initialized, initialize now
-        #ifndef GL_ES_VERSION_2_0
-        if (!GLeeInitialized())
-            GLeeInit();
-        
-        if (!_GLEE_VERSION_2_0)
-        {
-            LOGERROR("OpenGL 2.0 is required");
-            Release(true, true);
-            return false;
-        }
-
-        if (!CheckExtension("EXT_framebuffer_object") || !CheckExtension("EXT_packed_depth_stencil") ||
-            !CheckExtension("EXT_texture_filter_anisotropic"))
-        {
-            LOGERROR("EXT_framebuffer_object, EXT_packed_depth_stencil and "
-                "EXT_texture_filter_anisotropic OpenGL extensions are required");
-            Release(true, true);
-            return false;
-        }
-        #endif
-        
-        compressedTextureSupport_ = CheckExtension("EXT_texture_compression_s3tc");
-    }
-    
-    // Set vsync
-    SDL_GL_SetSwapInterval(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);
-    
-    SDL_GetWindowSize(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);
-    SDL_GL_SwapWindow(impl_->window_);
-    
-    // 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()
-{
-    if (!IsInitialized())
-        return false;
-
-    // If we should be fullscreen, but are not currently active, do not render
-    if (fullscreen_ && (SDL_GetWindowFlags(impl_->window_) & SDL_WINDOW_MINIMIZED))
-        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()
-{
-    if (!IsInitialized())
-        return;
-    
-    PROFILE(Present);
-    
-    SendEvent(E_ENDRENDERING);
-    
-    SDL_GL_SwapWindow(impl_->window_);
-    
-    // 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;
-    
-    ClearParameterSources();
-    
-    // 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:
-                #ifndef GL_ES_VERSION_2_0
-                glUniformMatrix3fv(info->location_, count / 9, GL_TRUE, data);
-                #else
-                {
-                    for (unsigned i = 0; i < count; i += 9)
-                    {
-                        Matrix3 matrix(&data[i]);
-                        glUniformMatrix3fv(info->location_ + i / 3, 1, GL_FALSE, matrix.Transpose().Data());
-                    }
-                }
-                #endif
-                break;
-                
-            case GL_FLOAT_MAT4:
-                #ifndef GL_ES_VERSION_2_0
-                glUniformMatrix4fv(info->location_, count / 16, GL_TRUE, data);
-                #else
-                {
-                    for (unsigned i = 0; i < count; i += 16)
-                    {
-                        Matrix4 matrix(&data[i]);
-                        glUniformMatrix3fv(info->location_ + i / 4, 1, GL_FALSE, matrix.Transpose().Data());
-                    }
-                }
-                #endif
-                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)
-        {
-            #ifndef GL_ES_VERSION_2_0
-            glUniformMatrix3fv(info->location_, 1, GL_TRUE, matrix.Data());
-            #else
-            glUniformMatrix3fv(info->location_, 1, GL_FALSE, matrix.Transpose().Data());
-            #endif
-        }
-    }
-}
-
-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)
-        {
-            #ifndef GL_ES_VERSION_2_0
-            glUniformMatrix4fv(info->location_, 1, GL_TRUE, matrix.Data());
-            #else
-            glUniformMatrix4fv(info->location_, 1, GL_FALSE, matrix.Transpose().Data());
-            #endif
-        }
-    }
-}
-
-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.m10_;
-            data[2] = matrix.m20_;
-            data[3] = 0.0f;
-            data[4] = matrix.m01_;
-            data[5] = matrix.m11_;
-            data[6] = matrix.m21_;
-            data[7] = 0.0f;
-            data[8] = matrix.m02_;
-            data[9] = matrix.m12_;
-            data[10] = matrix.m22_;
-            data[11] = 0.0f;
-            data[12] = matrix.m03_;
-            data[13] = matrix.m13_;
-            data[14] = matrix.m23_;
-            data[15] = 1.0f;
-            
-            glUniformMatrix4fv(info->location_, 1, GL_FALSE, data);
-        }
-    }
-}
-
-bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
-{
-    if ((unsigned)shaderParameterSources_[group] == M_MAX_UNSIGNED || shaderParameterSources_[group] != source)
-    {
-        shaderParameterSources_[group] = source;
-        return true;
-    }
-    else
-        return false;
-}
-
-bool Graphics::HasShaderParameter(ShaderType type, StringHash param)
-{
-    return shaderProgram_ && shaderProgram_->HasParameter(param);
-    
-}
-
-bool Graphics::HasTextureUnit(TextureUnit unit)
-{
-    return shaderProgram_ && shaderProgram_->HasTextureUnit(unit);
-}
-
-
-void Graphics::ClearParameterSource(ShaderParameterGroup group)
-{
-    shaderParameterSources_[group] = (const void*)M_MAX_UNSIGNED;
-}
-
-void Graphics::ClearParameterSources()
-{
-    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
-        shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
-}
-
-void Graphics::ClearTransformSources()
-{
-    shaderParameterSources_[SP_CAMERA] = (const void*)M_MAX_UNSIGNED;
-    shaderParameterSources_[SP_OBJECTTRANSFORM] = (const void*)M_MAX_UNSIGNED;
-}
-
-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::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);
-            glPolygonOffset(adjustedSlopeScaledBias, adjustedConstantBias);
-        }
-        else
-            glDisable(GL_POLYGON_OFFSET_FILL);
-        
-        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::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
-{
-    PODVector<IntVector2> ret;
-    unsigned numModes = SDL_GetNumDisplayModes(0);
-    
-    for (unsigned i = 0; i < numModes; ++i)
-    {
-        SDL_DisplayMode mode;
-        SDL_GetDisplayMode(0, i, &mode);
-        int width = mode.w;
-        int height  = mode.h;
-        
-        // 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)
-{
-    gpuObjects_.Erase(gpuObjects_.Find(object));
-}
-
-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());
-        
-        if (impl_->context_)
-        {
-            SDL_GL_DeleteContext(impl_->context_);
-            impl_->context_ = 0;
-        }
-        if (closeWindow)
-        {
-            SDL_ShowCursor(SDL_TRUE);
-            SDL_DestroyWindow(impl_->window_);
-            impl_->window_ = 0;
-        }
-    }
-}
-
-void Graphics::CleanupRenderSurface(RenderSurface* surface)
-{
-    if (!surface)
-        return;
-    
-    // Flush pending FBO changes first if any
-    CommitFramebuffer();
-    
-    unsigned currentFbo = impl_->boundFbo_;
-    
-    // Go through all FBOs and clean up the surface from them
-    for (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()
-{
-    #ifndef GL_ES_VERSION_2_0
-    return GL_LUMINANCE32F_ARB;
-    #else
-    return GL_LUMINANCE;
-    #endif
-}
-
-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()
-{
-    #ifndef GL_ES_VERSION_2_0
-    return GL_DEPTH24_STENCIL8_EXT;
-    #else
-    return 0;
-    #endif
-}
-
-void Graphics::CheckFeatureSupport()
-{
-    // Check supported features: light pre-pass, deferred rendering and hardware depth texture
-    lightPrepassSupport_ = false;
-    deferredSupport_ = false;
-    hardwareDepthSupport_ = false;
-
-    int numSupportedRTs = 1;
-    #ifndef GL_ES_VERSION_2_0
-    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
-    #else
-    // For now disable shadow mapping on OpenGL ES
-    shadowMapFormat_ = 0;
-    hiresShadowMapFormat_ = 0;
-    #endif
-    
-    // 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_;
-    }
-    
-    #ifndef GL_ES_VERSION_2_0
-    // Setup readbuffers & drawbuffers if needed
-    if (i->second_.readBuffers_ != GL_NONE)
-    {
-        glReadBuffer(GL_NONE);
-        i->second_.readBuffers_ = GL_NONE;
-    }
-    
-    // Calculate the bit combination of non-zero color rendertargets to first check if the combination changed
-    unsigned newDrawBuffers = 0;
-    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-    {
-        if (renderTargets_[i])
-            newDrawBuffers |= 1 << i;
-    }
-    
-    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;
-    }
-    #endif
-    
-    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_;
-            }
-        }
-        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_ = texture->GetDepthBits();
-    }
-    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;
-    textureAnisotropy_ = 1;
-    colorWrite_ = true;
-    cullMode_ = CULL_NONE;
-    constantDepthBias_ = 0.0f;
-    slopeScaledDepthBias_ = 0.0f;
-    depthTestMode_ = CMP_ALWAYS;
-    depthWrite_ = true;
-    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);
-}
+//
+// 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 "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"
+
+#ifdef GL_ES_VERSION_2_0
+#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
+#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER
+#define GL_RENDERBUFFER_EXT GL_RENDERBUFFER
+#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
+#define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT
+#define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT
+#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE
+#define glClearDepth glClearDepthf
+#define glBindFramebufferEXT glBindFramebuffer
+#define glFramebufferTexture2DEXT glFramebufferTexture2D
+#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
+#define glGenFramebuffersEXT glGenFramebuffers
+#define glDeleteFramebuffersEXT glDeleteFramebuffers
+#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
+#endif
+
+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;
+
+OBJECTTYPESTATIC(Graphics);
+
+bool CheckExtension(const String& name)
+{
+    String extensions((const char*)glGetString(GL_EXTENSIONS));
+    return extensions.Find(name) != String::NPOS;
+}
+
+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),
+    compressedTextureSupport_(false),
+    numPrimitives_(0),
+    numBatches_(0),
+    defaultTextureFilterMode_(FILTER_BILINEAR),
+    shadowMapFormat_(GL_DEPTH_COMPONENT16),
+    hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
+    shaderParameterFrame_(0)
+{
+    ResetCachedState();
+    SetTextureUnitMappings();
+    
+    // If first instance in this process, initialize SDL under static mutex. Note that Graphics subsystem will also be in charge
+    // of shutting down SDL as a whole, so it should be the last SDL-using subsystem (Audio and Input also use SDL) alive
+    {
+        MutexLock lock(GetStaticMutex());
+        
+        if (!numInstances)
+            SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
+        ++numInstances;
+    }
+}
+
+Graphics::~Graphics()
+{
+    Close();
+    
+    delete impl_;
+    impl_ = 0;
+    
+    // If last instance in this process, shut down SDL under static mutex
+    {
+        MutexLock lock(GetStaticMutex());
+        
+        --numInstances;
+        if (!numInstances)
+            SDL_Quit();
+    }
+}
+
+void Graphics::SetWindowTitle(const String& windowTitle)
+{
+    windowTitle_ = windowTitle;
+    if (impl_->window_)
+        SDL_SetWindowTitle(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_)
+    {
+        SDL_GL_SetSwapInterval(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
+        {
+            SDL_DisplayMode mode;
+            SDL_GetDesktopDisplayMode(0, &mode);
+            width = mode.w;
+            height = mode.h;
+        }
+    }
+    
+    // Close the existing window and OpenGL context, mark GPU objects as lost
+    Release(false, true);
+    
+    {
+        // SDL window parameters are static, so need to operate under static lock
+        MutexLock lock(GetStaticMutex());
+        
+        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
+        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+        
+        if (multiSample > 1)
+        {
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multiSample);
+        }
+        else
+        {
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+        }
+        
+        unsigned flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
+        int x = fullscreen ? 0 : SDL_WINDOWPOS_UNDEFINED;
+        int y = fullscreen ? 0 : SDL_WINDOWPOS_UNDEFINED;
+        if (fullscreen)
+            flags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
+        
+        impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), x, y, width, height, flags);
+        if (!impl_->window_)
+        {
+            LOGERROR("Could not open window");
+            return false;
+        }
+        
+        impl_->context_ = SDL_GL_CreateContext(impl_->window_);
+        if (!impl_->context_)
+        {
+            LOGERROR("Could not create OpenGL context");
+            return false;
+        }
+        
+        // If OpenGL extensions not yet initialized, initialize now
+        #ifndef GL_ES_VERSION_2_0
+        if (!GLeeInitialized())
+            GLeeInit();
+        
+        if (!_GLEE_VERSION_2_0)
+        {
+            LOGERROR("OpenGL 2.0 is required");
+            Release(true, true);
+            return false;
+        }
+
+        if (!CheckExtension("EXT_framebuffer_object") || !CheckExtension("EXT_packed_depth_stencil") ||
+            !CheckExtension("EXT_texture_filter_anisotropic"))
+        {
+            LOGERROR("EXT_framebuffer_object, EXT_packed_depth_stencil and "
+                "EXT_texture_filter_anisotropic OpenGL extensions are required");
+            Release(true, true);
+            return false;
+        }
+        #endif
+        
+        compressedTextureSupport_ = CheckExtension("EXT_texture_compression_s3tc");
+    }
+    
+    // Set vsync
+    SDL_GL_SetSwapInterval(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);
+    
+    SDL_GetWindowSize(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);
+    SDL_GL_SwapWindow(impl_->window_);
+    
+    // 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()
+{
+    if (!IsInitialized())
+        return false;
+
+    // If we should be fullscreen, but are not currently active, do not render
+    if (fullscreen_ && (SDL_GetWindowFlags(impl_->window_) & SDL_WINDOW_MINIMIZED))
+        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()
+{
+    if (!IsInitialized())
+        return;
+    
+    PROFILE(Present);
+    
+    SendEvent(E_ENDRENDERING);
+    
+    SDL_GL_SwapWindow(impl_->window_);
+    
+    // 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;
+    
+    ClearParameterSources();
+    
+    // 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:
+                #ifndef GL_ES_VERSION_2_0
+                glUniformMatrix3fv(info->location_, count / 9, GL_TRUE, data);
+                #else
+                {
+                    for (unsigned i = 0; i < count; i += 9)
+                    {
+                        Matrix3 matrix(&data[i]);
+                        glUniformMatrix3fv(info->location_ + i / 3, 1, GL_FALSE, matrix.Transpose().Data());
+                    }
+                }
+                #endif
+                break;
+                
+            case GL_FLOAT_MAT4:
+                #ifndef GL_ES_VERSION_2_0
+                glUniformMatrix4fv(info->location_, count / 16, GL_TRUE, data);
+                #else
+                {
+                    for (unsigned i = 0; i < count; i += 16)
+                    {
+                        Matrix4 matrix(&data[i]);
+                        glUniformMatrix3fv(info->location_ + i / 4, 1, GL_FALSE, matrix.Transpose().Data());
+                    }
+                }
+                #endif
+                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)
+        {
+            #ifndef GL_ES_VERSION_2_0
+            glUniformMatrix3fv(info->location_, 1, GL_TRUE, matrix.Data());
+            #else
+            glUniformMatrix3fv(info->location_, 1, GL_FALSE, matrix.Transpose().Data());
+            #endif
+        }
+    }
+}
+
+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)
+        {
+            #ifndef GL_ES_VERSION_2_0
+            glUniformMatrix4fv(info->location_, 1, GL_TRUE, matrix.Data());
+            #else
+            glUniformMatrix4fv(info->location_, 1, GL_FALSE, matrix.Transpose().Data());
+            #endif
+        }
+    }
+}
+
+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.m10_;
+            data[2] = matrix.m20_;
+            data[3] = 0.0f;
+            data[4] = matrix.m01_;
+            data[5] = matrix.m11_;
+            data[6] = matrix.m21_;
+            data[7] = 0.0f;
+            data[8] = matrix.m02_;
+            data[9] = matrix.m12_;
+            data[10] = matrix.m22_;
+            data[11] = 0.0f;
+            data[12] = matrix.m03_;
+            data[13] = matrix.m13_;
+            data[14] = matrix.m23_;
+            data[15] = 1.0f;
+            
+            glUniformMatrix4fv(info->location_, 1, GL_FALSE, data);
+        }
+    }
+}
+
+bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
+{
+    if ((unsigned)shaderParameterSources_[group] == M_MAX_UNSIGNED || shaderParameterSources_[group] != source)
+    {
+        shaderParameterSources_[group] = source;
+        return true;
+    }
+    else
+        return false;
+}
+
+bool Graphics::HasShaderParameter(ShaderType type, StringHash param)
+{
+    return shaderProgram_ && shaderProgram_->HasParameter(param);
+    
+}
+
+bool Graphics::HasTextureUnit(TextureUnit unit)
+{
+    return shaderProgram_ && shaderProgram_->HasTextureUnit(unit);
+}
+
+
+void Graphics::ClearParameterSource(ShaderParameterGroup group)
+{
+    shaderParameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearParameterSources()
+{
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearTransformSources()
+{
+    shaderParameterSources_[SP_CAMERA] = (const void*)M_MAX_UNSIGNED;
+    shaderParameterSources_[SP_OBJECTTRANSFORM] = (const void*)M_MAX_UNSIGNED;
+}
+
+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::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);
+            glPolygonOffset(adjustedSlopeScaledBias, adjustedConstantBias);
+        }
+        else
+            glDisable(GL_POLYGON_OFFSET_FILL);
+        
+        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::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
+{
+    PODVector<IntVector2> ret;
+    unsigned numModes = SDL_GetNumDisplayModes(0);
+    
+    for (unsigned i = 0; i < numModes; ++i)
+    {
+        SDL_DisplayMode mode;
+        SDL_GetDisplayMode(0, i, &mode);
+        int width = mode.w;
+        int height  = mode.h;
+        
+        // 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)
+{
+    gpuObjects_.Erase(gpuObjects_.Find(object));
+}
+
+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());
+        
+        if (impl_->context_)
+        {
+            SDL_GL_DeleteContext(impl_->context_);
+            impl_->context_ = 0;
+        }
+        if (closeWindow)
+        {
+            SDL_ShowCursor(SDL_TRUE);
+            SDL_DestroyWindow(impl_->window_);
+            impl_->window_ = 0;
+        }
+    }
+}
+
+void Graphics::CleanupRenderSurface(RenderSurface* surface)
+{
+    if (!surface)
+        return;
+    
+    // Flush pending FBO changes first if any
+    CommitFramebuffer();
+    
+    unsigned currentFbo = impl_->boundFbo_;
+    
+    // Go through all FBOs and clean up the surface from them
+    for (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()
+{
+    #ifndef GL_ES_VERSION_2_0
+    return GL_LUMINANCE32F_ARB;
+    #else
+    return GL_LUMINANCE;
+    #endif
+}
+
+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()
+{
+    #ifndef GL_ES_VERSION_2_0
+    return GL_DEPTH24_STENCIL8_EXT;
+    #else
+    return GL_DEPTH_COMPONENT;
+    #endif
+}
+
+void Graphics::CheckFeatureSupport()
+{
+    // Check supported features: light pre-pass, deferred rendering and hardware depth texture
+    lightPrepassSupport_ = false;
+    deferredSupport_ = false;
+    hardwareDepthSupport_ = false;
+    
+    int numSupportedRTs = 1;
+    
+    #ifndef GL_ES_VERSION_2_0
+    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;
+    }
+    #else
+    if (!CheckExtension("GL_OES_depth_texture"))
+    {
+        shadowMapFormat_ = 0;
+        hiresShadowMapFormat_ = 0;
+    }
+    else
+    {
+        shadowMapFormat_ = GL_DEPTH_COMPONENT;
+        hiresShadowMapFormat_ = GL_DEPTH_COMPONENT;
+        hardwareDepthSupport_ = true;
+    }
+    #endif
+}
+
+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_;
+    }
+    
+    #ifndef GL_ES_VERSION_2_0
+    // Setup readbuffers & drawbuffers if needed
+    if (i->second_.readBuffers_ != GL_NONE)
+    {
+        glReadBuffer(GL_NONE);
+        i->second_.readBuffers_ = GL_NONE;
+    }
+    
+    // Calculate the bit combination of non-zero color rendertargets to first check if the combination changed
+    unsigned newDrawBuffers = 0;
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+    {
+        if (renderTargets_[i])
+            newDrawBuffers |= 1 << i;
+    }
+    
+    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;
+    }
+    #endif
+    
+    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();
+        #ifndef GL_ES_VERSION_2_0
+        bool hasStencil = texture->GetFormat() == GetDepthStencilFormat();
+        #else
+        bool hasStencil = false;
+        #endif
+        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_;
+            }
+        }
+        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_ = texture->GetDepthBits();
+    }
+    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;
+    textureAnisotropy_ = 1;
+    colorWrite_ = true;
+    cullMode_ = CULL_NONE;
+    constantDepthBias_ = 0.0f;
+    slopeScaledDepthBias_ = 0.0f;
+    depthTestMode_ = CMP_ALWAYS;
+    depthWrite_ = true;
+    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_["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);
+}

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

@@ -225,7 +225,7 @@ public:
     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.
+    /// Return dummy color texture format for shadow maps.
     unsigned GetDummyColorFormat() const { return 0; }
     /// Return shadow map depth texture format, or 0 if not supported.
     unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
@@ -237,7 +237,7 @@ public:
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     /// Return whether deferred rendering is supported.
     bool GetDeferredSupport() const { return deferredSupport_; }
-    /// Return whether hardware depth texture is supported.
+    /// Return whether hardware depth texture is supported. On OpenGL ES this means the depth texture extension.
     bool GetHardwareDepthSupport() const { return hardwareDepthSupport_; }
     /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
     bool GetHardwareShadowSupport() const { return true; }

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

@@ -139,7 +139,7 @@ bool ShaderProgram::Link()
             ShaderParameter newParam;
             newParam.location_ = location;
             newParam.type_ = type;
-            shaderParameters_[StringHash(paramName)] = newParam;    
+            shaderParameters_[StringHash(paramName)] = newParam;
         }
         else if (name[0] == 's')
         {

+ 9 - 2
Engine/Graphics/OpenGL/OGLTexture.cpp

@@ -302,7 +302,7 @@ unsigned Texture::GetExternalFormat(unsigned format)
     else
         return format;
     #else
-    if (format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24_OES || format == GL_DEPTH_COMPONENT32_OES)
+    if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT24_OES || format == GL_DEPTH_COMPONENT32_OES)
         return GL_DEPTH_COMPONENT;
     else
         return format;
@@ -315,8 +315,15 @@ unsigned Texture::GetDataType(unsigned format)
     if (format == GL_DEPTH24_STENCIL8_EXT)
         return GL_UNSIGNED_INT_24_8_EXT;
     else
-    #endif
         return GL_UNSIGNED_BYTE;
+    #else
+    if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT24_OES || format == GL_DEPTH_COMPONENT32_OES)
+        return GL_UNSIGNED_INT;
+    else if (format == GL_DEPTH_COMPONENT16)
+        return GL_UNSIGNED_SHORT;
+    else
+        return GL_UNSIGNED_BYTE;
+    #endif
 }
 
 void Texture::LoadParameters()

+ 18 - 5
Engine/Graphics/OpenGL/OGLTexture2D.cpp

@@ -389,8 +389,15 @@ bool Texture2D::Create()
     if (!width_ || !height_)
         return false;
     
-    // For packed depth-stencil, create a renderbuffer instead of a texture if depth texture is not properly supported
+    unsigned externalFormat = GetExternalFormat(format_);
+    unsigned dataType = GetDataType(format_);
+    
+    // Create a renderbuffer instead of a texture if depth texture is not properly supported
+    #ifndef GL_ES_VERSION_2_0
     if (!graphics_->GetHardwareDepthSupport() && format_ == Graphics::GetDepthStencilFormat())
+    #else
+    if (!graphics_->GetHardwareDepthSupport() && externalFormat == GL_DEPTH_COMPONENT)
+    #endif
     {
         if (renderSurface_)
         {
@@ -408,11 +415,17 @@ bool Texture2D::Create()
     graphics_->SetTextureForUpdate(this);
     
     // If not compressed, create the initial level 0 texture with null data
-    unsigned externalFormat = GetExternalFormat(format_);
-    unsigned dataType = GetDataType(format_);
-    
+    bool success = true;
     if (!IsCompressed())
+    {
+        glGetError();
         glTexImage2D(target_, 0, format_, width_, height_, 0, externalFormat, dataType, 0);
+        if (glGetError())
+        {
+            LOGERROR("Failed to create texture");
+            success = false;
+        }
+    }
     
     // If depth format, get the depth size
     #ifndef GL_ES_VERSION_2_0
@@ -443,5 +456,5 @@ bool Texture2D::Create()
     UpdateParameters();
     graphics_->SetTexture(0, 0);
     
-    return true;
+    return success;
 }

+ 9 - 1
Engine/Graphics/OpenGL/OGLTextureCube.cpp

@@ -511,11 +511,19 @@ bool TextureCube::Create()
     unsigned externalFormat = GetExternalFormat(format_);
     unsigned dataType = GetDataType(format_);
     
+    bool success = true;
     if (!IsCompressed())
     {
+        glGetError();
         for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
             glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format_, width_, height_, 0, externalFormat, dataType, 0);
+            if (glGetError())
+                success = false;
+        }
     }
+    if (!success)
+        LOGERROR("Failed to create texture");
     
     // Set mipmapping
     levels_ = requestedLevels_;
@@ -538,5 +546,5 @@ bool TextureCube::Create()
     UpdateParameters();
     graphics_->SetTexture(0, 0);
     
-    return true;
+    return success;
 }

+ 4 - 2
Engine/Graphics/Renderer.cpp

@@ -844,8 +844,8 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     SharedPtr<Texture2D> newShadowMap(new Texture2D(context_));
     int retries = 3;
     
+    // OpenGL: create shadow map only. Color rendertarget is not needed
     #ifdef USE_OPENGL
-    // Create shadow map only. Color rendertarget is not needed
     while (retries)
     {
         if (!newShadowMap->SetSize(width, height, shadowMapFormat, TEXTURE_DEPTHSTENCIL))
@@ -856,13 +856,15 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
         }
         else
         {
+            #ifndef GL_ES_VERSION_2_0
             newShadowMap->SetFilterMode(FILTER_BILINEAR);
+            #endif
             newShadowMap->SetShadowCompare(true);
             break;
         }
     }
     #else
-    // Create shadow map and dummy color rendertarget
+    // Direct3D9: create shadow map and dummy color rendertarget
     while (retries)
     {
         if (!newShadowMap->SetSize(width, height, shadowMapFormat, TEXTURE_DEPTHSTENCIL))

+ 5 - 1
Engine/Graphics/View.cpp

@@ -1712,7 +1712,11 @@ void View::ProcessLight(LightQueryResult& query, unsigned threadIndex)
     // If shadow distance non-zero, check it
     if (isShadowed && light->GetShadowDistance() > 0.0f && light->GetDistance() > light->GetShadowDistance())
         isShadowed = false;
-    
+    // OpenGL ES can not support point light shadows
+    #ifdef GL_ES_VERSION_2_0
+    if (isShadowed && type == LIGHT_POINT)
+        isShadowed = false;
+    #endif
     // Get lit geometries. They must match the light mask and be inside the main camera frustum to be considered
     PODVector<Drawable*>& tempDrawables = tempDrawables_[threadIndex];
     query.litGeometries_.Clear();

+ 20 - 7
SourceAssets/GLSLShaders/Lighting.frag

@@ -47,16 +47,29 @@ float GetShadow(vec4 shadowPos)
         #else
             vec2 offsets = cShadowMapInvSize;
         #endif
-        vec4 inLight = vec4(
-            shadow2DProj(sShadowMap, shadowPos).r,
-            shadow2DProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)).r,
-            shadow2DProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
-            shadow2DProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
-        );
+        #ifndef GL_ES
+            vec4 inLight = vec4(
+                shadow2DProj(sShadowMap, shadowPos).r,
+                shadow2DProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)).r,
+                shadow2DProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
+                shadow2DProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
+            );
+        #else
+            vec4 inLight = vec4(
+                texture2DProj(sShadowMap, shadowPos).r * shadowPos.w > shadowPos.z,
+                texture2DProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)).r * shadowPos.w > shadowPos.z,
+                texture2DProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r * shadowPos.w > shadowPos.z,
+                texture2DProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw)).r * shadowPos.w > shadowPos.z
+            );
+        #endif
         return cShadowIntensity.y + dot(inLight, vec4(cShadowIntensity.x));
     #else
         // Take one sample
-        float inLight = shadow2DProj(sShadowMap, shadowPos).r;
+        #ifndef GL_ES
+            float inLight = shadow2DProj(sShadowMap, shadowPos).r;
+        #else
+            float inLight = texture2DProj(sShadowMap, shadowPos).r * shadowPos.w > shadowPos.z;
+        #endif
         return cShadowIntensity.y + cShadowIntensity.x * inLight;
     #endif
 }

+ 2 - 1
SourceAssets/GLSLShaders/Samplers.frag

@@ -3,7 +3,6 @@ uniform samplerCube sDiffCubeMap;
 uniform sampler2D sNormalMap;
 uniform sampler2D sSpecMap;
 uniform sampler2D sEmissiveMap;
-uniform sampler2D sDetailMap;
 uniform sampler2D sEnvMap;
 uniform samplerCube sEnvCubeMap;
 uniform sampler2D sLightRampMap;
@@ -17,6 +16,8 @@ uniform sampler2D sLightBuffer;
 uniform sampler2DShadow sShadowMap;
 uniform samplerCube sFaceSelectCubeMap;
 uniform samplerCube sIndirectionCubeMap;
+#else
+uniform sampler2D sShadowMap;
 #endif
 
 vec3 DecodeNormal(vec4 normalInput)

+ 9 - 10
SourceAssets/HLSLShaders/Samplers.hlsl

@@ -3,19 +3,18 @@ samplerCUBE sDiffCubeMap : register(S0);
 sampler2D sNormalMap : register(S1);
 sampler2D sSpecMap : register(S2);
 sampler2D sEmissiveMap : register(S3);
-sampler2D sDetailMap : register(S4);
-sampler2D sEnvMap : register(S5);
-samplerCUBE sEnvCubeMap : register(S5);
-sampler1D sLightRampMap : register(S6);
-sampler2D sLightSpotMap : register(S7);
-samplerCUBE sLightCubeMap : register(S7);
-sampler2D sShadowMap : register(S8);
-samplerCUBE sFaceSelectCubeMap : register(S9);
-samplerCUBE sIndirectionCubeMap : register(S10);
+sampler2D sEnvMap : register(S4);
+samplerCUBE sEnvCubeMap : register(S4);
+sampler1D sLightRampMap : register(S5);
+sampler2D sLightSpotMap : register(S6);
+samplerCUBE sLightCubeMap : register(S6);
+sampler2D sShadowMap : register(S7);
+samplerCUBE sFaceSelectCubeMap : register(S8);
+samplerCUBE sIndirectionCubeMap : register(S9);
 sampler2D sAlbedoBuffer : register(S0);
 sampler2D sNormalBuffer : register(S1);
 sampler2D sDepthBuffer : register(S2);
-sampler2D sLightBuffer : register(S6);
+sampler2D sLightBuffer : register(S5);
 
 float4 Sample(sampler2D map, float2 texCoord)
 {