blob: 16c45b36dcad2d0c56fd0dd5ef183c5e74b1dc6e [file] [log] [blame]
/*
* Copyright (C) 2020-2021 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 "RemoteGraphicsContextGL.h"
#if ENABLE(GPU_PROCESS) && ENABLE(WEBGL)
#include "GPUConnectionToWebProcess.h"
#include "QualifiedRenderingResourceIdentifier.h"
#include "RemoteGraphicsContextGLMessages.h"
#include "RemoteGraphicsContextGLProxyMessages.h"
#include "StreamConnectionWorkQueue.h"
#include <WebCore/GraphicsContextGLOpenGL.h>
#include <WebCore/NotImplemented.h>
#include <wtf/MainThread.h>
#include <wtf/NeverDestroyed.h>
#if ENABLE(VIDEO)
#include "RemoteVideoFrameObjectHeap.h"
#endif
namespace WebKit {
using namespace WebCore;
namespace {
template<typename S, int I, typename T>
Vector<S> vectorCopyCast(const T& arrayReference)
{
return { reinterpret_cast<const S*>(arrayReference.template data<I>()), arrayReference.size() };
}
}
// Currently we have one global WebGL processing instance.
IPC::StreamConnectionWorkQueue& remoteGraphicsContextGLStreamWorkQueue()
{
static NeverDestroyed<IPC::StreamConnectionWorkQueue> instance("RemoteGraphicsContextGL work queue");
return instance.get();
}
#if !PLATFORM(COCOA) && !PLATFORM(WIN) && !USE(LIBGBM)
Ref<RemoteGraphicsContextGL> RemoteGraphicsContextGL::create(GPUConnectionToWebProcess& gpuConnectionToWebProcess, GraphicsContextGLAttributes&& attributes, GraphicsContextGLIdentifier graphicsContextGLIdentifier, RemoteRenderingBackend& renderingBackend, IPC::StreamConnectionBuffer&& stream)
{
ASSERT_NOT_REACHED();
auto instance = adoptRef(*new RemoteGraphicsContextGL(gpuConnectionToWebProcess, graphicsContextGLIdentifier, renderingBackend, WTFMove(stream)));
instance->initialize(WTFMove(attributes));
return instance;
}
#endif
RemoteGraphicsContextGL::RemoteGraphicsContextGL(GPUConnectionToWebProcess& gpuConnectionToWebProcess, GraphicsContextGLIdentifier graphicsContextGLIdentifier, RemoteRenderingBackend& renderingBackend, IPC::StreamConnectionBuffer&& stream)
: m_gpuConnectionToWebProcess(gpuConnectionToWebProcess)
, m_streamConnection(IPC::StreamServerConnection::create(gpuConnectionToWebProcess.connection(), WTFMove(stream), remoteGraphicsContextGLStreamWorkQueue()))
, m_graphicsContextGLIdentifier(graphicsContextGLIdentifier)
, m_renderingBackend(renderingBackend)
#if ENABLE(VIDEO)
, m_videoFrameObjectHeap(gpuConnectionToWebProcess.videoFrameObjectHeap())
#endif
, m_renderingResourcesRequest(ScopedWebGLRenderingResourcesRequest::acquire())
, m_webProcessIdentifier(gpuConnectionToWebProcess.webProcessIdentifier())
{
assertIsMainRunLoop();
}
RemoteGraphicsContextGL::~RemoteGraphicsContextGL()
{
ASSERT(!m_streamConnection);
ASSERT(!m_context);
}
void RemoteGraphicsContextGL::initialize(GraphicsContextGLAttributes&& attributes)
{
assertIsMainRunLoop();
m_streamConnection->open();
remoteGraphicsContextGLStreamWorkQueue().dispatch([attributes = WTFMove(attributes), protectedThis = Ref { *this }]() mutable {
protectedThis->workQueueInitialize(WTFMove(attributes));
});
m_streamConnection->startReceivingMessages(*this, Messages::RemoteGraphicsContextGL::messageReceiverName(), m_graphicsContextGLIdentifier.toUInt64());
}
void RemoteGraphicsContextGL::stopListeningForIPC(Ref<RemoteGraphicsContextGL>&& refFromConnection)
{
assertIsMainRunLoop();
m_streamConnection->invalidate();
m_streamConnection->stopReceivingMessages(Messages::RemoteGraphicsContextGL::messageReceiverName(), m_graphicsContextGLIdentifier.toUInt64());
remoteGraphicsContextGLStreamWorkQueue().dispatch([protectedThis = WTFMove(refFromConnection)]() {
protectedThis->workQueueUninitialize();
protectedThis->m_renderingResourcesRequest = { };
});
}
#if PLATFORM(MAC)
void RemoteGraphicsContextGL::displayWasReconfigured()
{
assertIsMainRunLoop();
remoteGraphicsContextGLStreamWorkQueue().dispatch([protectedThis = Ref { *this }]() {
assertIsCurrent(protectedThis->workQueue());
protectedThis->m_context->updateContextOnDisplayReconfiguration();
});
}
#endif
void RemoteGraphicsContextGL::workQueueInitialize(WebCore::GraphicsContextGLAttributes&& attributes)
{
assertIsCurrent(workQueue());
platformWorkQueueInitialize(WTFMove(attributes));
if (m_context) {
m_context->setClient(this);
String extensions = m_context->getString(GraphicsContextGL::EXTENSIONS);
String requestableExtensions = m_context->getString(GraphicsContextGL::REQUESTABLE_EXTENSIONS_ANGLE);
send(Messages::RemoteGraphicsContextGLProxy::WasCreated(true, remoteGraphicsContextGLStreamWorkQueue().wakeUpSemaphore(), m_streamConnection->clientWaitSemaphore(), extensions, requestableExtensions));
} else
send(Messages::RemoteGraphicsContextGLProxy::WasCreated(false, { }, { }, emptyString(), emptyString()));
}
void RemoteGraphicsContextGL::workQueueUninitialize()
{
assertIsCurrent(workQueue());
m_context->setClient(nullptr);
m_context = nullptr;
m_streamConnection = nullptr;
}
void RemoteGraphicsContextGL::didComposite()
{
assertIsCurrent(workQueue());
}
void RemoteGraphicsContextGL::forceContextLost()
{
assertIsCurrent(workQueue());
send(Messages::RemoteGraphicsContextGLProxy::WasLost());
}
void RemoteGraphicsContextGL::dispatchContextChangedNotification()
{
assertIsCurrent(workQueue());
send(Messages::RemoteGraphicsContextGLProxy::WasChanged());
}
void RemoteGraphicsContextGL::reshape(int32_t width, int32_t height)
{
assertIsCurrent(workQueue());
if (width && height)
m_context->reshape(width, height);
else
forceContextLost();
}
#if !PLATFORM(COCOA) && !PLATFORM(WIN) && !USE(LIBGBM)
void RemoteGraphicsContextGL::prepareForDisplay(CompletionHandler<void()>&& completionHandler)
{
assertIsCurrent(workQueue());
notImplemented();
completionHandler();
}
#endif
void RemoteGraphicsContextGL::synthesizeGLError(uint32_t error)
{
assertIsCurrent(workQueue());
m_context->synthesizeGLError(static_cast<GCGLenum>(error));
}
void RemoteGraphicsContextGL::getError(CompletionHandler<void(uint32_t)>&& completionHandler)
{
assertIsCurrent(workQueue());
completionHandler(static_cast<uint32_t>(m_context->getError()));
}
void RemoteGraphicsContextGL::ensureExtensionEnabled(String&& extension)
{
assertIsCurrent(workQueue());
m_context->ensureExtensionEnabled(extension);
}
void RemoteGraphicsContextGL::markContextChanged()
{
assertIsCurrent(workQueue());
m_context->markContextChanged();
}
void RemoteGraphicsContextGL::paintRenderingResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer, CompletionHandler<void()>&& completionHandler)
{
// Immediately turn the RenderingResourceIdentifier (which is error-prone) to a QualifiedRenderingResourceIdentifier,
// and use a helper function to make sure that don't accidentally use the RenderingResourceIdentifier (because the helper function can't see it).
paintRenderingResultsToCanvasWithQualifiedIdentifier({ imageBuffer, m_webProcessIdentifier }, WTFMove(completionHandler));
}
void RemoteGraphicsContextGL::paintRenderingResultsToCanvasWithQualifiedIdentifier(QualifiedRenderingResourceIdentifier imageBuffer, CompletionHandler<void()>&& completionHandler)
{
assertIsCurrent(workQueue());
paintPixelBufferToImageBuffer(m_context->readRenderingResultsForPainting(), imageBuffer, WTFMove(completionHandler));
}
void RemoteGraphicsContextGL::paintCompositedResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer, CompletionHandler<void()>&& completionHandler)
{
// Immediately turn the RenderingResourceIdentifier (which is error-prone) to a QualifiedRenderingResourceIdentifier,
// and use a helper function to make sure that don't accidentally use the RenderingResourceIdentifier (because the helper function can't see it).
paintCompositedResultsToCanvasWithQualifiedIdentifier({ imageBuffer, m_webProcessIdentifier }, WTFMove(completionHandler));
}
void RemoteGraphicsContextGL::paintCompositedResultsToCanvasWithQualifiedIdentifier(QualifiedRenderingResourceIdentifier imageBuffer, CompletionHandler<void()>&& completionHandler)
{
assertIsCurrent(workQueue());
paintPixelBufferToImageBuffer(m_context->readCompositedResultsForPainting(), imageBuffer, WTFMove(completionHandler));
}
#if ENABLE(MEDIA_STREAM)
void RemoteGraphicsContextGL::paintCompositedResultsToVideoFrame(CompletionHandler<void(std::optional<WebKit::RemoteVideoFrameProxy::Properties>&&)>&& completionHandler)
{
assertIsCurrent(workQueue());
std::optional<WebKit::RemoteVideoFrameProxy::Properties> result;
if (auto videoFrame = m_context->paintCompositedResultsToVideoFrame())
result = m_videoFrameObjectHeap->add(videoFrame.releaseNonNull());
completionHandler(WTFMove(result));
}
#endif
void RemoteGraphicsContextGL::paintPixelBufferToImageBuffer(RefPtr<WebCore::PixelBuffer>&& pixelBuffer, QualifiedRenderingResourceIdentifier target, CompletionHandler<void()>&& completionHandler)
{
assertIsCurrent(workQueue());
// FIXME: We do not have functioning read/write fences in RemoteRenderingBackend. Thus this is synchronous,
// as are the messages that call these.
Lock lock;
Condition conditionVariable;
bool isFinished = false;
// FIXME: This should not be needed. Maybe ArrayBufferView should be ThreadSafeRefCounted as it is used in accross multiple threads.
// The call below is synchronous and we transfer the ownership of the `pixelBuffer`.
if (is<ByteArrayPixelBuffer>(pixelBuffer))
downcast<ByteArrayPixelBuffer>(*pixelBuffer).data().disableThreadingChecks();
m_renderingBackend->dispatch([&, contextAttributes = m_context->contextAttributes()]() mutable {
if (auto imageBuffer = m_renderingBackend->remoteResourceCache().cachedImageBuffer(target)) {
// Here we do not try to play back pending commands for imageBuffer. Currently this call is only made for empty
// image buffers and there's no good way to add display lists.
if (pixelBuffer)
GraphicsContextGL::paintToCanvas(contextAttributes, pixelBuffer.releaseNonNull(), imageBuffer->backendSize(), imageBuffer->context());
else
imageBuffer->context().clearRect({ IntPoint(), imageBuffer->backendSize() });
// Unfortunately "flush" implementation in RemoteRenderingBackend overloads ordering and effects.
imageBuffer->flushContext();
}
Locker locker { lock };
isFinished = true;
conditionVariable.notifyOne();
});
Locker locker { lock };
conditionVariable.wait(lock, [&] {
return isFinished;
});
completionHandler();
}
#if ENABLE(VIDEO) && !USE(AVFOUNDATION)
void RemoteGraphicsContextGL::copyTextureFromVideoFrame(WebKit::RemoteVideoFrameReadReference read, uint32_t, uint32_t, int32_t, uint32_t, uint32_t, uint32_t, bool, bool , CompletionHandler<void(bool)>&& completionHandler)
{
notImplemented();
m_videoFrameObjectHeap->get(WTFMove(read));
completionHandler(false);
}
#endif
void RemoteGraphicsContextGL::simulateEventForTesting(WebCore::GraphicsContextGL::SimulatedEventForTesting event)
{
assertIsCurrent(workQueue());
// FIXME: only run this in testing mode. https://bugs.webkit.org/show_bug.cgi?id=222544
if (event == WebCore::GraphicsContextGL::SimulatedEventForTesting::Timeout) {
// Simulate the timeout by just discarding the context. The subsequent messages act like
// unauthorized or old messages from Web process, they are skipped.
callOnMainRunLoop([gpuConnectionToWebProcess = m_gpuConnectionToWebProcess, identifier = m_graphicsContextGLIdentifier]() {
if (auto connectionToWeb = gpuConnectionToWebProcess.get())
connectionToWeb->releaseGraphicsContextGLForTesting(identifier);
});
return;
}
if (event == WebCore::GraphicsContextGL::SimulatedEventForTesting::ContextChange) {
#if PLATFORM(MAC)
callOnMainRunLoop([weakConnection = m_gpuConnectionToWebProcess]() {
if (auto connection = weakConnection.get())
connection->dispatchDisplayWasReconfiguredForTesting();
});
#endif
return;
}
m_context->simulateEventForTesting(event);
}
void RemoteGraphicsContextGL::readnPixels0(int32_t x, int32_t y, int32_t width, int32_t height, uint32_t format, uint32_t type, IPC::ArrayReference<uint8_t>&& data, CompletionHandler<void(IPC::ArrayReference<uint8_t>)>&& completionHandler)
{
assertIsCurrent(workQueue());
Vector<uint8_t, 4> pixels(data);
m_context->readnPixels(x, y, width, height, format, type, pixels);
completionHandler(IPC::ArrayReference<uint8_t>(reinterpret_cast<uint8_t*>(pixels.data()), pixels.size()));
}
void RemoteGraphicsContextGL::readnPixels1(int32_t x, int32_t y, int32_t width, int32_t height, uint32_t format, uint32_t type, uint64_t offset)
{
assertIsCurrent(workQueue());
m_context->readnPixels(x, y, width, height, format, type, static_cast<GCGLintptr>(offset));
}
void RemoteGraphicsContextGL::multiDrawArraysANGLE(uint32_t mode, IPC::ArrayReferenceTuple<int32_t, int32_t>&& firstsAndCounts)
{
assertIsCurrent(workQueue());
// Copy the arrays. The contents are to be verified. The data might be in memory region shared by the caller.
Vector<GCGLint> firsts = vectorCopyCast<GCGLint, 0>(firstsAndCounts);
Vector<GCGLsizei> counts = vectorCopyCast<GCGLsizei, 1>(firstsAndCounts);
m_context->multiDrawArraysANGLE(mode, GCGLSpanTuple { firsts, counts });
}
void RemoteGraphicsContextGL::multiDrawArraysInstancedANGLE(uint32_t mode, IPC::ArrayReferenceTuple<int32_t, int32_t, int32_t>&& firstsCountsAndInstanceCounts)
{
assertIsCurrent(workQueue());
// Copy the arrays. The contents are to be verified. The data might be in memory region shared by the caller.
Vector<GCGLint> firsts = vectorCopyCast<GCGLint, 0>(firstsCountsAndInstanceCounts);
Vector<GCGLsizei> counts = vectorCopyCast<GCGLsizei, 1>(firstsCountsAndInstanceCounts);
Vector<GCGLsizei> instanceCounts = vectorCopyCast<GCGLsizei, 2>(firstsCountsAndInstanceCounts);
m_context->multiDrawArraysInstancedANGLE(mode, GCGLSpanTuple { firsts, counts, instanceCounts });
}
void RemoteGraphicsContextGL::multiDrawElementsANGLE(uint32_t mode, IPC::ArrayReferenceTuple<int32_t, int32_t>&& countsAndOffsets, uint32_t type)
{
assertIsCurrent(workQueue());
// Copy the arrays. The contents are to be verified. The data might be in memory region shared by the caller.
const Vector<GCGLsizei> counts = vectorCopyCast<GCGLsizei, 0>(countsAndOffsets);
// Currently offsets are copied in the m_context.
const GCGLsizei* offsets = reinterpret_cast<const GCGLsizei*>(countsAndOffsets.data<1>());
m_context->multiDrawElementsANGLE(mode, GCGLSpanTuple { counts.data(), offsets, counts.size() }, type);
}
void RemoteGraphicsContextGL::multiDrawElementsInstancedANGLE(uint32_t mode, IPC::ArrayReferenceTuple<int32_t, int32_t, int32_t>&& countsOffsetsAndInstanceCounts, uint32_t type)
{
assertIsCurrent(workQueue());
// Copy the arrays. The contents are to be verified. The data might be in memory region shared by the caller.
const Vector<GCGLsizei> counts = vectorCopyCast<GCGLsizei, 0>(countsOffsetsAndInstanceCounts);
// Currently offsets are copied in the m_context.
const GCGLsizei* offsets = reinterpret_cast<const GCGLsizei*>(countsOffsetsAndInstanceCounts.data<1>());
const Vector<GCGLsizei> instanceCounts = vectorCopyCast<GCGLsizei, 2>(countsOffsetsAndInstanceCounts);
m_context->multiDrawElementsInstancedANGLE(mode, GCGLSpanTuple { counts.data(), offsets, instanceCounts.data(), counts.size() }, type);
}
void RemoteGraphicsContextGL::paintRenderingResultsToPixelBuffer(CompletionHandler<void(std::optional<IPC::PixelBufferReference>&&)>&& completionHandler)
{
std::optional<IPC::PixelBufferReference> returnValue;
assertIsCurrent(workQueue());
if (auto pixelBuffer = m_context->paintRenderingResultsToPixelBuffer())
returnValue = pixelBuffer.releaseNonNull();
completionHandler(WTFMove(returnValue));
}
} // namespace WebKit
#endif