Home | History | Annotate | Download | only in wgl
      1 /**************************************************************************
      2  *
      3  * Copyright 2008-2009 Vmware, Inc.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 #include <windows.h>
     29 
     30 #include "pipe/p_screen.h"
     31 #include "util/u_memory.h"
     32 #include "hud/hud_context.h"
     33 #include "util/os_time.h"
     34 #include "state_tracker/st_api.h"
     35 
     36 #include "stw_icd.h"
     37 #include "stw_framebuffer.h"
     38 #include "stw_device.h"
     39 #include "stw_winsys.h"
     40 #include "stw_tls.h"
     41 #include "stw_context.h"
     42 #include "stw_st.h"
     43 
     44 
     45 /**
     46  * Search the framebuffer with the matching HWND while holding the
     47  * stw_dev::fb_mutex global lock.
     48  * If a stw_framebuffer is found, lock it and return the pointer.
     49  * Else, return NULL.
     50  */
     51 static struct stw_framebuffer *
     52 stw_framebuffer_from_hwnd_locked(HWND hwnd)
     53 {
     54    struct stw_framebuffer *fb;
     55 
     56    for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
     57       if (fb->hWnd == hwnd) {
     58          stw_framebuffer_lock(fb);
     59          assert(fb->mutex.RecursionCount == 1);
     60          return fb;
     61       }
     62 
     63    return NULL;
     64 }
     65 
     66 
     67 /**
     68  * Decrement the reference count on the given stw_framebuffer object.
     69  * If the reference count hits zero, destroy the object.
     70  *
     71  * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
     72  * locked.  After this function completes, the fb's mutex will be unlocked.
     73  */
     74 void
     75 stw_framebuffer_release_locked(struct stw_framebuffer *fb)
     76 {
     77    struct stw_framebuffer **link;
     78 
     79    assert(fb);
     80    assert(stw_own_mutex(&fb->mutex));
     81    assert(stw_own_mutex(&stw_dev->fb_mutex));
     82 
     83    /* check the reference count */
     84    fb->refcnt--;
     85    if (fb->refcnt) {
     86       stw_framebuffer_unlock(fb);
     87       return;
     88    }
     89 
     90    /* remove this stw_framebuffer from the device's linked list */
     91    link = &stw_dev->fb_head;
     92    while (*link != fb)
     93       link = &(*link)->next;
     94    assert(*link);
     95    *link = fb->next;
     96    fb->next = NULL;
     97 
     98    if (fb->shared_surface)
     99       stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
    100                                                 fb->shared_surface);
    101 
    102    stw_st_destroy_framebuffer_locked(fb->stfb);
    103 
    104    stw_framebuffer_unlock(fb);
    105 
    106    DeleteCriticalSection(&fb->mutex);
    107 
    108    FREE( fb );
    109 }
    110 
    111 
    112 /**
    113  * Query the size of the given framebuffer's on-screen window and update
    114  * the stw_framebuffer's width/height.
    115  */
    116 static void
    117 stw_framebuffer_get_size(struct stw_framebuffer *fb)
    118 {
    119    LONG width, height;
    120    RECT client_rect;
    121    RECT window_rect;
    122    POINT client_pos;
    123 
    124    /*
    125     * Sanity checking.
    126     */
    127    assert(fb->hWnd);
    128    assert(fb->width && fb->height);
    129    assert(fb->client_rect.right  == fb->client_rect.left + fb->width);
    130    assert(fb->client_rect.bottom == fb->client_rect.top  + fb->height);
    131 
    132    /*
    133     * Get the client area size.
    134     */
    135    if (!GetClientRect(fb->hWnd, &client_rect)) {
    136       return;
    137    }
    138 
    139    assert(client_rect.left == 0);
    140    assert(client_rect.top == 0);
    141    width  = client_rect.right  - client_rect.left;
    142    height = client_rect.bottom - client_rect.top;
    143 
    144    fb->minimized = width == 0 || height == 0;
    145 
    146    if (width <= 0 || height <= 0) {
    147       /*
    148        * When the window is minimized GetClientRect will return zeros.  Simply
    149        * preserve the current window size, until the window is restored or
    150        * maximized again.
    151        */
    152       return;
    153    }
    154 
    155    if (width != fb->width || height != fb->height) {
    156       fb->must_resize = TRUE;
    157       fb->width = width;
    158       fb->height = height;
    159    }
    160 
    161    client_pos.x = 0;
    162    client_pos.y = 0;
    163    if (ClientToScreen(fb->hWnd, &client_pos) &&
    164        GetWindowRect(fb->hWnd, &window_rect)) {
    165       fb->client_rect.left = client_pos.x - window_rect.left;
    166       fb->client_rect.top  = client_pos.y - window_rect.top;
    167    }
    168 
    169    fb->client_rect.right  = fb->client_rect.left + fb->width;
    170    fb->client_rect.bottom = fb->client_rect.top  + fb->height;
    171 
    172 #if 0
    173    debug_printf("\n");
    174    debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
    175    debug_printf("%s: client_position = (%li, %li)\n",
    176                 __FUNCTION__, client_pos.x, client_pos.y);
    177    debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
    178                 __FUNCTION__,
    179                 window_rect.left, window_rect.top,
    180                 window_rect.right, window_rect.bottom);
    181    debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
    182                 __FUNCTION__,
    183                 fb->client_rect.left, fb->client_rect.top,
    184                 fb->client_rect.right, fb->client_rect.bottom);
    185 #endif
    186 }
    187 
    188 
    189 /**
    190  * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
    191  * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
    192  */
    193 LRESULT CALLBACK
    194 stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)
    195 {
    196    struct stw_tls_data *tls_data;
    197    PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
    198    struct stw_framebuffer *fb;
    199 
    200    tls_data = stw_tls_get_data();
    201    if (!tls_data)
    202       return 0;
    203 
    204    if (nCode < 0 || !stw_dev)
    205        return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
    206 
    207    /* We check that the stw_dev object is initialized before we try to do
    208     * anything with it.  Otherwise, in multi-threaded programs there's a
    209     * chance of executing this code before the stw_dev object is fully
    210     * initialized.
    211     */
    212    if (stw_dev && stw_dev->initialized) {
    213       if (pParams->message == WM_WINDOWPOSCHANGED) {
    214          /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
    215           * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
    216           * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
    217           * can be masked out by the application.
    218           */
    219          LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
    220          if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
    221              !(lpWindowPos->flags & SWP_NOMOVE) ||
    222              !(lpWindowPos->flags & SWP_NOSIZE)) {
    223             fb = stw_framebuffer_from_hwnd( pParams->hwnd );
    224             if (fb) {
    225                /* Size in WINDOWPOS includes the window frame, so get the size
    226                 * of the client area via GetClientRect.
    227                 */
    228                stw_framebuffer_get_size(fb);
    229                stw_framebuffer_unlock(fb);
    230             }
    231          }
    232       }
    233       else if (pParams->message == WM_DESTROY) {
    234          stw_lock_framebuffers(stw_dev);
    235          fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
    236          if (fb)
    237             stw_framebuffer_release_locked(fb);
    238          stw_unlock_framebuffers(stw_dev);
    239       }
    240    }
    241 
    242    return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
    243 }
    244 
    245 
    246 /**
    247  * Create a new stw_framebuffer object which corresponds to the given
    248  * HDC/window.  If successful, we return the new stw_framebuffer object
    249  * with its mutex locked.
    250  */
    251 struct stw_framebuffer *
    252 stw_framebuffer_create(HDC hdc, int iPixelFormat)
    253 {
    254    HWND hWnd;
    255    struct stw_framebuffer *fb;
    256    const struct stw_pixelformat_info *pfi;
    257 
    258    /* We only support drawing to a window. */
    259    hWnd = WindowFromDC( hdc );
    260    if (!hWnd)
    261       return NULL;
    262 
    263    fb = CALLOC_STRUCT( stw_framebuffer );
    264    if (fb == NULL)
    265       return NULL;
    266 
    267    fb->hWnd = hWnd;
    268    fb->iPixelFormat = iPixelFormat;
    269 
    270    /*
    271     * We often need a displayable pixel format to make GDI happy. Set it
    272     * here (always 1, i.e., out first pixel format) where appropriate.
    273     */
    274    fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
    275       ? iPixelFormat : 1;
    276 
    277    fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
    278    fb->stfb = stw_st_create_framebuffer( fb );
    279    if (!fb->stfb) {
    280       FREE( fb );
    281       return NULL;
    282    }
    283 
    284    fb->refcnt = 1;
    285 
    286    /*
    287     * Windows can be sometimes have zero width and or height, but we ensure
    288     * a non-zero framebuffer size at all times.
    289     */
    290 
    291    fb->must_resize = TRUE;
    292    fb->width  = 1;
    293    fb->height = 1;
    294    fb->client_rect.left   = 0;
    295    fb->client_rect.top    = 0;
    296    fb->client_rect.right  = fb->client_rect.left + fb->width;
    297    fb->client_rect.bottom = fb->client_rect.top  + fb->height;
    298 
    299    stw_framebuffer_get_size(fb);
    300 
    301    InitializeCriticalSection(&fb->mutex);
    302 
    303    /* This is the only case where we lock the stw_framebuffer::mutex before
    304     * stw_dev::fb_mutex, since no other thread can know about this framebuffer
    305     * and we must prevent any other thread from destroying it before we return.
    306     */
    307    stw_framebuffer_lock(fb);
    308 
    309    stw_lock_framebuffers(stw_dev);
    310    fb->next = stw_dev->fb_head;
    311    stw_dev->fb_head = fb;
    312    stw_unlock_framebuffers(stw_dev);
    313 
    314    return fb;
    315 }
    316 
    317 
    318 /**
    319  * Update the framebuffer's size if necessary.
    320  */
    321 void
    322 stw_framebuffer_update(struct stw_framebuffer *fb)
    323 {
    324    assert(fb->stfb);
    325    assert(fb->height);
    326    assert(fb->width);
    327 
    328    /* XXX: It would be nice to avoid checking the size again -- in theory
    329     * stw_call_window_proc would have cought the resize and stored the right
    330     * size already, but unfortunately threads created before the DllMain is
    331     * called don't get a DLL_THREAD_ATTACH notification, and there is no way
    332     * to know of their existing without using the not very portable PSAPI.
    333     */
    334    stw_framebuffer_get_size(fb);
    335 }
    336 
    337 
    338 /**
    339  * Try to free all stw_framebuffer objects associated with the device.
    340  */
    341 void
    342 stw_framebuffer_cleanup(void)
    343 {
    344    struct stw_framebuffer *fb;
    345    struct stw_framebuffer *next;
    346 
    347    if (!stw_dev)
    348       return;
    349 
    350    stw_lock_framebuffers(stw_dev);
    351 
    352    fb = stw_dev->fb_head;
    353    while (fb) {
    354       next = fb->next;
    355 
    356       stw_framebuffer_lock(fb);
    357       stw_framebuffer_release_locked(fb);
    358 
    359       fb = next;
    360    }
    361    stw_dev->fb_head = NULL;
    362 
    363    stw_unlock_framebuffers(stw_dev);
    364 }
    365 
    366 
    367 /**
    368  * Given an hdc, return the corresponding stw_framebuffer.
    369  * The returned stw_framebuffer will have its mutex locked.
    370  */
    371 static struct stw_framebuffer *
    372 stw_framebuffer_from_hdc_locked(HDC hdc)
    373 {
    374    HWND hwnd;
    375 
    376    hwnd = WindowFromDC(hdc);
    377    if (!hwnd) {
    378       return NULL;
    379    }
    380 
    381    return stw_framebuffer_from_hwnd_locked(hwnd);
    382 }
    383 
    384 
    385 /**
    386  * Given an HDC, return the corresponding stw_framebuffer.
    387  * The returned stw_framebuffer will have its mutex locked.
    388  */
    389 struct stw_framebuffer *
    390 stw_framebuffer_from_hdc(HDC hdc)
    391 {
    392    struct stw_framebuffer *fb;
    393 
    394    if (!stw_dev)
    395       return NULL;
    396 
    397    stw_lock_framebuffers(stw_dev);
    398    fb = stw_framebuffer_from_hdc_locked(hdc);
    399    stw_unlock_framebuffers(stw_dev);
    400 
    401    return fb;
    402 }
    403 
    404 
    405 /**
    406  * Given an HWND, return the corresponding stw_framebuffer.
    407  * The returned stw_framebuffer will have its mutex locked.
    408  */
    409 struct stw_framebuffer *
    410 stw_framebuffer_from_hwnd(HWND hwnd)
    411 {
    412    struct stw_framebuffer *fb;
    413 
    414    stw_lock_framebuffers(stw_dev);
    415    fb = stw_framebuffer_from_hwnd_locked(hwnd);
    416    stw_unlock_framebuffers(stw_dev);
    417 
    418    return fb;
    419 }
    420 
    421 
    422 BOOL APIENTRY
    423 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
    424 {
    425    uint count;
    426    uint index;
    427    struct stw_framebuffer *fb;
    428 
    429    if (!stw_dev)
    430       return FALSE;
    431 
    432    index = (uint) iPixelFormat - 1;
    433    count = stw_pixelformat_get_count();
    434    if (index >= count)
    435       return FALSE;
    436 
    437    fb = stw_framebuffer_from_hdc_locked(hdc);
    438    if (fb) {
    439       /*
    440        * SetPixelFormat must be called only once.  However ignore
    441        * pbuffers, for which the framebuffer object is created first.
    442        */
    443       boolean bPbuffer = fb->bPbuffer;
    444 
    445       stw_framebuffer_unlock( fb );
    446 
    447       return bPbuffer;
    448    }
    449 
    450    fb = stw_framebuffer_create(hdc, iPixelFormat);
    451    if (!fb) {
    452       return FALSE;
    453    }
    454 
    455    stw_framebuffer_unlock( fb );
    456 
    457    /* Some applications mistakenly use the undocumented wglSetPixelFormat
    458     * function instead of SetPixelFormat, so we call SetPixelFormat here to
    459     * avoid opengl32.dll's wglCreateContext to fail */
    460    if (GetPixelFormat(hdc) == 0) {
    461       BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
    462       if (!bRet) {
    463 	  debug_printf("SetPixelFormat failed\n");
    464       }
    465    }
    466 
    467    return TRUE;
    468 }
    469 
    470 
    471 int
    472 stw_pixelformat_get(HDC hdc)
    473 {
    474    int iPixelFormat = 0;
    475    struct stw_framebuffer *fb;
    476 
    477    fb = stw_framebuffer_from_hdc(hdc);
    478    if (fb) {
    479       iPixelFormat = fb->iPixelFormat;
    480       stw_framebuffer_unlock(fb);
    481    }
    482 
    483    return iPixelFormat;
    484 }
    485 
    486 
    487 BOOL APIENTRY
    488 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
    489 {
    490    struct stw_framebuffer *fb;
    491    struct pipe_screen *screen;
    492    struct pipe_resource *res;
    493 
    494    if (!stw_dev)
    495       return FALSE;
    496 
    497    fb = stw_framebuffer_from_hdc( hdc );
    498    if (fb == NULL)
    499       return FALSE;
    500 
    501    screen = stw_dev->screen;
    502 
    503    res = (struct pipe_resource *)data->pPrivateData;
    504 
    505    if (data->hSharedSurface != fb->hSharedSurface) {
    506       if (fb->shared_surface) {
    507          stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
    508          fb->shared_surface = NULL;
    509       }
    510 
    511       fb->hSharedSurface = data->hSharedSurface;
    512 
    513       if (data->hSharedSurface &&
    514          stw_dev->stw_winsys->shared_surface_open) {
    515          fb->shared_surface =
    516             stw_dev->stw_winsys->shared_surface_open(screen,
    517                                                      fb->hSharedSurface);
    518       }
    519    }
    520 
    521    if (!fb->minimized) {
    522       if (fb->shared_surface) {
    523          stw_dev->stw_winsys->compose(screen,
    524                                       res,
    525                                       fb->shared_surface,
    526                                       &fb->client_rect,
    527                                       data->PresentHistoryToken);
    528       }
    529       else {
    530          stw_dev->stw_winsys->present( screen, res, hdc );
    531       }
    532    }
    533 
    534    stw_framebuffer_update(fb);
    535    stw_notify_current_locked(fb);
    536 
    537    stw_framebuffer_unlock(fb);
    538 
    539    return TRUE;
    540 }
    541 
    542 
    543 /**
    544  * Queue a composition.
    545  *
    546  * The stw_framebuffer object must have its mutex locked.  The mutex will
    547  * be unlocked here before returning.
    548  */
    549 BOOL
    550 stw_framebuffer_present_locked(HDC hdc,
    551                                struct stw_framebuffer *fb,
    552                                struct pipe_resource *res)
    553 {
    554    if (stw_dev->callbacks.wglCbPresentBuffers &&
    555       stw_dev->stw_winsys->compose) {
    556       GLCBPRESENTBUFFERSDATA data;
    557 
    558       memset(&data, 0, sizeof data);
    559       data.magic1 = 2;
    560       data.magic2 = 0;
    561       data.AdapterLuid = stw_dev->AdapterLuid;
    562       data.rect = fb->client_rect;
    563       data.pPrivateData = (void *)res;
    564 
    565       stw_notify_current_locked(fb);
    566       stw_framebuffer_unlock(fb);
    567 
    568       return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
    569    }
    570    else {
    571       struct pipe_screen *screen = stw_dev->screen;
    572 
    573       stw_dev->stw_winsys->present( screen, res, hdc );
    574 
    575       stw_framebuffer_update(fb);
    576       stw_notify_current_locked(fb);
    577       stw_framebuffer_unlock(fb);
    578 
    579       return TRUE;
    580    }
    581 }
    582 
    583 
    584 /**
    585  * This is called just before issuing the buffer swap/present.
    586  * We query the current time and determine if we should sleep before
    587  * issuing the swap/present.
    588  * This is a bit of a hack and is certainly not very accurate but it
    589  * basically works.
    590  * This is for the WGL_ARB_swap_interval extension.
    591  */
    592 static void
    593 wait_swap_interval(struct stw_framebuffer *fb)
    594 {
    595    /* Note: all time variables here are in units of microseconds */
    596    int64_t cur_time = os_time_get_nano() / 1000;
    597 
    598    if (fb->prev_swap_time != 0) {
    599       /* Compute time since previous swap */
    600       int64_t delta = cur_time - fb->prev_swap_time;
    601       int64_t min_swap_period =
    602          1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval;
    603 
    604       /* If time since last swap is less than wait period, wait.
    605        * Note that it's possible for the delta to be negative because of
    606        * rollover.  See https://bugs.freedesktop.org/show_bug.cgi?id=102241
    607        */
    608       if ((delta >= 0) && (delta < min_swap_period)) {
    609          float fudge = 1.75f;  /* emperical fudge factor */
    610          int64_t wait = (min_swap_period - delta) * fudge;
    611          os_time_sleep(wait);
    612       }
    613    }
    614 
    615    fb->prev_swap_time = cur_time;
    616 }
    617 
    618 
    619 BOOL APIENTRY
    620 DrvSwapBuffers(HDC hdc)
    621 {
    622    struct stw_context *ctx;
    623    struct stw_framebuffer *fb;
    624 
    625    if (!stw_dev)
    626       return FALSE;
    627 
    628    fb = stw_framebuffer_from_hdc( hdc );
    629    if (fb == NULL)
    630       return FALSE;
    631 
    632    if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
    633       stw_framebuffer_unlock(fb);
    634       return TRUE;
    635    }
    636 
    637    ctx = stw_current_context();
    638    if (ctx) {
    639       if (ctx->hud) {
    640          /* Display the HUD */
    641          struct pipe_resource *back =
    642             stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
    643          if (back) {
    644             hud_run(ctx->hud, NULL, back);
    645          }
    646       }
    647 
    648       if (ctx->current_framebuffer == fb) {
    649          /* flush current context */
    650          ctx->st->flush(ctx->st, ST_FLUSH_END_OF_FRAME, NULL);
    651       }
    652    }
    653 
    654    if (stw_dev->swap_interval != 0) {
    655       wait_swap_interval(fb);
    656    }
    657 
    658    return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
    659 }
    660 
    661 
    662 BOOL APIENTRY
    663 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
    664 {
    665    if (fuPlanes & WGL_SWAP_MAIN_PLANE)
    666       return DrvSwapBuffers(hdc);
    667 
    668    return FALSE;
    669 }
    670