Off-Screen Rendering Results in Invalid OnPaint Buffer

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Voltageist » Sat Sep 12, 2020 1:07 am

Hi! So I've created a mini-example to simulate OSR in my binary. Here are my files:

ClientApp.cpp
Code: Select all
#include "ClientApp.h"
#include "ClientHandler.h"

#include <include/wrapper/cef_helpers.h>
#include <include/views/cef_browser_view.h>

class MyCefRenderHandler : public CefRenderHandler
{
    public:
        MyCefRenderHandler(int w, int h) :
            width(w),
            height(h)
        {
        }

        ~MyCefRenderHandler()
        {
        }

        void
        GetViewRect(
            CefRefPtr<CefBrowser> browser,
            CefRect &rect)
        {
            rect = CefRect(0, 0, width, height);
        }

        void
        OnPaint(
            CefRefPtr<CefBrowser> browser,
            PaintElementType type,
            const RectList &dirtyRects,
            const void *buffer,
            int w,
            int h)
        {
            memcpy(my_buffer, buffer, w * h * 4);

            printf("size:   %ld\n", sizeof(buffer));

            SaveBuffer();
        }

        void
        SaveBuffer()
        {
            printf("This is where I'll eventually save my render to a file\n");
            // FILE *rendered_file;
            // rendered_file = fopen("test_1920x1080.bgra", "wb");
            // fwrite(my_buffer, sizeof(uint8_t), sizeof(my_buffer), rendered_file);
            //fclose(rendered_file);
        }

    private:
        int width;
        int height;

        /* Off-sceen rendering parameters  */
        uint8_t *my_buffer;
        int macro_count = 0;

        IMPLEMENT_REFCOUNTING(MyCefRenderHandler);
};

ClientApp::ClientApp()
{
}

void
ClientApp::OnContextInitialized()
{
        CEF_REQUIRE_UI_THREAD();

        CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine();

        int width = 1920;
        int height = 1080;

        /* ClientHandler implements browser-level callbacks */
        MyCefRenderHandler *myCefRenderHandler = new MyCefRenderHandler(width, height);

        /* MyCefRenderHandlerimplements off-screen rendering callbacks  */
        CefRefPtr<ClientHandler> handler = new ClientHandler(myCefRenderHandler);

     /* Specify CEF browser settings here */
     CefBrowserSettings browser_settings;
        browser_settings.webgl = STATE_ENABLED;

     /* Check if a "--url=" value was provided via the command-line. If so, use that instead of the default URL */
     std::string url;
     url = command_line->GetSwitchValue("url");
     if (url.empty())
     {
             url = "http://www.google.com";
     }

        /* Information used when creating the native window */
        CefWindowInfo window_info;
        window_info.SetAsWindowless(0);
        window_info.windowless_rendering_enabled = true;

        /* Create the first browser window (default) */
        CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings, NULL, NULL);
}

void
ClientApp::OnBeforeCommandLineProcessing(
  const CefString& process_type,
  CefRefPtr<CefCommandLine> command_line)
{
        printf("ClientApp OnBeforeCommandLineProcessing\n");

        /* Add "single-process" to the commandline if it doesn not already exist */
        if (!command_line->HasSwitch("disable-gpu"))
        {
            command_line->AppendSwitch("disable-gpu");
        }

        if (!command_line->HasSwitch("off-screen-rendering-enabled"))
        {
            command_line->AppendSwitch("off-screen-rendering-enabled");
        }

        if (!command_line->HasSwitch("disable-gpu-compositing"))
        {
            command_line->AppendSwitch("disable-gpu-compositingd");
        }

        if (!command_line->HasSwitch("enable-begin-frame-scheduling"))
        {
            command_line->AppendSwitch("enable-begin-frame-scheduling");
        }

        if (!command_line->HasSwitch("disable-d3d11"))
        {
            command_line->AppendSwitch("disable-d3d11");
        }
}


ClientApp.h
Code: Select all
#pragma once

#include <include/cef_app.h>
#include <include/cef_client.h>

class ClientApp : public CefApp,
                  public CefBrowserProcessHandler,
                  public CefRenderProcessHandler
{
    public:
        ClientApp();

        /* CefApp methods */
        virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE { return this; }
        virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE { return this; }

        /* CefBrowserProcessHandler methods */
        virtual void OnContextInitialized() OVERRIDE;

        /* Programmatically configure command-line arguments */
        virtual void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) OVERRIDE;

        private:
           /* Include the default reference counting implementation */
                IMPLEMENT_REFCOUNTING(ClientApp);
};


ClientHandler.cpp
Code: Select all
#include "ClientHandler.h"

#include <X11/Xatom.h>
#include <X11/Xlib.h>

#include <sstream>
#include <string>

#include <include/base/cef_bind.h>
#include <include/cef_app.h>
#include <include/cef_parser.h>
#include <include/views/cef_browser_view.h>
#include <include/views/cef_window.h>
#include <include/wrapper/cef_closure_task.h>
#include <include/wrapper/cef_helpers.h>

ClientHandler *g_instance = NULL;

void
ClientHandler::PlatformTitleChange(
    CefRefPtr<CefBrowser> browser,
    const CefString& title)
{
    std::string titleStr(title);

    #if defined(USE_X11)
        /* Retrieve the X11 display shared with Chromium */
        ::Display* display = cef_get_xdisplay();
        DCHECK(display);

        /* Retrieve the X11 window handle for the browser */
        ::Window window = browser->GetHost()->GetWindowHandle();
        DCHECK(window != kNullWindowHandle);

        /* Retrieve the atoms required by the below XChangeProperty call */
        const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
        Atom atoms[2];

        int result = XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms);
        if (!result)
        {
            NOTREACHED();
        }

        /* Set the window title */
        XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
                        reinterpret_cast<const unsigned char*>(titleStr.c_str()),
                        titleStr.size());

        XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str());
    #endif  // defined(USE_X11)
}

/* Returns a data: URI with the specified contents */
std::string
GetDataURI(
    const std::string& data,
    const std::string& mime_type)
{
    return "data:" + mime_type + ";base64," + CefURIEncode(CefBase64Encode(data.data(), data.size()), false).ToString();
}

ClientHandler::ClientHandler(CefRefPtr<CefRenderHandler> ptr) : handler(ptr)
{
    printf("ClientHandler constructor\n");
    is_closing_ = false;
    DCHECK(!g_instance);
    g_instance = this;
}

ClientHandler::~ClientHandler()
{
    printf("ClientHandler destructor\n");
    g_instance = NULL;
}

ClientHandler *
ClientHandler::GetInstance()
{
    return g_instance;
}

void
ClientHandler::OnTitleChange(
    CefRefPtr<CefBrowser> browser,
    const CefString& title)
{
    printf("ClientHandler OnTitleChange\n");

    CEF_REQUIRE_UI_THREAD();

    /* Set the title of the window using platform APIs */
    PlatformTitleChange(browser, title);
}

void
ClientHandler::OnAfterCreated(
    CefRefPtr<CefBrowser> browser)
{
    printf("ClientHandler OnAfterCreated\n");

    CEF_REQUIRE_UI_THREAD();

    /* Add to the list of existing browsers */
    browser_list_.push_back(browser);
}

bool
ClientHandler::DoClose(
    CefRefPtr<CefBrowser> browser)
{
    printf("ClientHandler DoClose\n");

    CEF_REQUIRE_UI_THREAD();

    /* Closing the main window requires special handling. See the DoClose() documentation in the CEF header for a detailed
     * destription of this process */
    if (browser_list_.size() == 1)
    {
        /* Set a flag to indicate that the window close should be allowed */
        is_closing_ = true;
    }

    /*Allow the close. For windowed browsers this will result in the OS close event being sent */
    return false;
}

void
ClientHandler::OnBeforeClose(
    CefRefPtr<CefBrowser> browser)
{
    printf("ClientHandler OnBeforeClose\n");

    CEF_REQUIRE_UI_THREAD();

    /* Remove from the list of existing browsers */
    BrowserList::iterator bit = browser_list_.begin();
    for (; bit != browser_list_.end(); ++bit)
    {
        if ((*bit)->IsSame(browser))
        {
            browser_list_.erase(bit);
            break;
        }
    }

    if (browser_list_.empty())
    {
        /* All browser windows have closed. Quit the application message loop */
        CefQuitMessageLoop();
    }
}

void
ClientHandler::OnLoadError(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    ErrorCode errorCode,
    const CefString& errorText,
    const CefString& failedUrl)
{
    printf("ClientHandler OnLoadError\n");

    CEF_REQUIRE_UI_THREAD();

    /* Don't display an error for downloaded files */
    if (errorCode == ERR_ABORTED)
    {
        return;
    }

    /* Display a load error message using a data: URI */
    std::stringstream ss;
    ss << "<html><body bgcolor=\"white\">" "<h2>Failed to load URL "
       << std::string(failedUrl) << " with error " << std::string(errorText)
       << " (" << errorCode << ").</h2></body></html>";

    frame->LoadURL(GetDataURI(ss.str(), "text/html"));
}

void
ClientHandler::CloseAllBrowsers(
    bool force_close)
{
    printf("ClientHandler CloseAllBrowsers\n");

    if (!CefCurrentlyOn(TID_UI))
    {
      /* Execute on the UI thread */
      CefPostTask(TID_UI, base::Bind(&ClientHandler::CloseAllBrowsers, this, force_close));
      return;
    }

    if (browser_list_.empty())
    {
          return;
    }

    BrowserList::const_iterator it = browser_list_.begin();
    for (; it != browser_list_.end(); ++it)
    {
          (*it)->GetHost()->CloseBrowser(force_close);
    }
}


ClientHandler.h
Code: Select all
#pragma once

#include <include/cef_render_process_handler.h>
#include <include/cef_client.h>
#include <include/cef_v8.h>
#include <include/cef_browser.h>

#include <list>

class ClientHandler : public CefClient,
                      public CefDisplayHandler,
                      public CefLifeSpanHandler,
                      public CefLoadHandler
{
    public:
        ClientHandler(CefRefPtr<CefRenderHandler> ptr);
        ~ClientHandler();

        /* Provide access to the single global instance of this object */
        static ClientHandler* GetInstance();

        /* CefClient methods */
        virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler()
            OVERRIDE { return this; }

        virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler()
            OVERRIDE { return this; }

        virtual CefRefPtr<CefLoadHandler> GetLoadHandler()
            OVERRIDE { return this; }

        virtual CefRefPtr<CefRenderHandler> GetRenderHandler()
            OVERRIDE { return handler; }

        /* CefDisplayHandler methods */
        virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE;

        /* CefLifeSpanHandler methods */
        virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
        virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
        virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;

        /* CefLoadHandler methods */
        virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode,
                                 const CefString& errorText, const CefString& failedUrl) OVERRIDE;

        /* Request that all existing browser windows close */
        void CloseAllBrowsers(bool force_close);

        bool IsClosing() const { return is_closing_; }

    private:
        /* Platform-specific implementation */
        void PlatformTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title);

        /* List of existing browser windows. Only accessed on the CEF UI thread */
        typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
        BrowserList browser_list_;

        bool is_closing_;

        /* Parameters used for rendering (both off-screen and default) */
        CefRefPtr<CefRenderHandler> handler;

        IMPLEMENT_REFCOUNTING(ClientHandler);
};


main.cpp
Code: Select all
#include <X11/Xlib.h>

#include <thread>
#include <chrono>

#include <include/internal/cef_linux.h>

#include <include/cef_app.h>
#include <include/cef_browser.h>
#include <include/cef_frame.h>

#include "ClientHandler.h"
#include "ClientApp.h"

/* The parent/child processes will ocasionally receive non-fatal errors that will result in a crash. These macros will
 * intercept them and handle them appropriately to prevent any crashes. */
namespace
{
    int
    XErrorHandlerImpl(
        Display* display,
        XErrorEvent* event)
    {
        LOG(WARNING) << "X error received: "
                     << "type " << event->type << ", "
                     << "serial " << event->serial << ", "
                     << "error_code " << static_cast<int>(event->error_code) << ", "
                     << "request_code " << static_cast<int>(event->request_code)
                     << ", "
                     << "minor_code " << static_cast<int>(event->minor_code);
        return 0;
    }

    int
    XIOErrorHandlerImpl(
        Display* display)
    {
        return 0;
    }
}

void CefInitSettings(CefSettings& settings)
{
    /* Make browser process message loop run in a separate thread */
    settings.multi_threaded_message_loop = false;

    /* The resources (cef.pak and/or devtools_resources.pak) directory */
    CefString(&settings.resources_dir_path) = CefString();

    /* The locales directory */
    CefString(&settings.locales_dir_path) = CefString();

    /* Ignore errors related to invalid SSL certificates */
    settings.ignore_certificate_errors = false;

    /* Used when creating a windowless browser (off-screen rendering) */
    settings.windowless_rendering_enabled = true;

    #if !defined(CEF_USE_SANDBOX)
        settings.no_sandbox = true;
    #endif
}

/* Program entry-point function. */
int main(int argc, char* argv[])
{
    /* Structure for passing command-line arguments. */
    CefMainArgs main_args(argc, argv);

     int exit_code = CefExecuteProcess(main_args, NULL, NULL);
     if (exit_code >= 0)
     {
         /* The sub-process has completed so return here */
         return exit_code;
    }

    CefRefPtr<ClientApp> app(new ClientApp);

    /* Install xlib error handlers so that the application won't be terminated on non-fatal errors */
    XSetErrorHandler(XErrorHandlerImpl);
    XSetIOErrorHandler(XIOErrorHandlerImpl);

    /* Populate this structure to customize CEF behavior */
    CefSettings settings;
    CefInitSettings(settings);

    CefString(&settings.locales_dir_path) = "/home/dangelone/Downloads/temp/chrome-to-libbb/locales";
    CefString(&settings.resources_dir_path) = "/home/dangelone/Downloads/temp/chrome-to-libbb/";

    /* Initialize CEF in the main process. */
    CefInitialize(main_args, settings, app.get(), NULL);

    /* Run the CEF message loop. This will block until CefQuitMessageLoop() is called */
    CefRunMessageLoop();

    /* Shut down CEF */
    CefShutdown();

    return 0;
}


My issue is that when the OnPaint() callback is called my memcpy(...) function has a segmentation fault. This I because my buffer's size seems to be 8 for some reason. Does anyone see any issues with my code that would cause my OSR to result in an invalid buffer?
Voltageist
Techie
 
Posts: 16
Joined: Fri Jul 31, 2020 9:37 am

Re: Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Czarek » Sat Sep 12, 2020 4:28 am

You didn't allocate memory to my_buffer, so you're copying buffer to some random memory. Use malloc to allocate and realloc to extend it when buffer size changes.
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. My LinkedIn.
User avatar
Czarek
Virtuoso
 
Posts: 1927
Joined: Sun Nov 06, 2011 2:12 am

Re: Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Voltageist » Sat Sep 12, 2020 2:33 pm

Czarek wrote:You didn't allocate memory to my_buffer, so you're copying buffer to some random memory. Use malloc to allocate and realloc to extend it when buffer size changes.


Whoops, nice catch! Now that I've allocated memory for my buffer the memcpy(…) function works without a segmentation fault.

Now, when I print sizeof(buffer) in OnPaint(…) I still get a size of '8', is this correct? Because opening my saved BGRA file in Vooya results in an invalid image.
Voltageist
Techie
 
Posts: 16
Joined: Fri Jul 31, 2020 9:37 am

Re: Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Czarek » Sat Sep 12, 2020 4:37 pm

In your case it prints you size of the pointer which is 8 bytes on your system, but it can vary. See https://stackoverflow.com/questions/1281686/
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. My LinkedIn.
User avatar
Czarek
Virtuoso
 
Posts: 1927
Joined: Sun Nov 06, 2011 2:12 am

Re: Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Voltageist » Sun Sep 13, 2020 3:31 am

Czarek wrote:In your case it prints you size of the pointer which is 8 bytes on your system, but it can vary. See https://stackoverflow.com/questions/1281686/


I understand, thanks a lot! I'm a little newer to programming, still learning the ropes.

Do you see any issues with my code? My current implementation for SaveBuffer(…) results in an invalid image when viewed from Vooya.
Voltageist
Techie
 
Posts: 16
Joined: Fri Jul 31, 2020 9:37 am

Re: Off-Screen Rendering Results in Invalid OnPaint Buffer

Postby Czarek » Sun Sep 13, 2020 5:32 am

You need to write w * h * 4 bytes, but instead you use sizeof.
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. My LinkedIn.
User avatar
Czarek
Virtuoso
 
Posts: 1927
Joined: Sun Nov 06, 2011 2:12 am


Return to Support Forum

Who is online

Users browsing this forum: Google [Bot] and 24 guests