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