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