Skip to content

Explicit RenderTarget Operations#

Gamethread Update & Upload To GPU#

Look at UCanvasRenderTarget2D & UCanvas

C++
void UCanvasRenderTarget2D::RepaintCanvas()
{
  // Create or find the canvas object to use to render onto the texture.  Multiple canvas render target textures can share the same canvas.
  static const FName CanvasName(TEXT("CanvasRenderTarget2DCanvas"));
  UCanvas* Canvas = (UCanvas*)StaticFindObjectFast(UCanvas::StaticClass(), GetTransientPackage(), CanvasName);
  if (Canvas == nullptr)
  {
    Canvas = NewObject<UCanvas>(GetTransientPackage(), CanvasName);
    Canvas->AddToRoot();
  }

  // Create the FCanvas which does the actual rendering.
  const UWorld* WorldPtr = World.Get();
  const ERHIFeatureLevel::Type FeatureLevel = WorldPtr != nullptr ? World->FeatureLevel : GMaxRHIFeatureLevel;
  FCanvas RenderCanvas(GameThread_GetRenderTargetResource(), nullptr, FApp::GetCurrentTime() - GStartTime, FApp::GetDeltaTime(), FApp::GetCurrentTime() - GStartTime, FeatureLevel);

  Canvas->Init(GetSurfaceWidth(), GetSurfaceHeight(), nullptr, &RenderCanvas);
  Canvas->Update();

  // Update the resource immediately to remove it from the deferred resource update list. This prevents the texture
  // from being cleared each frame.
  UpdateResourceImmediate(bShouldClearRenderTargetOnReceiveUpdate);

  // Enqueue the rendering command to set up the rendering canvas.
  ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
    (
      CanvasRenderTargetMakeCurrentCommand,
      FTextureRenderTarget2DResource*,
      TextureRenderTarget,
      (FTextureRenderTarget2DResource*)GameThread_GetRenderTargetResource(),
      {
        SetRenderTarget(RHICmdList, TextureRenderTarget->GetRenderTargetTexture(), FTexture2DRHIRef(), true);
        RHICmdList.SetViewport(0, 0, 0.0f, TextureRenderTarget->GetSizeXY().X, TextureRenderTarget->GetSizeXY().Y, 1.0f);
      }
  );


  if (!IsPendingKill() && OnCanvasRenderTargetUpdate.IsBound())
  {
    OnCanvasRenderTargetUpdate.Broadcast(Canvas, GetSurfaceWidth(), GetSurfaceHeight());
  }

  ReceiveUpdate(Canvas, GetSurfaceWidth(), GetSurfaceHeight());

  // Clean up and flush the rendering canvas.
  Canvas->Canvas = nullptr;
  RenderCanvas.Flush_GameThread();

  // Enqueue the rendering command to copy the freshly rendering texture resource back to the render target RHI
  // so that the texture is updated and available for rendering.
  ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
    (
      CanvasRenderTargetResolveCommand,
      FTextureRenderTargetResource*,
      RenderTargetResource,
      GameThread_GetRenderTargetResource(),
      {
        RHICmdList.CopyToResolveTarget(RenderTargetResource->GetRenderTargetTexture(), RenderTargetResource->TextureRHI, true, FResolveParams());
      }
  );
}

Render from Game Thread:#

C++
bool FCanvasTriangleRendererItem::Render_GameThread(const FCanvas* Canvas)
{
  float CurrentRealTime = 0.f;
  float CurrentWorldTime = 0.f;
  float DeltaWorldTime = 0.f;

  if (!bFreezeTime)
  {
    CurrentRealTime = Canvas->GetCurrentRealTime();
    CurrentWorldTime = Canvas->GetCurrentWorldTime();
    DeltaWorldTime = Canvas->GetCurrentDeltaWorldTime();
  }

  checkSlow(Data);
  // current render target set for the canvas
  const FRenderTarget* CanvasRenderTarget = Canvas->GetRenderTarget();
  FSceneViewFamily* ViewFamily = new FSceneViewFamily(FSceneViewFamily::ConstructionValues(
    CanvasRenderTarget,
    Canvas->GetScene(),
    FEngineShowFlags(ESFIM_Game))
    .SetWorldTimes(CurrentWorldTime, DeltaWorldTime, CurrentRealTime)
    .SetGammaCorrection(CanvasRenderTarget->GetDisplayGamma()));

  FIntRect ViewRect(FIntPoint(0, 0), CanvasRenderTarget->GetSizeXY());

  // make a temporary view
  FSceneViewInitOptions ViewInitOptions;
  ViewInitOptions.ViewFamily = ViewFamily;
  ViewInitOptions.SetViewRectangle(ViewRect);
  ViewInitOptions.ViewOrigin = FVector::ZeroVector;
  ViewInitOptions.ViewRotationMatrix = FMatrix::Identity;
  ViewInitOptions.ProjectionMatrix = Data->Transform.GetMatrix();
  ViewInitOptions.BackgroundColor = FLinearColor::Black;
  ViewInitOptions.OverlayColor = FLinearColor::White;

  FSceneView* View = new FSceneView(ViewInitOptions);

  bool bNeedsToSwitchVerticalAxis = RHINeedsToSwitchVerticalAxis(Canvas->GetShaderPlatform()) && XOR(IsMobileHDR(),Canvas->GetAllowSwitchVerticalAxis());

  struct FDrawTriangleParameters
  {
    FSceneView* View;
    FRenderData* RenderData;
    uint32 bIsHitTesting : 1;
    uint32 AllowedCanvasModes;
  };
  FDrawTriangleParameters DrawTriangleParameters =
  {
    View,
    Data,
    (uint32)Canvas->IsHitTesting(),
    Canvas->GetAllowedModes()
  };

  InitTriangleBuffers(&Data->VertexFactory, Data->Triangles, *View, bNeedsToSwitchVerticalAxis);

  FDrawTriangleParameters Parameters = DrawTriangleParameters;
  ENQUEUE_RENDER_COMMAND(DrawTriangleCommand)(
    [Parameters](FRHICommandListImmediate& RHICmdList)
    {
      FDrawingPolicyRenderState DrawRenderState(*Parameters.View);

      // disable depth test & writes
      DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());

      SCOPED_DRAW_EVENT(RHICmdList, CanvasDrawTriangle);
      for (int32 TriIdx = 0; TriIdx < Parameters.RenderData->Triangles.Num(); TriIdx++)
      {
        const FRenderData::FTriangleInst& Tri = Parameters.RenderData->Triangles[TriIdx];
        // update the FMeshBatch
        FMeshBatch& TriMesh = Parameters.RenderData->TriMesh.TriMeshElement;
        TriMesh.VertexFactory = &Parameters.RenderData->VertexFactory;
        TriMesh.MaterialRenderProxy = Parameters.RenderData->MaterialRenderProxy;
        TriMesh.Elements[0].BaseVertexIndex = 3 * TriIdx;

        GetRendererModule().DrawTileMesh(RHICmdList, DrawRenderState, *Parameters.View, TriMesh, Parameters.bIsHitTesting, Tri.HitProxyId);
      }

      Parameters.RenderData->StaticMeshVertexBuffers.PositionVertexBuffer.ReleaseResource();
      Parameters.RenderData->StaticMeshVertexBuffers.StaticMeshVertexBuffer.ReleaseResource();
      Parameters.RenderData->StaticMeshVertexBuffers.ColorVertexBuffer.ReleaseResource();
      Parameters.RenderData->TriMesh.ReleaseResource();
      Parameters.RenderData->VertexFactory.ReleaseResource();

      delete Parameters.View->Family;
      delete Parameters.View;
      if (Parameters.AllowedCanvasModes & FCanvas::Allow_DeleteOnRender)
      {
        delete Parameters.RenderData;
      }
    });

  if (Canvas->GetAllowedModes() & FCanvas::Allow_DeleteOnRender)
  {
    Data = nullptr;
  }
  return true;
}