Download

Remove Corners of a Border in Slate

I’m currently trying to have a modified border that renders as usual with all child widgets. After all childs were rendered, I want to set the pixels in the corners to Alpha=0 to retrieve Round corners, or other forms.

To do that I have a widget that inherits from SBorder and overwrites OnPaint methode:


int32 SBorderRound::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
    // Call to parent first to draw the Border as usual
    const bool bEnabled = ShouldBeEnabled(bParentEnabled);
    int32 maxLayerId = SBorder::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bEnabled);

    // Now cut of the corners of the rendering
    const FSlateBrush* BrushResource = BorderImage.Get();
    if (BrushResource && BrushResource->DrawAs != ESlateBrushDrawType::NoDrawType)
    {
        drawer->geometry = AllottedGeometry.ToPaintGeometry();

        FSlateDrawElement::MakeCustom(
            OutDrawElements,
            LayerId,
            drawer
        );
    }

    return maxLayerId;
}


and a using custom draw element:


/**
 * Custom Slater Drawer for SBorderRound
 */
class USERINTERFACE_API FBorderRoundDrawer : public ICustomSlateElement
{
public:
    FBorderRoundDrawer();
    ~FBorderRoundDrawer();
    virtual void DrawRenderThread(class FRHICommandListImmediate& RHICmdList, const void* textureTarget) override;

    FPaintGeometry geometry;
private:
    // Access only on RenderThread
    FSlateCanvasRenderTarget* renderTarget;
};




void FBorderRoundDrawer::DrawRenderThread(class FRHICommandListImmediate& RHICmdList, const void* textureTarget)
{
// Alternative: https://answers.unrealengine.com/questions/214453/view.html

   check(IsInRenderingThread());
   FTexture2DRHIRef textureRHIRef = *(FTexture2DRHIRef*)textureTarget;
   renderTarget->SetRenderTargetTexture(textureRHIRef);
   uint32 DestStride;
   uint8* DestBuffer = (uint8*)RHILockTexture2D(textureRHIRef.GetReference(), 0, RLM_WriteOnly, DestStride, false, false);
// NOTE that the texture is not edited
   RHIUnlockTexture2D(textureRHIRef.GetReference(), 0, false, false);

    renderTarget->ClearRenderTargetTexture();
}



Wrapping the Child of SBorder in a custom UClass and placing it in UMG Editor results in:

The render result shows that ICustomSlateElement::DrawRenderThread is passed the texture for the entire window.
One problem is obviously that the whole screen is gone greyish, but I have no clue why that should have happend. The problem is


   uint32 DestStride;
   uint8* DestBuffer = (uint8*)RHILockTexture2D(textureRHIRef.GetReference(), 0, RLM_WriteOnly, DestStride, false, false);
// NOTE that the texture is not edited
   RHIUnlockTexture2D(textureRHIRef.GetReference(), 0, false, false);

Commenting these lines, the render of the Window is fine. I did nothing to the texture, so why is it broken? I was never able to find the code where elements are actually drawn to a texture, so I have no clue how it is usually done.

According to



void FSlateRHIRenderingPolicy::DrawElements(
    FRHICommandListImmediate& RHICmdList,
    FSlateBackBuffer& BackBuffer,
    FTexture2DRHIRef& ColorTarget,
    FTexture2DRHIRef& DepthStencilTarget,
    const TArray<FSlateRenderBatch>& RenderBatches,
    const TArray<FSlateClippingState> RenderClipStates,
    const FSlateRenderingOptions& Options)
{
...
                // This element is custom and has no Slate geometry.  Tell it to render itself now
                CustomDrawer->DrawRenderThread(RHICmdList, &BackBuffer.GetRenderTargetTexture());
...
}


the entire texture for the window is passed to ICustomSlateElement::DrawRenderThread. Removing pixels here will definitly result in issues. How can I retrieve the layer that the Border was rendered to?

Edit:
Is there even a actual layer? Is the layer passed to SWidget::OnPaint only a index for sorting what gets rendered first? So If I want to round the corners of a Border, I actually need to NOT draw them in the first place?