dxgi_native.cpp revision 92bc1111f3c5dd3616bd591129aebd3f4307e5b4
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 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 390struct 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 { 439use_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 724static 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 731struct 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.flatshade = 1; 758 rasterizer = pipe->create_rasterizer_state(pipe, &rs_state); 759 760 struct pipe_blend_state blendd; 761 memset(&blendd, 0, sizeof(blendd)); 762 blendd.rt[0].colormask = PIPE_MASK_RGBA; 763 blend = pipe->create_blend_state(pipe, &blendd); 764 765 struct pipe_depth_stencil_alpha_state zsad; 766 memset(&zsad, 0, sizeof(zsad)); 767 zsa = pipe->create_depth_stencil_alpha_state(pipe, &zsad); 768 769 struct pipe_vertex_element velem[2]; 770 memset(&velem[0], 0, sizeof(velem[0]) * 2); 771 velem[0].src_offset = 0; 772 velem[0].src_format = PIPE_FORMAT_R32G32_FLOAT; 773 velem[1].src_offset = 8; 774 velem[1].src_format = PIPE_FORMAT_R32G32_FLOAT; 775 elements = pipe->create_vertex_elements_state(pipe, 2, &velem[0]); 776 777 for(unsigned stretch = 0; stretch < 2; ++stretch) 778 { 779 struct pipe_sampler_state sampler_state; 780 memset(&sampler_state, 0, sizeof(sampler_state)); 781 sampler_state.min_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST; 782 sampler_state.mag_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST; 783 sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 784 sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 785 sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 786 sampler_state.normalized_coords = normalized; 787 788 sampler[stretch] = pipe->create_sampler_state(pipe, &sampler_state); 789 } 790 791 fs = util_make_fragment_tex_shader(pipe, normalized ? TGSI_TEXTURE_2D : TGSI_TEXTURE_RECT, TGSI_INTERPOLATE_LINEAR); 792 793 const unsigned semantic_names[] = { TGSI_SEMANTIC_POSITION, TGSI_SEMANTIC_GENERIC }; 794 const unsigned semantic_indices[] = { 0, 0 }; 795 vs = util_make_vertex_passthrough_shader(pipe, 2, semantic_names, semantic_indices); 796 797 vbuf.buffer = pipe_buffer_create(pipe->screen, PIPE_BIND_VERTEX_BUFFER, 798 PIPE_USAGE_STREAM, sizeof(quad_data)); 799 vbuf.buffer_offset = 0; 800 vbuf.stride = 4 * sizeof(float); 801 pipe_buffer_write(pipe, vbuf.buffer, 0, sizeof(quad_data), quad_data); 802 803 memset(&clip, 0, sizeof(clip)); 804 805 memset(&draw, 0, sizeof(draw)); 806 draw.mode = PIPE_PRIM_QUADS; 807 draw.count = 4; 808 draw.instance_count = 1; 809 draw.max_index = ~0; 810 } 811 812 void blit(struct pipe_surface* surf, struct pipe_sampler_view* view, unsigned x, unsigned y, unsigned w, unsigned h) 813 { 814 struct pipe_framebuffer_state fb; 815 memset(&fb, 0, sizeof(fb)); 816 fb.nr_cbufs = 1; 817 fb.cbufs[0] = surf; 818 fb.width = surf->width; 819 fb.height = surf->height; 820 821 struct pipe_viewport_state viewport; 822 float half_width = w * 0.5f; 823 float half_height = h * 0.5f; 824 viewport.scale[0] = half_width; 825 viewport.scale[1] = half_height; 826 viewport.scale[2] = 1.0f; 827 viewport.scale[3] = 1.0f; 828 viewport.translate[0] = x + half_width; 829 viewport.translate[1] = y + half_height; 830 viewport.translate[2] = 0.0f; 831 viewport.translate[3] = 1.0f; 832 833 bool stretch = view->texture->width0 != w || view->texture->height0 != h; 834 if(pipe->render_condition) 835 pipe->render_condition(pipe, 0, 0); 836 pipe->set_framebuffer_state(pipe, &fb); 837 pipe->bind_fragment_sampler_states(pipe, 1, &sampler[stretch]); 838 pipe->set_viewport_state(pipe, &viewport); 839 pipe->set_clip_state(pipe, &clip); 840 pipe->bind_rasterizer_state(pipe, rasterizer); 841 pipe->bind_depth_stencil_alpha_state(pipe, zsa); 842 pipe->bind_blend_state(pipe, blend); 843 pipe->bind_vertex_elements_state(pipe, elements); 844 pipe->set_vertex_buffers(pipe, 1, &vbuf); 845 pipe->bind_fs_state(pipe, fs); 846 pipe->bind_vs_state(pipe, vs); 847 if(pipe->bind_gs_state) 848 pipe->bind_gs_state(pipe, 0); 849 if(pipe->bind_stream_output_state) 850 pipe->bind_stream_output_state(pipe, 0); 851 pipe->set_fragment_sampler_views(pipe, 1, &view); 852 853 pipe->draw_vbo(pipe, &draw); 854 } 855 856 ~dxgi_blitter() 857 { 858 pipe->delete_blend_state(pipe, blend); 859 pipe->delete_rasterizer_state(pipe, rasterizer); 860 pipe->delete_depth_stencil_alpha_state(pipe, zsa); 861 pipe->delete_sampler_state(pipe, sampler[0]); 862 pipe->delete_sampler_state(pipe, sampler[1]); 863 pipe->delete_vertex_elements_state(pipe, elements); 864 pipe->delete_vs_state(pipe, vs); 865 pipe->delete_fs_state(pipe, fs); 866 pipe->screen->resource_destroy(pipe->screen, vbuf.buffer); 867 } 868}; 869 870struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory> 871{ 872 ComPtr<IDXGIDevice>dxgi_device; 873 ComPtr<IGalliumDevice>gallium_device; 874 ComPtr<GalliumDXGIAdapter> adapter; 875 ComPtr<IDXGIOutput> target; 876 877 DXGI_SWAP_CHAIN_DESC desc; 878 879 struct native_surface* surface; 880 const struct native_config* config; 881 882 void* window; 883 struct pipe_resource* resources[NUM_NATIVE_ATTACHMENTS]; 884 int width; 885 int height; 886 unsigned seq_num; 887 bool ever_validated; 888 bool needs_validation; 889 unsigned present_count; 890 891 ComPtr<IDXGISurface> buffer0; 892 struct pipe_resource* gallium_buffer0; 893 struct pipe_sampler_view* gallium_buffer0_view; 894 895 struct pipe_context* pipe; 896 bool owns_pipe; 897 898 BOOL fullscreen; 899 900 std::auto_ptr<dxgi_blitter> blitter; 901 bool formats_compatible; 902 903 GalliumDXGISwapChain(GalliumDXGIFactory* factory, IUnknown* p_device, const DXGI_SWAP_CHAIN_DESC& p_desc) 904 : GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>(factory), desc(p_desc), surface(0) 905 { 906 HRESULT hr; 907 908 hr = p_device->QueryInterface(IID_IGalliumDevice, (void**)&gallium_device); 909 if(!SUCCEEDED(hr)) 910 throw hr; 911 912 hr = p_device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_device); 913 if(!SUCCEEDED(hr)) 914 throw hr; 915 916 hr = dxgi_device->GetAdapter((IDXGIAdapter**)&adapter); 917 if(!SUCCEEDED(hr)) 918 throw hr; 919 920 memset(resources, 0, sizeof(resources)); 921 922 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL && desc.BufferCount != 1) 923 { 924 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; 925 // change the returned desc, so that the application might perhaps notice what we did and react well 926 desc.BufferCount = 1; 927 } 928 929 pipe = gallium_device->GetGalliumContext(); 930 owns_pipe = false; 931 if(!pipe) 932 { 933 pipe = adapter->display->screen->context_create(adapter->display->screen, 0); 934 owns_pipe = true; 935 } 936 937 blitter.reset(new dxgi_blitter(pipe)); 938 window = 0; 939 940 hr = resolve_zero_width_height(true); 941 if(!SUCCEEDED(hr)) 942 throw hr; 943 } 944 945 void init_for_window() 946 { 947 if(surface) 948 { 949 surface->destroy(surface); 950 surface = 0; 951 } 952 953 unsigned config_num; 954 if(!strcmp(parent->platform->name, "X11")) 955 { 956 XWindowAttributes xwa; 957 XGetWindowAttributes((Display*)parent->display, (Window)window, &xwa); 958 assert(adapter->configs_by_native_visual_id.count(xwa.visual->visualid)); 959 config_num = adapter->configs_by_native_visual_id[xwa.visual->visualid]; 960 } 961 else 962 { 963 enum pipe_format format = dxgi_to_pipe_format[desc.BufferDesc.Format]; 964 if(!adapter->configs_by_pipe_format.count(format)) 965 { 966 if(adapter->configs_by_pipe_format.empty()) 967 throw E_FAIL; 968 // TODO: choose the best match 969 format = (pipe_format)adapter->configs_by_pipe_format.begin()->first; 970 } 971 // TODO: choose the best config 972 config_num = adapter->configs_by_pipe_format.find(format)->second; 973 } 974 975 config = adapter->configs[config_num]; 976 surface = adapter->display->create_window_surface(adapter->display, (EGLNativeWindowType)window, config); 977 surface->user_data = this; 978 979 width = 0; 980 height = 0; 981 seq_num = 0; 982 present_count = 0; 983 needs_validation = true; 984 ever_validated = false; 985 986 formats_compatible = util_is_format_compatible( 987 util_format_description(dxgi_to_pipe_format[desc.BufferDesc.Format]), 988 util_format_description(config->color_format)); 989 } 990 991 ~GalliumDXGISwapChain() 992 { 993 if(owns_pipe) 994 pipe->destroy(pipe); 995 } 996 997 virtual HRESULT STDMETHODCALLTYPE GetDevice( 998 REFIID riid, 999 void **pdevice) 1000 { 1001 return dxgi_device->QueryInterface(riid, pdevice); 1002 } 1003 1004 HRESULT create_buffer0() 1005 { 1006 HRESULT hr; 1007 ComPtr<IDXGISurface> new_buffer0; 1008 DXGI_USAGE usage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT; 1009 if(desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD) 1010 usage |= DXGI_USAGE_DISCARD_ON_PRESENT; 1011 // for our blitter 1012 usage |= DXGI_USAGE_SHADER_INPUT; 1013 1014 DXGI_SURFACE_DESC surface_desc; 1015 surface_desc.Format = desc.BufferDesc.Format; 1016 surface_desc.Width = desc.BufferDesc.Width; 1017 surface_desc.Height = desc.BufferDesc.Height; 1018 surface_desc.SampleDesc = desc.SampleDesc; 1019 hr = dxgi_device->CreateSurface(&surface_desc, 1, usage, 0, &new_buffer0); 1020 if(!SUCCEEDED(hr)) 1021 return hr; 1022 1023 ComPtr<IGalliumResource> gallium_resource; 1024 hr = new_buffer0->QueryInterface(IID_IGalliumResource, (void**)&gallium_resource); 1025 if(!SUCCEEDED(hr)) 1026 return hr; 1027 1028 struct pipe_resource* new_gallium_buffer0 = gallium_resource->GetGalliumResource(); 1029 if(!new_gallium_buffer0) 1030 return E_FAIL; 1031 1032 buffer0.reset(new_buffer0.steal()); 1033 gallium_buffer0 = new_gallium_buffer0; 1034 struct pipe_sampler_view templat; 1035 memset(&templat, 0, sizeof(templat)); 1036 templat.texture = gallium_buffer0; 1037 templat.swizzle_r = 0; 1038 templat.swizzle_g = 1; 1039 templat.swizzle_b = 2; 1040 templat.swizzle_a = 3; 1041 templat.format = gallium_buffer0->format; 1042 gallium_buffer0_view = pipe->create_sampler_view(pipe, gallium_buffer0, &templat); 1043 return S_OK; 1044 } 1045 1046 bool validate() 1047 { 1048 unsigned new_seq_num; 1049 needs_validation = false; 1050 1051 if(!surface->validate(surface, (1 << NATIVE_ATTACHMENT_BACK_LEFT) | (1 << NATIVE_ATTACHMENT_FRONT_LEFT), &new_seq_num, resources, &width, &height)) 1052 return false; 1053 1054 if(!ever_validated || seq_num != new_seq_num) 1055 { 1056 seq_num = new_seq_num; 1057 ever_validated = true; 1058 } 1059 return true; 1060 } 1061 1062 HRESULT resolve_zero_width_height(bool force = false) 1063 { 1064 if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height) 1065 return S_OK; 1066 1067 unsigned width, height; 1068 HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height); 1069 if(!SUCCEEDED(hr)) 1070 return hr; 1071 1072 // On Windows, 8 is used, and a debug message saying so gets printed 1073 if(!width) 1074 width = 8; 1075 if(!height) 1076 height = 8; 1077 1078 if(!desc.BufferDesc.Width) 1079 desc.BufferDesc.Width = width; 1080 if(!desc.BufferDesc.Height) 1081 desc.BufferDesc.Height = height; 1082 return S_OK; 1083 } 1084 1085 virtual HRESULT STDMETHODCALLTYPE Present( 1086 UINT sync_interval, 1087 UINT flags) 1088 { 1089 HRESULT hr; 1090 if(flags & DXGI_PRESENT_TEST) 1091 return parent->backend->TestPresent(desc.OutputWindow); 1092 1093 if(!buffer0) 1094 { 1095 HRESULT hr = create_buffer0(); 1096 if(!SUCCEEDED(hr)) 1097 return hr; 1098 } 1099 1100 void* cur_window = 0; 1101 RECT rect; 1102 RGNDATA* rgndata; 1103 BOOL preserve_aspect_ratio; 1104 unsigned dst_w, dst_h; 1105 bool db; 1106 struct pipe_resource* dst; 1107 struct pipe_resource* src; 1108 struct pipe_surface* dst_surface; 1109 enum native_attachment att; 1110 1111 void* present_cookie; 1112 hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio); 1113 if(hr != S_OK) 1114 return hr; 1115 1116 if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom) 1117 goto end_present; 1118 1119 if(cur_window != window) 1120 { 1121 window = cur_window; 1122 init_for_window(); 1123 } 1124 1125 if(needs_validation) 1126 { 1127 if(!validate()) 1128 return DXGI_ERROR_DEVICE_REMOVED; 1129 } 1130 1131 db = !!(config->buffer_mask & (1 << NATIVE_ATTACHMENT_BACK_LEFT)); 1132 dst = resources[db ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT]; 1133 src = gallium_buffer0; 1134 dst_surface = 0; 1135 1136 assert(src); 1137 assert(dst); 1138 1139 /* TODO: sharing the context for blitting won't work correctly if queries are active 1140 * Hopefully no one is crazy enough to keep queries active while presenting, expecting 1141 * sensible results. 1142 * We could alternatively force using another context, but that might cause inefficiency issues 1143 */ 1144 1145 if((unsigned)rect.right > dst->width0) 1146 rect.right = dst->width0; 1147 if((unsigned)rect.bottom > dst->height0) 1148 rect.bottom = dst->height0; 1149 if(rect.left > rect.right) 1150 rect.left = rect.right; 1151 if(rect.top > rect.bottom) 1152 rect.top = rect.bottom; 1153 1154 if(rect.left >= rect.right && rect.top >= rect.bottom) 1155 goto end_present; 1156 1157 dst_w = rect.right - rect.left; 1158 dst_h = rect.bottom - rect.top; 1159 1160 // TODO: add support for rgndata 1161// if(preserve_aspect_ratio || !rgndata) 1162 if(1) 1163 { 1164 unsigned blit_x, blit_y, blit_w, blit_h; 1165 float black[4] = {0, 0, 0, 0}; 1166 1167 if(!formats_compatible || src->width0 != dst_w || src->height0 != dst_h) { 1168 struct pipe_surface templat; 1169 templat.usage = PIPE_BIND_RENDER_TARGET; 1170 templat.format = dst->format; 1171 templat.u.tex.level = 0; 1172 templat.u.tex.first_layer = 0; 1173 templat.u.tex.last_layer = 0; 1174 dst_surface = pipe->create_surface(pipe, dst, &templat); 1175 } 1176 1177 if(preserve_aspect_ratio) 1178 { 1179 int delta = src->width0 * dst_h - dst_w * src->height0; 1180 if(delta > 0) 1181 { 1182 blit_w = dst_w; 1183 blit_h = dst_w * src->height0 / src->width0; 1184 } 1185 else if(delta < 0) 1186 { 1187 blit_w = dst_h * src->width0 / src->height0; 1188 blit_h = dst_h; 1189 } 1190 else 1191 { 1192 blit_w = dst_w; 1193 blit_h = dst_h; 1194 } 1195 1196 blit_x = (dst_w - blit_w) >> 1; 1197 blit_y = (dst_h - blit_h) >> 1; 1198 } 1199 else 1200 { 1201 blit_x = 0; 1202 blit_y = 0; 1203 blit_w = dst_w; 1204 blit_h = dst_h; 1205 } 1206 1207 if(blit_x) 1208 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, blit_x, dst_h); 1209 if(blit_y) 1210 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, dst_w, blit_y); 1211 1212 if(formats_compatible && blit_w == src->width0 && blit_h == src->height0) 1213 { 1214 pipe_box box; 1215 box.x = box.y = box.z; 1216 box.width = blit_w; 1217 box.height = blit_h; 1218 box.z = 1; 1219 pipe->resource_copy_region(pipe, dst, 0, rect.left, rect.top, 0, src, 0, &box); 1220 } 1221 else 1222 { 1223 blitter->blit(dst_surface, gallium_buffer0_view, rect.left + blit_x, rect.top + blit_y, blit_w, blit_h); 1224 if(!owns_pipe) 1225 gallium_device->RestoreGalliumState(); 1226 } 1227 1228 if(blit_w != dst_w) 1229 pipe->clear_render_target(pipe, dst_surface, black, rect.left + blit_x + blit_w, rect.top, dst_w - blit_x - blit_w, dst_h); 1230 if(blit_h != dst_h) 1231 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top + blit_y + blit_h, dst_w, dst_h - blit_y - blit_h); 1232 } 1233 1234 if(dst_surface) 1235 pipe->surface_destroy(pipe, dst_surface); 1236 1237 pipe->flush(pipe, 0); 1238 1239 att = (db) ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT; 1240 if(!surface->present(surface, att, FALSE, 0)) 1241 return DXGI_ERROR_DEVICE_REMOVED; 1242 1243end_present: 1244 parent->backend->EndPresent(desc.OutputWindow, present_cookie); 1245 1246 ++present_count; 1247 return S_OK; 1248 } 1249 1250 virtual HRESULT STDMETHODCALLTYPE GetBuffer( 1251 UINT Buffer, 1252 REFIID riid, 1253 void **ppSurface) 1254 { 1255 if(Buffer > 0) 1256 { 1257 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL) 1258 std::cerr << "DXGI unimplemented: GetBuffer(n) with n > 0 not supported, returning buffer 0 instead!" << std::endl; 1259 else 1260 std::cerr << "DXGI error: in GetBuffer(n), n must be 0 for DXGI_SWAP_EFFECT_DISCARD\n" << std::endl; 1261 } 1262 1263 if(!buffer0) 1264 { 1265 HRESULT hr = create_buffer0(); 1266 if(!SUCCEEDED(hr)) 1267 return hr; 1268 } 1269 return buffer0->QueryInterface(riid, ppSurface); 1270 } 1271 1272 /* TODO: implement somehow */ 1273 virtual HRESULT STDMETHODCALLTYPE SetFullscreenState( 1274 BOOL fullscreen, 1275 IDXGIOutput *target) 1276 { 1277 fullscreen = fullscreen; 1278 target = target; 1279 return S_OK; 1280 } 1281 1282 virtual HRESULT STDMETHODCALLTYPE GetFullscreenState( 1283 BOOL *out_fullscreen, 1284 IDXGIOutput **out_target) 1285 { 1286 if(out_fullscreen) 1287 *out_fullscreen = fullscreen; 1288 if(out_target) 1289 *out_target = target.ref(); 1290 return S_OK; 1291 } 1292 1293 virtual HRESULT STDMETHODCALLTYPE GetDesc( 1294 DXGI_SWAP_CHAIN_DESC *out_desc) 1295 { 1296 *out_desc = desc; 1297 return S_OK; 1298 } 1299 1300 virtual HRESULT STDMETHODCALLTYPE ResizeBuffers( 1301 UINT buffer_count, 1302 UINT width, 1303 UINT height, 1304 DXGI_FORMAT new_format, 1305 UINT swap_chain_flags) 1306 { 1307 if(buffer0) 1308 { 1309 buffer0.p->AddRef(); 1310 ULONG v = buffer0.p->Release(); 1311 // we must fail if there are any references to buffer0 other than ours 1312 if(v > 1) 1313 return E_FAIL; 1314 pipe_sampler_view_reference(&gallium_buffer0_view, 0); 1315 buffer0 = (IUnknown*)NULL; 1316 gallium_buffer0 = 0; 1317 } 1318 1319 if(desc.SwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL) 1320 desc.BufferCount = buffer_count; 1321 desc.BufferDesc.Format = new_format; 1322 desc.BufferDesc.Width = width; 1323 desc.BufferDesc.Height = height; 1324 desc.Flags = swap_chain_flags; 1325 return resolve_zero_width_height(); 1326 } 1327 1328 virtual HRESULT STDMETHODCALLTYPE ResizeTarget( 1329 const DXGI_MODE_DESC *out_new_target_parameters) 1330 { 1331 /* TODO: implement */ 1332 return S_OK; 1333 } 1334 1335 virtual HRESULT STDMETHODCALLTYPE GetContainingOutput( 1336 IDXGIOutput **out_output) 1337 { 1338 *out_output = adapter->outputs[0].ref(); 1339 return S_OK; 1340 } 1341 1342 virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics( 1343 DXGI_FRAME_STATISTICS *out_stats) 1344 { 1345 memset(out_stats, 0, sizeof(*out_stats)); 1346#ifdef _WIN32 1347 QueryPerformanceCounter(&out_stats->SyncQPCTime); 1348#endif 1349 out_stats->PresentCount = present_count; 1350 out_stats->PresentRefreshCount = present_count; 1351 out_stats->SyncRefreshCount = present_count; 1352 return S_OK; 1353 } 1354 1355 virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount( 1356 UINT *last_present_count) 1357 { 1358 *last_present_count = present_count; 1359 return S_OK; 1360 } 1361}; 1362 1363static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain) 1364{ 1365 ((GalliumDXGISwapChain*)swap_chain)->needs_validation = true; 1366} 1367 1368static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter) 1369{ 1370 try 1371 { 1372 *out_adapter = new GalliumDXGIAdapter(factory, platform, dpy); 1373 return S_OK; 1374 } 1375 catch(HRESULT hr) 1376 { 1377 return hr; 1378 } 1379} 1380 1381static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output) 1382{ 1383 try 1384 { 1385 *out_output = new GalliumDXGIOutput(adapter, name, connector); 1386 return S_OK; 1387 } 1388 catch(HRESULT hr) 1389 { 1390 return hr; 1391 } 1392} 1393 1394static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain) 1395{ 1396 try 1397 { 1398 *out_swap_chain = new GalliumDXGISwapChain(factory, device, desc); 1399 return S_OK; 1400 } 1401 catch(HRESULT hr) 1402 { 1403 return hr; 1404 } 1405} 1406 1407struct dxgi_binding 1408{ 1409 const struct native_platform* platform; 1410 void* display; 1411 IGalliumDXGIBackend* backend; 1412}; 1413 1414static dxgi_binding dxgi_default_binding; 1415static __thread dxgi_binding dxgi_thread_binding; 1416static const struct native_event_handler dxgi_event_handler = { 1417 GalliumDXGIAdapter::handle_invalid_surface, 1418 dxgi_loader_create_drm_screen, 1419 dxgi_loader_create_sw_screen 1420}; 1421 1422void STDMETHODCALLTYPE GalliumDXGIUseNothing() 1423{ 1424 dxgi_thread_binding.platform = 0; 1425 dxgi_thread_binding.display = 0; 1426 if(dxgi_thread_binding.backend) 1427 dxgi_thread_binding.backend->Release(); 1428 dxgi_thread_binding.backend = 0; 1429} 1430 1431#ifdef GALLIUM_DXGI_USE_X11 1432void STDMETHODCALLTYPE GalliumDXGIUseX11Display(Display* dpy, IGalliumDXGIBackend* backend) 1433{ 1434 GalliumDXGIUseNothing(); 1435 dxgi_thread_binding.platform = native_get_x11_platform(&dxgi_event_handler); 1436 dxgi_thread_binding.display = dpy; 1437 1438 if(backend) 1439 { 1440 dxgi_thread_binding.backend = backend; 1441 backend->AddRef(); 1442 } 1443} 1444#endif 1445 1446/* 1447#ifdef GALLIUM_DXGI_USE_DRM 1448void STDMETHODCALLTYPE GalliumDXGIUseDRMCard(int fd) 1449{ 1450 GalliumDXGIUseNothing(); 1451 dxgi_thread_binding.platform = native_get_drm_platform(&dxgi_event_handler); 1452 dxgi_thread_binding.display = (void*)fd; 1453 dxgi_thread_binding.backend = 0; 1454} 1455#endif 1456 1457#ifdef GALLIUM_DXGI_USE_FBDEV 1458void STDMETHODCALLTYPE GalliumDXGIUseFBDev(int fd) 1459{ 1460 GalliumDXGIUseNothing(); 1461 dxgi_thread_binding.platform = native_get_fbdev_platform(&dxgi_event_handler); 1462 dxgi_thread_binding.display = (void*)fd; 1463 dxgi_thread_binding.backend = 0; 1464} 1465#endif 1466 1467#ifdef GALLIUM_DXGI_USE_GDI 1468void STDMETHODCALLTYPE GalliumDXGIUseHDC(HDC hdc, PFNHWNDRESOLVER resolver, void* resolver_cookie) 1469{ 1470 GalliumDXGIUseNothing(); 1471 dxgi_thread_binding.platform = native_get_gdi_platform(&dxgi_event_handler); 1472 dxgi_thread_binding.display = (void*)hdc; 1473 dxgi_thread_binding.backend = 0; 1474} 1475#endif 1476*/ 1477void STDMETHODCALLTYPE GalliumDXGIMakeDefault() 1478{ 1479 if(dxgi_default_binding.backend) 1480 dxgi_default_binding.backend->Release(); 1481 dxgi_default_binding = dxgi_thread_binding; 1482 if(dxgi_default_binding.backend) 1483 dxgi_default_binding.backend->AddRef(); 1484} 1485 1486 /* TODO: why did Microsoft add this? should we do something different for DXGI 1.0 and 1.1? 1487 * Or perhaps what they actually mean is "only create a single factory in your application"? 1488 * TODO: should we use a singleton here, so we never have multiple DXGI objects for the same thing? */ 1489 HRESULT STDMETHODCALLTYPE CreateDXGIFactory1( 1490 REFIID riid, 1491 void **out_factory 1492) 1493 { 1494 GalliumDXGIFactory* factory; 1495 *out_factory = 0; 1496 if(dxgi_thread_binding.platform) 1497 factory = new GalliumDXGIFactory(dxgi_thread_binding.platform, dxgi_thread_binding.display, dxgi_thread_binding.backend); 1498 else if(dxgi_default_binding.platform) 1499 factory = new GalliumDXGIFactory(dxgi_default_binding.platform, dxgi_default_binding.display, dxgi_default_binding.backend); 1500 else 1501 factory = new GalliumDXGIFactory(native_get_x11_platform(&dxgi_event_handler), NULL, NULL); 1502 HRESULT hres = factory->QueryInterface(riid, out_factory); 1503 factory->Release(); 1504 return hres; 1505 } 1506 1507 HRESULT STDMETHODCALLTYPE CreateDXGIFactory( 1508 REFIID riid, 1509 void **out_factor 1510) 1511 { 1512 return CreateDXGIFactory1(riid, out_factor); 1513 } 1514