#include "RenderDevice.h"
#include "ResourceManager.h"
#include <sstream>
#include <iomanip>


RenderDevice::RenderDevice() 
: _root(0), _overlay(0), _debugOverlayCamera(0), _debugInfo(0), _quad(0), 
_lightCamera(0), _sceneCamera(0), _combineCamera(0), _ssaoCamera(0), _noiseTexture(0),
_flagResize(false), _hWnd(NULL),  _time(0.0), _mpf(0.0), milisecCounter(0.0), frameCounter(-1), _nodeCount(0),
_enableLights(true), _enableShadows(true), _enableTextures(true), _enableAo(true), _enableGi(true), 
g_LightsEnabled(0), g_TexturesEnabled(0), g_AoEnabled(0), g_GiEnabled(0),
_gbufferFillTime(0.0), _shadowMapGenTime(0.0), _lightCalcTime(0.0), _ssaoTime(0.0), _combineTime(0.0), _tonemapTime(0.0), _bloomTime(0.0), _overlayTime(0.0),
_perfMon(0), _perfCnt1(0), _perfCnt2(0), _perfCnt3(0), _perfCnt4(0), _perfCnt5(0), _perfCnt6(0), _perfCnt7(0), _perfCnt8(0)
{
    ZeroMemory(&_dmScreenSettings, sizeof(_dmScreenSettings));
    QueryPerformanceFrequency(&_freq);
    ZeroMemory(&_cnt1, sizeof(LARGE_INTEGER));
    ZeroMemory(&_cnt2, sizeof(LARGE_INTEGER));

    Matrix4 identity;
    identity.identity();

    _worldMatrix.push_front(identity);
    _normalMatrix.push_front(identity);
}

void RenderDevice::enableLights(bool state)
{
    _enableLights = state;
    g_LightsEnabled->setValue(_enableLights);
}

void RenderDevice::enableShadows(bool state)
{
    _enableShadows = state;
}

void RenderDevice::enableTextures(bool state)
{
    _enableTextures = state;
    g_TexturesEnabled->setValue(_enableTextures);
}

void RenderDevice::enableAo(bool state)
{
    _enableAo = state;
    g_AoEnabled->setValue(_enableAo);
}

void RenderDevice::enableGi(bool state)
{
    _enableGi = state;
    g_GiEnabled->setValue(_enableGi);
}

Camera* RenderDevice::getCamera()
{
    return _sceneCamera;
}

void RenderDevice::setCameraManipulator(CameraManipulator* cameraManipulator)
{
    _cameraManipulator = cameraManipulator;
    _cameraManipulator->setCamera(_sceneCamera);
}

void RenderDevice::init(HWND hWnd, DEVMODE* dmScreenSettings)
{
    _hWnd = hWnd;
    memcpy(&_dmScreenSettings, dmScreenSettings, sizeof(*dmScreenSettings)); 

    if ( FAILED( create() ) ) {
        std::wstring message = L"Failed to create ";
        message += getDeviceName();
        MessageBox(NULL, message.c_str(), L"Error", MB_OK | MB_ICONERROR);
        PostMessage(_hWnd, WM_QUIT, 0, 0);    
        return;
    }

    ResourceManager::instance().setRenderDevice(this);

    _perfMon = createPerfMonitor();
    _perfCnt1 = createPerfCounter();
    _perfCnt2 = createPerfCounter();
    _perfCnt3 = createPerfCounter();
    _perfCnt4 = createPerfCounter();
    _perfCnt5 = createPerfCounter();
    _perfCnt6 = createPerfCounter();
    _perfCnt7 = createPerfCounter();
    _perfCnt8 = createPerfCounter();

    Shader* spriteShader = createShader(L"sprite.fxo");
    Shader* materialShader = createShader(L"material.fxo");
    Shader* combineShader = createShader(L"combine.fxo");
    Shader* ssaoShader = createShader(L"ssao.fxo");

    g_LightsEnabled = combineShader->createScalarVariable("g_LightsEnabled");
    g_LightsEnabled->setValue(_enableLights);

    g_TexturesEnabled = combineShader->createScalarVariable("g_TexturesEnabled");
    g_TexturesEnabled->setValue(_enableTextures);

    g_AoEnabled = combineShader->createScalarVariable("g_AoEnabled");
    g_AoEnabled->setValue(_enableAo);

    g_GiEnabled = combineShader->createScalarVariable("g_GiEnabled");
    g_GiEnabled->setValue(_enableGi);

    Shader::ScalarVariable* g_SampleRad = ssaoShader->createScalarVariable("g_SampleRad");
    g_SampleRad->setValue(19.80624f);

    Shader::ScalarVariable* g_Jitter = ssaoShader->createScalarVariable("g_Jitter");
    g_Jitter->setValue(0.0f);

    Shader::ScalarVariable* g_Intensity = ssaoShader->createScalarVariable("g_Intensity");
    g_Intensity->setValue(3.0f);

    Shader::ScalarVariable* g_Scale = ssaoShader->createScalarVariable("g_Scale");
    g_Scale->setValue(23.0f);

    Texture* noiseTexture = ResourceManager::instance().loadTexture(L"textures/noise.dds");
    Shader::ShaderResourceVariable* g_NoiseTexture = ssaoShader->createShaderResourceVariable("g_NoiseTexture");
    g_NoiseTexture->setValue(noiseTexture);

    _quad = new Sprite();
    _quad->setPosition(Vector3(0.0f, 0.0f, 0.0f));
    _quad->setScale(Vector3(1.0f, 1.0f, 1.0f));

    _downsampleRenderTarget = createRenderTarget((uint32)ceil(0.25*_dmScreenSettings.dmPelsWidth), (uint32)ceil(0.25*_dmScreenSettings.dmPelsHeight), 1);
    _downsampleShader = createShader(L"downsample.fxo");
    
    _downsampleCamera = new Camera();
    _downsampleCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _downsampleCamera->attachOutput(_downsampleRenderTarget, 0);
    _downsampleCamera->setShader(_downsampleShader);
    _downsampleCamera->addChild(_quad);

    _verticalBlurRenderTarget = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    _verticalBlurRenderTargetDownsampled = createRenderTarget((uint32)ceil(0.25*_dmScreenSettings.dmPelsWidth), (uint32)ceil(0.25*_dmScreenSettings.dmPelsHeight), 1);
    _verticalBlurShader = createShader(L"vertical_blur.fxo");
    _verticalBlurShaderTextureHeightVariable = _verticalBlurShader->createScalarVariable("g_TextureHeight");

    _verticalBlurCamera = new Camera();
    _verticalBlurCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _verticalBlurCamera->attachOutput(_verticalBlurRenderTarget, 0);
    _verticalBlurCamera->setShader(_verticalBlurShader);
    _verticalBlurCamera->addChild(_quad);

    _horizontalBlurRenderTarget = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    _horizontalBlurRenderTargetDownsampled = createRenderTarget((uint32)ceil(0.25*_dmScreenSettings.dmPelsWidth), (uint32)ceil(0.25*_dmScreenSettings.dmPelsHeight), 1);
    _horizontalBlurShader = createShader(L"horizontal_blur.fxo");
    _horizontalBlurShaderTextureWidthVariable = _horizontalBlurShader->createScalarVariable("g_TextureWidth");

    _horizontalBlurCamera = new Camera();
    _horizontalBlurCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _horizontalBlurCamera->attachInput(_verticalBlurRenderTarget, 0);
    _horizontalBlurCamera->attachOutput(_horizontalBlurRenderTarget, 0);
    _horizontalBlurCamera->setShader(_horizontalBlurShader);
    _horizontalBlurCamera->addChild(_quad);


    _brightPassRenderTarget = createRenderTarget((uint32)ceil(0.25*_dmScreenSettings.dmPelsWidth), (uint32)ceil(0.25*_dmScreenSettings.dmPelsHeight), 1);
    _brightPassShader = createShader(L"bright_pass.fxo");

    _brightPassCamera = new Camera();
    _brightPassCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _brightPassCamera->attachOutput(_brightPassRenderTarget, 0);
    _brightPassCamera->setShader(_brightPassShader);
    _brightPassCamera->addChild(_quad);

    

    _debugOverlayCamera = new Camera();
    _debugOverlayCamera->setOrtho2DProjection(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    _debugOverlayCamera->setShader(spriteShader);
    
    _debugInfo = new Text(L"");
    _debugInfo->setFont(L"Arial", L"ffffff", 16.0f, Text::FONT_STYLE_REGULAR, Text::V_ALIGN_TOP | Text::H_ALIGN_RIGHT);
    _debugInfo->setPosition(Vector3(_dmScreenSettings.dmPelsWidth - 512.0f, 24.0f, 0.0f));
    _debugInfo->setScale(Vector3(512.0f, 512.0f, 0.0f));
    _debugInfo->setMaxWidth(512.0f);
    _debugInfo->setMaxHeight(512.0f);

    _debugOverlayCamera->addChild(_debugInfo);
    

    _sceneCamera = new Camera();
    _sceneCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _sceneCamera->setPerspectiveProjection(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 0.78539816339744830961566084581988f, 1.0f, 1000.0f);
    _sceneCamera->setShader(materialShader);
    materialShader->attach(_sceneCamera);

    p0_mrt0 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p0_mrt1 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p0_mrt2 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p0_mrt3 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p0_dsb  = createDepthStencilBuffer(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);

    p1_mrt0 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p1_mrt1 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p1_mrt2 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);

    p2_mrt0 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);

    p3_mrt0 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    p3_mrt1 = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);

    _sceneCamera->attachOutput(p0_mrt0, 0);
    _sceneCamera->attachOutput(p0_mrt1, 1);
    _sceneCamera->attachOutput(p0_mrt2, 2);
    _sceneCamera->attachOutput(p0_mrt3, 3);
    _sceneCamera->attachDepthStencilBuffer(p0_dsb);
 
    _ssaoCamera = new Camera();
    _ssaoCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _ssaoCamera->setShader(ssaoShader);
    ssaoShader->attach(_sceneCamera);

    _ssaoCamera->attachInput(p0_mrt2, 0);
    _ssaoCamera->attachInput(p1_mrt0, 1);

    _ssaoCamera->attachOutput(p2_mrt0, 0);

    _ssaoCamera->addChild(_quad);

    _lightCamera = new Camera();
    _lightCamera->setRenderTargetImplementation(Camera::TEXTURE);
    
    _lightCamera->attachInput(p0_mrt0, 0);
    _lightCamera->attachInput(p0_mrt1, 1);
    _lightCamera->attachInput(p0_mrt2, 2);

    _lightCamera->attachOutput(p1_mrt0, 0);
    _lightCamera->attachOutput(p1_mrt1, 1);

    _lightCamera->addChild(_quad);

    _combineCamera = new Camera();
    _combineCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _combineCamera->setShader(combineShader);

    _combineCamera->attachInput(p1_mrt0, 0);
    _combineCamera->attachInput(p1_mrt1, 1);
    _combineCamera->attachInput(p2_mrt0, 2);
    _combineCamera->attachInput(p0_mrt3, 3);
    
    _combineCamera->attachOutput(p3_mrt0, 0);
    _combineCamera->attachOutput(p3_mrt1, 1);

    _combineCamera->addChild(_quad);

    _tonemapRenderTarget = createRenderTarget(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight, 1);
    _tonemapShader = createShader(L"tonemap.fxo");
    _mipLevels = _tonemapShader->createScalarVariable("g_MipLevels");

    _tonemapCamera = new Camera();
    _tonemapCamera->setRenderTargetImplementation(Camera::TEXTURE);
    _tonemapCamera->setShader(_tonemapShader);
    _tonemapCamera->attachInput(p3_mrt0, 0);
    _tonemapCamera->attachInput(p3_mrt1, 1);
    _tonemapCamera->attachOutput(_tonemapRenderTarget, 0);
    _tonemapCamera->addChild(_quad);

    _overlayCamera = new Camera();
    _overlayCamera->setOrtho2DProjection(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    _overlayCamera->setShader(spriteShader);

    spriteShader->attach(_overlayCamera);


    _brightPassCamera->attachInput(_tonemapRenderTarget, 0);

    _bloomCamera = new Camera();
    _bloomShader = createShader(L"bloom.fxo");
    _bloomCamera->setShader(_bloomShader);
    _bloomCamera->addChild(_quad);

    _bloomCamera->attachInput(_tonemapRenderTarget, 0);
    _bloomCamera->attachInput(_brightPassRenderTarget, 1);

    _tempShadowMap = createRenderTarget(512, 512, 1);
}

void RenderDevice::resizeCanvas(DEVMODE* dmScreenSettings)
{
    memcpy(&_dmScreenSettings, dmScreenSettings, sizeof(*dmScreenSettings));
    _flagResize = true;

    _debugInfo->setPosition(Vector3(_dmScreenSettings.dmPelsWidth - 512.0f, 24.0f, 0.0f));
}

void RenderDevice::operator()()
{
    while (canRun()) {
        if (isPaused()) {
            Sleep(100);
        }
        else {
            if (_flagResize) {
                createDeviceResources();
                recreateSizedResources();
                _flagResize = false;
            }

            render();
        }
    }

    destroy();
}

void RenderDevice::parseGraph(Node* node, bool extractLights)
{
    if (node == 0) return;
    if (!node->isVisible()) return;

    _nodeCount++;

    node->lock();


    Matrix4 currentWorldMatrix;
    Matrix4 currentNormalMatrix;

    currentWorldMatrix.identity();
    currentNormalMatrix.identity();

 
    if (Light* light = dynamic_cast<Light*>(node)) {
        if (extractLights) {
            LightSource lightSource;
            lightSource.light = light;
            lightSource.world = _worldMatrix.front();
            _lightSources.push_back(lightSource);
        }

        for (std::list<Node*>::iterator iNode = light->getChildren().begin(); iNode != light->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);
    }

    else if (Geode* geode = dynamic_cast<Geode*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        _transformStack.push_front(geode->getWorldMatrix());

        currentWorldMatrix.identity();
        for (std::list<Matrix4>::iterator iWorld = _transformStack.begin(); iWorld != _transformStack.end(); ++iWorld)
            currentWorldMatrix *= (*iWorld);

        currentNormalMatrix = ((currentWorldMatrix * _activeCamera.front()->getView()).inverse()).transpose();
        //currentNormalMatrix = (currentWorldMatrix.inverse()).transpose();

        _worldMatrix.push_front(currentWorldMatrix);
        _normalMatrix.push_front(currentNormalMatrix);

        drawGeode(geode);

        for (std::list<Node*>::iterator iNode = geode->getChildren().begin(); iNode != geode->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);

        _normalMatrix.pop_front();
        _worldMatrix.pop_front();

        _transformStack.pop_front();

        if (node->getShader() != 0)
            _shader.pop_front();
    }

    else if (Text* text = dynamic_cast<Text*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        _transformStack.push_front(text->getWorldMatrix());

        currentWorldMatrix.identity();
        for (std::list<Matrix4>::iterator iWorld = _transformStack.begin(); iWorld != _transformStack.end(); ++iWorld)
            currentWorldMatrix *= (*iWorld);

        currentNormalMatrix = ((currentWorldMatrix * _activeCamera.front()->getView()).inverse()).transpose();
        //currentNormalMatrix = (currentWorldMatrix.inverse()).transpose();

        _worldMatrix.push_front(currentWorldMatrix);
        _normalMatrix.push_front(currentNormalMatrix);

        drawText(text);

        for (std::list<Node*>::iterator iNode = text->getChildren().begin(); iNode != text->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);

        _normalMatrix.pop_front();
        _worldMatrix.pop_front();

        _transformStack.pop_front();

        if (node->getShader() != 0)
            _shader.pop_front();
    }

    else if (Sprite* sprite = dynamic_cast<Sprite*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        _transformStack.push_front(sprite->getWorldMatrix());

        currentWorldMatrix.identity();
        for (std::list<Matrix4>::iterator iWorld = _transformStack.begin(); iWorld != _transformStack.end(); ++iWorld)
            currentWorldMatrix *= (*iWorld);

        currentNormalMatrix = ((currentWorldMatrix * _activeCamera.front()->getView()).inverse()).transpose();
        //currentNormalMatrix = (currentWorldMatrix.inverse()).transpose();

        _worldMatrix.push_front(currentWorldMatrix);
        _normalMatrix.push_front(currentNormalMatrix);

        drawSprite(sprite);

        for (std::list<Node*>::iterator iNode = sprite->getChildren().begin(); iNode != sprite->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);

        _normalMatrix.pop_front();
        _worldMatrix.pop_front();

        _transformStack.pop_front();

        if (node->getShader() != 0)
           _shader.pop_front();
    }

    else if (Transform* transform = dynamic_cast<Transform*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        _transformStack.push_front(transform->getWorldMatrix());

        currentWorldMatrix.identity();
        for (std::list<Matrix4>::iterator iWorld = _transformStack.begin(); iWorld != _transformStack.end(); ++iWorld)
            currentWorldMatrix *= (*iWorld);

        currentNormalMatrix = ((currentWorldMatrix * _activeCamera.front()->getView()).inverse()).transpose();
        //currentNormalMatrix = (currentWorldMatrix.inverse()).transpose();

        _worldMatrix.push_front(currentWorldMatrix);
        _normalMatrix.push_front(currentNormalMatrix);

        for (std::list<Node*>::iterator iNode = transform->getChildren().begin(); iNode != transform->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);

        _normalMatrix.pop_front();
        _worldMatrix.pop_front();

        _transformStack.pop_front();

        if (node->getShader() != 0)
            _shader.pop_front();
    }

    else if (Camera* camera = dynamic_cast<Camera*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        camera->updateProjection(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);

        _activeCamera.push_front(camera);

        currentNormalMatrix = ((_worldMatrix.front() * camera->getView()).inverse()).transpose();
        _normalMatrix.push_front(currentNormalMatrix);


        if (camera->getRenderTargetImplementation() == Camera::TEXTURE) {
            setRenderTargets(camera->getOutputBuffers(), camera->getDepthStencilBuffer());
            clearRenderTargets();
        }
        else {
            setRenderTargets(0, 0);
        }
     
        for (std::list<Node*>::iterator iNode = camera->getChildren().begin(); iNode != camera->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);


        _normalMatrix.pop_front();
        _activeCamera.pop_front();

        if (node->getShader() != 0)
            _shader.pop_front();

        if (!_activeCamera.empty()) {
            if (_activeCamera.front()->getRenderTargetImplementation() == Camera::TEXTURE)
                setRenderTargets(_activeCamera.front()->getOutputBuffers(), _activeCamera.front()->getDepthStencilBuffer());
            else
                setRenderTargets(0, 0);
        }
        
    }
    
    else if (Group* group = dynamic_cast<Group*>(node)) {
        if (node->getShader() != 0)
            _shader.push_front(node->getShader());

        for (std::list<Node*>::iterator iNode = group->getChildren().begin(); iNode != group->getChildren().end(); ++iNode)
            parseGraph(*iNode, extractLights);

        if (node->getShader() != 0)
            _shader.pop_front();
    }

    node->unlock();
}

void RenderDevice::renderLightsAndAO()
{
    bool hasLights = !_lightSources.empty();

    if (hasLights) {
        setRenderTargets(_lightCamera->getOutputBuffers(), _lightCamera->getDepthStencilBuffer());
        clearRenderTargets();
    }
    
    while (!_lightSources.empty()) {
        LightSource lightSource = _lightSources.front();
        _lightSources.pop_front();

        lightSource.light->enableShadows(_enableShadows);

        if (_enableShadows && lightSource.light->getShadowed()) {
            lightSource.light->setLightView(lightSource.world);

            Camera* shadowCamera = lightSource.light->getShadowCamera();

            shadowCamera->getShader()->g_ViewMatrix->setValue((float*)shadowCamera->getView()[0]);
            shadowCamera->getShader()->g_ProjectionMatrix->setValue((float*)shadowCamera->getProjection()[0]);
          
            setRenderTargets(shadowCamera->getOutputBuffers(), shadowCamera->getDepthStencilBuffer());
            clearRenderTargets(1000.0f, 1000.0f*1000.0f);

            _activeCamera.push_front(shadowCamera);
            _shader.push_front(shadowCamera->getShader());

            parseGraph(_root, false);

            _shader.pop_front();
            _activeCamera.pop_front();

            lightSource.light->getShadowMap()->resolve(lightSource.light->getResolvedShadowMap());

            _verticalBlurCamera->attachInput(lightSource.light->getResolvedShadowMap(), 0);
            _verticalBlurCamera->attachOutput(_tempShadowMap, 0);
            _verticalBlurShaderTextureHeightVariable->setValue((int)lightSource.light->getResolvedShadowMap()->getHeight());

            _horizontalBlurCamera->attachInput(_tempShadowMap, 0);
            _horizontalBlurCamera->attachOutput(lightSource.light->getResolvedShadowMap(), 0);
            _horizontalBlurShaderTextureWidthVariable->setValue((int)lightSource.light->getResolvedShadowMap()->getWidth());
           
            parseGraph(_verticalBlurCamera);
            parseGraph(_horizontalBlurCamera);
            
        }

        setRenderTargets(_lightCamera->getOutputBuffers(), _lightCamera->getDepthStencilBuffer());

        _activeCamera.push_front(_lightCamera);

        lightSource.light->apply();
        
        _shader.push_front(lightSource.light->getShader());
        _worldMatrix.push_front(lightSource.world);

        drawSprite(_quad);

        _worldMatrix.pop_front();
        _shader.pop_front();
    
        _activeCamera.pop_front();
    }
    
    _perfCnt3->mark();
    
    if (_enableAo || _enableGi) {
        setRenderTargets(_ssaoCamera->getOutputBuffers(), _ssaoCamera->getDepthStencilBuffer());
        clearRenderTargets();

        _activeCamera.push_front(_ssaoCamera);
        _shader.push_front(_ssaoCamera->getShader());

        drawSprite(_quad);

        _shader.pop_front();
        _activeCamera.pop_front();
    }
    
}

void RenderDevice::render()
{
    _nodeCount = 0;

    clear();

    _perfMon->begin();

    p0_mrt0->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p0_mrt1->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p0_mrt2->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p0_mrt3->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p0_dsb->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);

    p1_mrt0->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p1_mrt1->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p1_mrt2->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);

    p2_mrt0->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);

    p3_mrt0->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    p3_mrt1->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);

    _downsampleRenderTarget->resize((uint32)ceil(_dmScreenSettings.dmPelsWidth*0.25), (uint32)ceil(_dmScreenSettings.dmPelsHeight*0.25));

    _tonemapRenderTarget->resize(_dmScreenSettings.dmPelsWidth, _dmScreenSettings.dmPelsHeight);
    
    _brightPassRenderTarget->resize((uint32)ceil(_dmScreenSettings.dmPelsWidth*0.25), (uint32)ceil(_dmScreenSettings.dmPelsHeight*0.25));

    
    _perfCnt1->mark();
    parseGraph(_sceneCamera);
    _perfCnt2->mark();
    

    renderLightsAndAO();


    _perfCnt4->mark();
    parseGraph(_combineCamera);
    _perfCnt5->mark();


    p3_mrt1->generateMipmaps();

    _mipLevels->setValue((int)p3_mrt1->getMipLevelCount());
    parseGraph(_tonemapCamera);

    _perfCnt6->mark();
    

    parseGraph(_brightPassCamera);

    
    _verticalBlurCamera->attachInput(_brightPassRenderTarget, 0);
    _verticalBlurCamera->attachOutput(_downsampleRenderTarget, 0);
    _verticalBlurShaderTextureHeightVariable->setValue((int)_brightPassRenderTarget->getHeight());

    _horizontalBlurCamera->attachInput(_downsampleRenderTarget, 0);
    _horizontalBlurCamera->attachOutput(_brightPassRenderTarget, 0);
    _horizontalBlurShaderTextureWidthVariable->setValue((int)_brightPassRenderTarget->getWidth());
   
    parseGraph(_verticalBlurCamera);
    parseGraph(_horizontalBlurCamera);

    parseGraph(_verticalBlurCamera);
    parseGraph(_horizontalBlurCamera);


    parseGraph(_bloomCamera);
    _bloomTime = _stopWatch.getElapsedTime();

    _perfCnt7->mark();

    parseGraph(_overlayCamera);
    parseGraph(_debugOverlayCamera);

    _perfCnt8->mark();

    _perfMon->end();

    flush();
    
    
    if (_perfMon->isValid()) {
        double scale = 1.0 / (0.001 * _perfMon->getFrequency());
        _gbufferFillTime = (_perfCnt2->recall() - _perfCnt1->recall()) * scale;
        _shadowMapGenTime = (_perfCnt3->recall() - _perfCnt2->recall()) * scale * 0.5;
        _lightCalcTime = _shadowMapGenTime;
        _ssaoTime = (_perfCnt4->recall() - _perfCnt3->recall()) * scale;
        _combineTime = (_perfCnt5->recall() - _perfCnt4->recall()) * scale;
        _tonemapTime = (_perfCnt6->recall() - _perfCnt5->recall()) * scale;
        _bloomTime = (_perfCnt7->recall() - _perfCnt6->recall()) * scale;
        _overlayTime = (_perfCnt8->recall() - _perfCnt7->recall()) * scale;
    } 
    else { 
        _gbufferFillTime = 0.0;
        _shadowMapGenTime = 0.0;
        _lightCalcTime = 0.0;
        _ssaoTime = 0.0;
        _combineTime = 0.0;
        _tonemapTime = 0.0;
        _bloomTime = 0.0;
        _overlayTime = 0.0;
    }

    QueryPerformanceCounter(&_cnt2);

    double frameTime = (_cnt2.QuadPart - _cnt1.QuadPart) / (0.001 * _freq.QuadPart);
    milisecCounter += frameTime;
    _time += frameTime;
    frameCounter++;
    if (milisecCounter >= 1000.0) {
        _mpf = milisecCounter / frameCounter;
        milisecCounter = 0.0;
        frameCounter = 0;

        std::wstringstream stream;
        stream << (int)(1000.0/_mpf) << L" fps (" << _mpf << L" miliseconds per frame)" << std::endl << L"Node count = " << _nodeCount << std::endl << L"Activity Mask = ";
        for (int i = 0; i < ResourceManager::MAX_THREAD_NUM; ++i)
            stream << ResourceManager::instance().getActivityMask()[i] ? L"1" : L"0";
        
        stream << "\nG-Buffer fill time: " << std::fixed << std::setprecision(4) << _gbufferFillTime << " ms";
        stream << "\nShadow maps gen time: " << std::fixed << std::setprecision(4) << _shadowMapGenTime << " ms";
        stream << "\nLight pass: " << std::fixed << std::setprecision(4) << _lightCalcTime << " ms";
        stream << "\nSSAO pass: " << std::fixed << std::setprecision(4) << _ssaoTime << " ms"; 
        stream << "\nCombine pass: " << std::fixed << std::setprecision(4) << _combineTime << " ms"; 
        stream << "\nTonemap pass: " << std::fixed << std::setprecision(4) << _tonemapTime << " ms"; 
        stream << "\nBloom pass: " << std::fixed << std::setprecision(4) << _bloomTime << " ms"; 
        stream << "\nOverlay pass: " << std::fixed << std::setprecision(4) << _overlayTime << " ms";
        stream << "\nMisc: " << std::fixed << std::setprecision(4) << frameTime - (_gbufferFillTime + _shadowMapGenTime + _lightCalcTime + _ssaoTime + _combineTime + _tonemapTime + _bloomTime + _overlayTime) << " ms";
        
        _debugInfo->setText(stream.str().c_str());
    }

    _cnt1 = _cnt2;

    _cameraManipulator->update((float)frameTime);
}


void RenderDevice::setSceneData(Node* node)
{
    if (_root != 0)
        _sceneCamera->removeChild(_root);

    _root = node;

    if (_root != 0)
        _sceneCamera->addChild(_root);
}

void RenderDevice::setOverlayData(Node* node)
{
    if (_overlay != 0)
        _overlayCamera->removeChild(_overlay);

    _overlay = node;

    if (_overlay != 0)
        _overlayCamera->addChild(_overlay);
}