blob: b7432e0b51496d2cc328194367eaf69957f61323 [file] [log] [blame]
/*
* Copyright (C) 2020-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RemoteRenderingBackendProxy.h"
#if ENABLE(GPU_PROCESS)
#include "BufferIdentifierSet.h"
#include "GPUConnectionToWebProcess.h"
#include "Logging.h"
#include "PlatformRemoteImageBufferProxy.h"
#include "RemoteRenderingBackendMessages.h"
#include "RemoteRenderingBackendProxyMessages.h"
#include "SwapBuffersDisplayRequirement.h"
#include "WebCoreArgumentCoders.h"
#include "WebPage.h"
#include "WebProcess.h"
#include <JavaScriptCore/TypedArrayInlines.h>
#include <wtf/text/TextStream.h>
namespace WebKit {
using namespace WebCore;
std::unique_ptr<RemoteRenderingBackendProxy> RemoteRenderingBackendProxy::create(WebPage& webPage)
{
return std::unique_ptr<RemoteRenderingBackendProxy>(new RemoteRenderingBackendProxy(webPage));
}
RemoteRenderingBackendProxy::RemoteRenderingBackendProxy(WebPage& webPage)
: m_parameters {
RenderingBackendIdentifier::generate(),
webPage.webPageProxyIdentifier(),
webPage.identifier()
}
{
}
RemoteRenderingBackendProxy::~RemoteRenderingBackendProxy()
{
for (auto& markAsVolatileHandlers : m_markAsVolatileRequests.values())
markAsVolatileHandlers(false);
if (!m_gpuProcessConnection)
return;
m_gpuProcessConnection->connection().send(Messages::GPUConnectionToWebProcess::ReleaseRenderingBackend(renderingBackendIdentifier()), 0, IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply);
disconnectGPUProcess();
}
GPUProcessConnection& RemoteRenderingBackendProxy::ensureGPUProcessConnection()
{
if (!m_gpuProcessConnection) {
m_gpuProcessConnection = &WebProcess::singleton().ensureGPUProcessConnection();
m_gpuProcessConnection->addClient(*this);
static constexpr auto connectionBufferSize = 1 << 21;
auto [streamConnection, dedicatedConnectionClientIdentifer] = IPC::StreamClientConnection::createWithDedicatedConnection(*this, connectionBufferSize);
m_streamConnection = WTFMove(streamConnection);
m_streamConnection->open();
m_gpuProcessConnection->connection().send(Messages::GPUConnectionToWebProcess::CreateRenderingBackend(m_parameters, dedicatedConnectionClientIdentifer, m_streamConnection->streamBuffer()), 0, IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply);
}
return *m_gpuProcessConnection;
}
void RemoteRenderingBackendProxy::gpuProcessConnectionDidClose(GPUProcessConnection&)
{
if (!m_gpuProcessConnection)
return;
disconnectGPUProcess();
// Note: The cache will call back to this to setup a new connection.
m_remoteResourceCacheProxy.remoteResourceCacheWasDestroyed();
}
void RemoteRenderingBackendProxy::disconnectGPUProcess()
{
m_gpuProcessConnection->removeClient(*this);
m_gpuProcessConnection->messageReceiverMap().removeMessageReceiver(*this);
m_gpuProcessConnection = nullptr;
if (m_destroyGetPixelBufferSharedMemoryTimer.isActive())
m_destroyGetPixelBufferSharedMemoryTimer.stop();
m_getPixelBufferSharedMemory = nullptr;
m_renderingUpdateID = { };
m_didRenderingUpdateID = { };
m_streamConnection->invalidate();
m_streamConnection = nullptr;
}
RemoteRenderingBackendProxy::DidReceiveBackendCreationResult RemoteRenderingBackendProxy::waitForDidCreateImageBufferBackend()
{
if (!streamConnection().waitForAndDispatchImmediately<Messages::RemoteRenderingBackendProxy::DidCreateImageBufferBackend>(renderingBackendIdentifier(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives))
return DidReceiveBackendCreationResult::TimeoutOrIPCFailure;
return DidReceiveBackendCreationResult::ReceivedAnyResponse;
}
bool RemoteRenderingBackendProxy::waitForDidFlush()
{
return streamConnection().waitForAndDispatchImmediately<Messages::RemoteRenderingBackendProxy::DidFlush>(renderingBackendIdentifier(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives);
}
void RemoteRenderingBackendProxy::createRemoteImageBuffer(ImageBuffer& imageBuffer)
{
auto logicalSize = imageBuffer.logicalSize();
sendToStream(Messages::RemoteRenderingBackend::CreateImageBuffer(logicalSize, imageBuffer.renderingMode(), imageBuffer.renderingPurpose(), imageBuffer.resolutionScale(), imageBuffer.colorSpace(), imageBuffer.pixelFormat(), imageBuffer.renderingResourceIdentifier()));
}
RefPtr<ImageBuffer> RemoteRenderingBackendProxy::createImageBuffer(const FloatSize& size, RenderingMode renderingMode, RenderingPurpose purpose, float resolutionScale, const DestinationColorSpace& colorSpace, PixelFormat pixelFormat)
{
RefPtr<ImageBuffer> imageBuffer;
if (renderingMode == RenderingMode::Accelerated) {
// Unless DOM rendering is always enabled when any GPU process rendering is enabled,
// we need to create ImageBuffers for e.g. Canvas that are actually mapped into the
// Web Content process, so they can be painted into the tiles.
if (!WebProcess::singleton().shouldUseRemoteRenderingFor(RenderingPurpose::DOM))
imageBuffer = AcceleratedRemoteImageBufferMappedProxy::create(size, resolutionScale, colorSpace, pixelFormat, purpose, *this);
else
imageBuffer = AcceleratedRemoteImageBufferProxy::create(size, resolutionScale, colorSpace, pixelFormat, purpose, *this);
}
if (!imageBuffer)
imageBuffer = UnacceleratedRemoteImageBufferProxy::create(size, resolutionScale, colorSpace, pixelFormat, purpose, *this);
if (imageBuffer) {
createRemoteImageBuffer(*imageBuffer);
return imageBuffer;
}
return nullptr;
}
bool RemoteRenderingBackendProxy::getPixelBufferForImageBuffer(RenderingResourceIdentifier imageBuffer, const PixelBufferFormat& destinationFormat, const IntRect& srcRect, Span<uint8_t> result)
{
if (auto handle = updateSharedMemoryForGetPixelBuffer(result.size())) {
SharedMemory::IPCHandle ipcHandle { WTFMove(*handle), m_getPixelBufferSharedMemory->size() };
auto sendResult = sendSyncToStream(Messages::RemoteRenderingBackend::GetPixelBufferForImageBufferWithNewMemory(imageBuffer, ipcHandle, destinationFormat, srcRect),
Messages::RemoteRenderingBackend::GetPixelBufferForImageBufferWithNewMemory::Reply());
if (!sendResult)
return false;
} else {
if (!m_getPixelBufferSharedMemory)
return false;
auto sendResult = sendSyncToStream(Messages::RemoteRenderingBackend::GetPixelBufferForImageBuffer(imageBuffer, destinationFormat, srcRect), Messages::RemoteRenderingBackend::GetPixelBufferForImageBuffer::Reply());
if (!sendResult)
return false;
}
memcpy(result.data(), m_getPixelBufferSharedMemory->data(), result.size());
return true;
}
void RemoteRenderingBackendProxy::putPixelBufferForImageBuffer(RenderingResourceIdentifier imageBuffer, const PixelBuffer& pixelBuffer, const IntRect& srcRect, const IntPoint& destPoint, AlphaPremultiplication destFormat)
{
sendToStream(Messages::RemoteRenderingBackend::PutPixelBufferForImageBuffer(imageBuffer, IPC::PixelBufferReference { Ref { const_cast<PixelBuffer&>(pixelBuffer) } }, srcRect, destPoint, destFormat));
}
std::optional<SharedMemory::Handle> RemoteRenderingBackendProxy::updateSharedMemoryForGetPixelBuffer(size_t dataSize)
{
if (m_destroyGetPixelBufferSharedMemoryTimer.isActive())
m_destroyGetPixelBufferSharedMemoryTimer.stop();
if (m_getPixelBufferSharedMemory && dataSize <= m_getPixelBufferSharedMemory->size()) {
m_destroyGetPixelBufferSharedMemoryTimer.startOneShot(5_s);
return std::nullopt;
}
destroyGetPixelBufferSharedMemory();
auto memory = SharedMemory::allocate(dataSize);
if (!memory)
return std::nullopt;
SharedMemory::Handle handle;
if (!memory->createHandle(handle, SharedMemory::Protection::ReadWrite))
return std::nullopt;
if (handle.isNull())
return std::nullopt;
m_getPixelBufferSharedMemory = WTFMove(memory);
handle.takeOwnershipOfMemory(MemoryLedger::Graphics);
m_destroyGetPixelBufferSharedMemoryTimer.startOneShot(5_s);
return handle;
}
void RemoteRenderingBackendProxy::destroyGetPixelBufferSharedMemory()
{
if (!m_getPixelBufferSharedMemory)
return;
m_getPixelBufferSharedMemory = nullptr;
sendToStream(Messages::RemoteRenderingBackend::DestroyGetPixelBufferSharedMemory());
}
String RemoteRenderingBackendProxy::getDataURLForImageBuffer(const String& mimeType, std::optional<double> quality, PreserveResolution preserveResolution, RenderingResourceIdentifier renderingResourceIdentifier)
{
String urlString;
sendSyncToStream(Messages::RemoteRenderingBackend::GetDataURLForImageBuffer(mimeType, quality, preserveResolution, renderingResourceIdentifier), Messages::RemoteRenderingBackend::GetDataURLForImageBuffer::Reply(urlString));
return urlString;
}
Vector<uint8_t> RemoteRenderingBackendProxy::getDataForImageBuffer(const String& mimeType, std::optional<double> quality, RenderingResourceIdentifier renderingResourceIdentifier)
{
Vector<uint8_t> data;
sendSyncToStream(Messages::RemoteRenderingBackend::GetDataForImageBuffer(mimeType, quality, renderingResourceIdentifier), Messages::RemoteRenderingBackend::GetDataForImageBuffer::Reply(data));
return data;
}
RefPtr<ShareableBitmap> RemoteRenderingBackendProxy::getShareableBitmap(RenderingResourceIdentifier imageBuffer, PreserveResolution preserveResolution)
{
ShareableBitmap::Handle handle;
auto sendResult = sendSyncToStream(Messages::RemoteRenderingBackend::GetShareableBitmapForImageBuffer(imageBuffer, preserveResolution), Messages::RemoteRenderingBackend::GetShareableBitmapForImageBuffer::Reply(handle));
if (handle.isNull())
return { };
handle.takeOwnershipOfMemory(MemoryLedger::Graphics);
ASSERT_UNUSED(sendResult, sendResult);
return ShareableBitmap::create(handle);
}
RefPtr<Image> RemoteRenderingBackendProxy::getFilteredImage(RenderingResourceIdentifier imageBuffer, Filter& filter)
{
ShareableBitmap::Handle handle;
auto sendResult = sendSyncToStream(Messages::RemoteRenderingBackend::GetFilteredImageForImageBuffer(imageBuffer, IPC::FilterReference { filter }), Messages::RemoteRenderingBackend::GetFilteredImageForImageBuffer::Reply(handle));
ASSERT_UNUSED(sendResult, sendResult);
if (handle.isNull())
return { };
handle.takeOwnershipOfMemory(MemoryLedger::Graphics);
auto bitmap = ShareableBitmap::create(handle);
if (!bitmap)
return { };
return bitmap->createImage();
}
void RemoteRenderingBackendProxy::cacheNativeImage(const ShareableBitmap::Handle& handle, RenderingResourceIdentifier renderingResourceIdentifier)
{
sendToStream(Messages::RemoteRenderingBackend::CacheNativeImage(handle, renderingResourceIdentifier));
}
void RemoteRenderingBackendProxy::cacheFont(Ref<Font>&& font)
{
sendToStream(Messages::RemoteRenderingBackend::CacheFont(WTFMove(font)));
}
void RemoteRenderingBackendProxy::cacheDecomposedGlyphs(Ref<DecomposedGlyphs>&& decomposedGlyphs)
{
sendToStream(Messages::RemoteRenderingBackend::CacheDecomposedGlyphs(WTFMove(decomposedGlyphs)));
}
void RemoteRenderingBackendProxy::deleteAllFonts()
{
sendToStream(Messages::RemoteRenderingBackend::DeleteAllFonts());
}
void RemoteRenderingBackendProxy::releaseRemoteResource(RenderingResourceIdentifier renderingResourceIdentifier)
{
sendToStream(Messages::RemoteRenderingBackend::ReleaseRemoteResource(renderingResourceIdentifier));
}
auto RemoteRenderingBackendProxy::prepareBuffersForDisplay(const Vector<LayerPrepareBuffersData>& prepareBuffersInput) -> Vector<SwapBuffersResult>
{
if (prepareBuffersInput.isEmpty())
return { };
auto bufferIdentifier = [](ImageBuffer* buffer) -> std::optional<RenderingResourceIdentifier> {
if (!buffer)
return std::nullopt;
return buffer->renderingResourceIdentifier();
};
auto clearBackendHandle = [](ImageBuffer* buffer) {
if (!buffer)
return;
if (auto* backend = buffer->ensureBackendCreated()) {
auto* sharing = backend->toBackendSharing();
if (is<ImageBufferBackendHandleSharing>(sharing))
downcast<ImageBufferBackendHandleSharing>(*sharing).clearBackendHandle();
}
};
Vector<PrepareBackingStoreBuffersInputData> inputData;
inputData.reserveInitialCapacity(prepareBuffersInput.size());
for (const auto& perLayerData : prepareBuffersInput) {
// Clear all the buffer's MachSendRights to avoid all the surfaces appearing to be in-use.
// We get back the new front buffer's MachSendRight in the reply.
clearBackendHandle(perLayerData.buffers.front.get());
clearBackendHandle(perLayerData.buffers.back.get());
clearBackendHandle(perLayerData.buffers.secondaryBack.get());
inputData.uncheckedAppend({
{
bufferIdentifier(perLayerData.buffers.front.get()),
bufferIdentifier(perLayerData.buffers.back.get()),
bufferIdentifier(perLayerData.buffers.secondaryBack.get())
},
perLayerData.supportsPartialRepaint,
perLayerData.hasEmptyDirtyRegion
});
}
Vector<PrepareBackingStoreBuffersOutputData> outputData;
auto sendResult = sendSyncToStream(Messages::RemoteRenderingBackend::PrepareBuffersForDisplay(inputData), Messages::RemoteRenderingBackend::PrepareBuffersForDisplay::Reply(outputData));
if (!sendResult) {
// GPU Process crashed. Set the output data to all null buffers, requiring a full display.
outputData.resize(inputData.size());
for (auto& perLayerOutputData : outputData)
perLayerOutputData.displayRequirement = SwapBuffersDisplayRequirement::NeedsFullDisplay;
}
RELEASE_ASSERT_WITH_MESSAGE(inputData.size() == outputData.size(), "PrepareBuffersForDisplay: mismatched buffer vector sizes");
auto fetchBufferWithIdentifier = [&](std::optional<RenderingResourceIdentifier> identifier, std::optional<ImageBufferBackendHandle>&& handle = std::nullopt, bool isFrontBuffer = false) -> RefPtr<ImageBuffer> {
if (!identifier)
return nullptr;
auto* buffer = m_remoteResourceCacheProxy.cachedImageBuffer(*identifier);
if (!buffer)
return nullptr;
if (handle) {
if (auto* backend = buffer->ensureBackendCreated()) {
auto* sharing = backend->toBackendSharing();
if (is<ImageBufferBackendHandleSharing>(sharing))
downcast<ImageBufferBackendHandleSharing>(*sharing).setBackendHandle(WTFMove(*handle));
}
}
if (isFrontBuffer) {
// We know the GPU Process always sets the new front buffer to be non-volatile.
buffer->setVolatilityState(VolatilityState::NonVolatile);
}
return buffer;
};
Vector<SwapBuffersResult> result;
result.reserveInitialCapacity(outputData.size());
for (auto& perLayerOutputData : outputData) {
result.uncheckedAppend({
{
fetchBufferWithIdentifier(perLayerOutputData.bufferSet.front, WTFMove(perLayerOutputData.frontBufferHandle), true),
fetchBufferWithIdentifier(perLayerOutputData.bufferSet.back),
fetchBufferWithIdentifier(perLayerOutputData.bufferSet.secondaryBack)
},
perLayerOutputData.displayRequirement
});
}
return result;
}
void RemoteRenderingBackendProxy::markSurfacesVolatile(Vector<WebCore::RenderingResourceIdentifier>&& identifiers, CompletionHandler<void(bool)>&& completionHandler)
{
auto requestIdentifier = MarkSurfacesAsVolatileRequestIdentifier::generate();
m_markAsVolatileRequests.add(requestIdentifier, WTFMove(completionHandler));
sendToStream(Messages::RemoteRenderingBackend::MarkSurfacesVolatile(requestIdentifier, identifiers));
}
void RemoteRenderingBackendProxy::didMarkLayersAsVolatile(MarkSurfacesAsVolatileRequestIdentifier requestIdentifier, const Vector<WebCore::RenderingResourceIdentifier>& markedVolatileBufferIdentifiers, bool didMarkAllLayersAsVolatile)
{
ASSERT(requestIdentifier);
auto completionHandler = m_markAsVolatileRequests.take(requestIdentifier);
if (!completionHandler)
return;
for (auto identifier : markedVolatileBufferIdentifiers) {
auto imageBuffer = m_remoteResourceCacheProxy.cachedImageBuffer(identifier);
if (imageBuffer)
imageBuffer->setVolatilityState(WebCore::VolatilityState::Volatile);
}
completionHandler(didMarkAllLayersAsVolatile);
}
void RemoteRenderingBackendProxy::finalizeRenderingUpdate()
{
sendToStream(Messages::RemoteRenderingBackend::FinalizeRenderingUpdate(m_renderingUpdateID));
m_remoteResourceCacheProxy.finalizeRenderingUpdate();
m_renderingUpdateID.increment();
}
void RemoteRenderingBackendProxy::didCreateImageBufferBackend(ImageBufferBackendHandle handle, RenderingResourceIdentifier renderingResourceIdentifier)
{
auto imageBuffer = m_remoteResourceCacheProxy.cachedImageBuffer(renderingResourceIdentifier);
if (!imageBuffer)
return;
if (imageBuffer->renderingMode() == RenderingMode::Unaccelerated)
imageBuffer->setBackend(UnacceleratedImageBufferShareableBackend::create(imageBuffer->parameters(), WTFMove(handle)));
else if (imageBuffer->canMapBackingStore())
imageBuffer->setBackend(AcceleratedImageBufferShareableMappedBackend::create(imageBuffer->parameters(), WTFMove(handle)));
else
imageBuffer->setBackend(AcceleratedImageBufferRemoteBackend::create(imageBuffer->parameters(), WTFMove(handle)));
}
void RemoteRenderingBackendProxy::didFlush(GraphicsContextFlushIdentifier flushIdentifier, RenderingResourceIdentifier renderingResourceIdentifier)
{
if (auto imageBuffer = m_remoteResourceCacheProxy.cachedImageBuffer(renderingResourceIdentifier))
imageBuffer->didFlush(flushIdentifier);
}
void RemoteRenderingBackendProxy::didFinalizeRenderingUpdate(RenderingUpdateID didRenderingUpdateID)
{
ASSERT(didRenderingUpdateID <= m_renderingUpdateID);
m_didRenderingUpdateID = std::min(didRenderingUpdateID, m_renderingUpdateID);
}
RenderingBackendIdentifier RemoteRenderingBackendProxy::renderingBackendIdentifier() const
{
return m_parameters.identifier;
}
RenderingBackendIdentifier RemoteRenderingBackendProxy::ensureBackendCreated()
{
ensureGPUProcessConnection();
return renderingBackendIdentifier();
}
IPC::StreamClientConnection& RemoteRenderingBackendProxy::streamConnection()
{
ensureGPUProcessConnection();
if (UNLIKELY(!m_streamConnection->hasSemaphores()))
m_streamConnection->waitForAndDispatchImmediately<Messages::RemoteRenderingBackendProxy::DidInitialize>(renderingBackendIdentifier(), 3_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives);
return *m_streamConnection;
}
void RemoteRenderingBackendProxy::didInitialize(IPC::Semaphore&& wakeUp, IPC::Semaphore&& clientWait)
{
if (!m_streamConnection) {
ASSERT_NOT_REACHED();
return;
}
m_streamConnection->setSemaphores(WTFMove(wakeUp), WTFMove(clientWait));
}
bool RemoteRenderingBackendProxy::isCached(const ImageBuffer& imageBuffer) const
{
if (auto cachedImageBuffer = m_remoteResourceCacheProxy.cachedImageBuffer(imageBuffer.renderingResourceIdentifier())) {
ASSERT(cachedImageBuffer == &imageBuffer);
return true;
}
return false;
}
} // namespace WebKit
#endif // ENABLE(GPU_PROCESS)