| /* |
| * Copyright (C) 2017 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 "CAD3DRenderer.h" |
| |
| #include "Image.h" |
| #include "ImageConversion.h" |
| #include "InvertColorPS.h" |
| #include <CoreGraphics/CGSRegion.h> |
| #include <QuartzCore/CACFContext.h> |
| #include <QuartzCore/CARenderOGL.h> |
| #include <limits> |
| #include <string> |
| #include <wtf/RefPtr.h> |
| |
| // Define strings from the HLSL effect that are referenced later |
| #define HLSL_TECHNIQUE "InvertColor" |
| #define SCENE_TEXTURE "sceneTexture" |
| |
| typedef HRESULT (CALLBACK *LPFNDLL_Direct3DCreate9Ex)(UINT, void **); |
| |
| namespace WKQCA { |
| |
| static const DWORD customVertexFormat = D3DFVF_XYZRHW | D3DFVF_TEX1; |
| |
| D3DPostProcessingContext::D3DPostProcessingContext(const CComPtr<IDirect3DTexture9>& sceneTexture, const CComPtr<IDirect3DVertexBuffer9>& overlayQuad) |
| : m_sceneTexture(sceneTexture) |
| , m_overlayQuad(overlayQuad) |
| { |
| } |
| |
| static IDirect3D9* s_d3d; |
| static IDirect3D9* d3d() |
| { |
| static bool initialized; |
| |
| if (initialized) |
| return s_d3d; |
| initialized = true; |
| |
| HMODULE d3dLibHandle = LoadLibrary(TEXT("d3d9.dll")); |
| if (!d3dLibHandle) |
| return nullptr; |
| |
| // AVFoundationCF requires us to create an IDirect3DDevice9Ex instead of an IDirect3DDevice9 for HW |
| // video decoding to work, because IDirect3DDevice9Ex::ResetEx() allows the HW decoder to persist |
| // during a reset, while IDirect3DDevice9::Reset() requires that the HW decoder be destroyed. |
| LPFNDLL_Direct3DCreate9Ex d3dCreate9Ex = (LPFNDLL_Direct3DCreate9Ex)GetProcAddress(d3dLibHandle, "Direct3DCreate9Ex"); |
| if (d3dCreate9Ex) { |
| d3dCreate9Ex(D3D_SDK_VERSION, (void **)&s_d3d); |
| |
| if (s_d3d) { |
| D3DCAPS9 caps; |
| HRESULT hr = s_d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps); |
| |
| // HW video decoding requires D3DCAPS2_DYNAMICTEXTURES, which not all d3d9 devices support, |
| // so fall back to plain old IDirect3DDevice9 if it isn't available. |
| if (!FAILED(hr) && (caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES)) |
| return s_d3d; |
| |
| // Release the IDirect3DDevice9Ex, it doesn't have the features we require so we should |
| // allocate and use a plain old IDirect3D9. |
| s_d3d->Release(); |
| } |
| } |
| |
| s_d3d = Direct3DCreate9(D3D_SDK_VERSION); |
| |
| return s_d3d; |
| } |
| |
| static D3DPRESENT_PARAMETERS initialPresentationParameters(const CGSize& size) |
| { |
| D3DPRESENT_PARAMETERS parameters = {0}; |
| parameters.Windowed = TRUE; |
| parameters.SwapEffect = D3DSWAPEFFECT_COPY; |
| parameters.BackBufferCount = 1; |
| parameters.BackBufferFormat = D3DFMT_UNKNOWN; |
| parameters.BackBufferHeight = size.height; |
| parameters.BackBufferWidth = size.width; |
| parameters.MultiSampleType = D3DMULTISAMPLE_NONE; |
| |
| return parameters; |
| } |
| |
| CAD3DRenderer& CAD3DRenderer::shared() |
| { |
| static CAD3DRenderer& shared = *new CAD3DRenderer; |
| return shared; |
| } |
| |
| CAD3DRenderer::CAD3DRenderer() |
| { |
| } |
| |
| CComPtr<IDirect3DSwapChain9> CAD3DRenderer::swapChain(CWindow window, const CGSize& size) |
| { |
| auto locker = holdLock(m_lock); |
| |
| bool useDefaultSwapChain = false; |
| |
| if (!m_d3dDevice) { |
| if (!initialize(window, size)) |
| return nullptr; |
| ASSERT(m_d3dDevice); |
| // Since we used this window to initialize the device, the device's default swap chain |
| // should be associated with this window. |
| useDefaultSwapChain = true; |
| } |
| |
| if (m_deviceIsLost) { |
| if (!resetD3DDevice(window, size)) |
| return nullptr; |
| ASSERT(!m_deviceIsLost); |
| // Since we used this window to reset the device, the device's default swap chain should be |
| // associated with this window. |
| useDefaultSwapChain = true; |
| } |
| |
| if (useDefaultSwapChain) { |
| CComPtr<IDirect3DSwapChain9> defaultSwapChain; |
| if (SUCCEEDED(m_d3dDevice->GetSwapChain(0, &defaultSwapChain))) { |
| #if !ASSERT_DISABLED |
| // Since we just initialized or reset the device, its default swap chain should be |
| // associated with and sized to match this window. |
| D3DPRESENT_PARAMETERS parameters; |
| ASSERT(SUCCEEDED(defaultSwapChain->GetPresentParameters(¶meters))); |
| ASSERT(parameters.hDeviceWindow == window); |
| CRect clientRect; |
| ASSERT(window.GetClientRect(clientRect)); |
| ASSERT(parameters.BackBufferHeight == clientRect.Height()); |
| ASSERT(parameters.BackBufferWidth == clientRect.Width()); |
| #endif |
| return defaultSwapChain; |
| } |
| } |
| |
| D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(size); |
| parameters.hDeviceWindow = window; |
| |
| CComPtr<IDirect3DSwapChain9> result; |
| if (FAILED(m_d3dDevice->CreateAdditionalSwapChain(¶meters, &result))) |
| return nullptr; |
| |
| return result; |
| } |
| |
| struct CustomVertex { |
| // position |
| float x; |
| float y; |
| float z; |
| float rhw; // indicates that the position is already transformed to screen coordinates |
| |
| // texture coordinates |
| float tu; |
| float tv; |
| }; |
| |
| std::unique_ptr<D3DPostProcessingContext> CAD3DRenderer::createD3DPostProcessingContext(IDirect3DSwapChain9* swapChain, const CGSize& size) |
| { |
| ASSERT_ARG(swapChain, swapChain); |
| |
| // Get the back buffer format of the swap chain; we want to create the scene texture using the same format. |
| D3DPRESENT_PARAMETERS swapChainParameters; |
| if (FAILED(swapChain->GetPresentParameters(&swapChainParameters))) |
| return nullptr; |
| |
| // FIXME: We should fall back to software rendering if CreateTexture() returns D3DERR_OUTOFVIDEOMEMORY. |
| CComPtr<IDirect3DTexture9> sceneTexture; |
| HRESULT hr = m_d3dDevice->CreateTexture(size.width, size.height, 1, D3DUSAGE_RENDERTARGET, swapChainParameters.BackBufferFormat, D3DPOOL_DEFAULT, &sceneTexture, 0); |
| if (FAILED(hr)) { |
| ASSERT(hr != D3DERR_OUTOFVIDEOMEMORY); |
| return nullptr; |
| } |
| |
| // Create a quad in screen coordinates onto which sceneTexture will be |
| // applied. In D3D, pixels are referenced from the center of a 1x1 cell, so |
| // to properly encapsulate the cell we need to offset the quad boundaries by |
| // 0.5 pixels. |
| const float left = -0.5; |
| const float right = size.width - 0.5; |
| const float top = -0.5; |
| const float bottom = size.height - 0.5; |
| const float z = 1; |
| const float rhw = 1; |
| CustomVertex overlayQuadVertices[4] = { |
| { left, top, z, rhw, 0, 0 }, |
| { right, top, z, rhw, 1, 0 }, |
| { left, bottom, z, rhw, 0, 1 }, |
| { right, bottom, z, rhw, 1, 1 } |
| }; |
| |
| CComPtr<IDirect3DVertexBuffer9> overlayQuad; |
| if (FAILED(m_d3dDevice->CreateVertexBuffer(sizeof(overlayQuadVertices), D3DUSAGE_WRITEONLY, customVertexFormat, D3DPOOL_DEFAULT, &overlayQuad, 0))) |
| return nullptr; |
| |
| void* overlayQuadBuffer; |
| if (FAILED(overlayQuad->Lock(0, 0, &overlayQuadBuffer, 0))) |
| return nullptr; |
| |
| memcpy(overlayQuadBuffer, overlayQuadVertices, sizeof(overlayQuadVertices)); |
| overlayQuad->Unlock(); |
| |
| return makeUnique<D3DPostProcessingContext>(sceneTexture, overlayQuad); |
| } |
| |
| // FIXME: <rdar://6507851> Share this code with CoreAnimation's |
| // CA::OGL::DX9Context::update function. |
| static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps) |
| { |
| // CoreAnimation needs two or more texture units. |
| if (caps.MaxTextureBlendStages < 2) |
| return false; |
| |
| // CoreAnimation needs non-power-of-two textures. |
| if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) |
| return false; |
| |
| // CoreAnimation needs vertex shader 2.0 or greater. |
| if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2) |
| return false; |
| |
| // CoreAnimation needs pixel shader 2.0 or greater. |
| if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2) |
| return false; |
| |
| return true; |
| } |
| |
| bool CAD3DRenderer::initialize(CWindow window, const CGSize& size) |
| { |
| #if !ASSERT_DISABLED |
| if (!m_deviceThreadID) |
| m_deviceThreadID = ::GetCurrentThreadId(); |
| // MSDN says that IDirect3D9::CreateDevice must "be called from the same thread that handles |
| // window messages." <http://msdn.microsoft.com/en-us/library/bb147224(VS.85).aspx>. |
| ASSERT(::GetCurrentThreadId() == m_deviceThreadID); |
| ASSERT(::GetCurrentThreadId() == window.GetWindowThreadID()); |
| #endif |
| |
| if (m_initialized) |
| return m_d3dDevice; |
| m_initialized = true; |
| |
| if (!d3d()) |
| return false; |
| |
| D3DCAPS9 d3dCaps; |
| if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) |
| return false; |
| |
| DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED; |
| if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) |
| behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; |
| else |
| behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; |
| |
| D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(size); |
| parameters.hDeviceWindow = window; |
| |
| CComPtr<IDirect3DDevice9> device; |
| CComQIPtr<IDirect3D9Ex> d3d9Ex = d3d(); |
| HRESULT hr; |
| |
| if (d3d9Ex) |
| hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 0, behaviorFlags, ¶meters, 0, (IDirect3DDevice9Ex **)&device); |
| else |
| hr = s_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 0, behaviorFlags, ¶meters, &device); |
| if (FAILED(hr)) |
| return false; |
| |
| // Now that we've created the IDirect3DDevice9 based on the capabilities we |
| // got from the IDirect3D9 global object, we requery the device for its |
| // actual capabilities. The capabilities returned by the device can |
| // sometimes be more complete, for example when using software vertex |
| // processing. |
| D3DCAPS9 deviceCaps; |
| if (FAILED(device->GetDeviceCaps(&deviceCaps))) |
| return false; |
| |
| if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps)) |
| return false; |
| |
| m_d3dDevice.Attach(device.Detach()); |
| m_usingDirect3D9Ex = d3d9Ex; |
| m_renderOGLContext = CARenderOGLNew(&kCARenderDX9Callbacks, m_d3dDevice, 0); |
| |
| return true; |
| } |
| |
| static void D3DMatrixOrthoOffCenterRH(D3DMATRIX* pOut, float l, float r, float b, float t, float zn, float zf) |
| { |
| pOut->_11 = 2 / (r-l); |
| pOut->_12 = 0; |
| pOut->_13 = 0; |
| pOut->_14 = 0; |
| |
| pOut->_21 = 0; |
| pOut->_22 = 2 / (t-b); |
| pOut->_23 = 0; |
| pOut->_24 = 0; |
| |
| pOut->_31 = 0; |
| pOut->_32 = 0; |
| pOut->_33 = 1 / (zn-zf); |
| pOut->_34 = 0; |
| |
| pOut->_41 = (l+r) / (l-r); |
| pOut->_42 = (t+b) / (b-t); |
| pOut->_43 = zn / (zn-zf); |
| pOut->_44 = 1; |
| } |
| |
| static bool prepareDevice(IDirect3DDevice9* device, const CGRect& bounds, IDirect3DSwapChain9* swapChain, D3DPostProcessingContext* postProcessingContext) |
| { |
| CComPtr<IDirect3DSurface9> backBuffer; |
| |
| // If we will post-process the scene, we want to render to the scene texture rather than the back buffer. |
| if (postProcessingContext) { |
| if (FAILED(postProcessingContext->sceneTexture()->GetSurfaceLevel(0, &backBuffer))) |
| return false; |
| } else { |
| if (FAILED(swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer))) |
| return false; |
| } |
| |
| CComPtr<IDirect3DSurface9> currentRenderTarget; |
| if (FAILED(device->GetRenderTarget(0, ¤tRenderTarget))) |
| return false; |
| |
| if (backBuffer != currentRenderTarget && FAILED(device->SetRenderTarget(0, backBuffer))) |
| return false; |
| |
| float x0 = bounds.origin.x; |
| float y0 = bounds.origin.y; |
| float x1 = x0 + bounds.size.width; |
| float y1 = y0 + bounds.size.height; |
| |
| D3DMATRIX projection; |
| D3DMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f); |
| |
| if (FAILED(device->SetTransform(D3DTS_PROJECTION, &projection))) |
| return false; |
| |
| return true; |
| } |
| |
| static CGRect updateBounds(CARenderUpdate* update) |
| { |
| CGSRegionObj rgn = CARenderUpdateCopyRegion(update); |
| if (!rgn) |
| return CGRectZero; |
| |
| CGRect result; |
| CGError error = CGSGetRegionBounds(rgn, &result); |
| CGSReleaseRegion(rgn); |
| |
| return error == kCGErrorSuccess ? result : CGRectZero; |
| } |
| |
| CAD3DRenderer::RenderResult CAD3DRenderer::renderAndPresent(const CGRect& bounds, IDirect3DSwapChain9* swapChain, D3DPostProcessingContext* postProcessingContext, CACFContextRef context, CFTimeInterval& nextRenderTime) |
| { |
| ASSERT_ARG(swapChain, swapChain); |
| ASSERT_ARG(context, context); |
| |
| auto locker = holdLock(m_lock); |
| |
| CGRect unusedDirtyRect; |
| RenderResult result = renderInternal(bounds, swapChain, postProcessingContext, context, unusedDirtyRect, nextRenderTime); |
| switch (result) { |
| case DeviceBecameLost: |
| ASSERT_NOT_REACHED(); |
| case DeviceIsLost: |
| case OtherFailure: |
| return result; |
| case NoRenderingRequired: |
| case Success: |
| break; |
| } |
| |
| // Note that we only use the bounds size, not its origin, when deciding where in the |
| // window to render. As with NSViews, the bounds origin determines where content is |
| // positioned within the view, not where the view is positioned in its parent. |
| CRect destinationRect(0, 0, CGRectGetWidth(bounds), CGRectGetHeight(bounds)); |
| |
| // FIXME: Passing a pDirtyRegion based on the CARenderUpdate's dirty region to |
| // IDirect3DSwapChain9::Present might increase performance. |
| if (swapChain->Present(0, destinationRect, 0, 0, 0) != D3DERR_DEVICELOST) |
| return Success; |
| |
| // The device was lost. Record that this happened so that we'll try to reset it before |
| // we try to create a new swap chain. |
| setDeviceIsLost(true); |
| |
| // We can't render until the device is reset, which the caller will have to take care of. |
| nextRenderTime = std::numeric_limits<CFTimeInterval>::infinity(); |
| |
| return DeviceBecameLost; |
| } |
| |
| CAD3DRenderer::RenderResult CAD3DRenderer::renderToImage(const CGRect& bounds, IDirect3DSwapChain9* swapChain, D3DPostProcessingContext* postProcessingContext, CACFContextRef context, RefPtr<Image>& outImage, CGPoint& imageOrigin, CFTimeInterval& nextRenderTime) |
| { |
| ASSERT_ARG(swapChain, swapChain); |
| ASSERT_ARG(context, context); |
| |
| auto locker = holdLock(m_lock); |
| |
| CGRect dirtyRect; |
| RenderResult result = renderInternal(bounds, swapChain, postProcessingContext, context, dirtyRect, nextRenderTime); |
| switch (result) { |
| case DeviceBecameLost: |
| ASSERT_NOT_REACHED(); |
| case DeviceIsLost: |
| case OtherFailure: |
| case NoRenderingRequired: |
| return result; |
| case Success: |
| break; |
| } |
| |
| ASSERT(!CGRectIsEmpty(dirtyRect)); |
| |
| dirtyRect = CGRectIntersection(dirtyRect, bounds); |
| CRect dirtyWinRect(CPoint(CGRectGetMinX(dirtyRect), CGRectGetHeight(bounds) - CGRectGetMaxY(dirtyRect)), CSize(CGRectGetWidth(dirtyRect), CGRectGetHeight(dirtyRect))); |
| |
| RefPtr<Image> image; |
| HRESULT hr = getBackBufferRectAsImage(m_d3dDevice, swapChain, dirtyWinRect, image); |
| if (SUCCEEDED(hr)) { |
| outImage = WTFMove(image); |
| imageOrigin = dirtyRect.origin; |
| return Success; |
| } |
| |
| if (hr != D3DERR_DEVICELOST) |
| return OtherFailure; |
| |
| // The device was lost. Record that this happened so that we'll try to reset it before |
| // we try to create a new swap chain. |
| setDeviceIsLost(true); |
| |
| // We can't render until the device is reset, which the caller will have to take care of. |
| nextRenderTime = std::numeric_limits<CFTimeInterval>::infinity(); |
| |
| return DeviceBecameLost; |
| } |
| |
| void CAD3DRenderer::setDeviceIsLost(bool lost) |
| { |
| ASSERT_WITH_MESSAGE(!m_lock.tryLock(), "m_lock must be held when calling this function"); |
| |
| if (m_deviceIsLost == lost) |
| return; |
| |
| m_deviceIsLost = lost; |
| |
| if (!m_deviceIsLost) |
| return; |
| |
| // We have to release any existing D3D resources before we try to reset the device. |
| // We'll reallocate them as needed. See |
| // <http://msdn.microsoft.com/en-us/library/bb174425(v=VS.85).aspx>. |
| |
| // This will cause CA to release its D3D resources. |
| CARenderOGLPurge(m_renderOGLContext); |
| } |
| |
| CAD3DRenderer::RenderResult CAD3DRenderer::renderInternal(const CGRect& bounds, IDirect3DSwapChain9* swapChain, D3DPostProcessingContext* postProcessingContext, CACFContextRef context, CGRect& dirtyRect, CFTimeInterval& nextRenderTime) |
| { |
| ASSERT_ARG(swapChain, swapChain); |
| ASSERT_ARG(context, context); |
| |
| ASSERT_WITH_MESSAGE(!m_lock.tryLock(), "m_lock must be held when calling this function"); |
| ASSERT(m_d3dDevice); |
| ASSERT(m_renderOGLContext); |
| |
| nextRenderTime = std::numeric_limits<CFTimeInterval>::infinity(); |
| |
| if (m_deviceIsLost) |
| return DeviceIsLost; |
| |
| if (!prepareDevice(m_d3dDevice, bounds, swapChain, postProcessingContext)) |
| return OtherFailure; |
| |
| char space[4096]; |
| CARenderUpdate* u = CARenderUpdateBegin(space, sizeof(space), CACurrentMediaTime(), 0, 0, &bounds); |
| if (!u) |
| return OtherFailure; |
| |
| CARenderContext* renderContext = static_cast<CARenderContext*>(CACFContextGetRenderContext(context)); |
| CARenderContextLock(renderContext); |
| CARenderUpdateAddContext(u, renderContext); |
| CARenderContextUnlock(renderContext); |
| |
| nextRenderTime = CARenderUpdateGetNextTime(u); |
| |
| bool didRender = false; |
| |
| dirtyRect = updateBounds(u); |
| if (!CGRectIsEmpty(dirtyRect)) { |
| m_d3dDevice->BeginScene(); |
| CARenderOGLRender(m_renderOGLContext, u); |
| if (postProcessingContext) |
| postProcess(bounds, swapChain, postProcessingContext); |
| m_d3dDevice->EndScene(); |
| didRender = true; |
| } |
| |
| CARenderUpdateFinish(u); |
| |
| return didRender ? Success : NoRenderingRequired; |
| } |
| |
| void CAD3DRenderer::postProcess(const CGRect& bounds, IDirect3DSwapChain9* swapChain, D3DPostProcessingContext* context) |
| { |
| // The scene has been rendered to the scene texture, so now draw a quad to |
| // the back buffer and apply the scene texture to the quad with the |
| // InvertColor HLSL technique. |
| |
| ASSERT_ARG(swapChain, swapChain); |
| ASSERT_ARG(context, context); |
| |
| CComPtr<IDirect3DSurface9> backBuffer; |
| if (FAILED(swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer))) |
| return; |
| |
| if (FAILED(m_d3dDevice->SetRenderTarget(0, backBuffer))) |
| return; |
| |
| if (FAILED(m_d3dDevice->SetFVF(customVertexFormat))) |
| return; |
| |
| if (FAILED(m_d3dDevice->SetStreamSource(0, context->overlayQuad(), 0, sizeof(CustomVertex)))) |
| return; |
| |
| createShaderIfNeeded(); |
| |
| m_d3dDevice->SetTexture(0, context->sceneTexture()); |
| m_d3dDevice->SetPixelShader(m_pixelShader); |
| m_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); |
| m_d3dDevice->SetPixelShader(nullptr); |
| } |
| |
| bool CAD3DRenderer::resetD3DDevice(CWindow window, const CGSize& size) |
| { |
| ASSERT(m_d3dDevice); |
| ASSERT(m_renderOGLContext); |
| ASSERT(m_deviceIsLost); |
| |
| // MSDN says that IDirect3DDevice9::Reset must "be called from the same thread that handles |
| // window messages." <http://msdn.microsoft.com/en-us/library/bb147224(VS.85).aspx>. |
| ASSERT(::GetCurrentThreadId() == m_deviceThreadID); |
| ASSERT(::GetCurrentThreadId() == window.GetWindowThreadID()); |
| |
| HRESULT hr = m_d3dDevice->TestCooperativeLevel(); |
| |
| if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) { |
| // The device cannot be reset at this time. We'll try again later. |
| return false; |
| } |
| |
| setDeviceIsLost(false); |
| |
| if (hr == D3D_OK) { |
| // The device wasn't lost after all. |
| return true; |
| } |
| |
| // We can reset the device. |
| |
| D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(size); |
| parameters.hDeviceWindow = window; |
| if (m_usingDirect3D9Ex) { |
| CComQIPtr<IDirect3DDevice9Ex> d3d9Ex = m_d3dDevice; |
| ASSERT(d3d9Ex); |
| hr = d3d9Ex->ResetEx(¶meters, nullptr); |
| } else |
| hr = m_d3dDevice->Reset(¶meters); |
| |
| // TestCooperativeLevel told us the device may be reset now, so we should |
| // not be told here that the device is lost. |
| ASSERT(hr != D3DERR_DEVICELOST); |
| |
| return true; |
| } |
| |
| |
| void CAD3DRenderer::createShaderIfNeeded() |
| { |
| if (m_pixelShader) |
| return; |
| |
| m_d3dDevice->CreatePixelShader(reinterpret_cast<const DWORD*>(g_ps20_InvertColorPS), &m_pixelShader); |
| } |
| |
| void CAD3DRenderer::release() |
| { |
| if (m_renderOGLContext) |
| CARenderOGLDestroy(m_renderOGLContext); |
| m_d3dDevice = nullptr; |
| if (s_d3d) |
| s_d3d->Release(); |
| } |
| |
| } // namespace WKQCA |