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