blob: 699a1fee2618af3bd003ca6df220a083b3a5536a [file] [log] [blame]
/*
* Copyright (C) 2011 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 "LayerTreeHostCAWin.h"
#if HAVE(WKQCA)
#include "DrawingAreaImpl.h"
#include "ShareableBitmap.h"
#include "UpdateInfo.h"
#include "WebPage.h"
#include <WebCore/GraphicsLayerCA.h>
#include <WebCore/LayerChangesFlusher.h>
#include <WebCore/PlatformCALayer.h>
#include <WebCore/WebCoreInstanceHandle.h>
#include <WebKitQuartzCoreAdditions/WKCACFImage.h>
#include <WebKitQuartzCoreAdditions/WKCACFView.h>
#include <wtf/CurrentTime.h>
#include <wtf/Threading.h>
#ifdef DEBUG_ALL
#pragma comment(lib, "WebKitQuartzCoreAdditions_debug")
#else
#pragma comment(lib, "WebKitQuartzCoreAdditions")
#endif
using namespace WebCore;
namespace WebKit {
static HWND dummyWindow;
static LPCWSTR dummyWindowClass = L"LayerTreeHostCAWindowClass";
static size_t validLayerTreeHostCount;
static void registerDummyWindowClass()
{
static bool didRegister;
if (didRegister)
return;
didRegister = true;
WNDCLASSW wndClass = {0};
wndClass.lpszClassName = dummyWindowClass;
wndClass.lpfnWndProc = ::DefWindowProcW;
wndClass.hInstance = instanceHandle();
::RegisterClassW(&wndClass);
}
// This window is never shown. It is only needed so that D3D can determine the display mode, etc.
static HWND createDummyWindow()
{
registerDummyWindowClass();
return ::CreateWindowW(dummyWindowClass, 0, WS_POPUP, 0, 0, 10, 10, 0, 0, instanceHandle(), 0);
}
bool LayerTreeHostCAWin::supportsAcceleratedCompositing()
{
static bool initialized;
static bool supportsAcceleratedCompositing;
if (initialized)
return supportsAcceleratedCompositing;
initialized = true;
ASSERT(!dummyWindow);
dummyWindow = createDummyWindow();
RetainPtr<WKCACFViewRef> view(AdoptCF, WKCACFViewCreate(kWKCACFViewDrawingDestinationImage));
CGRect fakeBounds = CGRectMake(0, 0, 10, 10);
WKCACFViewUpdate(view.get(), dummyWindow, &fakeBounds);
supportsAcceleratedCompositing = WKCACFViewCanDraw(view.get());
WKCACFViewUpdate(view.get(), 0, 0);
::DestroyWindow(dummyWindow);
dummyWindow = 0;
return supportsAcceleratedCompositing;
}
PassRefPtr<LayerTreeHostCAWin> LayerTreeHostCAWin::create(WebPage* webPage)
{
RefPtr<LayerTreeHostCAWin> host = adoptRef(new LayerTreeHostCAWin(webPage));
host->initialize();
return host.release();
}
LayerTreeHostCAWin::LayerTreeHostCAWin(WebPage* webPage)
: LayerTreeHostCA(webPage)
, m_isFlushingLayerChanges(false)
, m_nextDisplayTime(0)
{
}
LayerTreeHostCAWin::~LayerTreeHostCAWin()
{
}
void LayerTreeHostCAWin::platformInitialize(LayerTreeContext&)
{
++validLayerTreeHostCount;
if (!dummyWindow)
dummyWindow = createDummyWindow();
m_view.adoptCF(WKCACFViewCreate(kWKCACFViewDrawingDestinationImage));
WKCACFViewSetContextUserData(m_view.get(), static_cast<AbstractCACFLayerTreeHost*>(this));
WKCACFViewSetLayer(m_view.get(), rootLayer()->platformLayer());
WKCACFViewSetContextDidChangeCallback(m_view.get(), contextDidChangeCallback, this);
CGRect bounds = m_webPage->bounds();
WKCACFViewUpdate(m_view.get(), dummyWindow, &bounds);
}
void LayerTreeHostCAWin::invalidate()
{
LayerChangesFlusher::shared().cancelPendingFlush(this);
WKCACFViewUpdate(m_view.get(), 0, 0);
WKCACFViewSetContextUserData(m_view.get(), 0);
WKCACFViewSetLayer(m_view.get(), 0);
WKCACFViewSetContextDidChangeCallback(m_view.get(), 0, 0);
LayerTreeHostCA::invalidate();
if (--validLayerTreeHostCount)
return;
::DestroyWindow(dummyWindow);
dummyWindow = 0;
}
void LayerTreeHostCAWin::scheduleLayerFlush()
{
LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
}
bool LayerTreeHostCAWin::participatesInDisplay()
{
return true;
}
bool LayerTreeHostCAWin::needsDisplay()
{
return timeUntilNextDisplay() <= 0;
}
double LayerTreeHostCAWin::timeUntilNextDisplay()
{
return m_nextDisplayTime - currentTime();
}
static IntSize size(WKCACFImageRef image)
{
return IntSize(WKCACFImageGetWidth(image), WKCACFImageGetHeight(image));
}
static PassRefPtr<ShareableBitmap> toShareableBitmap(WKCACFImageRef image)
{
size_t fileMappingSize;
HANDLE mapping = WKCACFImageCopyFileMapping(image, &fileMappingSize);
if (!mapping)
return 0;
RefPtr<SharedMemory> sharedMemory = SharedMemory::adopt(mapping, fileMappingSize, SharedMemory::ReadWrite);
if (!sharedMemory) {
::CloseHandle(mapping);
return 0;
}
// WKCACFImage never has an alpha channel.
return ShareableBitmap::create(size(image), 0, sharedMemory.release());
}
void LayerTreeHostCAWin::display(UpdateInfo& updateInfo)
{
CGPoint imageOrigin;
CFTimeInterval nextDrawTime;
RetainPtr<WKCACFImageRef> image(AdoptCF, WKCACFViewCopyDrawnImage(m_view.get(), &imageOrigin, &nextDrawTime));
m_nextDisplayTime = nextDrawTime - CACurrentMediaTime() + currentTime();
if (!image)
return;
RefPtr<ShareableBitmap> bitmap = toShareableBitmap(image.get());
if (!bitmap)
return;
if (!bitmap->createHandle(updateInfo.bitmapHandle))
return;
updateInfo.updateRectBounds = IntRect(IntPoint(imageOrigin.x, m_webPage->size().height() - imageOrigin.y - bitmap->size().height()), bitmap->size());
updateInfo.updateRects.append(updateInfo.updateRectBounds);
}
void LayerTreeHostCAWin::sizeDidChange(const IntSize& newSize)
{
LayerTreeHostCA::sizeDidChange(newSize);
CGRect bounds = CGRectMake(0, 0, newSize.width(), newSize.height());
WKCACFViewUpdate(m_view.get(), dummyWindow, &bounds);
WKCACFViewFlushContext(m_view.get());
}
void LayerTreeHostCAWin::forceRepaint()
{
LayerTreeHostCA::forceRepaint();
WKCACFViewFlushContext(m_view.get());
}
void LayerTreeHostCAWin::contextDidChangeCallback(WKCACFViewRef view, void* info)
{
// This should only be called on a background thread when no changes have actually
// been committed to the context, eg. when a video frame has been added to an image
// queue, so return without triggering animations etc.
if (!isMainThread())
return;
LayerTreeHostCAWin* host = static_cast<LayerTreeHostCAWin*>(info);
ASSERT_ARG(view, view == host->m_view);
host->contextDidChange();
}
void LayerTreeHostCAWin::contextDidChange()
{
// Send currentTime to the pending animations. This function is called by CACF in a callback
// which occurs after the drawInContext calls. So currentTime is very close to the time
// the animations actually start
double currentTime = WTF::currentTime();
HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it)
(*it)->animationStarted(currentTime);
m_pendingAnimatedLayers.clear();
m_nextDisplayTime = 0;
static_cast<DrawingAreaImpl*>(m_webPage->drawingArea())->setLayerHostNeedsDisplay();
}
PlatformCALayer* LayerTreeHostCAWin::rootLayer() const
{
return static_cast<GraphicsLayerCA*>(LayerTreeHostCA::rootLayer())->platformCALayer();
}
void LayerTreeHostCAWin::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)
{
m_pendingAnimatedLayers.add(layer);
}
void LayerTreeHostCAWin::layerTreeDidChange()
{
if (m_isFlushingLayerChanges) {
// The layer tree is changing as a result of flushing GraphicsLayer changes to their
// underlying PlatformCALayers. We'll flush those changes to the context as part of that
// process, so there's no need to schedule another flush here.
return;
}
// The layer tree is changing as a result of someone modifying a PlatformCALayer that doesn't
// have a corresponding GraphicsLayer. Schedule a flush since we won't schedule one through the
// normal GraphicsLayer mechanisms.
LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
}
void LayerTreeHostCAWin::flushPendingLayerChangesNow()
{
RefPtr<LayerTreeHostCA> protector(this);
m_isFlushingLayerChanges = true;
// Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if
// requested.
performScheduledLayerFlush();
// Flush changes stored up in PlatformCALayers to the context so they will be rendered.
WKCACFViewFlushContext(m_view.get());
m_isFlushingLayerChanges = false;
}
} // namespace WebKit
#endif // HAVE(WKQCA)