Home | History | Annotate | Download | only in d3d9
      1 //
      2 // Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 
      7 // SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain.
      8 
      9 #include "libGLESv2/renderer/d3d/d3d9/SwapChain9.h"
     10 #include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h"
     11 #include "libGLESv2/renderer/d3d/d3d9/formatutils9.h"
     12 #include "libGLESv2/renderer/d3d/d3d9/Renderer9.h"
     13 
     14 namespace rx
     15 {
     16 
     17 SwapChain9::SwapChain9(Renderer9 *renderer, HWND window, HANDLE shareHandle,
     18                        GLenum backBufferFormat, GLenum depthBufferFormat)
     19     : mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat)
     20 {
     21     mSwapChain = NULL;
     22     mBackBuffer = NULL;
     23     mDepthStencil = NULL;
     24     mRenderTarget = NULL;
     25     mOffscreenTexture = NULL;
     26     mWidth = -1;
     27     mHeight = -1;
     28     mSwapInterval = -1;
     29 }
     30 
     31 SwapChain9::~SwapChain9()
     32 {
     33     release();
     34 }
     35 
     36 void SwapChain9::release()
     37 {
     38     SafeRelease(mSwapChain);
     39     SafeRelease(mBackBuffer);
     40     SafeRelease(mDepthStencil);
     41     SafeRelease(mRenderTarget);
     42     SafeRelease(mOffscreenTexture);
     43 
     44     if (mWindow)
     45     {
     46         mShareHandle = NULL;
     47     }
     48 }
     49 
     50 static DWORD convertInterval(EGLint interval)
     51 {
     52 #ifdef ANGLE_FORCE_VSYNC_OFF
     53     return D3DPRESENT_INTERVAL_IMMEDIATE;
     54 #else
     55     switch(interval)
     56     {
     57       case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
     58       case 1: return D3DPRESENT_INTERVAL_ONE;
     59       case 2: return D3DPRESENT_INTERVAL_TWO;
     60       case 3: return D3DPRESENT_INTERVAL_THREE;
     61       case 4: return D3DPRESENT_INTERVAL_FOUR;
     62       default: UNREACHABLE();
     63     }
     64 
     65     return D3DPRESENT_INTERVAL_DEFAULT;
     66 #endif
     67 }
     68 
     69 EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight)
     70 {
     71     // D3D9 does not support resizing swap chains without recreating them
     72     return reset(backbufferWidth, backbufferHeight, mSwapInterval);
     73 }
     74 
     75 EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
     76 {
     77     IDirect3DDevice9 *device = mRenderer->getDevice();
     78 
     79     if (device == NULL)
     80     {
     81         return EGL_BAD_ACCESS;
     82     }
     83 
     84     // Evict all non-render target textures to system memory and release all resources
     85     // before reallocating them to free up as much video memory as possible.
     86     device->EvictManagedResources();
     87 
     88     HRESULT result;
     89 
     90     // Release specific resources to free up memory for the new render target, while the
     91     // old render target still exists for the purpose of preserving its contents.
     92     SafeRelease(mSwapChain);
     93     SafeRelease(mBackBuffer);
     94     SafeRelease(mOffscreenTexture);
     95     SafeRelease(mDepthStencil);
     96 
     97     HANDLE *pShareHandle = NULL;
     98     if (!mWindow && mRenderer->getShareHandleSupport())
     99     {
    100         pShareHandle = &mShareHandle;
    101     }
    102 
    103     const d3d9::TextureFormat &backBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mBackBufferFormat);
    104     result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
    105                                    backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT, &mOffscreenTexture,
    106                                    pShareHandle);
    107     if (FAILED(result))
    108     {
    109         ERR("Could not create offscreen texture: %08lX", result);
    110         release();
    111 
    112         if (d3d9::isDeviceLostError(result))
    113         {
    114             return EGL_CONTEXT_LOST;
    115         }
    116         else
    117         {
    118             return EGL_BAD_ALLOC;
    119         }
    120     }
    121 
    122     IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
    123 
    124     result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
    125     ASSERT(SUCCEEDED(result));
    126 
    127     if (oldRenderTarget)
    128     {
    129         RECT rect =
    130         {
    131             0, 0,
    132             mWidth, mHeight
    133         };
    134 
    135         if (rect.right > static_cast<LONG>(backbufferWidth))
    136         {
    137             rect.right = backbufferWidth;
    138         }
    139 
    140         if (rect.bottom > static_cast<LONG>(backbufferHeight))
    141         {
    142             rect.bottom = backbufferHeight;
    143         }
    144 
    145         mRenderer->endScene();
    146 
    147         result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
    148         ASSERT(SUCCEEDED(result));
    149 
    150         SafeRelease(oldRenderTarget);
    151     }
    152 
    153     const d3d9::TextureFormat &depthBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mDepthBufferFormat);
    154 
    155     if (mWindow)
    156     {
    157         D3DPRESENT_PARAMETERS presentParameters = {0};
    158         presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat;
    159         presentParameters.BackBufferCount = 1;
    160         presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat;
    161         presentParameters.EnableAutoDepthStencil = FALSE;
    162         presentParameters.Flags = 0;
    163         presentParameters.hDeviceWindow = mWindow;
    164         presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
    165         presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
    166         presentParameters.PresentationInterval = convertInterval(swapInterval);
    167         presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    168         presentParameters.Windowed = TRUE;
    169         presentParameters.BackBufferWidth = backbufferWidth;
    170         presentParameters.BackBufferHeight = backbufferHeight;
    171 
    172         // http://crbug.com/140239
    173         // http://crbug.com/143434
    174         //
    175         // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
    176         // when using the integrated Intel. This rounds the width up rather than down.
    177         //
    178         // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
    179         // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
    180         if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL)
    181         {
    182             presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
    183         }
    184 
    185         result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
    186 
    187         if (FAILED(result))
    188         {
    189             ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
    190 
    191             ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
    192             release();
    193 
    194             if (d3d9::isDeviceLostError(result))
    195             {
    196                 return EGL_CONTEXT_LOST;
    197             }
    198             else
    199             {
    200                 return EGL_BAD_ALLOC;
    201             }
    202         }
    203 
    204         result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
    205         ASSERT(SUCCEEDED(result));
    206         InvalidateRect(mWindow, NULL, FALSE);
    207     }
    208 
    209     if (mDepthBufferFormat != GL_NONE)
    210     {
    211         result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
    212                                                    depthBufferd3dFormatInfo.renderFormat,
    213                                                    D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
    214 
    215         if (FAILED(result))
    216         {
    217             ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
    218 
    219             ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
    220             release();
    221 
    222             if (d3d9::isDeviceLostError(result))
    223             {
    224                 return EGL_CONTEXT_LOST;
    225             }
    226             else
    227             {
    228                 return EGL_BAD_ALLOC;
    229             }
    230         }
    231     }
    232 
    233     mWidth = backbufferWidth;
    234     mHeight = backbufferHeight;
    235     mSwapInterval = swapInterval;
    236 
    237     return EGL_SUCCESS;
    238 }
    239 
    240 // parameters should be validated/clamped by caller
    241 EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
    242 {
    243     if (!mSwapChain)
    244     {
    245         return EGL_SUCCESS;
    246     }
    247 
    248     IDirect3DDevice9 *device = mRenderer->getDevice();
    249 
    250     // Disable all pipeline operations
    251     device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    252     device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    253     device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    254     device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    255     device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    256     device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
    257     device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
    258     device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
    259     device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
    260     device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
    261     device->SetPixelShader(NULL);
    262     device->SetVertexShader(NULL);
    263 
    264     device->SetRenderTarget(0, mBackBuffer);
    265     device->SetDepthStencilSurface(NULL);
    266 
    267     device->SetTexture(0, mOffscreenTexture);
    268     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    269     device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    270     device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    271     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
    272     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
    273     device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    274     device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
    275     device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
    276 
    277     for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++)
    278     {
    279         device->SetStreamSourceFreq(streamIndex, 1);
    280     }
    281 
    282     D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
    283     device->SetViewport(&viewport);
    284 
    285     float x1 = x - 0.5f;
    286     float y1 = (mHeight - y - height) - 0.5f;
    287     float x2 = (x + width) - 0.5f;
    288     float y2 = (mHeight - y) - 0.5f;
    289 
    290     float u1 = x / float(mWidth);
    291     float v1 = y / float(mHeight);
    292     float u2 = (x + width) / float(mWidth);
    293     float v2 = (y + height) / float(mHeight);
    294 
    295     float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
    296                         {x2, y1, 0.0f, 1.0f, u2, v2},
    297                         {x2, y2, 0.0f, 1.0f, u2, v1},
    298                         {x1, y2, 0.0f, 1.0f, u1, v1}};   // x, y, z, rhw, u, v
    299 
    300     mRenderer->startScene();
    301     device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
    302     mRenderer->endScene();
    303 
    304     device->SetTexture(0, NULL);
    305 
    306     RECT rect =
    307     {
    308         x, mHeight - y - height,
    309         x + width, mHeight - y
    310     };
    311 
    312     HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
    313 
    314     mRenderer->markAllStateDirty();
    315 
    316     if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
    317     {
    318         return EGL_BAD_ALLOC;
    319     }
    320 
    321     // On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the windows is
    322     // in the process of entering/exiting fullscreen. This code doesn't seem to have any documentation.  The
    323     // device appears to be ok after emitting this error so simply return a failure to swap.
    324     if (result == 0x88760873)
    325     {
    326         return EGL_BAD_MATCH;
    327     }
    328 
    329     // http://crbug.com/313210
    330     // If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific
    331     // device removed bug with lost contexts when reinstalling drivers.
    332     if (FAILED(result))
    333     {
    334         mRenderer->notifyDeviceLost();
    335         return EGL_CONTEXT_LOST;
    336     }
    337 
    338     return EGL_SUCCESS;
    339 }
    340 
    341 // Increments refcount on surface.
    342 // caller must Release() the returned surface
    343 // TODO: remove the AddRef to match SwapChain11
    344 IDirect3DSurface9 *SwapChain9::getRenderTarget()
    345 {
    346     if (mRenderTarget)
    347     {
    348         mRenderTarget->AddRef();
    349     }
    350 
    351     return mRenderTarget;
    352 }
    353 
    354 // Increments refcount on surface.
    355 // caller must Release() the returned surface
    356 // TODO: remove the AddRef to match SwapChain11
    357 IDirect3DSurface9 *SwapChain9::getDepthStencil()
    358 {
    359     if (mDepthStencil)
    360     {
    361         mDepthStencil->AddRef();
    362     }
    363 
    364     return mDepthStencil;
    365 }
    366 
    367 // Increments refcount on texture.
    368 // caller must Release() the returned texture
    369 // TODO: remove the AddRef to match SwapChain11
    370 IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
    371 {
    372     if (mOffscreenTexture)
    373     {
    374         mOffscreenTexture->AddRef();
    375     }
    376 
    377     return mOffscreenTexture;
    378 }
    379 
    380 SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain)
    381 {
    382     ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain));
    383     return static_cast<rx::SwapChain9*>(swapChain);
    384 }
    385 
    386 void SwapChain9::recreate()
    387 {
    388     if (!mSwapChain)
    389     {
    390         return;
    391     }
    392 
    393     IDirect3DDevice9 *device = mRenderer->getDevice();
    394     if (device == NULL)
    395     {
    396         return;
    397     }
    398 
    399     D3DPRESENT_PARAMETERS presentParameters;
    400     HRESULT result = mSwapChain->GetPresentParameters(&presentParameters);
    401     ASSERT(SUCCEEDED(result));
    402 
    403     IDirect3DSwapChain9* newSwapChain = NULL;
    404     result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain);
    405     if (FAILED(result))
    406     {
    407         return;
    408     }
    409 
    410     SafeRelease(mSwapChain);
    411     mSwapChain = newSwapChain;
    412 
    413     SafeRelease(mBackBuffer);
    414     result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
    415     ASSERT(SUCCEEDED(result));
    416 }
    417 
    418 }
    419