⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 269809 in webkit


Ignore:
Timestamp:
Nov 13, 2020, 6:11:30 PM (6 years ago)
Author:
Chris Dumez
Message:

[GPUProcess] Add basic GPUProcess crash handling for canvas
https://bugs.webkit.org/show_bug.cgi?id=218924

Reviewed by Simon Fraser.

Source/WebKit:

Update RemoteRenderingBackendProxy to be a GPUProcessConnection::Client so that it
gets notified when the IPC connection to the GPUProcess gets severed. When this
happens, RemoteRenderingBackendProxy clears all its SharedMemory handles and
sends IPC messages to the GPU process to recreate all the ImageBuffers we had.

  • WebProcess/GPU/graphics/RemoteImageBufferProxy.h:

(WebKit::RemoteImageBufferProxy::create):
(WebKit::RemoteImageBufferProxy::clearBackend):
(WebKit::RemoteImageBufferProxy::size const):
(WebKit::RemoteImageBufferProxy::renderingMode const):
(WebKit::RemoteImageBufferProxy::colorSpace const):
(WebKit::RemoteImageBufferProxy::pixelFormat const):
(WebKit::RemoteImageBufferProxy::RemoteImageBufferProxy):

  • WebProcess/GPU/graphics/RemoteRenderingBackendProxy.cpp:

(WebKit::RemoteRenderingBackendProxy::RemoteRenderingBackendProxy):
(WebKit::RemoteRenderingBackendProxy::connectToGPUProcess):
(WebKit::recreateImage):
(WebKit::RemoteRenderingBackendProxy::gpuProcessConnectionDidClose):
(WebKit::RemoteRenderingBackendProxy::createImageBuffer):

  • WebProcess/GPU/graphics/RemoteRenderingBackendProxy.h:
  • WebProcess/GPU/graphics/RemoteResourceCacheProxy.h:

(WebKit::RemoteResourceCacheProxy::imageBuffers const):

Tools:

Add API test coverage.

  • TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm:

(convertToCGImage):
(getPixelIndex):
(TEST):

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r269808 r269809  
     12020-11-13  Chris Dumez  <cdumez@apple.com>
     2
     3        [GPUProcess] Add basic GPUProcess crash handling for canvas
     4        https://bugs.webkit.org/show_bug.cgi?id=218924
     5
     6        Reviewed by Simon Fraser.
     7
     8        Update RemoteRenderingBackendProxy to be a GPUProcessConnection::Client so that it
     9        gets notified when the IPC connection to the GPUProcess gets severed. When this
     10        happens, RemoteRenderingBackendProxy clears all its SharedMemory handles and
     11        sends IPC messages to the GPU process to recreate all the ImageBuffers we had.
     12
     13        * WebProcess/GPU/graphics/RemoteImageBufferProxy.h:
     14        (WebKit::RemoteImageBufferProxy::create):
     15        (WebKit::RemoteImageBufferProxy::clearBackend):
     16        (WebKit::RemoteImageBufferProxy::size const):
     17        (WebKit::RemoteImageBufferProxy::renderingMode const):
     18        (WebKit::RemoteImageBufferProxy::colorSpace const):
     19        (WebKit::RemoteImageBufferProxy::pixelFormat const):
     20        (WebKit::RemoteImageBufferProxy::RemoteImageBufferProxy):
     21        * WebProcess/GPU/graphics/RemoteRenderingBackendProxy.cpp:
     22        (WebKit::RemoteRenderingBackendProxy::RemoteRenderingBackendProxy):
     23        (WebKit::RemoteRenderingBackendProxy::connectToGPUProcess):
     24        (WebKit::recreateImage):
     25        (WebKit::RemoteRenderingBackendProxy::gpuProcessConnectionDidClose):
     26        (WebKit::RemoteRenderingBackendProxy::createImageBuffer):
     27        * WebProcess/GPU/graphics/RemoteRenderingBackendProxy.h:
     28        * WebProcess/GPU/graphics/RemoteResourceCacheProxy.h:
     29        (WebKit::RemoteResourceCacheProxy::imageBuffers const):
     30
    1312020-11-13  Chris Dumez  <cdumez@apple.com>
    232
  • trunk/Source/WebKit/WebProcess/GPU/graphics/RemoteImageBufferProxy.h

    r269753 r269809  
    4949
    5050public:
    51     static RefPtr<RemoteImageBufferProxy> create(const WebCore::FloatSize& size, float resolutionScale, RemoteRenderingBackendProxy& remoteRenderingBackendProxy)
     51    static RefPtr<RemoteImageBufferProxy> create(const WebCore::FloatSize& size, WebCore::RenderingMode renderingMode, float resolutionScale, WebCore::ColorSpace colorSpace, WebCore::PixelFormat pixelFormat, RemoteRenderingBackendProxy& remoteRenderingBackendProxy)
    5252    {
    5353        if (BackendType::calculateBackendSize(size, resolutionScale).isEmpty())
    5454            return nullptr;
    5555
    56         return adoptRef(new RemoteImageBufferProxy(size, remoteRenderingBackendProxy));
     56        return adoptRef(new RemoteImageBufferProxy(size, renderingMode, resolutionScale, colorSpace, pixelFormat, remoteRenderingBackendProxy));
    5757    }
    5858
     
    6666    }
    6767
     68    void clearBackend() { m_backend = nullptr; }
     69
    6870    void createBackend(const WebCore::FloatSize& logicalSize, const WebCore::IntSize& backendSize, float resolutionScale, WebCore::ColorSpace colorSpace, WebCore::PixelFormat pixelFormat, ImageBufferBackendHandle handle)
    6971    {
     
    7779    }
    7880
     81    const WebCore::FloatSize& size() const { return m_size; }
     82    WebCore::RenderingMode renderingMode() const { return m_renderingMode; }
     83    float resolutionScale() const final { return m_resolutionScale; }
     84    WebCore::ColorSpace colorSpace() const { return m_colorSpace; }
     85    WebCore::PixelFormat pixelFormat() const { return m_pixelFormat; }
     86
    7987protected:
    80     RemoteImageBufferProxy(const WebCore::FloatSize& size, RemoteRenderingBackendProxy& remoteRenderingBackendProxy)
     88    RemoteImageBufferProxy(const WebCore::FloatSize& size, WebCore::RenderingMode renderingMode, float resolutionScale, WebCore::ColorSpace colorSpace, WebCore::PixelFormat pixelFormat, RemoteRenderingBackendProxy& remoteRenderingBackendProxy)
    8189        : BaseDisplayListImageBuffer(size, this)
    8290        , m_remoteRenderingBackendProxy(makeWeakPtr(remoteRenderingBackendProxy))
     91        , m_size(size)
     92        , m_renderingMode(renderingMode)
     93        , m_resolutionScale(resolutionScale)
     94        , m_colorSpace(colorSpace)
     95        , m_pixelFormat(pixelFormat)
    8396    {
    8497        ASSERT(m_remoteRenderingBackendProxy);
     
    304317    WeakPtr<RemoteRenderingBackendProxy> m_remoteRenderingBackendProxy;
    305318    size_t m_itemCountInCurrentDisplayList { 0 };
     319    WebCore::FloatSize m_size;
     320    WebCore::RenderingMode m_renderingMode;
     321    float m_resolutionScale;
     322    WebCore::ColorSpace m_colorSpace;
     323    WebCore::PixelFormat m_pixelFormat;
    306324};
    307325
  • trunk/Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.cpp

    r269710 r269809  
    3131#include "DisplayListWriterHandle.h"
    3232#include "GPUConnectionToWebProcess.h"
    33 #include "GPUProcessConnection.h"
    3433#include "ImageDataReference.h"
    3534#include "PlatformRemoteImageBufferProxy.h"
     
    5049RemoteRenderingBackendProxy::RemoteRenderingBackendProxy()
    5150{
    52     // Register itself as a MessageReceiver in the GPUProcessConnection.
    53     IPC::MessageReceiverMap& messageReceiverMap = WebProcess::singleton().ensureGPUProcessConnection().messageReceiverMap();
    54     messageReceiverMap.addMessageReceiver(Messages::RemoteRenderingBackendProxy::messageReceiverName(), m_renderingBackendIdentifier.toUInt64(), *this);
    55 
    56     // Create the RemoteRenderingBackend
    57     send(Messages::GPUConnectionToWebProcess::CreateRenderingBackend(m_renderingBackendIdentifier), 0);
     51    connectToGPUProcess();
    5852}
    5953
     
    6862}
    6963
     64void RemoteRenderingBackendProxy::connectToGPUProcess()
     65{
     66    auto& connection = WebProcess::singleton().ensureGPUProcessConnection();
     67    connection.addClient(*this);
     68    connection.messageReceiverMap().addMessageReceiver(Messages::RemoteRenderingBackendProxy::messageReceiverName(), m_renderingBackendIdentifier.toUInt64(), *this);
     69
     70    send(Messages::GPUConnectionToWebProcess::CreateRenderingBackend(m_renderingBackendIdentifier), 0);
     71}
     72
     73template<typename T>
     74static void recreateImageBuffer(RemoteRenderingBackendProxy& proxy, T& imageBuffer, RenderingResourceIdentifier resourceIdentifier, RenderingBackendIdentifier renderingBackendIdentifier)
     75{
     76    imageBuffer.clearBackend();
     77    proxy.send(Messages::RemoteRenderingBackend::CreateImageBuffer(imageBuffer.size(), imageBuffer.renderingMode(), imageBuffer.resolutionScale(), imageBuffer.colorSpace(), imageBuffer.pixelFormat(), resourceIdentifier), renderingBackendIdentifier);
     78}
     79
     80void RemoteRenderingBackendProxy::reestablishGPUProcessConnection()
     81{
     82    connectToGPUProcess();
     83
     84    for (auto& pair : m_remoteResourceCacheProxy.imageBuffers()) {
     85        if (auto& baseImageBuffer = pair.value) {
     86            if (is<AcceleratedRemoteImageBufferProxy>(*baseImageBuffer))
     87                recreateImageBuffer(*this, downcast<AcceleratedRemoteImageBufferProxy>(*baseImageBuffer), pair.key, m_renderingBackendIdentifier);
     88            else
     89                recreateImageBuffer(*this, downcast<UnacceleratedRemoteImageBufferProxy>(*baseImageBuffer), pair.key, m_renderingBackendIdentifier);
     90        }
     91    }
     92}
     93
     94void RemoteRenderingBackendProxy::gpuProcessConnectionDidClose(GPUProcessConnection& previousConnection)
     95{
     96    previousConnection.removeClient(*this);
     97
     98    m_identifiersOfReusableHandles.clear();
     99    m_identifiersOfHandlesAvailableForWriting.clear();
     100    m_sharedDisplayListHandles.clear();
     101
     102    reestablishGPUProcessConnection();
     103}
     104
    70105IPC::Connection* RemoteRenderingBackendProxy::messageSenderConnection() const
    71106{
     
    95130
    96131    if (renderingMode == RenderingMode::Accelerated)
    97         imageBuffer = AcceleratedRemoteImageBufferProxy::create(size, resolutionScale, *this);
     132        imageBuffer = AcceleratedRemoteImageBufferProxy::create(size, renderingMode, resolutionScale, colorSpace, pixelFormat, *this);
    98133
    99134    if (!imageBuffer)
    100         imageBuffer = UnacceleratedRemoteImageBufferProxy::create(size, resolutionScale, *this);
     135        imageBuffer = UnacceleratedRemoteImageBufferProxy::create(size, renderingMode, resolutionScale, colorSpace, pixelFormat, *this);
    101136
    102137    if (imageBuffer) {
  • trunk/Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.h

    r269710 r269809  
    2828#if ENABLE(GPU_PROCESS)
    2929
     30#include "GPUProcessConnection.h"
    3031#include "ImageBufferBackendHandle.h"
    3132#include "MessageReceiver.h"
     
    5758    : public IPC::MessageSender
    5859    , private IPC::MessageReceiver
    59     , public CanMakeWeakPtr<RemoteRenderingBackendProxy> {
     60    , public GPUProcessConnection::Client {
    6061public:
    6162    static std::unique_ptr<RemoteRenderingBackendProxy> create();
     
    8788    RemoteRenderingBackendProxy();
    8889
     90    // GPUProcessConnection::Client
     91    void gpuProcessConnectionDidClose(GPUProcessConnection&) final;
     92
     93    void connectToGPUProcess();
     94    void reestablishGPUProcessConnection();
    8995    void updateReusableHandles();
    9096
  • trunk/Source/WebKit/WebProcess/GPU/graphics/RemoteResourceCacheProxy.h

    r269708 r269809  
    5050    void cacheNativeImage(WebCore::NativeImage&);
    5151
     52    using ImageBufferHashMap = HashMap<WebCore::RenderingResourceIdentifier, WeakPtr<WebCore::ImageBuffer>>;
     53    const ImageBufferHashMap& imageBuffers() const { return m_imageBuffers; }
     54
    5255private:
    53     using ImageBufferHashMap = HashMap<WebCore::RenderingResourceIdentifier, WeakPtr<WebCore::ImageBuffer>>;
    5456    using NativeImageHashMap = HashMap<WebCore::RenderingResourceIdentifier, WeakPtr<WebCore::NativeImage>>;
    5557   
  • trunk/Tools/ChangeLog

    r269804 r269809  
     12020-11-13  Chris Dumez  <cdumez@apple.com>
     2
     3        [GPUProcess] Add basic GPUProcess crash handling for canvas
     4        https://bugs.webkit.org/show_bug.cgi?id=218924
     5
     6        Reviewed by Simon Fraser.
     7
     8        Add API test coverage.
     9
     10        * TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm:
     11        (convertToCGImage):
     12        (getPixelIndex):
     13        (TEST):
     14
    1152020-11-13  Aakash Jain  <aakash_jain@apple.com>
    216
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm

    r269750 r269809  
    3636#import <wtf/RetainPtr.h>
    3737
     38#if PLATFORM(MAC)
     39typedef NSImage *PlatformImage;
     40typedef NSWindow *PlatformWindow;
     41
     42static RetainPtr<CGImageRef> convertToCGImage(NSImage *image)
     43{
     44    return [image CGImageForProposedRect:nil context:nil hints:nil];
     45}
     46
     47#else
     48typedef UIImage *PlatformImage;
     49typedef UIWindow *PlatformWindow;
     50
     51static RetainPtr<CGImageRef> convertToCGImage(UIImage *image)
     52{
     53    return image.CGImage;
     54}
     55#endif
     56
     57static NSInteger getPixelIndex(NSInteger x, NSInteger y, NSInteger width)
     58{
     59    return (y * width + x) * 4;
     60}
     61
    3862TEST(GPUProcess, RelaunchOnCrash)
    3963{
     
    261285    EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]);
    262286}
     287
     288static NSString *testCanvasPage = @"<body> \n"
     289    "<canvas id='myCanvas' width='400px' height='400px'>\n"
     290    "<script> \n"
     291    "var context = document.getElementById('myCanvas').getContext('2d'); \n"
     292    "</script> \n"
     293    "</body>";
     294
     295TEST(GPUProcess, CanvasBasicCrashHandling)
     296{
     297    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     298    for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) {
     299        if ([feature.key isEqualToString:@"UseGPUProcessForCanvasRenderingEnabled"]) {
     300            [[configuration preferences] _setEnabled:YES forInternalDebugFeature:feature];
     301            break;
     302        }
     303    }
     304
     305    NSInteger viewWidth = 400;
     306    NSInteger viewHeight = 400;
     307    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, viewWidth, viewHeight) configuration:configuration.get() addToWindow:NO]);
     308
     309    RetainPtr<PlatformWindow> window;
     310    CGFloat backingScaleFactor;
     311
     312#if PLATFORM(MAC)
     313    window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
     314    [[window contentView] addSubview:webView.get()];
     315    backingScaleFactor = [window backingScaleFactor];
     316#elif PLATFORM(IOS_FAMILY)
     317    window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
     318    [window addSubview:webView.get()];
     319    backingScaleFactor = [[window screen] scale];
     320#endif
     321
     322    [webView synchronouslyLoadHTMLString:testCanvasPage];
     323
     324    auto webViewPID = [webView _webProcessIdentifier];
     325
     326    // Try painting the canvas red.
     327    __block bool done = false;
     328    [webView evaluateJavaScript:@"context.fillStyle = '#FF0000'; context.fillRect(0, 0, 400, 400);" completionHandler:^(id result, NSError *error) {
     329        EXPECT_TRUE(!error);
     330        done = true;
     331    }];
     332    TestWebKitAPI::Util::run(&done);
     333
     334    [webView waitForNextPresentationUpdate];
     335
     336    // The GPU process should have been launched.
     337    auto* processPool = configuration.get().processPool;
     338    unsigned timeout = 0;
     339    while (![processPool _gpuProcessIdentifier] && timeout++ < 100)
     340        TestWebKitAPI::Util::sleep(0.1);
     341    auto gpuProcessPID = [processPool _gpuProcessIdentifier];
     342    EXPECT_NE(0, gpuProcessPID);
     343
     344    auto snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
     345    [snapshotConfiguration setRect:NSMakeRect(0, 0, 150, 150)];
     346    [snapshotConfiguration setSnapshotWidth:@(150)];
     347    [snapshotConfiguration setAfterScreenUpdates:YES];
     348
     349    // Make sure a red square is painted.
     350    done = false;
     351    [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
     352        EXPECT_TRUE(!error);
     353
     354        RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
     355        RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
     356
     357        NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
     358        NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
     359
     360        uint8_t *rgba = (unsigned char *)calloc(viewWidthInPixels * viewHeightInPixels * 4, sizeof(unsigned char));
     361        RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
     362        CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
     363
     364        NSInteger pixelIndex = getPixelIndex(50, 50, viewWidthInPixels);
     365        EXPECT_EQ(255, rgba[pixelIndex]);
     366        EXPECT_EQ(0, rgba[pixelIndex + 1]);
     367        EXPECT_EQ(0, rgba[pixelIndex + 2]);
     368
     369        pixelIndex = getPixelIndex(100, 100, viewWidthInPixels);
     370        EXPECT_EQ(255, rgba[pixelIndex]);
     371        EXPECT_EQ(0, rgba[pixelIndex + 1]);
     372        EXPECT_EQ(0, rgba[pixelIndex + 2]);
     373
     374        free(rgba);
     375
     376        done = true;
     377    }];
     378    TestWebKitAPI::Util::run(&done);
     379
     380    // Kill the GPUProcess.
     381    kill(gpuProcessPID, 9);
     382
     383    // GPU Process should get relaunched.
     384    timeout = 0;
     385    while ((![processPool _gpuProcessIdentifier] || [processPool _gpuProcessIdentifier] == gpuProcessPID) && timeout++ < 100)
     386        TestWebKitAPI::Util::sleep(0.1);
     387    EXPECT_NE([processPool _gpuProcessIdentifier], 0);
     388    EXPECT_NE([processPool _gpuProcessIdentifier], gpuProcessPID);
     389    gpuProcessPID = [processPool _gpuProcessIdentifier];
     390
     391    // WebProcess should not have crashed.
     392    EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]);
     393
     394    // Try painting the canvas green.
     395    done = false;
     396    [webView evaluateJavaScript:@"context.fillStyle = '#00FF00'; context.fillRect(0, 0, 400, 400);" completionHandler:^(id result, NSError *error) {
     397        EXPECT_TRUE(!error);
     398        done = true;
     399    }];
     400    TestWebKitAPI::Util::run(&done);
     401
     402    [webView waitForNextPresentationUpdate];
     403
     404    // Make sure a green square is painted.
     405    done = false;
     406    [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
     407        EXPECT_TRUE(!error);
     408
     409        RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
     410        RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
     411
     412        NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
     413        NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
     414
     415        uint8_t *rgba = (unsigned char *)calloc(viewWidthInPixels * viewHeightInPixels * 4, sizeof(unsigned char));
     416        RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
     417        CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
     418
     419        NSInteger pixelIndex = getPixelIndex(50, 50, viewWidthInPixels);
     420        EXPECT_EQ(0, rgba[pixelIndex]);
     421        EXPECT_EQ(255, rgba[pixelIndex + 1]);
     422        EXPECT_EQ(0, rgba[pixelIndex + 2]);
     423
     424        pixelIndex = getPixelIndex(100, 100, viewWidthInPixels);
     425        EXPECT_EQ(0, rgba[pixelIndex]);
     426        EXPECT_EQ(255, rgba[pixelIndex + 1]);
     427        EXPECT_EQ(0, rgba[pixelIndex + 2]);
     428
     429        free(rgba);
     430
     431        done = true;
     432    }];
     433    TestWebKitAPI::Util::run(&done);
     434}
Note: See TracChangeset for help on using the changeset viewer.