| /* |
| * 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 "RemoteImageBuffer.h" |
| |
| #if ENABLE(GPU_PROCESS) |
| |
| #include "IPCSemaphore.h" |
| #include "ImageBufferBackendHandleSharing.h" |
| #include "Logging.h" |
| #include "RemoteImageBufferGraphicsContext.h" |
| #include "RemoteImageBufferMessages.h" |
| #include "RemoteImageBufferProxyMessages.h" |
| #include "RemoteRenderingBackend.h" |
| #include "RemoteSharedResourceCache.h" |
| #include "StreamConnectionWorkQueue.h" |
| #include <WebCore/GraphicsContext.h> |
| #include <WebCore/RenderingResourceIdentifier.h> |
| #include <wtf/StdLibExtras.h> |
| |
| #define MESSAGE_CHECK(assertion, message) MESSAGE_CHECK_WITH_MESSAGE_BASE(assertion, m_renderingBackend->streamConnection(), message) |
| |
| namespace WebKit { |
| |
| using namespace WebCore; |
| |
| Ref<RemoteImageBuffer> RemoteImageBuffer::create(Ref<WebCore::ImageBuffer>&& imageBuffer, WebCore::RenderingResourceIdentifier identifier, RemoteGraphicsContextIdentifier contextIdentifier, RemoteRenderingBackend& renderingBackend) |
| { |
| auto instance = adoptRef(*new RemoteImageBuffer(WTF::move(imageBuffer), identifier, contextIdentifier, renderingBackend)); |
| instance->startListeningForIPC(); |
| return instance; |
| } |
| |
| RemoteImageBuffer::RemoteImageBuffer(Ref<WebCore::ImageBuffer>&& imageBuffer, WebCore::RenderingResourceIdentifier identifier, RemoteGraphicsContextIdentifier contextIdentifier, RemoteRenderingBackend& renderingBackend) |
| : m_imageBuffer(WTF::move(imageBuffer)) |
| , m_identifier(identifier) |
| , m_renderingBackend(renderingBackend) |
| , m_context(RemoteImageBufferGraphicsContext::create(m_imageBuffer, contextIdentifier, m_renderingBackend)) |
| { |
| m_renderingBackend->sharedResourceCache().didCreateImageBuffer(m_imageBuffer->renderingPurpose(), m_imageBuffer->renderingMode()); |
| |
| // If the ImageBuffer was an error buffer, this will fail and nullopt will be sent, signaling |
| // allocation failure. |
| auto* sharing = m_imageBuffer->toBackendSharing(); |
| auto handle = sharing ? downcast<ImageBufferBackendHandleSharing>(*sharing).createBackendHandle() : std::nullopt; |
| m_renderingBackend->streamConnection().send(Messages::RemoteImageBufferProxy::DidCreateBackend(WTF::move(handle)), m_identifier); |
| } |
| |
| RemoteImageBuffer::~RemoteImageBuffer() |
| { |
| m_renderingBackend->sharedResourceCache().didReleaseImageBuffer(m_imageBuffer->renderingPurpose(), m_imageBuffer->renderingMode()); |
| // Volatile image buffers do not have contexts. |
| if (m_imageBuffer->volatilityState() == WebCore::VolatilityState::Volatile) |
| return; |
| if (!m_imageBuffer->hasBackend()) |
| return; |
| // Unwind the context's state stack before destruction, since calls to restore may not have |
| // been flushed yet, or the web process may have terminated. |
| auto& context = m_imageBuffer->context(); |
| while (context.stackSize()) |
| context.restore(); |
| } |
| |
| void RemoteImageBuffer::startListeningForIPC() |
| { |
| m_renderingBackend->streamConnection().startReceivingMessages(*this, Messages::RemoteImageBuffer::messageReceiverName(), m_identifier.toUInt64()); |
| } |
| |
| void RemoteImageBuffer::stopListeningForIPC() |
| { |
| m_context.reset(); |
| m_renderingBackend->streamConnection().stopReceivingMessages(Messages::RemoteImageBuffer::messageReceiverName(), m_identifier.toUInt64()); |
| } |
| |
| Ref<WebCore::ImageBuffer> RemoteImageBuffer::sinkIntoImageBuffer(Ref<RemoteImageBuffer>&& remote) |
| { |
| Ref localRemote = WTF::move(remote); |
| RELEASE_ASSERT(localRemote->hasOneRef()); |
| Ref imageBuffer = localRemote->m_imageBuffer; |
| return imageBuffer; |
| } |
| |
| void RemoteImageBuffer::getPixelBuffer(WebCore::PixelBufferFormat destinationFormat, WebCore::IntPoint srcPoint, WebCore::IntSize srcSize, CompletionHandler<void()>&& completionHandler) |
| { |
| assertIsCurrent(workQueue()); |
| auto memory = m_renderingBackend->sharedMemoryForGetPixelBuffer(); |
| MESSAGE_CHECK(memory, "No shared memory for getPixelBufferForImageBuffer"); |
| MESSAGE_CHECK(WebCore::ImageBuffer::supportedPixelBufferFormats(destinationFormat.pixelFormat), "Pixel format not supported"); |
| MESSAGE_CHECK(m_imageBuffer->renderingMode() != RenderingMode::PDFDocument && m_imageBuffer->renderingMode() != RenderingMode::DisplayList, "Backend does not hold pixels"); |
| MESSAGE_CHECK(m_imageBuffer->renderingPurpose() != RenderingPurpose::LayerBacking, "We should not interact with the pixelBuffer for LayerBacking"); |
| WebCore::IntRect srcRect(srcPoint, srcSize); |
| if (auto pixelBuffer = m_imageBuffer->getPixelBuffer(destinationFormat, srcRect)) { |
| MESSAGE_CHECK(pixelBuffer->bytes().size() <= memory->size(), "Shmem for return of getPixelBuffer is too small"); |
| memcpySpan(memory->mutableSpan(), pixelBuffer->bytes()); |
| } else |
| zeroSpan(memory->mutableSpan()); |
| completionHandler(); |
| } |
| |
| void RemoteImageBuffer::getPixelBufferWithNewMemory(WebCore::SharedMemory::Handle&& handle, WebCore::PixelBufferFormat destinationFormat, WebCore::IntPoint srcPoint, WebCore::IntSize srcSize, CompletionHandler<void()>&& completionHandler) |
| { |
| assertIsCurrent(workQueue()); |
| m_renderingBackend->setSharedMemoryForGetPixelBuffer(nullptr); |
| auto sharedMemory = WebCore::SharedMemory::map(WTF::move(handle), WebCore::SharedMemory::Protection::ReadWrite); |
| MESSAGE_CHECK(sharedMemory, "Shared memory could not be mapped."); |
| m_renderingBackend->setSharedMemoryForGetPixelBuffer(WTF::move(sharedMemory)); |
| getPixelBuffer(WTF::move(destinationFormat), WTF::move(srcPoint), WTF::move(srcSize), WTF::move(completionHandler)); |
| } |
| |
| void RemoteImageBuffer::putPixelBuffer(const WebCore::PixelBufferSourceView& pixelBuffer, WebCore::IntPoint srcPoint, WebCore::IntSize srcSize, WebCore::IntPoint destPoint, WebCore::AlphaPremultiplication destFormat) |
| { |
| assertIsCurrent(workQueue()); |
| |
| MESSAGE_CHECK(m_imageBuffer->resolutionScale() == 1, "putPixelBuffer() should not be called if (resolutionScale() != 1)"); |
| MESSAGE_CHECK(m_imageBuffer->renderingMode() != RenderingMode::PDFDocument && m_imageBuffer->renderingMode() != RenderingMode::DisplayList, "Backend does not hold pixels"); |
| MESSAGE_CHECK(m_imageBuffer->renderingPurpose() != RenderingPurpose::LayerBacking, "We should not interact with the pixelBuffer for LayerBacking"); |
| |
| WebCore::IntRect srcRect(srcPoint, srcSize); |
| m_imageBuffer->putPixelBuffer(pixelBuffer, srcRect, destPoint, destFormat); |
| } |
| |
| void RemoteImageBuffer::copyNativeImage(RenderingResourceIdentifier imageIdentifier) |
| { |
| assertIsCurrent(workQueue()); |
| RefPtr image = m_imageBuffer->copyNativeImage(); |
| // FIXME: Handle OOM. |
| MESSAGE_CHECK(image, "OOM"); |
| bool success = m_renderingBackend->remoteResourceCache().cacheNativeImage(imageIdentifier, image.releaseNonNull()); |
| MESSAGE_CHECK(success, "NativeImage already exists"); |
| } |
| |
| void RemoteImageBuffer::filteredNativeImage(Ref<WebCore::Filter> filter, CompletionHandler<void(std::optional<WebCore::ShareableBitmap::Handle>&&)>&& completionHandler) |
| { |
| assertIsCurrent(workQueue()); |
| std::optional<WebCore::ShareableBitmap::Handle> handle = [&]() -> std::optional<WebCore::ShareableBitmap::Handle> { |
| Ref imageBuffer = m_imageBuffer; |
| auto image = imageBuffer->filteredNativeImage(filter); |
| if (!image) |
| return std::nullopt; |
| auto imageSize = image->size(); |
| auto bitmap = WebCore::ShareableBitmap::create({ imageSize, imageBuffer->colorSpace() }); |
| if (!bitmap) |
| return std::nullopt; |
| auto handle = bitmap->createHandle(); |
| if (m_renderingBackend->sharedResourceCache().resourceOwner()) |
| handle->setOwnershipOfMemory(m_renderingBackend->sharedResourceCache().resourceOwner(), WebCore::MemoryLedger::Graphics); |
| auto context = bitmap->createGraphicsContext(); |
| if (!context) |
| return std::nullopt; |
| context->drawNativeImage(*image, WebCore::FloatRect { { }, imageSize }, WebCore::FloatRect { { }, imageSize }); |
| return handle; |
| }(); |
| completionHandler(WTF::move(handle)); |
| } |
| |
| void RemoteImageBuffer::convertToLuminanceMask() |
| { |
| assertIsCurrent(workQueue()); |
| m_imageBuffer->convertToLuminanceMask(); |
| } |
| |
| void RemoteImageBuffer::transformToColorSpace(const WebCore::ColorSpace& colorSpace) |
| { |
| assertIsCurrent(workQueue()); |
| m_imageBuffer->transformToColorSpace(colorSpace); |
| } |
| |
| void RemoteImageBuffer::setFlushSignal(IPC::Signal&& signal) |
| { |
| m_flushSignal = WTF::move(signal); |
| } |
| |
| void RemoteImageBuffer::flushContext() |
| { |
| MESSAGE_CHECK(m_flushSignal, "Cannot flush context without a valid flush signal. Use FlushContextSync for synchronous flushing."); |
| assertIsCurrent(workQueue()); |
| m_imageBuffer->flushDrawingContext(); |
| m_flushSignal->signal(); |
| } |
| |
| void RemoteImageBuffer::flushContextSync(CompletionHandler<void()>&& completionHandler) |
| { |
| assertIsCurrent(workQueue()); |
| m_imageBuffer->flushDrawingContext(); |
| completionHandler(); |
| } |
| |
| #if ENABLE(RE_DYNAMIC_CONTENT_SCALING) |
| void RemoteImageBuffer::dynamicContentScalingDisplayList(CompletionHandler<void(std::optional<WebCore::DynamicContentScalingDisplayList>&&)>&& completionHandler) |
| { |
| assertIsCurrent(workQueue()); |
| auto displayList = m_imageBuffer->dynamicContentScalingDisplayList(); |
| completionHandler({ WTF::move(displayList) }); |
| } |
| #endif |
| |
| IPC::StreamConnectionWorkQueue& RemoteImageBuffer::workQueue() const |
| { |
| return m_renderingBackend->workQueue(); |
| } |
| |
| } // namespace WebKit |
| |
| #undef MESSAGE_CHECK |
| |
| #endif // ENABLE(GPU_PROCESS) |