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