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