void VRendererNodeCommon::OnHandleCallback(IVisCallbackDataObject_cl *pData)
{
  if (pData->m_pSender == &Vision::Callbacks.OnBeforeVideoChanged)
  {
    if (GetFinalTargetContext() != NULL && RendersIntoBackBuffer() && IsInitialized())
    {
      m_pDeinitDuringVideoResize = new VScopedRendererNodeDeinit(this);
    }
  }
  else if (pData->m_pSender == &Vision::Callbacks.OnVideoChanged)
  {
    V_SAFE_DELETE(m_pDeinitDuringVideoResize);
  }
#if defined(_VISION_WIN32) && defined(_VR_DX9)
  else if (pData->m_pSender == &Vision::Callbacks.OnEnterForeground)
  {
    if(IsInitialized())
    {
      InitializePostProcessors();
    }
  }
#endif
  else if (pData->m_pSender == &Vision::Callbacks.OnUpdateSceneFinished)
  {
    UpdateTimeOfDay();
  }
}
void VRendererNodeCommon::InitializePostProcessors()
{
  VASSERT_MSG(IsInitialized(), "The renderer node must be initialized before initializing the post processors.");

  ANALYSIS_IGNORE_WARNING_BLOCK_START(6385);
  ANALYSIS_IGNORE_WARNING_BLOCK_START(6211);

  // Increment the update counter to enable modifying the post processors without recursing
  m_iPostProcessorUpdateCounter++;

  VType* pCopyPostProcessorType = GetDefaultCopyPostprocessorType();

  bool bInvalidPostProcessorActive = false;
  do
  {
    bInvalidPostProcessorActive = false;
    DeInitializePostProcessors();

    VPostProcessingBaseComponent* pSimpleCopy = NULL;

    // Collect post processor components
    VMemoryTempBuffer<256> tempBuffer((Components().Count() + 1) * sizeof(VPostProcessingBaseComponent*));
    VPostProcessingBaseComponent** postProcessors = reinterpret_cast<VPostProcessingBaseComponent**>(tempBuffer.GetBuffer());
    int iPostProcessorIndex = 0;
    for(int iComponentIndex = 0; iComponentIndex < Components().Count(); iComponentIndex++)
    {
      if(VPostProcessingBaseComponent* pPostProcessor = vdynamic_cast<VPostProcessingBaseComponent*>(Components().GetAt(iComponentIndex)))
      {
        // Don't take the auto added copy PP into consideration, we'll handle that separately
        if(pCopyPostProcessorType != NULL && pPostProcessor->IsOfType(pCopyPostProcessorType))
        {
          pSimpleCopy = pPostProcessor;
          continue;
        }

        // HS#10443: Skip post-processors which do nothing - needs testing whether this works cleanly when the identity state changes
        if(!pPostProcessor->IsActive() /*!pPostProcessor->IsIdentity()*/)
        {
          continue;
        }

        postProcessors[iPostProcessorIndex] = pPostProcessor;
        iPostProcessorIndex++;
      }
    }

    int iNumPostProcessors = iPostProcessorIndex;

    qsort(postProcessors, iNumPostProcessors, sizeof(VPostProcessingBaseComponent*), ComparePostProcessorsByPriority);

    int iCopyPPIndex = iNumPostProcessors;

    // Scan backwards through post processors to find one which can take over the responsibility
    // of copying the scene to the final target context
    //
    //  This post processor must:
    //    - come after the MSAA resolve step
    //    - render an opaque full screen quad
    //    - not have any postprocessor afterwards that reads the accumulation buffer
    bool bUsesOffscreenRenderTarget = !m_bUsesDirectRenderToFinalTargetContext;
    for(int i = iNumPostProcessors - 1; i >= 0; i--)
    {
      if(postProcessors[i]->GetPriority() < VIS_RENDERCONTEXTPRIORITY_POSTPROCESSOR_RESOLVED)
      {
        bUsesOffscreenRenderTarget = true;
        break;
      }

      const unsigned int flags = postProcessors[i]->GetBufferUsageFlags();

      // Post processors that use their own render target can't be used for copying to the back buffer
      if((flags & VPostProcessingBaseComponent::USES_CUSTOM_RENDERTARGET) != 0)
      {
        bUsesOffscreenRenderTarget = true;
        break;
      }

      // Check first if the post processors draws an opaque full screen quad, because
      // a PP that draws a full screen quad AND samples the accumulation buffer
      // is still suitable for copying the accumulation buffer into the final target context (such as tonemapping).
      if((flags & VPostProcessingBaseComponent::DRAWS_FULLSCREEN_QUAD) != 0 && (flags & VPostProcessingBaseComponent::USES_BLENDING) == 0)
      {
        iCopyPPIndex = i;
        break;
      }

      if(flags & VPostProcessingBaseComponent::SAMPLES_ACCUMULATION_BUFFER)
      {
        bUsesOffscreenRenderTarget = true;
        break;
      }
    }

    VASSERT_MSG(bUsesOffscreenRenderTarget != m_bUsesDirectRenderToFinalTargetContext, "Renderer node indicated that it renders directly to the renderer node's final target context, but post-processors require an offscreen render target!");

    // If no suitable post processor was found, we need to make sure the scene is copied
    bool bNeedsManualCopyToTarget = (iCopyPPIndex == iNumPostProcessors) && bUsesOffscreenRenderTarget;

    // If we don't use an offscreen RT, we don't have a copy PP
    if (!bUsesOffscreenRenderTarget)
      iCopyPPIndex = -1;

    if(bNeedsManualCopyToTarget)
    {
      if (pCopyPostProcessorType != NULL)
      {
        if(pSimpleCopy == NULL)
        {
          pSimpleCopy = (VPostProcessingBaseComponent*)pCopyPostProcessorType->CreateInstance();
          VASSERT(pSimpleCopy != NULL);
          AddComponent(pSimpleCopy);
        }

        postProcessors[iNumPostProcessors] = pSimpleCopy;
        iNumPostProcessors++;
      }
    }
    else if(pSimpleCopy != NULL)
    {
      // Remove existing copy PP if not needed
      RemoveComponent(pSimpleCopy);
    }

    m_assignedContexts.EnsureCapacity(iNumPostProcessors);

    // Create a target context for each post processor
    for(iPostProcessorIndex = 0; iPostProcessorIndex < iNumPostProcessors; iPostProcessorIndex++)
    {
      VPostProcessingBaseComponent* pPostProcessor = postProcessors[iPostProcessorIndex];

      pPostProcessor->m_iTargetIndex = iPostProcessorIndex;

      const VisRenderContext_cl* pFinalTargetContext = GetFinalTargetContext();

      bool bRenderIntoFinalTargetContext = (iPostProcessorIndex >= iCopyPPIndex);

      int iPosX, iPosY, iWidth, iHeight;
      float zMin, zMax;
      if(bRenderIntoFinalTargetContext)
      {
        pFinalTargetContext->GetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax);
      }
      else
      {
        GetReferenceContext()->GetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax);
      }

      VisRenderContext_cl* pContext = new VisRenderContext_cl(pFinalTargetContext->GetCamera(), 90.0f, 90.0f, iWidth, iHeight, 0.0f, 0.0f, pFinalTargetContext->GetRenderFlags());
      pContext->SetRenderFilterMask(pFinalTargetContext->GetRenderFilterMask());
      pContext->SetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax);
      pContext->SetViewProperties(pFinalTargetContext->GetViewProperties());

      pContext->SetName(pPostProcessor->GetTypeId()->m_lpszClassName);

      pContext->SetVisibilityCollector(pFinalTargetContext->GetVisibilityCollector(), false);
      pContext->SetPriority(pPostProcessor->GetPriority());
      pContext->SetUserData(pPostProcessor);
      pContext->SetRenderLoop(new PostProcessRenderLoop_cl(pPostProcessor));

      if(bRenderIntoFinalTargetContext)
      {
        pContext->SetRenderAndDepthStencilTargets(pFinalTargetContext);

        if (bUsesOffscreenRenderTarget)
        {
          // If possible, try to give the post processors that render directly into the final target context a useful depth-stencil target.
          // This is only possible if the final target context has MSAA disabled.
          bool bCanReplaceDST = false;

          if(pFinalTargetContext->RendersIntoBackBuffer())
          {
            #if !defined(_VISION_ANDROID) && !defined(_VISION_TIZEN) && !defined(_VISION_NACL)
              // On Android, the back buffer context uses a fixed FBO, so we can't replace the DST.
              bCanReplaceDST = Vision::Video.GetCurrentConfig()->m_eMultiSample == VVIDEO_MULTISAMPLE_OFF;
            #endif
          }
          else if(pFinalTargetContext->GetRenderTarget(0) != NULL)
          {
            bCanReplaceDST = static_cast<VisRenderableTexture_cl*>(pFinalTargetContext->GetRenderTarget(0))->GetConfig()->m_iMultiSampling <= 1;
          }

          int iRefWidth, iRefHeight, iFinalWidth, iFinalHeight;
          pFinalTargetContext->GetSize(iFinalWidth, iFinalHeight);
          GetReferenceContext()->GetSize(iRefWidth, iRefHeight);

          if(iRefWidth != iFinalWidth || iRefHeight != iFinalHeight)
          {
            bCanReplaceDST = false;
          }

          if(bCanReplaceDST)
          {
            pContext->SetDepthStencilTarget(static_cast<VisRenderableTexture_cl*>(GetPostProcessDepthStencilTarget(VRTV_RESOLVED)));
          }
          else
          {
            hkvLog::Warning("Could not attach a depth-stencil target to the context of the \"%s\" post processor - depth testing will not work correctly.", pPostProcessor->GetTypeId()->m_lpszClassName);
          }
        }
      }
      else
      {
        VRenderTargetVersion_e targetVersion = (pPostProcessor->GetPriority() <= VIS_RENDERCONTEXTPRIORITY_POSTPROCESSOR_RESOLVED) ? VRTV_MSAA : VRTV_RESOLVED;

        if((pPostProcessor->GetBufferUsageFlags() & VPostProcessingBaseComponent::USES_CUSTOM_RENDERTARGET) == 0)
        {
          pContext->SetRenderTarget(0, static_cast<VisRenderableTexture_cl*>(GetPostProcessColorTarget(targetVersion)));
          pContext->SetDepthStencilTarget(static_cast<VisRenderableTexture_cl*>(GetPostProcessDepthStencilTarget(targetVersion)));
        }
      }

      m_assignedContexts.Add(pContext);

      pPostProcessor->InitializePostProcessor();

      // Validity can only be determined after initialization, so deactivate the invalid postprocessor and retry the entire context setup
      if(!pPostProcessor->IsValid())
      {
        // the post-processor will have deactivated itself by now
        pPostProcessor->SetActive(false);
        bInvalidPostProcessorActive = true;
      }
    }
  }
  while ( bInvalidPostProcessorActive );

  m_bPostProcessorAssignmentDirty = false;
  m_iPostProcessorUpdateCounter--;

  VisRenderContext_cl::ElementManagerDeleteAllUnRef();

  ANALYSIS_IGNORE_WARNING_BLOCK_END;
  ANALYSIS_IGNORE_WARNING_BLOCK_END;
}
void IVRendererNode::InitializeRenderer()
{
  VASSERT_MSG(GetFinalTargetContext() != NULL, "The final target context must be set before initialization.");
}