Mateusz Antoniak
Mateusz Antoniak

Reputation: 11

How to draw in sharpDX?

This is my first time using a sharpdx to draw a ellipse in screen and i have this code right here(Using c# .net framework windows forms), i dont know why, there a two several problems:

  1. When resizing the window, the ellipse is streched, and i dont know why
  2. The fps, and gpu information is not being displayed for me

Also i want to optimize my code as much as i can!

Here is my code:

    private WindowRenderTarget _renderTarget;
    private Ellipse el;
    private Stopwatch _stopwatch = new Stopwatch();
    private int _frameCount;
    private string _gpuDescription;
    public Form1()
    {
        InitializeComponent();
        InitializeDirect2D();

        el = new Ellipse
        {
            RadiusX = 50,
            RadiusY = 50,
            Point = new RawVector2(ClientSize.Width / 2.0f, ClientSize.Height / 2.0f)
        };

        using (var factory = new SharpDX.DXGI.Factory1())
        {
            using (var adapter = factory.GetAdapter1(0))
            {
                _gpuDescription = adapter.Description.Description;
            }
        }

        this.Text = _gpuDescription;
    }

    private void InitializeDirect2D()
    {
        var dpi = this.CreateGraphics().DpiX;
        var properties = new HwndRenderTargetProperties
        {
            Hwnd = this.Handle,
            PixelSize = new Size2(this.ClientSize.Width, this.ClientSize.Height),
            PresentOptions = PresentOptions.None
        };
        var renderTargetProperties = new RenderTargetProperties
        {
            DpiX = dpi,
            DpiY = dpi,
            Type = RenderTargetType.Hardware,
            Usage = RenderTargetUsage.GdiCompatible,
            PixelFormat = new PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Premultiplied)
        };
        _renderTarget = new WindowRenderTarget(new SharpDX.Direct2D1.Factory(), renderTargetProperties, properties);
    }



    private void Form1_Load(object sender, EventArgs e)
    {
        timer1.Start();
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        SolidColorBrush brush = new SolidColorBrush(_renderTarget, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));

        _renderTarget.BeginDraw();
        _renderTarget.Clear(new RawColor4(0, 0, 0, 1));

        _renderTarget.FillEllipse(el, brush);

        _renderTarget.EndDraw();

        
    }

    private void Form1_Resize(object sender, EventArgs e)
    {
        
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        _frameCount++;
        if (_stopwatch.ElapsedMilliseconds >= 1000)
        {
            this.Text = $"FPS: {_frameCount}, GPU: {_gpuDescription}";
            _frameCount = 0;
            _stopwatch.Restart();
        }
    }

I hope for someone to help me fix those problems, i really appreciate!

I have another code wchich basically moves the ellipse, but it dont work. Of course the coordinates are changing but the ellipse is not showing up

Code:

    private WindowRenderTarget _renderTarget;
    private Ellipse el;
    private Stopwatch _stopwatch = new Stopwatch();
    private int _frameCount;
    private string _gpuDescription;
    private float ellipseXPosition = 100; // Starting x-coordinate
    private const float MovementSpeed = 2f;
    public Form1()
    {
        InitializeComponent();
        InitializeDirect2D();


        el = new Ellipse
        {
            RadiusX = 50,
            RadiusY = 50,
            Point = new RawVector2(100, 100)
        };


        using (var factory = new SharpDX.DXGI.Factory1())
        {
            using (var adapter = factory.GetAdapter1(0))
            {
                _gpuDescription = adapter.Description.Description;
            }
        }

        _stopwatch.Start();
        // Force a repaint
        this.Invalidate();
    }


    private void InitializeDirect2D()
    {
        var dpi = this.CreateGraphics().DpiX;
        var properties = new HwndRenderTargetProperties
        {
            Hwnd = this.Handle,
            PixelSize = new Size2(this.ClientSize.Width, this.ClientSize.Height),
            PresentOptions = PresentOptions.None
        };
        var renderTargetProperties = new RenderTargetProperties
        {
            DpiX = dpi,
            DpiY = dpi,
            Type = RenderTargetType.Hardware,
            Usage = RenderTargetUsage.GdiCompatible,
            PixelFormat = new PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Premultiplied)
        };
        _renderTarget = new WindowRenderTarget(new SharpDX.Direct2D1.Factory(), renderTargetProperties, properties);
    }



    private void Form1_Load(object sender, EventArgs e)
    {
        
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        Console.WriteLine("Form1_Paint method called");
        SolidColorBrush brush = new SolidColorBrush(_renderTarget, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));

        _renderTarget.BeginDraw();
        _renderTarget.Clear(new RawColor4(0, 0, 0, 1));

        // Update the position of the ellipse
        el.Point = new RawVector2(ellipseXPosition, 100); // Y-coordinate remains the same

        // Rysowanie elipsy
        _renderTarget.FillEllipse(el, brush);

        _renderTarget.EndDraw();

        // Move the ellipse to the right for the next frame
        ellipseXPosition += MovementSpeed;

        // Check if the ellipse reaches the right boundary and reset its position
        if (ellipseXPosition > this.ClientSize.Width)
        {
            ellipseXPosition = -el.RadiusX; // Reset to the left side of the window
        }
    }

    private void Form1_Resize(object sender, EventArgs e)
    {
        if (_renderTarget != null)
        {
            _renderTarget.Resize(new Size2(ClientSize.Width, ClientSize.Height));
        }
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        _frameCount++;
        if (_stopwatch.ElapsedMilliseconds >= 1000)
        {
            this.Text = $"FPS: {_frameCount}, GPU: {_gpuDescription}";
            _frameCount = 0;
            _stopwatch.Restart();
        }
    }

And i have a question, if there is any document of how can i use the sharpDx 2d in c#?

Upvotes: 0

Views: 220

Answers (1)

Simon Mourier
Simon Mourier

Reputation: 138776

Your stopwatch is not started, so you can add something like this in constructor:

public Form1()
{
    InitializeComponent();
    InitializeDirect2D();

    _stopwatch.Start();
    ...
}
 

Also you must resize the render target if the window is resized, like this on form's resize event:

private void Form1_Resize(object sender, EventArgs e)
{
    _renderTarget.Resize(new Size2(ClientSize.Width, ClientSize.Height));
}

As for measuring FPS, you must use a Timer with a tick interval lower or equal yo your monitor frequency (this is far from being perfect as it also depends on other factors but at least it should you something more useful) so add this in the constructor too:

timer1.Interval = 16; // ~16ms or lower if your monitor is 60hz

As for the second code, since your drawing happens in Paint method, you must call Invalidate() each time you change its state, and you should't change the state in the Paint method, so

Start the timer somewhere:

private void Form1_Load(object sender, EventArgs e)
{
    timer1.Start();
}

Move the update state code in timer's tick handler (remove it from Paint) and call Invalidate in there (if you don't do this, resizing will cause unexpected moving effects):

private void timer1_Tick(object sender, EventArgs e)
{
    _frameCount++;
    if (_stopwatch.ElapsedMilliseconds >= 1000)
    {
        this.Text = $"FPS: {_frameCount}, GPU: {_gpuDescription}";
        _frameCount = 0;
        _stopwatch.Restart();
    }

    // update state
    ellipseXPosition += MovementSpeed;
    if (ellipseXPosition > this.ClientSize.Width)
    {
        ellipseXPosition = -el.RadiusX; // Reset to the left side of the window
    }
    
    // ask for repaint
    Invalidate();
}

Add this line to constructor:

SetStyle(ControlStyles.Opaque, true);

To ask Winforms not to erase background since you're already doing this in your renderTarget.Clear code.

Also last but not least, add a using on SolidColorBrush usage (or keep it aside render target so you don't create it each time) as it's a disposble resource:

using (var brush = new SolidColorBrush(_renderTarget, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f)))
{
    ...
}

Upvotes: 1

Related Questions