Merge branch 'lp-offset-twoside'
[mesa.git] / src / gallium / state_trackers / d3d1x / dxgi / src / dxgi_native.cpp
1 /**************************************************************************
2 *
3 * Copyright 2010 Luca Barbieri
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "dxgi_private.h"
28 extern "C" {
29 #include "native.h"
30 #include <util/u_format.h>
31 #include <util/u_inlines.h>
32 #include <util/u_simple_shaders.h>
33 #include <pipe/p_shader_tokens.h>
34 }
35 #include <iostream>
36 #include <memory>
37
38 struct GalliumDXGIOutput;
39 struct GalliumDXGIAdapter;
40 struct GalliumDXGISwapChain;
41 struct GalliumDXGIFactory;
42
43 static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain);
44 static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* adapter, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter);
45 static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output);
46 static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain);
47
48 template<typename Base = IDXGIObject, typename Parent = IDXGIObject>
49 struct GalliumDXGIObject : public GalliumPrivateDataComObject<Base>
50 {
51 ComPtr<Parent> parent;
52
53 GalliumDXGIObject(Parent* p_parent = 0)
54 {
55 this->parent = p_parent;
56 }
57
58 virtual HRESULT STDMETHODCALLTYPE GetParent(
59 REFIID riid,
60 void **out_parent)
61 {
62 return parent->QueryInterface(riid, out_parent);
63 }
64 };
65
66 COM_INTERFACE(IGalliumDXGIBackend, IUnknown)
67
68 // TODO: somehow check whether the window is fully obscured or not
69 struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
70 {
71 virtual HRESULT STDMETHODCALLTYPE BeginPresent(
72 HWND hwnd,
73 void** present_cookie,
74 void** window,
75 RECT *rect,
76 RGNDATA **rgndata,
77 BOOL* preserve_aspect_ratio
78 )
79 {
80 *window = (void*)hwnd;
81 rect->left = 0;
82 rect->top = 0;
83 rect->right = INT_MAX;
84 rect->bottom = INT_MAX;
85 *rgndata = 0;
86
87 // yes, because we like things looking good
88 *preserve_aspect_ratio = TRUE;
89 *present_cookie = 0;
90 return S_OK;
91 }
92
93 virtual void STDMETHODCALLTYPE EndPresent(
94 HWND hwnd,
95 void* present_cookie
96 )
97 {}
98
99 virtual HRESULT STDMETHODCALLTYPE TestPresent(HWND hwnd)
100 {
101 return S_OK;
102 }
103
104 virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
105 HWND hwnd,
106 unsigned* width,
107 unsigned* height
108 )
109 {
110 *width = 0;
111 *height = 0;
112 return S_OK;
113 }
114 };
115
116 // TODO: maybe install an X11 error hook, so we can return errors properly
117 struct GalliumDXGIX11IdentityBackend : public GalliumDXGIIdentityBackend
118 {
119 Display* dpy;
120
121 GalliumDXGIX11IdentityBackend(Display* dpy)
122 : dpy(dpy)
123 {}
124
125 virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
126 HWND hwnd,
127 unsigned* width,
128 unsigned* height
129 )
130 {
131 XWindowAttributes xwa;
132 XGetWindowAttributes(dpy, (Window)hwnd, &xwa);
133 *width = xwa.width;
134 *height = xwa.height;
135 return S_OK;
136 }
137 };
138
139 struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown>
140 {
141 HWND associated_window;
142 const struct native_platform* platform;
143 void* display;
144 ComPtr<IGalliumDXGIBackend> backend;
145 void* resolver_cookie;
146
147 GalliumDXGIFactory(const struct native_platform* platform, void* display, IGalliumDXGIBackend* p_backend)
148 : GalliumDXGIObject<IDXGIFactory1, IUnknown>((IUnknown*)NULL), platform(platform), display(display)
149 {
150 if(p_backend)
151 backend = p_backend;
152 else if(!strcmp(platform->name, "X11"))
153 backend.reset(new GalliumDXGIX11IdentityBackend((Display*)display));
154 else
155 backend.reset(new GalliumDXGIIdentityBackend());
156 }
157
158 virtual HRESULT STDMETHODCALLTYPE EnumAdapters(
159 UINT adapter,
160 IDXGIAdapter **out_adapter)
161 {
162 return EnumAdapters1(adapter, (IDXGIAdapter1**)out_adapter);
163 }
164
165 virtual HRESULT STDMETHODCALLTYPE EnumAdapters1(
166 UINT adapter,
167 IDXGIAdapter1 **out_adapter)
168 {
169 *out_adapter = 0;
170 if(adapter == 0)
171 {
172 return GalliumDXGIAdapterCreate(this, platform, display, out_adapter);
173 }
174 #if 0
175 // TODO: enable this
176 if(platform == native_get_x11_platform())
177 {
178 unsigned nscreens = ScreenCount((Display*)display);
179 if(adapter < nscreens)
180 {
181 unsigned def_screen = DefaultScreen(display);
182 if(adapter <= def_screen)
183 --adapter;
184 *out_adapter = GalliumDXGIAdapterCreate(this, platform, display, adapter);
185 return S_OK;
186 }
187 }
188 #endif
189 return DXGI_ERROR_NOT_FOUND;
190 }
191
192 /* TODO: this is a mysterious underdocumented magic API
193 * Can we have multiple windows associated?
194 * Can we have multiple windows associated if we use multiple factories?
195 * If so, what should GetWindowAssociation return?
196 * If not, does a new swapchain steal the association?
197 * Does this act for existing swapchains? For new swapchains?
198 */
199 virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
200 HWND window_handle,
201 UINT flags)
202 {
203 /* TODO: actually implement, for Wine, X11 and KMS*/
204 associated_window = window_handle;
205 return S_OK;
206 }
207
208 virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation(
209 HWND *pwindow_handle)
210 {
211 *pwindow_handle = associated_window;
212 return S_OK;
213 }
214
215 virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(
216 IUnknown *device,
217 DXGI_SWAP_CHAIN_DESC *desc,
218 IDXGISwapChain **out_swap_chain)
219 {
220 return GalliumDXGISwapChainCreate(this, device, *desc, out_swap_chain);
221 }
222
223 virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
224 HMODULE module,
225 IDXGIAdapter **out_adapter)
226 {
227 /* TODO: ignore the module, and just create a Gallium software screen */
228 *out_adapter = 0;
229 return E_NOTIMPL;
230 }
231
232 /* TODO: support hotplug */
233 virtual BOOL STDMETHODCALLTYPE IsCurrent( void)
234 {
235 return TRUE;
236 }
237 };
238
239 struct GalliumDXGIAdapter
240 : public GalliumMultiComObject<
241 GalliumDXGIObject<IDXGIAdapter1, GalliumDXGIFactory>,
242 IGalliumAdapter>
243 {
244 struct native_display* display;
245 const struct native_config** configs;
246 std::unordered_multimap<unsigned, unsigned> configs_by_pipe_format;
247 std::unordered_map<unsigned, unsigned> configs_by_native_visual_id;
248 const struct native_connector** connectors;
249 unsigned num_configs;
250 DXGI_ADAPTER_DESC1 desc;
251 std::vector<ComPtr<IDXGIOutput> > outputs;
252 int num_outputs;
253 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
385 struct 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 {
434 use_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
719 static 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
726 struct 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
865 struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>
866 {
867 ComPtr<IDXGIDevice>dxgi_device;
868 ComPtr<IGalliumDevice>gallium_device;
869 ComPtr<GalliumDXGIAdapter> adapter;
870 ComPtr<IDXGIOutput> target;
871
872 DXGI_SWAP_CHAIN_DESC desc;
873
874 struct native_surface* surface;
875 const struct native_config* config;
876
877 void* window;
878 struct pipe_resource* resources[NUM_NATIVE_ATTACHMENTS];
879 int width;
880 int height;
881 unsigned seq_num;
882 bool ever_validated;
883 bool needs_validation;
884 unsigned present_count;
885
886 ComPtr<IDXGISurface> buffer0;
887 struct pipe_resource* gallium_buffer0;
888 struct pipe_sampler_view* gallium_buffer0_view;
889
890 struct pipe_context* pipe;
891 bool owns_pipe;
892
893 BOOL fullscreen;
894
895 std::auto_ptr<dxgi_blitter> blitter;
896 bool formats_compatible;
897
898 GalliumDXGISwapChain(GalliumDXGIFactory* factory, IUnknown* p_device, const DXGI_SWAP_CHAIN_DESC& p_desc)
899 : GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>(factory), desc(p_desc), surface(0)
900 {
901 HRESULT hr;
902
903 hr = p_device->QueryInterface(IID_IGalliumDevice, (void**)&gallium_device);
904 if(!SUCCEEDED(hr))
905 throw hr;
906
907 hr = p_device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_device);
908 if(!SUCCEEDED(hr))
909 throw hr;
910
911 hr = dxgi_device->GetAdapter((IDXGIAdapter**)&adapter);
912 if(!SUCCEEDED(hr))
913 throw hr;
914
915 memset(resources, 0, sizeof(resources));
916
917 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL && desc.BufferCount != 1)
918 {
919 std::cerr << "Gallium DXGI: if DXGI_SWAP_EFFECT_SEQUENTIAL is specified, only buffer_count == 1 is implemented, but " << desc.BufferCount << " was specified: ignoring this" << std::endl;
920 // change the returned desc, so that the application might perhaps notice what we did and react well
921 desc.BufferCount = 1;
922 }
923
924 pipe = gallium_device->GetGalliumContext();
925 owns_pipe = false;
926 if(!pipe)
927 {
928 pipe = adapter->display->screen->context_create(adapter->display->screen, 0);
929 owns_pipe = true;
930 }
931
932 blitter.reset(new dxgi_blitter(pipe));
933 window = 0;
934
935 hr = resolve_zero_width_height(true);
936 if(!SUCCEEDED(hr))
937 throw hr;
938 }
939
940 void init_for_window()
941 {
942 if(surface)
943 {
944 surface->destroy(surface);
945 surface = 0;
946 }
947
948 unsigned config_num;
949 if(!strcmp(parent->platform->name, "X11"))
950 {
951 XWindowAttributes xwa;
952 XGetWindowAttributes((Display*)parent->display, (Window)window, &xwa);
953 assert(adapter->configs_by_native_visual_id.count(xwa.visual->visualid));
954 config_num = adapter->configs_by_native_visual_id[xwa.visual->visualid];
955 }
956 else
957 {
958 enum pipe_format format = dxgi_to_pipe_format[desc.BufferDesc.Format];
959 if(!adapter->configs_by_pipe_format.count(format))
960 {
961 if(adapter->configs_by_pipe_format.empty())
962 throw E_FAIL;
963 // TODO: choose the best match
964 format = (pipe_format)adapter->configs_by_pipe_format.begin()->first;
965 }
966 // TODO: choose the best config
967 config_num = adapter->configs_by_pipe_format.find(format)->second;
968 }
969
970 config = adapter->configs[config_num];
971 surface = adapter->display->create_window_surface(adapter->display, (EGLNativeWindowType)window, config);
972 surface->user_data = this;
973
974 width = 0;
975 height = 0;
976 seq_num = 0;
977 present_count = 0;
978 needs_validation = true;
979 ever_validated = false;
980
981 formats_compatible = util_is_format_compatible(
982 util_format_description(dxgi_to_pipe_format[desc.BufferDesc.Format]),
983 util_format_description(config->color_format));
984 }
985
986 ~GalliumDXGISwapChain()
987 {
988 if(owns_pipe)
989 pipe->destroy(pipe);
990 }
991
992 virtual HRESULT STDMETHODCALLTYPE GetDevice(
993 REFIID riid,
994 void **pdevice)
995 {
996 return dxgi_device->QueryInterface(riid, pdevice);
997 }
998
999 HRESULT create_buffer0()
1000 {
1001 HRESULT hr;
1002 ComPtr<IDXGISurface> new_buffer0;
1003 DXGI_USAGE usage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
1004 if(desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD)
1005 usage |= DXGI_USAGE_DISCARD_ON_PRESENT;
1006 // for our blitter
1007 usage |= DXGI_USAGE_SHADER_INPUT;
1008
1009 DXGI_SURFACE_DESC surface_desc;
1010 surface_desc.Format = desc.BufferDesc.Format;
1011 surface_desc.Width = desc.BufferDesc.Width;
1012 surface_desc.Height = desc.BufferDesc.Height;
1013 surface_desc.SampleDesc = desc.SampleDesc;
1014 hr = dxgi_device->CreateSurface(&surface_desc, 1, usage, 0, &new_buffer0);
1015 if(!SUCCEEDED(hr))
1016 return hr;
1017
1018 ComPtr<IGalliumResource> gallium_resource;
1019 hr = new_buffer0->QueryInterface(IID_IGalliumResource, (void**)&gallium_resource);
1020 if(!SUCCEEDED(hr))
1021 return hr;
1022
1023 struct pipe_resource* new_gallium_buffer0 = gallium_resource->GetGalliumResource();
1024 if(!new_gallium_buffer0)
1025 return E_FAIL;
1026
1027 buffer0.reset(new_buffer0.steal());
1028 gallium_buffer0 = new_gallium_buffer0;
1029 struct pipe_sampler_view templat;
1030 memset(&templat, 0, sizeof(templat));
1031 templat.texture = gallium_buffer0;
1032 templat.swizzle_r = 0;
1033 templat.swizzle_g = 1;
1034 templat.swizzle_b = 2;
1035 templat.swizzle_a = 3;
1036 templat.format = gallium_buffer0->format;
1037 gallium_buffer0_view = pipe->create_sampler_view(pipe, gallium_buffer0, &templat);
1038 return S_OK;
1039 }
1040
1041 bool validate()
1042 {
1043 unsigned new_seq_num;
1044 needs_validation = false;
1045
1046 if(!surface->validate(surface, (1 << NATIVE_ATTACHMENT_BACK_LEFT) | (1 << NATIVE_ATTACHMENT_FRONT_LEFT), &new_seq_num, resources, &width, &height))
1047 return false;
1048
1049 if(!ever_validated || seq_num != new_seq_num)
1050 {
1051 seq_num = new_seq_num;
1052 ever_validated = true;
1053 }
1054 return true;
1055 }
1056
1057 HRESULT resolve_zero_width_height(bool force = false)
1058 {
1059 if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height)
1060 return S_OK;
1061
1062 unsigned width, height;
1063 HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height);
1064 if(!SUCCEEDED(hr))
1065 return hr;
1066
1067 // On Windows, 8 is used, and a debug message saying so gets printed
1068 if(!width)
1069 width = 8;
1070 if(!height)
1071 height = 8;
1072
1073 if(!desc.BufferDesc.Width)
1074 desc.BufferDesc.Width = width;
1075 if(!desc.BufferDesc.Height)
1076 desc.BufferDesc.Height = height;
1077 return S_OK;
1078 }
1079
1080 virtual HRESULT STDMETHODCALLTYPE Present(
1081 UINT sync_interval,
1082 UINT flags)
1083 {
1084 HRESULT hr;
1085 if(flags & DXGI_PRESENT_TEST)
1086 return parent->backend->TestPresent(desc.OutputWindow);
1087
1088 if(!buffer0)
1089 {
1090 HRESULT hr = create_buffer0();
1091 if(!SUCCEEDED(hr))
1092 return hr;
1093 }
1094
1095 void* cur_window = 0;
1096 RECT rect;
1097 RGNDATA* rgndata;
1098 BOOL preserve_aspect_ratio;
1099 unsigned dst_w, dst_h;
1100 bool db;
1101 struct pipe_resource* dst;
1102 struct pipe_resource* src;
1103 struct pipe_surface* dst_surface;
1104 enum native_attachment att;
1105
1106 void* present_cookie;
1107 hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio);
1108 if(hr != S_OK)
1109 return hr;
1110
1111 if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom)
1112 goto end_present;
1113
1114 if(cur_window != window)
1115 {
1116 window = cur_window;
1117 init_for_window();
1118 }
1119
1120 if(needs_validation)
1121 {
1122 if(!validate())
1123 return DXGI_ERROR_DEVICE_REMOVED;
1124 }
1125
1126 db = !!(config->buffer_mask & (1 << NATIVE_ATTACHMENT_BACK_LEFT));
1127 dst = resources[db ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT];
1128 src = gallium_buffer0;
1129 dst_surface = 0;
1130
1131 assert(src);
1132 assert(dst);
1133
1134 /* TODO: sharing the context for blitting won't work correctly if queries are active
1135 * Hopefully no one is crazy enough to keep queries active while presenting, expecting
1136 * sensible results.
1137 * We could alternatively force using another context, but that might cause inefficiency issues
1138 */
1139
1140 if((unsigned)rect.right > dst->width0)
1141 rect.right = dst->width0;
1142 if((unsigned)rect.bottom > dst->height0)
1143 rect.bottom = dst->height0;
1144 if(rect.left > rect.right)
1145 rect.left = rect.right;
1146 if(rect.top > rect.bottom)
1147 rect.top = rect.bottom;
1148
1149 if(rect.left >= rect.right && rect.top >= rect.bottom)
1150 goto end_present;
1151
1152 dst_w = rect.right - rect.left;
1153 dst_h = rect.bottom - rect.top;
1154
1155 // TODO: add support for rgndata
1156 // if(preserve_aspect_ratio || !rgndata)
1157 if(1)
1158 {
1159 unsigned blit_x, blit_y, blit_w, blit_h;
1160 float black[4] = {0, 0, 0, 0};
1161
1162 if(!formats_compatible || src->width0 != dst_w || src->height0 != dst_h)
1163 dst_surface = pipe->screen->get_tex_surface(pipe->screen, dst, 0, 0, 0, PIPE_BIND_RENDER_TARGET);
1164
1165 if(preserve_aspect_ratio)
1166 {
1167 int delta = src->width0 * dst_h - dst_w * src->height0;
1168 if(delta > 0)
1169 {
1170 blit_w = dst_w;
1171 blit_h = dst_w * src->height0 / src->width0;
1172 }
1173 else if(delta < 0)
1174 {
1175 blit_w = dst_h * src->width0 / src->height0;
1176 blit_h = dst_h;
1177 }
1178 else
1179 {
1180 blit_w = dst_w;
1181 blit_h = dst_h;
1182 }
1183
1184 blit_x = (dst_w - blit_w) >> 1;
1185 blit_y = (dst_h - blit_h) >> 1;
1186 }
1187 else
1188 {
1189 blit_x = 0;
1190 blit_y = 0;
1191 blit_w = dst_w;
1192 blit_h = dst_h;
1193 }
1194
1195 if(blit_x)
1196 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, blit_x, dst_h);
1197 if(blit_y)
1198 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, dst_w, blit_y);
1199
1200 if(formats_compatible && blit_w == src->width0 && blit_h == src->height0)
1201 {
1202 pipe_subresource sr;
1203 sr.face = 0;
1204 sr.level = 0;
1205 pipe->resource_copy_region(pipe, dst, sr, rect.left, rect.top, 0, src, sr, 0, 0, 0, blit_w, blit_h);
1206 }
1207 else
1208 {
1209 blitter->blit(dst_surface, gallium_buffer0_view, rect.left + blit_x, rect.top + blit_y, blit_w, blit_h);
1210 if(!owns_pipe)
1211 gallium_device->RestoreGalliumState();
1212 }
1213
1214 if(blit_w != dst_w)
1215 pipe->clear_render_target(pipe, dst_surface, black, rect.left + blit_x + blit_w, rect.top, dst_w - blit_x - blit_w, dst_h);
1216 if(blit_h != dst_h)
1217 pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top + blit_y + blit_h, dst_w, dst_h - blit_y - blit_h);
1218 }
1219
1220 if(dst_surface)
1221 pipe->screen->tex_surface_destroy(dst_surface);
1222
1223 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE | PIPE_FLUSH_FRAME, 0);
1224
1225 att = (db) ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT;
1226 if(!surface->present(surface, att, FALSE, 0))
1227 return DXGI_ERROR_DEVICE_REMOVED;
1228
1229 end_present:
1230 parent->backend->EndPresent(desc.OutputWindow, present_cookie);
1231
1232 ++present_count;
1233 return S_OK;
1234 }
1235
1236 virtual HRESULT STDMETHODCALLTYPE GetBuffer(
1237 UINT Buffer,
1238 REFIID riid,
1239 void **ppSurface)
1240 {
1241 if(Buffer > 0)
1242 {
1243 if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL)
1244 std::cerr << "DXGI unimplemented: GetBuffer(n) with n > 0 not supported, returning buffer 0 instead!" << std::endl;
1245 else
1246 std::cerr << "DXGI error: in GetBuffer(n), n must be 0 for DXGI_SWAP_EFFECT_DISCARD\n" << std::endl;
1247 }
1248
1249 if(!buffer0)
1250 {
1251 HRESULT hr = create_buffer0();
1252 if(!SUCCEEDED(hr))
1253 return hr;
1254 }
1255 return buffer0->QueryInterface(riid, ppSurface);
1256 }
1257
1258 /* TODO: implement somehow */
1259 virtual HRESULT STDMETHODCALLTYPE SetFullscreenState(
1260 BOOL fullscreen,
1261 IDXGIOutput *target)
1262 {
1263 fullscreen = fullscreen;
1264 target = target;
1265 return S_OK;
1266 }
1267
1268 virtual HRESULT STDMETHODCALLTYPE GetFullscreenState(
1269 BOOL *out_fullscreen,
1270 IDXGIOutput **out_target)
1271 {
1272 if(out_fullscreen)
1273 *out_fullscreen = fullscreen;
1274 if(out_target)
1275 *out_target = target.ref();
1276 return S_OK;
1277 }
1278
1279 virtual HRESULT STDMETHODCALLTYPE GetDesc(
1280 DXGI_SWAP_CHAIN_DESC *out_desc)
1281 {
1282 *out_desc = desc;
1283 return S_OK;
1284 }
1285
1286 virtual HRESULT STDMETHODCALLTYPE ResizeBuffers(
1287 UINT buffer_count,
1288 UINT width,
1289 UINT height,
1290 DXGI_FORMAT new_format,
1291 UINT swap_chain_flags)
1292 {
1293 if(buffer0)
1294 {
1295 buffer0.p->AddRef();
1296 ULONG v = buffer0.p->Release();
1297 // we must fail if there are any references to buffer0 other than ours
1298 if(v > 1)
1299 return E_FAIL;
1300 pipe_sampler_view_reference(&gallium_buffer0_view, 0);
1301 buffer0 = (IUnknown*)NULL;
1302 gallium_buffer0 = 0;
1303 }
1304
1305 if(desc.SwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL)
1306 desc.BufferCount = buffer_count;
1307 desc.BufferDesc.Format = new_format;
1308 desc.BufferDesc.Width = width;
1309 desc.BufferDesc.Height = height;
1310 desc.Flags = swap_chain_flags;
1311 return resolve_zero_width_height();
1312 }
1313
1314 virtual HRESULT STDMETHODCALLTYPE ResizeTarget(
1315 const DXGI_MODE_DESC *out_new_target_parameters)
1316 {
1317 /* TODO: implement */
1318 return S_OK;
1319 }
1320
1321 virtual HRESULT STDMETHODCALLTYPE GetContainingOutput(
1322 IDXGIOutput **out_output)
1323 {
1324 *out_output = adapter->outputs[0].ref();
1325 return S_OK;
1326 }
1327
1328 virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
1329 DXGI_FRAME_STATISTICS *out_stats)
1330 {
1331 memset(out_stats, 0, sizeof(*out_stats));
1332 #ifdef _WIN32
1333 QueryPerformanceCounter(&out_stats->SyncQPCTime);
1334 #endif
1335 out_stats->PresentCount = present_count;
1336 out_stats->PresentRefreshCount = present_count;
1337 out_stats->SyncRefreshCount = present_count;
1338 return S_OK;
1339 }
1340
1341 virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount(
1342 UINT *last_present_count)
1343 {
1344 *last_present_count = present_count;
1345 return S_OK;
1346 }
1347 };
1348
1349 static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain)
1350 {
1351 ((GalliumDXGISwapChain*)swap_chain)->needs_validation = true;
1352 }
1353
1354 static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter)
1355 {
1356 try
1357 {
1358 *out_adapter = new GalliumDXGIAdapter(factory, platform, dpy);
1359 return S_OK;
1360 }
1361 catch(HRESULT hr)
1362 {
1363 return hr;
1364 }
1365 }
1366
1367 static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output)
1368 {
1369 try
1370 {
1371 *out_output = new GalliumDXGIOutput(adapter, name, connector);
1372 return S_OK;
1373 }
1374 catch(HRESULT hr)
1375 {
1376 return hr;
1377 }
1378 }
1379
1380 static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain)
1381 {
1382 try
1383 {
1384 *out_swap_chain = new GalliumDXGISwapChain(factory, device, desc);
1385 return S_OK;
1386 }
1387 catch(HRESULT hr)
1388 {
1389 return hr;
1390 }
1391 }
1392
1393 struct dxgi_binding
1394 {
1395 const struct native_platform* platform;
1396 void* display;
1397 IGalliumDXGIBackend* backend;
1398 };
1399
1400 static dxgi_binding dxgi_default_binding;
1401 static __thread dxgi_binding dxgi_thread_binding;
1402
1403 void STDMETHODCALLTYPE GalliumDXGIUseNothing()
1404 {
1405 dxgi_thread_binding.platform = 0;
1406 dxgi_thread_binding.display = 0;
1407 if(dxgi_thread_binding.backend)
1408 dxgi_thread_binding.backend->Release();
1409 dxgi_thread_binding.backend = 0;
1410 }
1411
1412 #ifdef GALLIUM_DXGI_USE_X11
1413 void STDMETHODCALLTYPE GalliumDXGIUseX11Display(Display* dpy, IGalliumDXGIBackend* backend)
1414 {
1415 GalliumDXGIUseNothing();
1416 dxgi_thread_binding.platform = native_get_x11_platform();
1417 dxgi_thread_binding.display = dpy;
1418
1419 if(backend)
1420 {
1421 dxgi_thread_binding.backend = backend;
1422 backend->AddRef();
1423 }
1424 }
1425 #endif
1426
1427 /*
1428 #ifdef GALLIUM_DXGI_USE_DRM
1429 void STDMETHODCALLTYPE GalliumDXGIUseDRMCard(int fd)
1430 {
1431 GalliumDXGIUseNothing();
1432 dxgi_thread_binding.platform = native_get_drm_platform();
1433 dxgi_thread_binding.display = (void*)fd;
1434 dxgi_thread_binding.backend = 0;
1435 }
1436 #endif
1437
1438 #ifdef GALLIUM_DXGI_USE_FBDEV
1439 void STDMETHODCALLTYPE GalliumDXGIUseFBDev(int fd)
1440 {
1441 GalliumDXGIUseNothing();
1442 dxgi_thread_binding.platform = native_get_fbdev_platform();
1443 dxgi_thread_binding.display = (void*)fd;
1444 dxgi_thread_binding.backend = 0;
1445 }
1446 #endif
1447
1448 #ifdef GALLIUM_DXGI_USE_GDI
1449 void STDMETHODCALLTYPE GalliumDXGIUseHDC(HDC hdc, PFNHWNDRESOLVER resolver, void* resolver_cookie)
1450 {
1451 GalliumDXGIUseNothing();
1452 dxgi_thread_binding.platform = native_get_gdi_platform();
1453 dxgi_thread_binding.display = (void*)hdc;
1454 dxgi_thread_binding.backend = 0;
1455 }
1456 #endif
1457 */
1458 void STDMETHODCALLTYPE GalliumDXGIMakeDefault()
1459 {
1460 if(dxgi_default_binding.backend)
1461 dxgi_default_binding.backend->Release();
1462 dxgi_default_binding = dxgi_thread_binding;
1463 if(dxgi_default_binding.backend)
1464 dxgi_default_binding.backend->AddRef();
1465 }
1466
1467 /* TODO: why did Microsoft add this? should we do something different for DXGI 1.0 and 1.1?
1468 * Or perhaps what they actually mean is "only create a single factory in your application"?
1469 * TODO: should we use a singleton here, so we never have multiple DXGI objects for the same thing? */
1470 HRESULT STDMETHODCALLTYPE CreateDXGIFactory1(
1471 REFIID riid,
1472 void **out_factory
1473 )
1474 {
1475 GalliumDXGIFactory* factory;
1476 *out_factory = 0;
1477 if(dxgi_thread_binding.platform)
1478 factory = new GalliumDXGIFactory(dxgi_thread_binding.platform, dxgi_thread_binding.display, dxgi_thread_binding.backend);
1479 else if(dxgi_default_binding.platform)
1480 factory = new GalliumDXGIFactory(dxgi_default_binding.platform, dxgi_default_binding.display, dxgi_default_binding.backend);
1481 else
1482 factory = new GalliumDXGIFactory(native_get_x11_platform(), NULL, NULL);
1483 HRESULT hres = factory->QueryInterface(riid, out_factory);
1484 factory->Release();
1485 return hres;
1486 }
1487
1488 HRESULT STDMETHODCALLTYPE CreateDXGIFactory(
1489 REFIID riid,
1490 void **out_factor
1491 )
1492 {
1493 return CreateDXGIFactory1(riid, out_factor);
1494 }