Access violation writing location when memcpy to wxNativePixelData

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
User avatar
zoxks
In need of some credit
In need of some credit
Posts: 8
Joined: Tue Apr 27, 2021 11:38 pm

Access violation writing location when memcpy to wxNativePixelData

Post by zoxks » Tue Apr 27, 2021 11:54 pm

I am attempting to make a bare-bones gstreamer->wxWidgets program that displays a simple test pattern. All is working well, until I actually attempt to memcpy the buffer from the supplied gstreamer frame to the wxBitmap wxNativePixelData. The code crashes with

Code: Select all

 0xC0000005: Access violation writing location 0x000001DE399D1000.
I'm not exactly sure what's up here as all the pointers seem valid, and the length of the buffer I'm trying to copy doesn't seem to be larger than the bitmap I created.

Here is the relevent code. I've also posted my cpp, header and main file in a zip if that is helpful.

Code: Select all

GstFlowReturn MainWindow::new_sample(GstElement* sink, CustomData* data)
{
    GstSample* sample;
    GstBuffer* buffer;
    GstMapInfo  info;

    // printf("=====> In Appsink::new_sample(): \n");
 
     if (!data->thisWidget->IsBufferReady)
     {
         printf("=====> In Appsink::new_sample(): "
                "Buffer is not ready to accept new sample.\n");
         data->thisWidget->RequestToDisplay(false);       // Ignore this sample
         return GST_FLOW_OK;
     }
 
    sample = gst_app_sink_pull_sample((GstAppSink*)sink);

    if (sample)
    {
        //g_print("Sample is valid...\n");
        buffer = gst_sample_get_buffer(sample);

        if (buffer)
        {
            //g_print("Buffer is valid... counter = %d\n", data->new_sample_counter);
            gst_buffer_map(buffer, &info, GST_MAP_READ);

            if (data->new_sample_counter == 1)
            {
                data->m_size = info.size;
                g_print("Size of MapInfo.data pointer: %llu bytes\n\n",
                    (unsigned long long)(sizeof(info.data)));  // Should be 8 bytes
            }

            if (data->new_sample_counter < 5)
            {

                g_print("#%d: ", data->new_sample_counter);
                /*g_print("Data size is %lu, MapInfo data pointer is %p\n", info.size, info.data);
                
                                printf("    data->ptrPtrBufferData  = %p\n",    data->ptrPtrBufferData );
                                printf("  *(data->ptrPtrBufferData) = %p\n",  *(data->ptrPtrBufferData));
                                printf(" **(data->ptrPtrBufferData) = %p\n", **(data->ptrPtrBufferData));*/
                
                data->new_sample_counter++;
            }
            

            if ((data->bmpBufferData) && (data->new_sample_counter > 1))
            {
                //printf("+++++> Calling memcpy() <+++++\n");
                memcpy(data->bmpBufferData, info.data, (size_t)info.size);

                //printf("+++++> Calling data->parent->RequestToDisplay() <+++++\n");
                data->thisWidget->RequestToDisplay(true);
            }

            gst_buffer_unmap(buffer, &info);
Yeah I'm kinda stumped on how to even continue diagnosing this at this point so any tips at all would be much appreciated.
Thanks! :D

Oh and this code is from a super helpful tutorial I found here: https://www.youtube.com/watch?v=1RotM1okx74
Attachments
WxWidgets_Test.zip
(7.25 KiB) Downloaded 17 times

User avatar
doublemax
Moderator
Moderator
Posts: 16026
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Access violation writing location when memcpy to wxNativePixelData

Post by doublemax » Wed Apr 28, 2021 5:31 am

In order to narrow it down, simplify it first. Create a separate sample that just creates a bitmap and uses that wxNativePixelData + memcpy trick to fill it. If it works, you have something to work with. If not, it will be much easier to debug.

I don't know about GTK, but at least under Windows this code would not work, because bitmaps lie upside down in memory (last row first). So using memcpy to write directly into the memory of a bitmap is always critical.
Use the source, Luke!

User avatar
zoxks
In need of some credit
In need of some credit
Posts: 8
Joined: Tue Apr 27, 2021 11:38 pm

Re: Access violation writing location when memcpy to wxNativePixelData

Post by zoxks » Wed Apr 28, 2021 3:21 pm

Thank you for the reply sir! I really should have thought to do that already :P
Here is a bare-bones sample that exhibits the same error as the gstreamer application. I hope I am doing this correctly. It honestly wouldn't surprise me if this is horribly wrong as I'm quite new to c++

Code: Select all

#include <wx/wxprec.h>
#include <wx/rawbmp.h>
#include <wx/frame.h>
#include <wx/panel.h>

class MyApp : public wxApp
{
    wxFrame* frame;
    wxPanel* pnlView;
    wxBitmap* bmpBuffer;

    void* bmpBufferData;

    public:
        bool OnInit()
        {

            bmpBuffer = new wxBitmap(640, 360, 24);

            wxNativePixelData pixelData(*bmpBuffer);

            bmpBufferData = (void*)pixelData.GetPixels().m_ptr;

            wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
            frame = new wxFrame(NULL, wxID_ANY, wxT("DC test"), wxPoint(50, 50), wxSize(800, 600));

            pnlView = new wxPanel(frame);

            pnlView->SetSize(wxSize(640, 480));

            sizer->Add(pnlView, 1, wxEXPAND);

            frame->SetSizer(sizer);

            frame->Show();
            
            char* testData = new char[640 * 360 * 3];

            memcpy(bmpBufferData, testData, 640*360*3);

            wxClientDC* dc = new wxClientDC(pnlView);

            dc->DrawBitmap(*bmpBuffer, 0, 0, false);

            delete dc;

            return true;
        }
};


wxIMPLEMENT_APP(MyApp);
Oh and yes, I am on Windows 10, and the tutorial was originally made for a linux OS, so I suspect that's where the problem came from.
Thanks! :D

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3042
Joined: Sun Jan 03, 2010 5:45 pm

Re: Access violation writing location when memcpy to wxNativePixelData

Post by PB » Wed Apr 28, 2021 4:38 pm

zoxks wrote:
Wed Apr 28, 2021 3:21 pm
Oh and yes, I am on Windows 10, and the tutorial was originally made for a linux OS
Sorry I may be missing something, but does the original memcpy code takes into account that:
  1. wxBitmap wraps the platform native bitmap, on MS Windows it is HBITMAP.
  2. wxBitmap in your code is created as a DIB (check its IsDIB() method).
  3. DIB bits may be stored as BGR and not RGB.
  4. DIB rows may be stored from bottom to top.
  5. starts of DIB rows (scanlines) may be DWORD-aligned.
By nature, such direct memory access to wxBitmap is not portable. Did you benchmark using wxNativePixelData? Did you notice that using it may be much, MUCH slower in the debug builds? In my experience, creating a wxBitmap may take some time too, so if possible it is best to reuse one instead of recreating it.

If you search this forum for "pixeldata", you will find several threads where various approaches to converting bitmap data are discussed and timed, such as this one: viewtopic.php?f=1&t=47176&p=198991#p198991

User avatar
zoxks
In need of some credit
In need of some credit
Posts: 8
Joined: Tue Apr 27, 2021 11:38 pm

Re: Access violation writing location when memcpy to wxNativePixelData

Post by zoxks » Thu Apr 29, 2021 6:57 pm

Thank you! I got it working with:

Code: Select all

  if (pixelData)
                {
                    //Create iterator for looping over the destination bitmap
                    wxNativePixelData::Iterator iterator(pixelData);

                    //Get the pointer to the start of the sample data
                    guint8* startPointer = info.data;

                    //Loop through the image and set the RGB values of the dest image
                    iterator.MoveTo(pixelData, 0, 0);
                    for (int row = 0; row < pixelData.GetHeight(); ++row)
                    {
                        wxNativePixelData::Iterator iterator(pixelData);

                        iterator.MoveTo(pixelData, 0, row);
                        for (int col = 0; col < pixelData.GetWidth(); ++col, ++iterator)
                        {

                            iterator.Red() = *(startPointer++);
                            iterator.Green() = *(startPointer++);
                            iterator.Blue() = *(startPointer++);
                        }
                    }

                    //Tell the main loop we have a new bitmap to display
                    data->thisWidget->RequestToDisplay(true);
                }
Does this seem reasonable to you, or is there a more efficient way I'm missing?

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3042
Joined: Sun Jan 03, 2010 5:45 pm

Re: Access violation writing location when memcpy to wxNativePixelData

Post by PB » Thu Apr 29, 2021 7:44 pm

zoxks wrote:
Thu Apr 29, 2021 6:57 pm
...
Does this seem reasonable to you, or is there a more efficient way I'm missing?
As I wrote before, the only possibly faster way I know is the non-portable direct bitmap access such as used e.g. here https://github.com/PBfordev/wxopencvtes ... mp.cpp#L22 (converting from OpenCV bitmap).

But aside from it being non-portable, depending on the input data format (e.g., BGR vs RGB, row order), it may not be even faster. The only answer is to profile and compare the release build. But I am no expert on writing fast code (or anything else really)....

User avatar
doublemax
Moderator
Moderator
Posts: 16026
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Access violation writing location when memcpy to wxNativePixelData

Post by doublemax » Thu Apr 29, 2021 8:16 pm

If you need to convert RGB to BGR and try to remain crossplatform withing the wxWidgets world, i don't think you can go any faster.

If you really need the last bit of performance, there are a few things you could try:

- check if you can tell gstreamer to provide the data in the format you need, i.e. BGR under Windows. If yes, you need to benchmark if it's actually faster (it could be that the it just copies the data around like your code, if you request BGR)

- Using SIMD instructions for the RGB to BGR conversion
http://ermig1979.github.io/Simd/help/gr ... 4f27e2865f
( i have no experience with this, just Googled it )

- use multiple threads for the RGB to BGR conversion
(should be very easy using OpenMP )

- check if 32bit bitmaps are actually faster (they need more memory, but are more "CPU friendly" because of data alignment

- if you just need to display the bitmap as fast as possible without drawing anything on top etc, using OpenGL to display the bitmap might be an option: viewtopic.php?p=183279#p183279
( This could eventually be optimized by directly rendering the camera buffer instead of copying the data once )
Use the source, Luke!

User avatar
zoxks
In need of some credit
In need of some credit
Posts: 8
Joined: Tue Apr 27, 2021 11:38 pm

Re: Access violation writing location when memcpy to wxNativePixelData

Post by zoxks » Thu Apr 29, 2021 11:48 pm

Ok, great! That works! :D
Thank you both for your help

Post Reply