Path: blob/21.2-virgl/src/gallium/frontends/wgl/stw_framebuffer.c
4561 views
/**************************************************************************1*2* Copyright 2008-2009 VMware, Inc.3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS18* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.20* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR21* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,22* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE23* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.24*25**************************************************************************/2627#include <windows.h>2829#include "pipe/p_screen.h"30#include "pipe/p_state.h"31#include "util/u_memory.h"32#include "hud/hud_context.h"33#include "util/os_time.h"34#include "frontend/api.h"3536#include <GL/gl.h>37#include "gldrv.h"38#include "stw_framebuffer.h"39#include "stw_device.h"40#include "stw_winsys.h"41#include "stw_tls.h"42#include "stw_context.h"43#include "stw_st.h"444546/**47* Search the framebuffer with the matching HWND while holding the48* stw_dev::fb_mutex global lock.49* If a stw_framebuffer is found, lock it and return the pointer.50* Else, return NULL.51*/52static struct stw_framebuffer *53stw_framebuffer_from_hwnd_locked(HWND hwnd)54{55struct stw_framebuffer *fb;5657for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)58if (fb->hWnd == hwnd) {59stw_framebuffer_lock(fb);60assert(fb->mutex.RecursionCount == 1);61return fb;62}6364return NULL;65}666768/**69* Decrement the reference count on the given stw_framebuffer object.70* If the reference count hits zero, destroy the object.71*72* Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be73* locked. After this function completes, the fb's mutex will be unlocked.74*/75void76stw_framebuffer_release_locked(struct stw_framebuffer *fb,77struct st_context_iface *stctx)78{79struct stw_framebuffer **link;8081assert(fb);82assert(stw_own_mutex(&fb->mutex));83assert(stw_own_mutex(&stw_dev->fb_mutex));8485/* check the reference count */86fb->refcnt--;87if (fb->refcnt) {88stw_framebuffer_unlock(fb);89return;90}9192/* remove this stw_framebuffer from the device's linked list */93link = &stw_dev->fb_head;94while (*link != fb)95link = &(*link)->next;96assert(*link);97*link = fb->next;98fb->next = NULL;99100if (fb->shared_surface)101stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,102fb->shared_surface);103104if (fb->winsys_framebuffer)105fb->winsys_framebuffer->destroy(fb->winsys_framebuffer, stctx ? stctx->pipe : NULL);106107stw_st_destroy_framebuffer_locked(fb->stfb);108109stw_framebuffer_unlock(fb);110111DeleteCriticalSection(&fb->mutex);112113FREE( fb );114}115116117/**118* Query the size of the given framebuffer's on-screen window and update119* the stw_framebuffer's width/height.120*/121static void122stw_framebuffer_get_size(struct stw_framebuffer *fb)123{124LONG width, height;125RECT client_rect;126RECT window_rect;127POINT client_pos;128129/*130* Sanity checking.131*/132assert(fb->hWnd);133assert(fb->width && fb->height);134assert(fb->client_rect.right == fb->client_rect.left + fb->width);135assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);136137/*138* Get the client area size.139*/140if (!GetClientRect(fb->hWnd, &client_rect)) {141return;142}143144assert(client_rect.left == 0);145assert(client_rect.top == 0);146width = client_rect.right - client_rect.left;147height = client_rect.bottom - client_rect.top;148149fb->minimized = width == 0 || height == 0;150151if (width <= 0 || height <= 0) {152/*153* When the window is minimized GetClientRect will return zeros. Simply154* preserve the current window size, until the window is restored or155* maximized again.156*/157return;158}159160if (width != fb->width || height != fb->height) {161fb->must_resize = TRUE;162fb->width = width;163fb->height = height;164}165166client_pos.x = 0;167client_pos.y = 0;168if (ClientToScreen(fb->hWnd, &client_pos) &&169GetWindowRect(fb->hWnd, &window_rect)) {170fb->client_rect.left = client_pos.x - window_rect.left;171fb->client_rect.top = client_pos.y - window_rect.top;172}173174fb->client_rect.right = fb->client_rect.left + fb->width;175fb->client_rect.bottom = fb->client_rect.top + fb->height;176177#if 0178debug_printf("\n");179debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);180debug_printf("%s: client_position = (%li, %li)\n",181__FUNCTION__, client_pos.x, client_pos.y);182debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",183__FUNCTION__,184window_rect.left, window_rect.top,185window_rect.right, window_rect.bottom);186debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",187__FUNCTION__,188fb->client_rect.left, fb->client_rect.top,189fb->client_rect.right, fb->client_rect.bottom);190#endif191}192193194/**195* @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx196* @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx197*/198LRESULT CALLBACK199stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)200{201struct stw_tls_data *tls_data;202PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;203struct stw_framebuffer *fb;204205tls_data = stw_tls_get_data();206if (!tls_data)207return 0;208209if (nCode < 0 || !stw_dev)210return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);211212/* We check that the stw_dev object is initialized before we try to do213* anything with it. Otherwise, in multi-threaded programs there's a214* chance of executing this code before the stw_dev object is fully215* initialized.216*/217if (stw_dev && stw_dev->initialized) {218if (pParams->message == WM_WINDOWPOSCHANGED) {219/* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according220* to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx221* WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it222* can be masked out by the application.223*/224LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;225if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||226!(lpWindowPos->flags & SWP_NOMOVE) ||227!(lpWindowPos->flags & SWP_NOSIZE)) {228fb = stw_framebuffer_from_hwnd( pParams->hwnd );229if (fb) {230/* Size in WINDOWPOS includes the window frame, so get the size231* of the client area via GetClientRect.232*/233stw_framebuffer_get_size(fb);234stw_framebuffer_unlock(fb);235}236}237}238else if (pParams->message == WM_DESTROY) {239stw_lock_framebuffers(stw_dev);240fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );241if (fb) {242struct stw_context *current_context = stw_current_context();243struct st_context_iface *ctx_iface = current_context &&244current_context->current_framebuffer == fb ? current_context->st : NULL;245stw_framebuffer_release_locked(fb, ctx_iface);246}247stw_unlock_framebuffers(stw_dev);248}249}250251return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);252}253254255/**256* Create a new stw_framebuffer object which corresponds to the given257* HDC/window. If successful, we return the new stw_framebuffer object258* with its mutex locked.259*/260struct stw_framebuffer *261stw_framebuffer_create(HDC hdc, int iPixelFormat)262{263HWND hWnd;264struct stw_framebuffer *fb;265const struct stw_pixelformat_info *pfi;266267/* We only support drawing to a window. */268hWnd = WindowFromDC( hdc );269if (!hWnd)270return NULL;271272fb = CALLOC_STRUCT( stw_framebuffer );273if (fb == NULL)274return NULL;275276fb->hWnd = hWnd;277fb->iPixelFormat = iPixelFormat;278279if (stw_dev->stw_winsys->create_framebuffer)280fb->winsys_framebuffer =281stw_dev->stw_winsys->create_framebuffer(stw_dev->screen, hdc, iPixelFormat);282283/*284* We often need a displayable pixel format to make GDI happy. Set it285* here (always 1, i.e., out first pixel format) where appropriate.286*/287fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count288? iPixelFormat : 1;289290fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );291fb->stfb = stw_st_create_framebuffer( fb );292if (!fb->stfb) {293FREE( fb );294return NULL;295}296297fb->refcnt = 1;298299/*300* Windows can be sometimes have zero width and or height, but we ensure301* a non-zero framebuffer size at all times.302*/303304fb->must_resize = TRUE;305fb->width = 1;306fb->height = 1;307fb->client_rect.left = 0;308fb->client_rect.top = 0;309fb->client_rect.right = fb->client_rect.left + fb->width;310fb->client_rect.bottom = fb->client_rect.top + fb->height;311312stw_framebuffer_get_size(fb);313314InitializeCriticalSection(&fb->mutex);315316/* This is the only case where we lock the stw_framebuffer::mutex before317* stw_dev::fb_mutex, since no other thread can know about this framebuffer318* and we must prevent any other thread from destroying it before we return.319*/320stw_framebuffer_lock(fb);321322stw_lock_framebuffers(stw_dev);323fb->next = stw_dev->fb_head;324stw_dev->fb_head = fb;325stw_unlock_framebuffers(stw_dev);326327return fb;328}329330331/**332* Update the framebuffer's size if necessary.333*/334void335stw_framebuffer_update(struct stw_framebuffer *fb)336{337assert(fb->stfb);338assert(fb->height);339assert(fb->width);340341/* XXX: It would be nice to avoid checking the size again -- in theory342* stw_call_window_proc would have cought the resize and stored the right343* size already, but unfortunately threads created before the DllMain is344* called don't get a DLL_THREAD_ATTACH notification, and there is no way345* to know of their existing without using the not very portable PSAPI.346*/347stw_framebuffer_get_size(fb);348}349350351/**352* Try to free all stw_framebuffer objects associated with the device.353*/354void355stw_framebuffer_cleanup(void)356{357struct stw_framebuffer *fb;358struct stw_framebuffer *next;359360if (!stw_dev)361return;362363stw_lock_framebuffers(stw_dev);364365fb = stw_dev->fb_head;366while (fb) {367next = fb->next;368369stw_framebuffer_lock(fb);370stw_framebuffer_release_locked(fb, NULL);371372fb = next;373}374stw_dev->fb_head = NULL;375376stw_unlock_framebuffers(stw_dev);377}378379380/**381* Given an hdc, return the corresponding stw_framebuffer.382* The returned stw_framebuffer will have its mutex locked.383*/384static struct stw_framebuffer *385stw_framebuffer_from_hdc_locked(HDC hdc)386{387HWND hwnd;388389hwnd = WindowFromDC(hdc);390if (!hwnd) {391return NULL;392}393394return stw_framebuffer_from_hwnd_locked(hwnd);395}396397398/**399* Given an HDC, return the corresponding stw_framebuffer.400* The returned stw_framebuffer will have its mutex locked.401*/402struct stw_framebuffer *403stw_framebuffer_from_hdc(HDC hdc)404{405struct stw_framebuffer *fb;406407if (!stw_dev)408return NULL;409410stw_lock_framebuffers(stw_dev);411fb = stw_framebuffer_from_hdc_locked(hdc);412stw_unlock_framebuffers(stw_dev);413414return fb;415}416417418/**419* Given an HWND, return the corresponding stw_framebuffer.420* The returned stw_framebuffer will have its mutex locked.421*/422struct stw_framebuffer *423stw_framebuffer_from_hwnd(HWND hwnd)424{425struct stw_framebuffer *fb;426427stw_lock_framebuffers(stw_dev);428fb = stw_framebuffer_from_hwnd_locked(hwnd);429stw_unlock_framebuffers(stw_dev);430431return fb;432}433434435BOOL APIENTRY436DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)437{438uint count;439uint index;440struct stw_framebuffer *fb;441442if (!stw_dev)443return FALSE;444445index = (uint) iPixelFormat - 1;446count = stw_pixelformat_get_count(hdc);447if (index >= count)448return FALSE;449450fb = stw_framebuffer_from_hdc_locked(hdc);451if (fb) {452/*453* SetPixelFormat must be called only once. However ignore454* pbuffers, for which the framebuffer object is created first.455*/456boolean bPbuffer = fb->bPbuffer;457458stw_framebuffer_unlock( fb );459460return bPbuffer;461}462463fb = stw_framebuffer_create(hdc, iPixelFormat);464if (!fb) {465return FALSE;466}467468stw_framebuffer_unlock( fb );469470/* Some applications mistakenly use the undocumented wglSetPixelFormat471* function instead of SetPixelFormat, so we call SetPixelFormat here to472* avoid opengl32.dll's wglCreateContext to fail */473if (GetPixelFormat(hdc) == 0) {474BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);475if (!bRet) {476debug_printf("SetPixelFormat failed\n");477}478}479480return TRUE;481}482483484int485stw_pixelformat_get(HDC hdc)486{487int iPixelFormat = 0;488struct stw_framebuffer *fb;489490fb = stw_framebuffer_from_hdc(hdc);491if (fb) {492iPixelFormat = fb->iPixelFormat;493stw_framebuffer_unlock(fb);494}495496return iPixelFormat;497}498499500BOOL APIENTRY501DrvPresentBuffers(HDC hdc, LPPRESENTBUFFERS data)502{503struct stw_framebuffer *fb;504struct stw_context *ctx;505struct pipe_screen *screen;506struct pipe_context *pipe;507struct pipe_resource *res;508509if (!stw_dev)510return FALSE;511512fb = stw_framebuffer_from_hdc( hdc );513if (fb == NULL)514return FALSE;515516screen = stw_dev->screen;517ctx = stw_current_context();518pipe = ctx ? ctx->st->pipe : NULL;519520res = (struct pipe_resource *)data->pPrivData;521522if (data->hSurface != fb->hSharedSurface) {523if (fb->shared_surface) {524stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);525fb->shared_surface = NULL;526}527528fb->hSharedSurface = data->hSurface;529530if (data->hSurface &&531stw_dev->stw_winsys->shared_surface_open) {532fb->shared_surface =533stw_dev->stw_winsys->shared_surface_open(screen,534fb->hSharedSurface);535}536}537538if (!fb->minimized) {539if (fb->shared_surface) {540stw_dev->stw_winsys->compose(screen,541res,542fb->shared_surface,543&fb->client_rect,544data->ullPresentToken);545}546else {547stw_dev->stw_winsys->present( screen, pipe, res, hdc );548}549}550551stw_framebuffer_update(fb);552stw_notify_current_locked(fb);553554stw_framebuffer_unlock(fb);555556return TRUE;557}558559560/**561* Queue a composition.562*563* The stw_framebuffer object must have its mutex locked. The mutex will564* be unlocked here before returning.565*/566BOOL567stw_framebuffer_present_locked(HDC hdc,568struct stw_framebuffer *fb,569struct pipe_resource *res)570{571if (fb->winsys_framebuffer) {572BOOL result = fb->winsys_framebuffer->present(fb->winsys_framebuffer);573574stw_framebuffer_update(fb);575stw_notify_current_locked(fb);576stw_framebuffer_unlock(fb);577578return result;579}580else if (stw_dev->callbacks.pfnPresentBuffers &&581stw_dev->stw_winsys->compose) {582PRESENTBUFFERSCB data;583584memset(&data, 0, sizeof data);585data.nVersion = 2;586data.syncType = PRESCB_SYNCTYPE_NONE;587data.luidAdapter = stw_dev->AdapterLuid;588data.updateRect = fb->client_rect;589data.pPrivData = (void *)res;590591stw_notify_current_locked(fb);592stw_framebuffer_unlock(fb);593594return stw_dev->callbacks.pfnPresentBuffers(hdc, &data);595}596else {597struct pipe_screen *screen = stw_dev->screen;598struct stw_context *ctx = stw_current_context();599struct pipe_context *pipe = ctx ? ctx->st->pipe : NULL;600601stw_dev->stw_winsys->present( screen, pipe, res, hdc );602603stw_framebuffer_update(fb);604stw_notify_current_locked(fb);605stw_framebuffer_unlock(fb);606607return TRUE;608}609}610611612/**613* This is called just before issuing the buffer swap/present.614* We query the current time and determine if we should sleep before615* issuing the swap/present.616* This is a bit of a hack and is certainly not very accurate but it617* basically works.618* This is for the WGL_ARB_swap_interval extension.619*/620static void621wait_swap_interval(struct stw_framebuffer *fb)622{623/* Note: all time variables here are in units of microseconds */624int64_t cur_time = os_time_get_nano() / 1000;625626if (fb->prev_swap_time != 0) {627/* Compute time since previous swap */628int64_t delta = cur_time - fb->prev_swap_time;629int64_t min_swap_period =6301.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval;631632/* If time since last swap is less than wait period, wait.633* Note that it's possible for the delta to be negative because of634* rollover. See https://bugs.freedesktop.org/show_bug.cgi?id=102241635*/636if ((delta >= 0) && (delta < min_swap_period)) {637float fudge = 1.75f; /* emperical fudge factor */638int64_t wait = (min_swap_period - delta) * fudge;639os_time_sleep(wait);640}641}642643fb->prev_swap_time = cur_time;644}645646647BOOL APIENTRY648DrvSwapBuffers(HDC hdc)649{650struct stw_context *ctx;651struct stw_framebuffer *fb;652653if (!stw_dev)654return FALSE;655656fb = stw_framebuffer_from_hdc( hdc );657if (fb == NULL)658return FALSE;659660if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {661stw_framebuffer_unlock(fb);662return TRUE;663}664665ctx = stw_current_context();666if (ctx) {667if (ctx->hud) {668/* Display the HUD */669struct pipe_resource *back =670stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);671if (back) {672hud_run(ctx->hud, NULL, back);673}674}675676if (ctx->current_framebuffer == fb) {677/* flush current context */678stw_st_flush(ctx->st, fb->stfb, ST_FLUSH_END_OF_FRAME);679}680}681682if (stw_dev->swap_interval != 0 && !fb->winsys_framebuffer) {683wait_swap_interval(fb);684}685686return stw_st_swap_framebuffer_locked(hdc, ctx->st, fb->stfb);687}688689690BOOL APIENTRY691DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)692{693if (fuPlanes & WGL_SWAP_MAIN_PLANE)694return DrvSwapBuffers(hdc);695696return FALSE;697}698699700