dxgi_native.cpp revision 4c7001462607e6e99e474d6271dd481d3f8f201c
1/************************************************************************** 2 * 3 * Copyright 2010 Luca Barbieri 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27#include "dxgi_private.h" 28extern "C" { 29#include "native.h" 30#include <util/u_format.h> 31#include <util/u_inlines.h> 32#include <util/u_simple_shaders.h> 33#include <pipe/p_shader_tokens.h> 34} 35#include <iostream> 36#include <memory> 37 38struct GalliumDXGIOutput; 39struct GalliumDXGIAdapter; 40struct GalliumDXGISwapChain; 41struct GalliumDXGIFactory; 42 43static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain); 44static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* adapter, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter); 45static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output); 46static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain); 47 48template<typename Base = IDXGIObject, typename Parent = IDXGIObject> 49struct GalliumDXGIObject : public GalliumPrivateDataComObject<Base> 50{ 51 ComPtr<Parent> parent; 52 53 GalliumDXGIObject(Parent* p_parent = 0) 54 { 55 this->parent = p_parent; 56 } 57 58 virtual HRESULT STDMETHODCALLTYPE GetParent( 59 REFIID riid, 60 void **out_parent) 61 { 62 return parent->QueryInterface(riid, out_parent); 63 } 64}; 65 66COM_INTERFACE(IGalliumDXGIBackend, IUnknown) 67 68// TODO: somehow check whether the window is fully obscured or not 69struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend> 70{ 71 virtual HRESULT STDMETHODCALLTYPE BeginPresent( 72 HWND hwnd, 73 void** present_cookie, 74 void** window, 75 RECT *rect, 76 RGNDATA **rgndata, 77 BOOL* preserve_aspect_ratio 78 ) 79 { 80 *window = (void*)hwnd; 81 rect->left = 0; 82 rect->top = 0; 83 rect->right = INT_MAX; 84 rect->bottom = INT_MAX; 85 *rgndata = 0; 86 87 // yes, because we like things looking good 88 *preserve_aspect_ratio = TRUE; 89 *present_cookie = 0; 90 return S_OK; 91 } 92 93 virtual void STDMETHODCALLTYPE EndPresent( 94 HWND hwnd, 95 void* present_cookie 96 ) 97 {} 98 99 virtual HRESULT STDMETHODCALLTYPE TestPresent(HWND hwnd) 100 { 101 return S_OK; 102 } 103 104 virtual HRESULT STDMETHODCALLTYPE GetPresentSize( 105 HWND hwnd, 106 unsigned* width, 107 unsigned* height 108 ) 109 { 110 *width = 0; 111 *height = 0; 112 return S_OK; 113 } 114}; 115 116// TODO: maybe install an X11 error hook, so we can return errors properly 117struct GalliumDXGIX11IdentityBackend : public GalliumDXGIIdentityBackend 118{ 119 Display* dpy; 120 121 GalliumDXGIX11IdentityBackend(Display* dpy) 122 : dpy(dpy) 123 {} 124 125 virtual HRESULT STDMETHODCALLTYPE GetPresentSize( 126 HWND hwnd, 127 unsigned* width, 128 unsigned* height 129 ) 130 { 131 XWindowAttributes xwa; 132 XGetWindowAttributes(dpy, (Window)hwnd, &xwa); 133 *width = xwa.width; 134 *height = xwa.height; 135 return S_OK; 136 } 137}; 138 139struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown> 140{ 141 HWND associated_window; 142 const struct native_platform* platform; 143 void* display; 144 ComPtr<IGalliumDXGIBackend> backend; 145 void* resolver_cookie; 146 147 GalliumDXGIFactory(const struct native_platform* platform, void* display, IGalliumDXGIBackend* p_backend) 148 : GalliumDXGIObject<IDXGIFactory1, IUnknown>((IUnknown*)NULL), platform(platform), display(display) 149 { 150 if(p_backend) 151 backend = p_backend; 152 else if(!strcmp(platform->name, "X11")) 153 backend.reset(new GalliumDXGIX11IdentityBackend((Display*)display)); 154 else 155 backend.reset(new GalliumDXGIIdentityBackend()); 156 } 157 158 virtual HRESULT STDMETHODCALLTYPE EnumAdapters( 159 UINT adapter, 160 IDXGIAdapter **out_adapter) 161 { 162 return EnumAdapters1(adapter, (IDXGIAdapter1**)out_adapter); 163 } 164 165 virtual HRESULT STDMETHODCALLTYPE EnumAdapters1( 166 UINT adapter, 167 IDXGIAdapter1 **out_adapter) 168 { 169 *out_adapter = 0; 170 if(adapter == 0) 171 { 172 return GalliumDXGIAdapterCreate(this, platform, display, out_adapter); 173 } 174#if 0 175 // TODO: enable this 176 if(platform == native_get_x11_platform()) 177 { 178 unsigned nscreens = ScreenCount((Display*)display); 179 if(adapter < nscreens) 180 { 181 unsigned def_screen = DefaultScreen(display); 182 if(adapter <= def_screen) 183 --adapter; 184 *out_adapter = GalliumDXGIAdapterCreate(this, platform, display, adapter); 185 return S_OK; 186 } 187 } 188#endif 189 return DXGI_ERROR_NOT_FOUND; 190 } 191 192 /* TODO: this is a mysterious underdocumented magic API 193 * Can we have multiple windows associated? 194 * Can we have multiple windows associated if we use multiple factories? 195 * If so, what should GetWindowAssociation return? 196 * If not, does a new swapchain steal the association? 197 * Does this act for existing swapchains? For new swapchains? 198 */ 199 virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation( 200 HWND window_handle, 201 UINT flags) 202 { 203 /* TODO: actually implement, for Wine, X11 and KMS*/ 204 associated_window = window_handle; 205 return S_OK; 206 } 207 208 virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation( 209 HWND *pwindow_handle) 210 { 211 *pwindow_handle = associated_window; 212 return S_OK; 213 } 214 215 virtual HRESULT STDMETHODCALLTYPE CreateSwapChain( 216 IUnknown *device, 217 DXGI_SWAP_CHAIN_DESC *desc, 218 IDXGISwapChain **out_swap_chain) 219 { 220 return GalliumDXGISwapChainCreate(this, device, *desc, out_swap_chain); 221 } 222 223 virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter( 224 HMODULE module, 225 IDXGIAdapter **out_adapter) 226 { 227 /* TODO: ignore the module, and just create a Gallium software screen */ 228 *out_adapter = 0; 229 return E_NOTIMPL; 230 } 231 232 /* TODO: support hotplug */ 233 virtual BOOL STDMETHODCALLTYPE IsCurrent( void) 234 { 235 return TRUE; 236 } 237}; 238 239struct GalliumDXGIAdapter 240 : public GalliumMultiComObject< 241 GalliumDXGIObject<IDXGIAdapter1, GalliumDXGIFactory>, 242 IGalliumAdapter> 243{ 244 struct native_display* display; 245 const struct native_config** configs; 246 std::unordered_multimap<unsigned, unsigned> configs_by_pipe_format; 247 std::unordered_map<unsigned, unsigned> configs_by_native_visual_id; 248 const struct native_connector** connectors; 249 unsigned num_configs; 250 DXGI_ADAPTER_DESC1 desc; 251 std::vector<ComPtr<IDXGIOutput> > outputs; 252 int num_outputs; 253 struct native_event_handler handler; 254 255 GalliumDXGIAdapter(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy) 256 { 257 this->parent = factory; 258 259 handler.invalid_surface = handle_invalid_surface; 260 handler.new_drm_screen = dxgi_loader_create_drm_screen; 261 handler.new_sw_screen = dxgi_loader_create_sw_screen; 262 display = platform->create_display(dpy, &handler, this); 263 if(!display) 264 throw E_FAIL; 265 memset(&desc, 0, sizeof(desc)); 266 std::string s = std::string("GalliumD3D on ") + display->screen->get_name(display->screen) + " by " + display->screen->get_vendor(display->screen); 267 268 /* hopefully no one will decide to use UTF-8 in Gallium name/vendor strings */ 269 for(int i = 0; i < std::min((int)s.size(), 127); ++i) 270 desc.Description[i] = (WCHAR)s[i]; 271 272 // TODO: add an interface to get these; for now, return mid/low values 273 desc.DedicatedVideoMemory = 256 << 20; 274 desc.DedicatedSystemMemory = 256 << 20; 275 desc.SharedSystemMemory = 1024 << 20; 276 277 // TODO: we should actually use an unique ID instead 278 *(void**)&desc.AdapterLuid = dpy; 279 280 configs = display->get_configs(display, (int*)&num_configs); 281 for(unsigned i = 0; i < num_configs; ++i) 282 { 283 if(configs[i]->window_bit) 284 { 285 configs_by_pipe_format.insert(std::make_pair(configs[i]->color_format, i)); 286 configs_by_native_visual_id[configs[i]->native_visual_id] = i; 287 } 288 } 289 290 connectors = 0; 291 num_outputs = 0; 292 293 if(display->modeset) 294 { 295 int num_crtcs; 296 297 connectors = display->modeset->get_connectors(display, &num_outputs, &num_crtcs); 298 if(!connectors) 299 num_outputs = 0; 300 else if(!num_outputs) 301 { 302 free(connectors); 303 connectors = 0; 304 } 305 } 306 if(!num_outputs) 307 num_outputs = 1; 308 } 309 310 static void handle_invalid_surface(struct native_display *ndpy, struct native_surface *nsurf, unsigned int seq_num) 311 { 312 GalliumDXGISwapChainRevalidate((IDXGISwapChain*)nsurf->user_data); 313 } 314 315 ~GalliumDXGIAdapter() 316 { 317 display->destroy(display); 318 free(configs); 319 free(connectors); 320 } 321 322 virtual HRESULT STDMETHODCALLTYPE EnumOutputs( 323 UINT output, 324 IDXGIOutput **out_output) 325 { 326 if(output >= (unsigned)num_outputs) 327 return DXGI_ERROR_NOT_FOUND; 328 329 if(connectors) 330 { 331 std::ostringstream ss; 332 ss << "output #" << output; 333 return GalliumDXGIOutputCreate(this, ss.str(), connectors[output], out_output); 334 } 335 else 336 return GalliumDXGIOutputCreate(this, "Unique output", NULL, out_output); 337 } 338 339 virtual HRESULT STDMETHODCALLTYPE GetDesc( 340 DXGI_ADAPTER_DESC *desc) 341 { 342 memcpy(desc, &desc, sizeof(*desc)); 343 return S_OK; 344 } 345 346 virtual HRESULT STDMETHODCALLTYPE GetDesc1( 347 DXGI_ADAPTER_DESC1 *desc) 348 { 349 memcpy(desc, &desc, sizeof(*desc)); 350 return S_OK; 351 } 352 353 virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport( 354 REFGUID interface_name, 355 LARGE_INTEGER *u_m_d_version) 356 { 357 // these number was taken from Windows 7 with Catalyst 10.8: its meaning is unclear 358 if(interface_name == IID_ID3D11Device || interface_name == IID_ID3D10Device1 || interface_name == IID_ID3D10Device) 359 { 360 u_m_d_version->QuadPart = 0x00080011000a0411ULL; 361 return S_OK; 362 } 363 return DXGI_ERROR_UNSUPPORTED; 364 } 365 366 pipe_screen* STDMETHODCALLTYPE GetGalliumScreen() 367 { 368 return display->screen; 369 } 370 371 pipe_screen* STDMETHODCALLTYPE GetGalliumReferenceSoftwareScreen() 372 { 373 // TODO: give a softpipe screen 374 return display->screen; 375 } 376 377 pipe_screen* STDMETHODCALLTYPE GetGalliumFastSoftwareScreen() 378 { 379 // TODO: give an llvmpipe screen 380 return display->screen; 381 } 382}; 383 384 385struct GalliumDXGIOutput : public GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter> 386{ 387 DXGI_OUTPUT_DESC desc; 388 const struct native_mode** modes; 389 DXGI_MODE_DESC* dxgi_modes; 390 unsigned num_modes; 391 const struct native_connector* connector; 392 DXGI_GAMMA_CONTROL* gamma; 393 394 GalliumDXGIOutput(GalliumDXGIAdapter* adapter, std::string name, const struct native_connector* connector = 0) 395 : GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter>(adapter), connector(connector) 396 { 397 memset(&desc, 0, sizeof(desc)); 398 for(unsigned i = 0; i < std::min(name.size(), sizeof(desc.DeviceName) - 1); ++i) 399 desc.DeviceName[i] = name[i]; 400 desc.AttachedToDesktop = TRUE; 401 /* TODO: should put an HMONITOR in desc.Monitor */ 402 403 gamma = 0; 404 num_modes = 0; 405 modes = 0; 406 if(connector) 407 { 408 modes = parent->display->modeset->get_modes(parent->display, connector, (int*)&num_modes); 409 if(modes && num_modes) 410 { 411 dxgi_modes = new DXGI_MODE_DESC[num_modes]; 412 for(unsigned i = 0; i < num_modes; ++i) 413 { 414 dxgi_modes[i].Width = modes[i]->width; 415 dxgi_modes[i].Height = modes[i]->height; 416 dxgi_modes[i].RefreshRate.Numerator = modes[i]->refresh_rate; 417 dxgi_modes[i].RefreshRate.Denominator = 1; 418 dxgi_modes[i].Scaling = DXGI_MODE_SCALING_UNSPECIFIED; 419 dxgi_modes[i].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; 420 } 421 } 422 else 423 { 424 if(modes) 425 { 426 free(modes); 427 modes = 0; 428 } 429 goto use_fake_mode; 430 } 431 } 432 else 433 { 434use_fake_mode: 435 dxgi_modes = new DXGI_MODE_DESC[1]; 436 dxgi_modes[0].Width = 1920; 437 dxgi_modes[0].Height = 1200; 438 dxgi_modes[0].RefreshRate.Numerator = 60; 439 dxgi_modes[0].RefreshRate.Denominator = 1; 440 dxgi_modes[0].Scaling = DXGI_MODE_SCALING_UNSPECIFIED; 441 dxgi_modes[0].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; 442 } 443 } 444 445 ~GalliumDXGIOutput() 446 { 447 delete [] dxgi_modes; 448 free(modes); 449 if(gamma) 450 delete gamma; 451 } 452 453 virtual HRESULT STDMETHODCALLTYPE GetDesc( 454 DXGI_OUTPUT_DESC *out_desc) 455 { 456 *out_desc = desc; 457 return S_OK; 458 } 459 460 virtual HRESULT STDMETHODCALLTYPE GetDisplayModeList( 461 DXGI_FORMAT enum_format, 462 UINT flags, 463 UINT *pcount, 464 DXGI_MODE_DESC *desc) 465 { 466 /* TODO: should we return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE when we don't 467 * support modesetting instead of fake modes? 468 */ 469 pipe_format format = dxgi_to_pipe_format[enum_format]; 470 if(parent->configs_by_pipe_format.count(format)) 471 { 472 if(!desc) 473 { 474 *pcount = num_modes; 475 return S_OK; 476 } 477 478 unsigned copy_modes = std::min(num_modes, *pcount); 479 for(unsigned i = 0; i < copy_modes; ++i) 480 { 481 desc[i] = dxgi_modes[i]; 482 desc[i].Format = enum_format; 483 } 484 *pcount = num_modes; 485 486 if(copy_modes < num_modes) 487 return DXGI_ERROR_MORE_DATA; 488 else 489 return S_OK; 490 } 491 else 492 { 493 *pcount = 0; 494 return S_OK; 495 } 496 } 497 498 virtual HRESULT STDMETHODCALLTYPE FindClosestMatchingMode( 499 const DXGI_MODE_DESC *pModeToMatch, 500 DXGI_MODE_DESC *closest_match, 501 IUnknown *concerned_device) 502 { 503 /* TODO: actually implement this */ 504 DXGI_FORMAT dxgi_format = pModeToMatch->Format; 505 enum pipe_format format = dxgi_to_pipe_format[dxgi_format]; 506 init_pipe_to_dxgi_format(); 507 if(!parent->configs_by_pipe_format.count(format)) 508 { 509 if(!concerned_device) 510 return E_FAIL; 511 else 512 { 513 format = parent->configs[0]->color_format; 514 dxgi_format = pipe_to_dxgi_format[format]; 515 } 516 } 517 518 *closest_match = dxgi_modes[0]; 519 closest_match->Format = dxgi_format; 520 return S_OK; 521 } 522 523 virtual HRESULT STDMETHODCALLTYPE WaitForVBlank( void) 524 { 525 return S_OK; 526 } 527 528 virtual HRESULT STDMETHODCALLTYPE TakeOwnership( 529 IUnknown *device, 530 BOOL exclusive) 531 { 532 return S_OK; 533 } 534 535 virtual void STDMETHODCALLTYPE ReleaseOwnership( void) 536 { 537 } 538 539 virtual HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities( 540 DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps) 541 { 542 memset(gamma_caps, 0, sizeof(*gamma_caps)); 543 return S_OK; 544 } 545 546 virtual HRESULT STDMETHODCALLTYPE SetGammaControl( 547 const DXGI_GAMMA_CONTROL *pArray) 548 { 549 if(!gamma) 550 gamma = new DXGI_GAMMA_CONTROL; 551 *gamma = *pArray; 552 return S_OK; 553 } 554 555 virtual HRESULT STDMETHODCALLTYPE GetGammaControl( 556 DXGI_GAMMA_CONTROL *pArray) 557 { 558 if(gamma) 559 *pArray = *gamma; 560 else 561 { 562 pArray->Scale.Red = 1; 563 pArray->Scale.Green = 1; 564 pArray->Scale.Blue = 1; 565 pArray->Offset.Red = 0; 566 pArray->Offset.Green = 0; 567 pArray->Offset.Blue = 0; 568 for(unsigned i = 0; i <= 1024; ++i) 569 pArray->GammaCurve[i].Red = pArray->GammaCurve[i].Green = pArray->GammaCurve[i].Blue = (float)i / 1024.0; 570 } 571 return S_OK; 572 } 573 574 virtual HRESULT STDMETHODCALLTYPE SetDisplaySurface( 575 IDXGISurface *scanout_surface) 576 { 577 return E_NOTIMPL; 578 } 579 580 virtual HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData( 581 IDXGISurface *destination) 582 { 583 return E_NOTIMPL; 584 } 585 586 virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics( 587 DXGI_FRAME_STATISTICS *stats) 588 { 589 memset(stats, 0, sizeof(*stats)); 590#ifdef _WIN32 591 QueryPerformanceCounter(&stats->SyncQPCTime); 592#endif 593 return E_NOTIMPL; 594 } 595}; 596 597/* Swap chain are rather complex, and Microsoft's documentation is rather 598 * lacking. As far as I know, this is the most thorough publicly available 599 * description of how swap chains work, based on multiple sources and 600 * experimentation. 601 * 602 * There are two modes (called "swap effects") that a swap chain can operate in: 603 * discard and sequential. 604 * 605 * In discard mode, things always look as if there is a single buffer, which 606 * you can get with GetBuffers(0). 607 * The 2D texture returned by GetBuffers(0) and can only be 608 * used as a render target view and for resource copies, since no CPU access 609 * flags are set and only the D3D11_BIND_RENDER_TARGET bind flag is set. 610 * On Present, it is copied to the actual display 611 * surface and the contents become undefined. 612 * D3D may internally use multiple buffers, but you can't observe this, except 613 * by looking at the buffer contents after Present (but those are undefined). 614 * If it uses multiple buffers internally, then it will normally use buffer_count buffers 615 * (this has latency implications). 616 * Discard mode seems to internally use a single buffer in windowed mode, 617 * even if DWM is enabled, and buffer_count buffers in fullscreen mode. 618 * 619 * In sequential mode, the runtime alllocates buffer_count buffers. 620 * You can get each with GetBuffers(n). 621 * GetBuffers(0) ALWAYS points to the backbuffer to be presented and has the 622 * same usage constraints as the discard mode. 623 * GetBuffer(n) with n > 0 points to resources that are identical to buffer 0, but 624 * are classified as "read-only resources" (due to DXGI_USAGE_READ_ONLY), 625 * meaning that you can't create render target views on them, or use them as 626 * a CopyResource/CopySubresourceRegion destination. 627 * It appears the only valid operation is to use them as a source for CopyResource 628 * and CopySubresourceRegion as well as just waiting for them to become 629 * buffer 0 again. 630 * Buffer n - 1 is always displayed on screen. 631 * When you call Present(), the contents of the buffers are rotated, so that buffer 0 632 * goes to buffer n - 1, and is thus displayed, and buffer 1 goes to buffer 0, becomes 633 * the accessible back buffer. 634 * The resources themselves are NOT rotated, so that you can still render on the 635 * same ID3D11Texture2D*, and views based on it, that you got before Present(). 636 * 637 * Present seems to happen by either copying the relevant buffer into the window, 638 * or alternatively making it the current one, either by programming the CRTC or 639 * by sending the resource name to the DWM compositor. 640 * 641 * Hence, you can call GetBuffer(0) once and keep using the same ID3D11Texture2D* 642 * and ID3D11RenderTargetView* (and other views if needed) you got from it. 643 * 644 * If the window gets resized, DXGI will then "emulate" all successive presentations, 645 * by using a stretched blit automatically. 646 * Thus, you should handle WM_SIZE and call ResizeBuffers to update the DXGI 647 * swapchain buffers size to the new window size. 648 * Doing so requires you to release all GetBuffers() results and anything referencing 649 * them, including views and Direct3D11 deferred context command lists (this is 650 * documented). 651 * 652 * How does Microsoft implement the rotation behavior? 653 * It turns out that it does it by calling RotateResourceIdentitiesDXGI in the user-mode 654 * DDI driver. 655 * This will rotate the kernel buffer handle, or possibly rotate the GPU virtual memory 656 * mappings. 657 * 658 * The reason this is done by driver instead of by the runtime appears to be that 659 * this is necessary to support driver-provided command list support, since otherwise 660 * the command list would not always target the current backbuffer, since it would 661 * be done at the driver level, while only the runtime knows about the rotation. 662 * 663 * OK, so how do we implement this in Gallium? 664 * 665 * There are three strategies: 666 * 1. Use a single buffer, and always copy it to a window system provided buffer, or 667 * just give the buffer to the window system if it supports that 668 * 2. Rotate the buffers in the D3D1x implementation, and recreate and rebind the views. 669 * Don't support driver-provided command lists 670 * 3. Add this rotation functionality to the Gallium driver, with the idea that it would rotate 671 * remap GPU virtual memory, so that virtual address are unchanged, but the physical 672 * ones are rotated (so that pushbuffers remain valid). 673 * If the driver does not support this, either fall back to (1), or have a layer doing this, 674 * putting a deferred context layer over this intermediate layer. 675 * 676 * (2) is not acceptable since it prevents an optimal implementation. 677 * (3) is the ideal solution, but it is complicated. 678 * 679 * Hence, we implement (1) for now, and will switch to (3) later. 680 * 681 * Note that (1) doesn't really work for DXGI_SWAP_EFFECT_SEQUENTIAL with more 682 * than one buffer, so we just pretend we got asked for a single buffer in that case 683 * Fortunately, no one seems to rely on that, so we'll just not implement it at first, and 684 * later perform the rotation with blits. 685 * Once we switch to (3), we'll just use real rotation to do it.. 686 * 687 * DXGI_SWAP_EFFECT_SEQUENTIAL with more than one buffer is of dubious use 688 * anyway, since you can only render or write to buffer 0, and other buffers can apparently 689 * be used only as sources for copies. 690 * I was unable to find any code using it either in DirectX SDK examples, or on the web. 691 * 692 * It seems the only reason you would use it is to not have to redraw from scratch, while 693 * also possibly avoid a copy compared to buffer_count == 1, assuming that your 694 * application is OK with having to redraw starting not from the last frame, but from 695 * one/two/more frames behind it. 696 * 697 * A better design would forbid the user specifying buffer_count explicitly, and 698 * would instead let the application give an upper bound on how old the buffer can 699 * become after presentation, with "infinite" being equivalent to discard. 700 * The runtime would then tell the application with frame number the buffer switched to 701 * after present. 702 * In addition, in a better design, the application would be allowed to specify the 703 * number of buffers available, having all them usable for rendering, so that things 704 * like video players could efficiently decode frames in parallel. 705 * Present would in such a better design gain a way to specify the number of buffers 706 * to present. 707 * 708 * Other miscellaneous info: 709 * DXGI_PRESENT_DO_NOT_SEQUENCE causes DXGI to hold the frame for another 710 * vblank interval without rotating the resource data. 711 * 712 * References: 713 * "DXGI Overview" in MSDN 714 * IDXGISwapChain documentation on MSDN 715 * "RotateResourceIdentitiesDXGI" on MSDN 716 * http://forums.xna.com/forums/p/42362/266016.aspx 717 */ 718 719static float quad_data[] = { 720 -1, -1, 0, 0, 721 -1, 1, 0, 1, 722 1, 1, 1, 1, 723 1, -1, 1, 0, 724}; 725 726struct dxgi_blitter 727{ 728 pipe_context* pipe; 729 bool normalized; 730 void* fs; 731 void* vs; 732 void* sampler[2]; 733 void* elements; 734 void* blend; 735 void* rasterizer; 736 void* zsa; 737 struct pipe_clip_state clip; 738 struct pipe_vertex_buffer vbuf; 739 struct pipe_draw_info draw; 740 741 dxgi_blitter(pipe_context* pipe) 742 : pipe(pipe) 743 { 744 //normalized = !!pipe->screen->get_param(pipe, PIPE_CAP_NPOT_TEXTURES); 745 // TODO: need to update buffer in unnormalized case 746 normalized = true; 747 748 struct pipe_rasterizer_state rs_state; 749 memset(&rs_state, 0, sizeof(rs_state)); 750 rs_state.cull_face = PIPE_FACE_NONE; 751 rs_state.gl_rasterization_rules = 1; 752 rs_state.flatshade = 1; 753 rasterizer = pipe->create_rasterizer_state(pipe, &rs_state); 754 755 struct pipe_blend_state blendd; 756 memset(&blendd, 0, sizeof(blendd)); 757 blendd.rt[0].colormask = PIPE_MASK_RGBA; 758 blend = pipe->create_blend_state(pipe, &blendd); 759 760 struct pipe_depth_stencil_alpha_state zsad; 761 memset(&zsad, 0, sizeof(zsad)); 762 zsa = pipe->create_depth_stencil_alpha_state(pipe, &zsad); 763 764 struct pipe_vertex_element velem[2]; 765 memset(&velem[0], 0, sizeof(velem[0]) * 2); 766 velem[0].src_offset = 0; 767 velem[0].src_format = PIPE_FORMAT_R32G32_FLOAT; 768 velem[1].src_offset = 8; 769 velem[1].src_format = PIPE_FORMAT_R32G32_FLOAT; 770 elements = pipe->create_vertex_elements_state(pipe, 2, &velem[0]); 771 772 for(unsigned stretch = 0; stretch < 2; ++stretch) 773 { 774 struct pipe_sampler_state sampler_state; 775 memset(&sampler_state, 0, sizeof(sampler_state)); 776 sampler_state.min_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST; 777 sampler_state.mag_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST; 778 sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 779 sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 780 sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 781 sampler_state.normalized_coords = normalized; 782 783 sampler[stretch] = pipe->create_sampler_state(pipe, &sampler_state); 784 } 785 786 fs = util_make_fragment_tex_shader(pipe, normalized ? TGSI_TEXTURE_2D : TGSI_TEXTURE_RECT, TGSI_INTERPOLATE_LINEAR); 787 788 const unsigned semantic_names[] = { TGSI_SEMANTIC_POSITION, TGSI_SEMANTIC_GENERIC }; 789 const unsigned semantic_indices[] = { 0, 0 }; 790 vs = util_make_vertex_passthrough_shader(pipe, 2, semantic_names, semantic_indices); 791 792 vbuf.buffer = pipe_buffer_create(pipe->screen, PIPE_BIND_VERTEX_BUFFER, sizeof(quad_data)); 793 vbuf.buffer_offset = 0; 794 vbuf.max_index = ~0; 795 vbuf.stride = 4 * sizeof(float); 796 pipe_buffer_write(pipe, vbuf.buffer, 0, sizeof(quad_data), quad_data); 797 798 memset(&clip, 0, sizeof(clip)); 799 800 memset(&draw, 0, sizeof(draw)); 801 draw.mode = PIPE_PRIM_QUADS; 802 draw.count = 4; 803 draw.instance_count = 1; 804 draw.max_index = ~0; 805 } 806 807 void blit(struct pipe_surface* surf, struct pipe_sampler_view* view, unsigned x, unsigned y, unsigned w, unsigned h) 808 { 809 struct pipe_framebuffer_state fb; 810 memset(&fb, 0, sizeof(fb)); 811 fb.nr_cbufs = 1; 812 fb.cbufs[0] = surf; 813 fb.width = surf->width; 814 fb.height = surf->height; 815 816 struct pipe_viewport_state viewport; 817 float half_width = w * 0.5f; 818 float half_height = h * 0.5f; 819 viewport.scale[0] = half_width; 820 viewport.scale[1] = half_height; 821 viewport.scale[2] = 1.0f; 822 viewport.scale[3] = 1.0f; 823 viewport.translate[0] = x + half_width; 824 viewport.translate[1] = y + half_height; 825 viewport.translate[2] = 0.0f; 826 viewport.translate[3] = 1.0f; 827 828 bool stretch = view->texture->width0 != w || view->texture->height0 != h; 829 if(pipe->render_condition) 830 pipe->render_condition(pipe, 0, 0); 831 pipe->set_framebuffer_state(pipe, &fb); 832 pipe->bind_fragment_sampler_states(pipe, 1, &sampler[stretch]); 833 pipe->set_viewport_state(pipe, &viewport); 834 pipe->set_clip_state(pipe, &clip); 835 pipe->bind_rasterizer_state(pipe, rasterizer); 836 pipe->bind_depth_stencil_alpha_state(pipe, zsa); 837 pipe->bind_blend_state(pipe, blend); 838 pipe->bind_vertex_elements_state(pipe, elements); 839 pipe->set_vertex_buffers(pipe, 1, &vbuf); 840 pipe->bind_fs_state(pipe, fs); 841 pipe->bind_vs_state(pipe, vs); 842 if(pipe->bind_gs_state) 843 pipe->bind_gs_state(pipe, 0); 844 if(pipe->bind_stream_output_state) 845 pipe->bind_stream_output_state(pipe, 0); 846 pipe->set_fragment_sampler_views(pipe, 1, &view); 847 848 pipe->draw_vbo(pipe, &draw); 849 } 850 851 ~dxgi_blitter() 852 { 853 pipe->delete_blend_state(pipe, blend); 854 pipe->delete_rasterizer_state(pipe, rasterizer); 855 pipe->delete_depth_stencil_alpha_state(pipe, zsa); 856 pipe->delete_sampler_state(pipe, sampler[0]); 857 pipe->delete_sampler_state(pipe, sampler[1]); 858 pipe->delete_vertex_elements_state(pipe, elements); 859 pipe->delete_vs_state(pipe, vs); 860 pipe->delete_fs_state(pipe, fs); 861 pipe->screen->resource_destroy(pipe->screen, vbuf.buffer); 862 } 863}; 864 865struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory> 866{ 867 ComPtr<IDXGIDevice>dxgi_device; 868 ComPtr<IGalliumDevice>gallium_device; 869 ComPtr<GalliumDXGIAdapter> adapter; 870 ComPtr<IDXGIOutput> target; 871 872 DXGI_SWAP_CHAIN_DESC desc; 873 874 struct native_surface* surface; 875 const struct native_config* config; 876 877 void* window; 878 struct pipe_resource* resources[NUM_NATIVE_ATTACHMENTS]; 879 int width; 880 int height; 881 unsigned seq_num; 882 bool ever_validated; 883 bool needs_validation; 884 unsigned present_count; 885 886 ComPtr<IDXGISurface> buffer0; 887 struct pipe_resource* gallium_buffer0; 888 struct pipe_sampler_view* gallium_buffer0_view; 889 890 struct pipe_context* pipe; 891 bool owns_pipe; 892 893 BOOL fullscreen; 894 895 std::auto_ptr<dxgi_blitter> blitter; 896 bool formats_compatible; 897 898 GalliumDXGISwapChain(GalliumDXGIFactory* factory, IUnknown* p_device, const DXGI_SWAP_CHAIN_DESC& p_desc) 899 : GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>(factory), desc(p_desc), surface(0) 900 { 901 HRESULT hr; 902 903 hr = p_device->QueryInterface(IID_IGalliumDevice, (void**)&gallium_device); 904 if(!SUCCEEDED(hr)) 905 throw hr; 906 907 hr = p_device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_device); 908 if(!SUCCEEDED(hr)) 909 throw hr; 910 911 hr = dxgi_device->GetAdapter((IDXGIAdapter**)&adapter); 912 if(!SUCCEEDED(hr)) 913 throw hr; 914 915 memset(resources, 0, sizeof(resources)); 916 917 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL && desc.BufferCount != 1) 918 { 919 std::cerr << "Gallium DXGI: if DXGI_SWAP_EFFECT_SEQUENTIAL is specified, only buffer_count == 1 is implemented, but " << desc.BufferCount << " was specified: ignoring this" << std::endl; 920 // change the returned desc, so that the application might perhaps notice what we did and react well 921 desc.BufferCount = 1; 922 } 923 924 pipe = gallium_device->GetGalliumContext(); 925 owns_pipe = false; 926 if(!pipe) 927 { 928 pipe = adapter->display->screen->context_create(adapter->display->screen, 0); 929 owns_pipe = true; 930 } 931 932 blitter.reset(new dxgi_blitter(pipe)); 933 window = 0; 934 935 hr = resolve_zero_width_height(true); 936 if(!SUCCEEDED(hr)) 937 throw hr; 938 } 939 940 void init_for_window() 941 { 942 if(surface) 943 { 944 surface->destroy(surface); 945 surface = 0; 946 } 947 948 unsigned config_num; 949 if(!strcmp(parent->platform->name, "X11")) 950 { 951 XWindowAttributes xwa; 952 XGetWindowAttributes((Display*)parent->display, (Window)window, &xwa); 953 assert(adapter->configs_by_native_visual_id.count(xwa.visual->visualid)); 954 config_num = adapter->configs_by_native_visual_id[xwa.visual->visualid]; 955 } 956 else 957 { 958 enum pipe_format format = dxgi_to_pipe_format[desc.BufferDesc.Format]; 959 if(!adapter->configs_by_pipe_format.count(format)) 960 { 961 if(adapter->configs_by_pipe_format.empty()) 962 throw E_FAIL; 963 // TODO: choose the best match 964 format = (pipe_format)adapter->configs_by_pipe_format.begin()->first; 965 } 966 // TODO: choose the best config 967 config_num = adapter->configs_by_pipe_format.find(format)->second; 968 } 969 970 config = adapter->configs[config_num]; 971 surface = adapter->display->create_window_surface(adapter->display, (EGLNativeWindowType)window, config); 972 surface->user_data = this; 973 974 width = 0; 975 height = 0; 976 seq_num = 0; 977 present_count = 0; 978 needs_validation = true; 979 ever_validated = false; 980 981 formats_compatible = util_is_format_compatible( 982 util_format_description(dxgi_to_pipe_format[desc.BufferDesc.Format]), 983 util_format_description(config->color_format)); 984 } 985 986 ~GalliumDXGISwapChain() 987 { 988 if(owns_pipe) 989 pipe->destroy(pipe); 990 } 991 992 virtual HRESULT STDMETHODCALLTYPE GetDevice( 993 REFIID riid, 994 void **pdevice) 995 { 996 return dxgi_device->QueryInterface(riid, pdevice); 997 } 998 999 HRESULT create_buffer0() 1000 { 1001 HRESULT hr; 1002 ComPtr<IDXGISurface> new_buffer0; 1003 DXGI_USAGE usage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT; 1004 if(desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD) 1005 usage |= DXGI_USAGE_DISCARD_ON_PRESENT; 1006 // for our blitter 1007 usage |= DXGI_USAGE_SHADER_INPUT; 1008 1009 DXGI_SURFACE_DESC surface_desc; 1010 surface_desc.Format = desc.BufferDesc.Format; 1011 surface_desc.Width = desc.BufferDesc.Width; 1012 surface_desc.Height = desc.BufferDesc.Height; 1013 surface_desc.SampleDesc = desc.SampleDesc; 1014 hr = dxgi_device->CreateSurface(&surface_desc, 1, usage, 0, &new_buffer0); 1015 if(!SUCCEEDED(hr)) 1016 return hr; 1017 1018 ComPtr<IGalliumResource> gallium_resource; 1019 hr = new_buffer0->QueryInterface(IID_IGalliumResource, (void**)&gallium_resource); 1020 if(!SUCCEEDED(hr)) 1021 return hr; 1022 1023 struct pipe_resource* new_gallium_buffer0 = gallium_resource->GetGalliumResource(); 1024 if(!new_gallium_buffer0) 1025 return E_FAIL; 1026 1027 buffer0.reset(new_buffer0.steal()); 1028 gallium_buffer0 = new_gallium_buffer0; 1029 struct pipe_sampler_view templat; 1030 memset(&templat, 0, sizeof(templat)); 1031 templat.texture = gallium_buffer0; 1032 templat.swizzle_r = 0; 1033 templat.swizzle_g = 1; 1034 templat.swizzle_b = 2; 1035 templat.swizzle_a = 3; 1036 templat.format = gallium_buffer0->format; 1037 gallium_buffer0_view = pipe->create_sampler_view(pipe, gallium_buffer0, &templat); 1038 return S_OK; 1039 } 1040 1041 bool validate() 1042 { 1043 unsigned new_seq_num; 1044 needs_validation = false; 1045 1046 if(!surface->validate(surface, (1 << NATIVE_ATTACHMENT_BACK_LEFT) | (1 << NATIVE_ATTACHMENT_FRONT_LEFT), &new_seq_num, resources, &width, &height)) 1047 return false; 1048 1049 if(!ever_validated || seq_num != new_seq_num) 1050 { 1051 seq_num = new_seq_num; 1052 ever_validated = true; 1053 } 1054 return true; 1055 } 1056 1057 HRESULT resolve_zero_width_height(bool force = false) 1058 { 1059 if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height) 1060 return S_OK; 1061 1062 unsigned width, height; 1063 HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height); 1064 if(!SUCCEEDED(hr)) 1065 return hr; 1066 1067 // On Windows, 8 is used, and a debug message saying so gets printed 1068 if(!width) 1069 width = 8; 1070 if(!height) 1071 height = 8; 1072 1073 if(!desc.BufferDesc.Width) 1074 desc.BufferDesc.Width = width; 1075 if(!desc.BufferDesc.Height) 1076 desc.BufferDesc.Height = height; 1077 return S_OK; 1078 } 1079 1080 virtual HRESULT STDMETHODCALLTYPE Present( 1081 UINT sync_interval, 1082 UINT flags) 1083 { 1084 HRESULT hr; 1085 if(flags & DXGI_PRESENT_TEST) 1086 return parent->backend->TestPresent(desc.OutputWindow); 1087 1088 if(!buffer0) 1089 { 1090 HRESULT hr = create_buffer0(); 1091 if(!SUCCEEDED(hr)) 1092 return hr; 1093 } 1094 1095 void* cur_window = 0; 1096 RECT rect; 1097 RGNDATA* rgndata; 1098 BOOL preserve_aspect_ratio; 1099 unsigned dst_w, dst_h; 1100 bool db; 1101 struct pipe_resource* dst; 1102 struct pipe_resource* src; 1103 struct pipe_surface* dst_surface; 1104 enum native_attachment att; 1105 1106 void* present_cookie; 1107 hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio); 1108 if(hr != S_OK) 1109 return hr; 1110 1111 if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom) 1112 goto end_present; 1113 1114 if(cur_window != window) 1115 { 1116 window = cur_window; 1117 init_for_window(); 1118 } 1119 1120 if(needs_validation) 1121 { 1122 if(!validate()) 1123 return DXGI_ERROR_DEVICE_REMOVED; 1124 } 1125 1126 db = !!(config->buffer_mask & (1 << NATIVE_ATTACHMENT_BACK_LEFT)); 1127 dst = resources[db ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT]; 1128 src = gallium_buffer0; 1129 dst_surface = 0; 1130 1131 assert(src); 1132 assert(dst); 1133 1134 /* TODO: sharing the context for blitting won't work correctly if queries are active 1135 * Hopefully no one is crazy enough to keep queries active while presenting, expecting 1136 * sensible results. 1137 * We could alternatively force using another context, but that might cause inefficiency issues 1138 */ 1139 1140 if((unsigned)rect.right > dst->width0) 1141 rect.right = dst->width0; 1142 if((unsigned)rect.bottom > dst->height0) 1143 rect.bottom = dst->height0; 1144 if(rect.left > rect.right) 1145 rect.left = rect.right; 1146 if(rect.top > rect.bottom) 1147 rect.top = rect.bottom; 1148 1149 if(rect.left >= rect.right && rect.top >= rect.bottom) 1150 goto end_present; 1151 1152 dst_w = rect.right - rect.left; 1153 dst_h = rect.bottom - rect.top; 1154 1155 // TODO: add support for rgndata 1156// if(preserve_aspect_ratio || !rgndata) 1157 if(1) 1158 { 1159 unsigned blit_x, blit_y, blit_w, blit_h; 1160 float black[4] = {0, 0, 0, 0}; 1161 1162 if(!formats_compatible || src->width0 != dst_w || src->height0 != dst_h) { 1163 struct pipe_surface templat; 1164 templat.usage = PIPE_BIND_RENDER_TARGET; 1165 templat.format = dst->format; 1166 templat.u.tex.level = 0; 1167 templat.u.tex.first_layer = 0; 1168 templat.u.tex.last_layer = 0; 1169 dst_surface = pipe->create_surface(pipe, dst, &templat); 1170 } 1171 1172 if(preserve_aspect_ratio) 1173 { 1174 int delta = src->width0 * dst_h - dst_w * src->height0; 1175 if(delta > 0) 1176 { 1177 blit_w = dst_w; 1178 blit_h = dst_w * src->height0 / src->width0; 1179 } 1180 else if(delta < 0) 1181 { 1182 blit_w = dst_h * src->width0 / src->height0; 1183 blit_h = dst_h; 1184 } 1185 else 1186 { 1187 blit_w = dst_w; 1188 blit_h = dst_h; 1189 } 1190 1191 blit_x = (dst_w - blit_w) >> 1; 1192 blit_y = (dst_h - blit_h) >> 1; 1193 } 1194 else 1195 { 1196 blit_x = 0; 1197 blit_y = 0; 1198 blit_w = dst_w; 1199 blit_h = dst_h; 1200 } 1201 1202 if(blit_x) 1203 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, blit_x, dst_h); 1204 if(blit_y) 1205 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, dst_w, blit_y); 1206 1207 if(formats_compatible && blit_w == src->width0 && blit_h == src->height0) 1208 { 1209 pipe_box box; 1210 box.x = box.y = box.z; 1211 box.width = blit_w; 1212 box.height = blit_h; 1213 box.z = 1; 1214 pipe->resource_copy_region(pipe, dst, 0, rect.left, rect.top, 0, src, 0, &box); 1215 } 1216 else 1217 { 1218 blitter->blit(dst_surface, gallium_buffer0_view, rect.left + blit_x, rect.top + blit_y, blit_w, blit_h); 1219 if(!owns_pipe) 1220 gallium_device->RestoreGalliumState(); 1221 } 1222 1223 if(blit_w != dst_w) 1224 pipe->clear_render_target(pipe, dst_surface, black, rect.left + blit_x + blit_w, rect.top, dst_w - blit_x - blit_w, dst_h); 1225 if(blit_h != dst_h) 1226 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top + blit_y + blit_h, dst_w, dst_h - blit_y - blit_h); 1227 } 1228 1229 if(dst_surface) 1230 pipe->surface_destroy(pipe, dst_surface); 1231 1232 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE | PIPE_FLUSH_FRAME, 0); 1233 1234 att = (db) ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT; 1235 if(!surface->present(surface, att, FALSE, 0)) 1236 return DXGI_ERROR_DEVICE_REMOVED; 1237 1238end_present: 1239 parent->backend->EndPresent(desc.OutputWindow, present_cookie); 1240 1241 ++present_count; 1242 return S_OK; 1243 } 1244 1245 virtual HRESULT STDMETHODCALLTYPE GetBuffer( 1246 UINT Buffer, 1247 REFIID riid, 1248 void **ppSurface) 1249 { 1250 if(Buffer > 0) 1251 { 1252 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL) 1253 std::cerr << "DXGI unimplemented: GetBuffer(n) with n > 0 not supported, returning buffer 0 instead!" << std::endl; 1254 else 1255 std::cerr << "DXGI error: in GetBuffer(n), n must be 0 for DXGI_SWAP_EFFECT_DISCARD\n" << std::endl; 1256 } 1257 1258 if(!buffer0) 1259 { 1260 HRESULT hr = create_buffer0(); 1261 if(!SUCCEEDED(hr)) 1262 return hr; 1263 } 1264 return buffer0->QueryInterface(riid, ppSurface); 1265 } 1266 1267 /* TODO: implement somehow */ 1268 virtual HRESULT STDMETHODCALLTYPE SetFullscreenState( 1269 BOOL fullscreen, 1270 IDXGIOutput *target) 1271 { 1272 fullscreen = fullscreen; 1273 target = target; 1274 return S_OK; 1275 } 1276 1277 virtual HRESULT STDMETHODCALLTYPE GetFullscreenState( 1278 BOOL *out_fullscreen, 1279 IDXGIOutput **out_target) 1280 { 1281 if(out_fullscreen) 1282 *out_fullscreen = fullscreen; 1283 if(out_target) 1284 *out_target = target.ref(); 1285 return S_OK; 1286 } 1287 1288 virtual HRESULT STDMETHODCALLTYPE GetDesc( 1289 DXGI_SWAP_CHAIN_DESC *out_desc) 1290 { 1291 *out_desc = desc; 1292 return S_OK; 1293 } 1294 1295 virtual HRESULT STDMETHODCALLTYPE ResizeBuffers( 1296 UINT buffer_count, 1297 UINT width, 1298 UINT height, 1299 DXGI_FORMAT new_format, 1300 UINT swap_chain_flags) 1301 { 1302 if(buffer0) 1303 { 1304 buffer0.p->AddRef(); 1305 ULONG v = buffer0.p->Release(); 1306 // we must fail if there are any references to buffer0 other than ours 1307 if(v > 1) 1308 return E_FAIL; 1309 pipe_sampler_view_reference(&gallium_buffer0_view, 0); 1310 buffer0 = (IUnknown*)NULL; 1311 gallium_buffer0 = 0; 1312 } 1313 1314 if(desc.SwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL) 1315 desc.BufferCount = buffer_count; 1316 desc.BufferDesc.Format = new_format; 1317 desc.BufferDesc.Width = width; 1318 desc.BufferDesc.Height = height; 1319 desc.Flags = swap_chain_flags; 1320 return resolve_zero_width_height(); 1321 } 1322 1323 virtual HRESULT STDMETHODCALLTYPE ResizeTarget( 1324 const DXGI_MODE_DESC *out_new_target_parameters) 1325 { 1326 /* TODO: implement */ 1327 return S_OK; 1328 } 1329 1330 virtual HRESULT STDMETHODCALLTYPE GetContainingOutput( 1331 IDXGIOutput **out_output) 1332 { 1333 *out_output = adapter->outputs[0].ref(); 1334 return S_OK; 1335 } 1336 1337 virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics( 1338 DXGI_FRAME_STATISTICS *out_stats) 1339 { 1340 memset(out_stats, 0, sizeof(*out_stats)); 1341#ifdef _WIN32 1342 QueryPerformanceCounter(&out_stats->SyncQPCTime); 1343#endif 1344 out_stats->PresentCount = present_count; 1345 out_stats->PresentRefreshCount = present_count; 1346 out_stats->SyncRefreshCount = present_count; 1347 return S_OK; 1348 } 1349 1350 virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount( 1351 UINT *last_present_count) 1352 { 1353 *last_present_count = present_count; 1354 return S_OK; 1355 } 1356}; 1357 1358static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain) 1359{ 1360 ((GalliumDXGISwapChain*)swap_chain)->needs_validation = true; 1361} 1362 1363static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter) 1364{ 1365 try 1366 { 1367 *out_adapter = new GalliumDXGIAdapter(factory, platform, dpy); 1368 return S_OK; 1369 } 1370 catch(HRESULT hr) 1371 { 1372 return hr; 1373 } 1374} 1375 1376static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output) 1377{ 1378 try 1379 { 1380 *out_output = new GalliumDXGIOutput(adapter, name, connector); 1381 return S_OK; 1382 } 1383 catch(HRESULT hr) 1384 { 1385 return hr; 1386 } 1387} 1388 1389static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain) 1390{ 1391 try 1392 { 1393 *out_swap_chain = new GalliumDXGISwapChain(factory, device, desc); 1394 return S_OK; 1395 } 1396 catch(HRESULT hr) 1397 { 1398 return hr; 1399 } 1400} 1401 1402struct dxgi_binding 1403{ 1404 const struct native_platform* platform; 1405 void* display; 1406 IGalliumDXGIBackend* backend; 1407}; 1408 1409static dxgi_binding dxgi_default_binding; 1410static __thread dxgi_binding dxgi_thread_binding; 1411 1412void STDMETHODCALLTYPE GalliumDXGIUseNothing() 1413{ 1414 dxgi_thread_binding.platform = 0; 1415 dxgi_thread_binding.display = 0; 1416 if(dxgi_thread_binding.backend) 1417 dxgi_thread_binding.backend->Release(); 1418 dxgi_thread_binding.backend = 0; 1419} 1420 1421#ifdef GALLIUM_DXGI_USE_X11 1422void STDMETHODCALLTYPE GalliumDXGIUseX11Display(Display* dpy, IGalliumDXGIBackend* backend) 1423{ 1424 GalliumDXGIUseNothing(); 1425 dxgi_thread_binding.platform = native_get_x11_platform(); 1426 dxgi_thread_binding.display = dpy; 1427 1428 if(backend) 1429 { 1430 dxgi_thread_binding.backend = backend; 1431 backend->AddRef(); 1432 } 1433} 1434#endif 1435 1436/* 1437#ifdef GALLIUM_DXGI_USE_DRM 1438void STDMETHODCALLTYPE GalliumDXGIUseDRMCard(int fd) 1439{ 1440 GalliumDXGIUseNothing(); 1441 dxgi_thread_binding.platform = native_get_drm_platform(); 1442 dxgi_thread_binding.display = (void*)fd; 1443 dxgi_thread_binding.backend = 0; 1444} 1445#endif 1446 1447#ifdef GALLIUM_DXGI_USE_FBDEV 1448void STDMETHODCALLTYPE GalliumDXGIUseFBDev(int fd) 1449{ 1450 GalliumDXGIUseNothing(); 1451 dxgi_thread_binding.platform = native_get_fbdev_platform(); 1452 dxgi_thread_binding.display = (void*)fd; 1453 dxgi_thread_binding.backend = 0; 1454} 1455#endif 1456 1457#ifdef GALLIUM_DXGI_USE_GDI 1458void STDMETHODCALLTYPE GalliumDXGIUseHDC(HDC hdc, PFNHWNDRESOLVER resolver, void* resolver_cookie) 1459{ 1460 GalliumDXGIUseNothing(); 1461 dxgi_thread_binding.platform = native_get_gdi_platform(); 1462 dxgi_thread_binding.display = (void*)hdc; 1463 dxgi_thread_binding.backend = 0; 1464} 1465#endif 1466*/ 1467void STDMETHODCALLTYPE GalliumDXGIMakeDefault() 1468{ 1469 if(dxgi_default_binding.backend) 1470 dxgi_default_binding.backend->Release(); 1471 dxgi_default_binding = dxgi_thread_binding; 1472 if(dxgi_default_binding.backend) 1473 dxgi_default_binding.backend->AddRef(); 1474} 1475 1476 /* TODO: why did Microsoft add this? should we do something different for DXGI 1.0 and 1.1? 1477 * Or perhaps what they actually mean is "only create a single factory in your application"? 1478 * TODO: should we use a singleton here, so we never have multiple DXGI objects for the same thing? */ 1479 HRESULT STDMETHODCALLTYPE CreateDXGIFactory1( 1480 REFIID riid, 1481 void **out_factory 1482) 1483 { 1484 GalliumDXGIFactory* factory; 1485 *out_factory = 0; 1486 if(dxgi_thread_binding.platform) 1487 factory = new GalliumDXGIFactory(dxgi_thread_binding.platform, dxgi_thread_binding.display, dxgi_thread_binding.backend); 1488 else if(dxgi_default_binding.platform) 1489 factory = new GalliumDXGIFactory(dxgi_default_binding.platform, dxgi_default_binding.display, dxgi_default_binding.backend); 1490 else 1491 factory = new GalliumDXGIFactory(native_get_x11_platform(), NULL, NULL); 1492 HRESULT hres = factory->QueryInterface(riid, out_factory); 1493 factory->Release(); 1494 return hres; 1495 } 1496 1497 HRESULT STDMETHODCALLTYPE CreateDXGIFactory( 1498 REFIID riid, 1499 void **out_factor 1500) 1501 { 1502 return CreateDXGIFactory1(riid, out_factor); 1503 } 1504