LegacyCACFLayerTreeHost.cpp [plain text]
#include "config.h"
#include "LegacyCACFLayerTreeHost.h"
#if USE(ACCELERATED_COMPOSITING)
#include "PlatformCALayer.h"
#include <QuartzCore/CABase.h>
#include <WebKitSystemInterface/WebKitSystemInterface.h>
#ifndef NDEBUG
#define D3D_DEBUG_INFO
#endif
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9")
#pragma comment(lib, "d3dx9")
using namespace std;
namespace WebCore {
static IDirect3D9* s_d3d = 0;
static IDirect3D9* d3d()
{
if (s_d3d)
return s_d3d;
if (!LoadLibrary(TEXT("d3d9.dll")))
return 0;
s_d3d = Direct3DCreate9(D3D_SDK_VERSION);
return s_d3d;
}
static D3DPRESENT_PARAMETERS initialPresentationParameters()
{
D3DPRESENT_PARAMETERS parameters = {0};
parameters.Windowed = TRUE;
parameters.SwapEffect = D3DSWAPEFFECT_COPY;
parameters.BackBufferCount = 1;
parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
parameters.MultiSampleType = D3DMULTISAMPLE_NONE;
return parameters;
}
static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps)
{
if (caps.MaxTextureBlendStages < 2)
return false;
if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL))
return false;
if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2)
return false;
if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2)
return false;
return true;
}
PassRefPtr<LegacyCACFLayerTreeHost> LegacyCACFLayerTreeHost::create()
{
return adoptRef(new LegacyCACFLayerTreeHost);
}
LegacyCACFLayerTreeHost::LegacyCACFLayerTreeHost()
: m_renderTimer(this, &LegacyCACFLayerTreeHost::renderTimerFired)
, m_context(wkCACFContextCreate())
, m_mightBeAbleToCreateDeviceLater(true)
, m_mustResetLostDeviceBeforeRendering(false)
{
#ifndef NDEBUG
char* printTreeFlag = getenv("CA_PRINT_TREE");
m_printTree = printTreeFlag && atoi(printTreeFlag);
#endif
}
LegacyCACFLayerTreeHost::~LegacyCACFLayerTreeHost()
{
wkCACFContextDestroy(m_context);
}
void LegacyCACFLayerTreeHost::initializeContext(void* userData, PlatformCALayer* layer)
{
wkCACFContextSetUserData(m_context, userData);
wkCACFContextSetLayer(m_context, layer->platformLayer());
}
bool LegacyCACFLayerTreeHost::createRenderer()
{
if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater)
return m_d3dDevice;
m_mightBeAbleToCreateDeviceLater = false;
D3DPRESENT_PARAMETERS parameters = initialPresentationParameters();
if (!d3d() || !::IsWindow(window()))
return false;
RECT rect;
GetClientRect(window(), &rect);
if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) {
parameters.BackBufferWidth = 1;
parameters.BackBufferHeight = 1;
}
D3DCAPS9 d3dCaps;
if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps)))
return false;
DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE;
if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps)
behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
COMPtr<IDirect3DDevice9> device;
if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window(), behaviorFlags, ¶meters, &device))) {
s_d3d->Release();
s_d3d = 0;
m_mightBeAbleToCreateDeviceLater = true;
return false;
}
D3DCAPS9 deviceCaps;
if (FAILED(device->GetDeviceCaps(&deviceCaps)))
return false;
if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps))
return false;
m_d3dDevice = device;
initD3DGeometry();
wkCACFContextSetD3DDevice(m_context, m_d3dDevice.get());
if (IsWindow(window())) {
rootLayer()->setBounds(bounds());
flushContext();
}
return true;
}
void LegacyCACFLayerTreeHost::destroyRenderer()
{
wkCACFContextSetLayer(m_context, 0);
wkCACFContextSetD3DDevice(m_context, 0);
m_d3dDevice = 0;
if (s_d3d)
s_d3d->Release();
s_d3d = 0;
m_mightBeAbleToCreateDeviceLater = true;
CACFLayerTreeHost::destroyRenderer();
}
void LegacyCACFLayerTreeHost::resize()
{
if (!m_d3dDevice)
return;
resetDevice(ChangedWindowSize);
if (rootLayer()) {
rootLayer()->setBounds(bounds());
flushContext();
}
}
void LegacyCACFLayerTreeHost::renderTimerFired(Timer<LegacyCACFLayerTreeHost>*)
{
paint();
}
void LegacyCACFLayerTreeHost::paint()
{
createRenderer();
if (!m_d3dDevice) {
if (m_mightBeAbleToCreateDeviceLater)
renderSoon();
return;
}
CACFLayerTreeHost::paint();
}
void LegacyCACFLayerTreeHost::render(const Vector<CGRect>& windowDirtyRects)
{
ASSERT(m_d3dDevice);
if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) {
renderSoon();
return;
}
CGRect bounds = this->bounds();
char space[4096];
if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), CACurrentMediaTime(), bounds, windowDirtyRects.data(), windowDirtyRects.size()))
return;
HRESULT err = S_OK;
CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity();
do {
WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context);
if (!e)
break;
Vector<D3DRECT, 64> rects;
for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) {
D3DRECT rect;
rect.x1 = r->origin.x;
rect.x2 = rect.x1 + r->size.width;
rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height);
rect.y2 = rect.y1 + r->size.height;
rects.append(rect);
}
wkCACFUpdateRectEnumeratorRelease(e);
timeToNextRender = wkCACFContextGetNextUpdateTime(m_context);
if (rects.isEmpty())
break;
m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0);
m_d3dDevice->BeginScene();
wkCACFContextRenderUpdate(m_context);
m_d3dDevice->EndScene();
err = m_d3dDevice->Present(0, 0, 0, 0);
if (err == D3DERR_DEVICELOST) {
wkCACFContextAddUpdateRect(m_context, bounds);
if (!resetDevice(LostDevice)) {
renderSoon();
return;
}
}
} while (err == D3DERR_DEVICELOST);
wkCACFContextFinishUpdate(m_context);
#ifndef NDEBUG
if (m_printTree)
rootLayer()->printTree();
#endif
if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity())
renderSoon();
}
void LegacyCACFLayerTreeHost::renderSoon()
{
if (!m_renderTimer.isActive())
m_renderTimer.startOneShot(0);
}
void LegacyCACFLayerTreeHost::flushContext()
{
wkCACFContextFlush(m_context);
contextDidChange();
}
void LegacyCACFLayerTreeHost::contextDidChange()
{
renderSoon();
CACFLayerTreeHost::contextDidChange();
}
CFTimeInterval LegacyCACFLayerTreeHost::lastCommitTime() const
{
return wkCACFContextGetLastCommitTime(m_context);
}
void LegacyCACFLayerTreeHost::initD3DGeometry()
{
ASSERT(m_d3dDevice);
CGRect bounds = this->bounds();
float x0 = bounds.origin.x;
float y0 = bounds.origin.y;
float x1 = x0 + bounds.size.width;
float y1 = y0 + bounds.size.height;
D3DXMATRIXA16 projection;
D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f);
m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection);
}
bool LegacyCACFLayerTreeHost::resetDevice(ResetReason reason)
{
ASSERT(m_d3dDevice);
ASSERT(m_context);
HRESULT hr = m_d3dDevice->TestCooperativeLevel();
if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) {
m_mustResetLostDeviceBeforeRendering = true;
return false;
}
m_mustResetLostDeviceBeforeRendering = false;
if (reason == LostDevice && hr == D3D_OK) {
return true;
}
wkCACFContextReleaseD3DResources(m_context);
D3DPRESENT_PARAMETERS parameters = initialPresentationParameters();
hr = m_d3dDevice->Reset(¶meters);
ASSERT(hr != D3DERR_DEVICELOST);
initD3DGeometry();
return true;
}
}
#endif // USE(ACCELERATED_COMPOSITING)