Can we use Render2D calls in script?

The one and only usage of Render2D class I could find in userspace code is custom drawing of a UI control.
(HOWTO: Create a custom control | Flax Documentation)

    public class MyControl : Control
    {
        ...
        /// <inheritdoc />
        public override void Draw()
        {
            base.Draw();
            Render2D.DrawTexture(Image, new Rectangle(Float2.Zero, Size), TintColor);
        }
    }

Every other occurences I found throughout the repository were engine GUI or Editor UI.
I tested a few other things if I can use 2D drawings on the run…

    public override unsafe void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
    {
           //custom model rendering here...
    }

Overriding Render method seems valid only for 3D rendering, calling Render2D returns error message.

“Calling Render2D is only valid during rendering.”

  • Declaring OnPostRender and bind it to the Main Render Task
    I saw this in engine C++ code, but couldn’t figure out it is available in user space code and available to extract Main rendering task instance with Binding.
    void GameBaseImpl::OnPostRender(GPUContext* context, RenderContext& renderContext) {}
    MainRenderTask::Instance->PostRender.Bind(&GameBaseImpl::OnPostRender);
    MainRenderTask::Instance->PostRender.Unbind(&OnPostRender);
void ImGuiPlugin::OnPostRender(GPUContext* context, RenderContext& renderContext)
{
    if (!IsReady() || !Enable || !EnableDrawing)
        return;
    PROFILE_GPU_CPU("ImGui");

    // Draw ImGui data into the output (via Render2D)
    const ImDrawData* drawData = ImGui::GetDrawData();
    if (!drawData)
        return;
    const Viewport viewport = renderContext.Task->GetOutputViewport();
    Render2D::Begin(context, renderContext.Task->GetOutputView(), nullptr, viewport);
    const ImVec2 displayPos = drawData->DisplayPos;
    for (int cmdListIndex = 0; cmdListIndex < drawData->CmdListsCount; cmdListIndex++)
    {
        const ImDrawList* cmdList = drawData->CmdLists[cmdListIndex];

        // Convert vertex buffer
        _vertices.Resize(cmdList->VtxBuffer.Size);
        _uvs.Resize(cmdList->VtxBuffer.Size);
        _colors.Resize(cmdList->VtxBuffer.Size);
        for (int32 i = 0; i < cmdList->VtxBuffer.Size; i++)
        {
            const ImDrawVert v = cmdList->VtxBuffer.Data[i];
            _vertices.Get()[i] = Float2(v.pos.x, v.pos.y);
            _uvs.Get()[i] = Float2(v.uv.x, v.uv.y);
            _colors.Get()[i] = Color((float)((v.col >> IM_COL32_R_SHIFT) & 0xFF) / 255.0f, (float)((v.col >> IM_COL32_G_SHIFT) & 0xFF) / 255.0f, (float)((v.col >> IM_COL32_B_SHIFT) & 0xFF) / 255.0f, (float)((v.col >> IM_COL32_A_SHIFT) & 0xFF) / 255.0f);
        }
        Span<Float2> vertices(_vertices.Get(), _vertices.Count());
        Span<Float2> uvs(_uvs.Get(), _uvs.Count());
        Span<Color> colors(_colors.Get(), _colors.Count());
        const uint16* indices = cmdList->IdxBuffer.Data;

        // Submit draw commands
        for (int cmdIndex = 0; cmdIndex < cmdList->CmdBuffer.Size; cmdIndex++)
        {
            const ImDrawCmd& cmd = cmdList->CmdBuffer[cmdIndex];
            if (cmd.UserCallback)
            {
                cmd.UserCallback(cmdList, &cmd);
            }
            else
            {
                // Perform scissors clipping
                const ImVec2 clipMin(cmd.ClipRect.x - displayPos.x, cmd.ClipRect.y - displayPos.y);
                const ImVec2 clipMax(cmd.ClipRect.z - displayPos.x, cmd.ClipRect.w - displayPos.y);
                if (clipMax.x <= clipMin.x || clipMax.y <= clipMin.y)
                    continue;
                Rectangle scissor(clipMin.x, clipMin.y, clipMax.x - clipMin.x, clipMax.y - clipMin.y);
                Render2D::PushClip(scissor);

                // Draw textured indexed triangles list
                auto tex = (GPUTexture*)cmd.GetTexID();
                Render2D::DrawTexturedTriangles(tex, Span<uint16>(indices + cmd.IdxOffset, cmd.ElemCount), vertices, uvs, colors);

                Render2D::PopClip();
            }
        }
    }

    Render2D::End();
}



Can we use Render2D in user script, like OnXXXRender() above?
Can we use Render2D in C# script not as a GUI control painting without encountering the message?

“Calling Render2D is only valid during rendering.”

You need to manually call Render2D.Begin and Render2D.End inside your OnPostRender to support invoking drawing method in between of begin/end pair (that’s what ImGUI does).

I couldn’t make the proper Begin~End blocks for both C# and C++ during the experiment.

i.e, Begin() takes parameter of context, view and viewport but couldn’t find the working examples to refer to configure them in running codes, especially in C#. (And met access violations in C++)

C#
Render2D.Begin(GPUContext context, GPUTextureView output, GPUTextureView depthBuffer, ref Viewport viewport)
 
C++
Render2D::Begin(GPUContext* context, GPUTexture* output, GPUTexture* depthBuffer, const Matrix& viewProjection)

Can I have the minimum viable code examples?