blob: 5d581aa2e3f6ddf5e00da3ce276477f29b893d96 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2010 Luca Barbieri
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#include "dxgi_private.h"
extern "C" {
#include "native.h"
#include "util/u_format.h"
#include "util/u_inlines.h"
#include "util/u_simple_shaders.h"
#include "pipe/p_shader_tokens.h"
}
#include <iostream>
#include <memory>
struct GalliumDXGIOutput;
struct GalliumDXGIAdapter;
struct GalliumDXGISwapChain;
struct GalliumDXGIFactory;
static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain);
static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* adapter, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter);
static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output);
static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain);
template<typename Base = IDXGIObject, typename Parent = IDXGIObject>
struct GalliumDXGIObject : public GalliumPrivateDataComObject<Base>
{
ComPtr<Parent> parent;
GalliumDXGIObject(Parent* p_parent = 0)
{
this->parent = p_parent;
}
virtual HRESULT STDMETHODCALLTYPE GetParent(
REFIID riid,
void **out_parent)
{
return parent->QueryInterface(riid, out_parent);
}
};
COM_INTERFACE(IGalliumDXGIBackend, IUnknown)
// TODO: somehow check whether the window is fully obscured or not
struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
{
virtual HRESULT STDMETHODCALLTYPE BeginPresent(
HWND hwnd,
void** present_cookie,
void** window,
RECT *rect,
RGNDATA **rgndata,
BOOL* preserve_aspect_ratio
)
{
*window = (void*)hwnd;
rect->left = 0;
rect->top = 0;
rect->right = INT_MAX;
rect->bottom = INT_MAX;
*rgndata = 0;
// yes, because we like things looking good
*preserve_aspect_ratio = TRUE;
*present_cookie = 0;
return S_OK;
}
virtual void STDMETHODCALLTYPE EndPresent(
HWND hwnd,
void* present_cookie
)
{}
virtual HRESULT STDMETHODCALLTYPE TestPresent(HWND hwnd)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
HWND hwnd,
unsigned* width,
unsigned* height
)
{
*width = 0;
*height = 0;
return S_OK;
}
};
// TODO: maybe install an X11 error hook, so we can return errors properly
struct GalliumDXGIX11IdentityBackend : public GalliumDXGIIdentityBackend
{
Display* dpy;
GalliumDXGIX11IdentityBackend(Display* dpy)
: dpy(dpy)
{}
virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
HWND hwnd,
unsigned* width,
unsigned* height
)
{
XWindowAttributes xwa;
XGetWindowAttributes(dpy, (Window)hwnd, &xwa);
*width = xwa.width;
*height = xwa.height;
return S_OK;
}
};
struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown>
{
HWND associated_window;
const struct native_platform* platform;
void* display;
ComPtr<IGalliumDXGIBackend> backend;
void* resolver_cookie;
GalliumDXGIFactory(const struct native_platform* platform, void* display, IGalliumDXGIBackend* p_backend)
: GalliumDXGIObject<IDXGIFactory1, IUnknown>((IUnknown*)NULL), platform(platform), display(display)
{
if(p_backend)
backend = p_backend;
else if(!strcmp(platform->name, "X11"))
backend.reset(new GalliumDXGIX11IdentityBackend((Display*)display));
else
backend.reset(new GalliumDXGIIdentityBackend());
}
virtual HRESULT STDMETHODCALLTYPE EnumAdapters(
UINT adapter,
IDXGIAdapter **out_adapter)
{
return EnumAdapters1(adapter, (IDXGIAdapter1**)out_adapter);
}
virtual HRESULT STDMETHODCALLTYPE EnumAdapters1(
UINT adapter,
IDXGIAdapter1 **out_adapter)
{
*out_adapter = 0;
if(adapter == 0)
{
return GalliumDXGIAdapterCreate(this, platform, display, out_adapter);
}
#if 0
// TODO: enable this
if(platform == native_get_x11_platform())
{
unsigned nscreens = ScreenCount((Display*)display);
if(adapter < nscreens)
{
unsigned def_screen = DefaultScreen(display);
if(adapter <= def_screen)
--adapter;
*out_adapter = GalliumDXGIAdapterCreate(this, platform, display, adapter);
return S_OK;
}
}
#endif
return DXGI_ERROR_NOT_FOUND;
}
/* TODO: this is a mysterious underdocumented magic API
* Can we have multiple windows associated?
* Can we have multiple windows associated if we use multiple factories?
* If so, what should GetWindowAssociation return?
* If not, does a new swapchain steal the association?
* Does this act for existing swapchains? For new swapchains?
*/
virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
HWND window_handle,
UINT flags)
{
/* TODO: actually implement, for Wine, X11 and KMS*/
associated_window = window_handle;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation(
HWND *pwindow_handle)
{
*pwindow_handle = associated_window;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(
IUnknown *device,
DXGI_SWAP_CHAIN_DESC *desc,
IDXGISwapChain **out_swap_chain)
{
return GalliumDXGISwapChainCreate(this, device, *desc, out_swap_chain);
}
virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
HMODULE module,
IDXGIAdapter **out_adapter)
{
/* TODO: ignore the module, and just create a Gallium software screen */
*out_adapter = 0;
return E_NOTIMPL;
}
/* TODO: support hotplug */
virtual BOOL STDMETHODCALLTYPE IsCurrent( void)
{
return TRUE;
}
};
struct GalliumDXGIAdapter
: public GalliumMultiComObject<
GalliumDXGIObject<IDXGIAdapter1, GalliumDXGIFactory>,
IGalliumAdapter>
{
struct native_display* display;
const struct native_config** configs;
std::unordered_multimap<unsigned, unsigned> configs_by_pipe_format;
std::unordered_map<unsigned, unsigned> configs_by_native_visual_id;
const struct native_connector** connectors;
unsigned num_configs;
DXGI_ADAPTER_DESC1 desc;
std::vector<ComPtr<IDXGIOutput> > outputs;
int num_outputs;
GalliumDXGIAdapter(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy)
{
this->parent = factory;
display = platform->create_display(dpy, FALSE);
if(!display)
display = platform->create_display(dpy, TRUE);
if (display) {
display->user_data = this;
if (!display->init_screen(display)) {
display->destroy(display);
display = NULL;
}
}
if(!display)
throw E_FAIL;
memset(&desc, 0, sizeof(desc));
std::string s = std::string("GalliumD3D on ") + display->screen->get_name(display->screen) + " by " + display->screen->get_vendor(display->screen);
/* hopefully no one will decide to use UTF-8 in Gallium name/vendor strings */
for(int i = 0; i < std::min((int)s.size(), 127); ++i)
desc.Description[i] = (WCHAR)s[i];
// TODO: add an interface to get these; for now, return mid/low values
desc.DedicatedVideoMemory = 256 << 20;
desc.DedicatedSystemMemory = 256 << 20;
desc.SharedSystemMemory = 1024 << 20;
// TODO: we should actually use an unique ID instead
*(void**)&desc.AdapterLuid = dpy;
configs = display->get_configs(display, (int*)&num_configs);
for(unsigned i = 0; i < num_configs; ++i)
{
if(configs[i]->window_bit)
{
configs_by_pipe_format.insert(std::make_pair(configs[i]->color_format, i));
configs_by_native_visual_id[configs[i]->native_visual_id] = i;
}
}
connectors = 0;
num_outputs = 0;
if(display->modeset)
{
int num_crtcs;
connectors = display->modeset->get_connectors(display, &num_outputs, &num_crtcs);
if(!connectors)
num_outputs = 0;
else if(!num_outputs)
{
free(connectors);
connectors = 0;
}
}
if(!num_outputs)
num_outputs = 1;
}
static void handle_invalid_surface(struct native_display *ndpy, struct native_surface *nsurf, unsigned int seq_num)
{
GalliumDXGISwapChainRevalidate((IDXGISwapChain*)nsurf->user_data);
}
~GalliumDXGIAdapter()
{
display->destroy(display);
free(configs);
free(connectors);
}
virtual HRESULT STDMETHODCALLTYPE EnumOutputs(
UINT output,
IDXGIOutput **out_output)
{
if(output >= (unsigned)num_outputs)
return DXGI_ERROR_NOT_FOUND;
if(connectors)
{
std::ostringstream ss;
ss << "output #" << output;
return GalliumDXGIOutputCreate(this, ss.str(), connectors[output], out_output);
}
else
return GalliumDXGIOutputCreate(this, "Unique output", NULL, out_output);
}
virtual HRESULT STDMETHODCALLTYPE GetDesc(
DXGI_ADAPTER_DESC *desc)
{
memcpy(desc, &desc, sizeof(*desc));
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetDesc1(
DXGI_ADAPTER_DESC1 *desc)
{
memcpy(desc, &desc, sizeof(*desc));
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport(
REFGUID interface_name,
LARGE_INTEGER *u_m_d_version)
{
// these number was taken from Windows 7 with Catalyst 10.8: its meaning is unclear
if(interface_name == IID_ID3D11Device || interface_name == IID_ID3D10Device1 || interface_name == IID_ID3D10Device)
{
u_m_d_version->QuadPart = 0x00080011000a0411ULL;
return S_OK;
}
return DXGI_ERROR_UNSUPPORTED;
}
pipe_screen* STDMETHODCALLTYPE GetGalliumScreen()
{
return display->screen;
}
pipe_screen* STDMETHODCALLTYPE GetGalliumReferenceSoftwareScreen()
{
// TODO: give a softpipe screen
return display->screen;
}
pipe_screen* STDMETHODCALLTYPE GetGalliumFastSoftwareScreen()
{
// TODO: give an llvmpipe screen
return display->screen;
}
};
struct GalliumDXGIOutput : public GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter>
{
DXGI_OUTPUT_DESC desc;
const struct native_mode** modes;
DXGI_MODE_DESC* dxgi_modes;
unsigned num_modes;
const struct native_connector* connector;
DXGI_GAMMA_CONTROL* gamma;
GalliumDXGIOutput(GalliumDXGIAdapter* adapter, std::string name, const struct native_connector* connector = 0)
: GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter>(adapter), connector(connector)
{
memset(&desc, 0, sizeof(desc));
for(unsigned i = 0; i < std::min(name.size(), sizeof(desc.DeviceName) - 1); ++i)
desc.DeviceName[i] = name[i];
desc.AttachedToDesktop = TRUE;
/* TODO: should put an HMONITOR in desc.Monitor */
gamma = 0;
num_modes = 0;
modes = 0;
if(connector)
{
modes = parent->display->modeset->get_modes(parent->display, connector, (int*)&num_modes);
if(modes && num_modes)
{
dxgi_modes = new DXGI_MODE_DESC[num_modes];
for(unsigned i = 0; i < num_modes; ++i)
{
dxgi_modes[i].Width = modes[i]->width;
dxgi_modes[i].Height = modes[i]->height;
dxgi_modes[i].RefreshRate.Numerator = modes[i]->refresh_rate;
dxgi_modes[i].RefreshRate.Denominator = 1;
dxgi_modes[i].Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
dxgi_modes[i].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
}
}
else
{
if(modes)
{
free(modes);
modes = 0;
}
goto use_fake_mode;
}
}
else
{
use_fake_mode:
dxgi_modes = new DXGI_MODE_DESC[1];
dxgi_modes[0].Width = 1920;
dxgi_modes[0].Height = 1200;
dxgi_modes[0].RefreshRate.Numerator = 60;
dxgi_modes[0].RefreshRate.Denominator = 1;
dxgi_modes[0].Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
dxgi_modes[0].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
}
}
~GalliumDXGIOutput()
{
delete [] dxgi_modes;
free(modes);
if(gamma)
delete gamma;
}
virtual HRESULT STDMETHODCALLTYPE GetDesc(
DXGI_OUTPUT_DESC *out_desc)
{
*out_desc = desc;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetDisplayModeList(
DXGI_FORMAT enum_format,
UINT flags,
UINT *pcount,
DXGI_MODE_DESC *desc)
{
/* TODO: should we return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE when we don't
* support modesetting instead of fake modes?
*/
pipe_format format = dxgi_to_pipe_format[enum_format];
if(parent->configs_by_pipe_format.count(format))
{
if(!desc)
{
*pcount = num_modes;
return S_OK;
}
unsigned copy_modes = std::min(num_modes, *pcount);
for(unsigned i = 0; i < copy_modes; ++i)
{
desc[i] = dxgi_modes[i];
desc[i].Format = enum_format;
}
*pcount = num_modes;
if(copy_modes < num_modes)
return DXGI_ERROR_MORE_DATA;
else
return S_OK;
}
else
{
*pcount = 0;
return S_OK;
}
}
virtual HRESULT STDMETHODCALLTYPE FindClosestMatchingMode(
const DXGI_MODE_DESC *pModeToMatch,
DXGI_MODE_DESC *closest_match,
IUnknown *concerned_device)
{
/* TODO: actually implement this */
DXGI_FORMAT dxgi_format = pModeToMatch->Format;
enum pipe_format format = dxgi_to_pipe_format[dxgi_format];
init_pipe_to_dxgi_format();
if(!parent->configs_by_pipe_format.count(format))
{
if(!concerned_device)
return E_FAIL;
else
{
format = parent->configs[0]->color_format;
dxgi_format = pipe_to_dxgi_format[format];
}
}
*closest_match = dxgi_modes[0];
closest_match->Format = dxgi_format;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE WaitForVBlank( void)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE TakeOwnership(
IUnknown *device,
BOOL exclusive)
{
return S_OK;
}
virtual void STDMETHODCALLTYPE ReleaseOwnership( void)
{
}
virtual HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities(
DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps)
{
memset(gamma_caps, 0, sizeof(*gamma_caps));
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE SetGammaControl(
const DXGI_GAMMA_CONTROL *pArray)
{
if(!gamma)
gamma = new DXGI_GAMMA_CONTROL;
*gamma = *pArray;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetGammaControl(
DXGI_GAMMA_CONTROL *pArray)
{
if(gamma)
*pArray = *gamma;
else
{
pArray->Scale.Red = 1;
pArray->Scale.Green = 1;
pArray->Scale.Blue = 1;
pArray->Offset.Red = 0;
pArray->Offset.Green = 0;
pArray->Offset.Blue = 0;
for(unsigned i = 0; i <= 1024; ++i)
pArray->GammaCurve[i].Red = pArray->GammaCurve[i].Green = pArray->GammaCurve[i].Blue = (float)i / 1024.0;
}
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE SetDisplaySurface(
IDXGISurface *scanout_surface)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData(
IDXGISurface *destination)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
DXGI_FRAME_STATISTICS *stats)
{
memset(stats, 0, sizeof(*stats));
#ifdef _WIN32
QueryPerformanceCounter(&stats->SyncQPCTime);
#endif
return E_NOTIMPL;
}
};
/* Swap chain are rather complex, and Microsoft's documentation is rather
* lacking. As far as I know, this is the most thorough publicly available
* description of how swap chains work, based on multiple sources and
* experimentation.
*
* There are two modes (called "swap effects") that a swap chain can operate in:
* discard and sequential.
*
* In discard mode, things always look as if there is a single buffer, which
* you can get with GetBuffers(0).
* The 2D texture returned by GetBuffers(0) and can only be
* used as a render target view and for resource copies, since no CPU access
* flags are set and only the D3D11_BIND_RENDER_TARGET bind flag is set.
* On Present, it is copied to the actual display
* surface and the contents become undefined.
* D3D may internally use multiple buffers, but you can't observe this, except
* by looking at the buffer contents after Present (but those are undefined).
* If it uses multiple buffers internally, then it will normally use buffer_count buffers
* (this has latency implications).
* Discard mode seems to internally use a single buffer in windowed mode,
* even if DWM is enabled, and buffer_count buffers in fullscreen mode.
*
* In sequential mode, the runtime alllocates buffer_count buffers.
* You can get each with GetBuffers(n).
* GetBuffers(0) ALWAYS points to the backbuffer to be presented and has the
* same usage constraints as the discard mode.
* GetBuffer(n) with n > 0 points to resources that are identical to buffer 0, but
* are classified as "read-only resources" (due to DXGI_USAGE_READ_ONLY),
* meaning that you can't create render target views on them, or use them as
* a CopyResource/CopySubresourceRegion destination.
* It appears the only valid operation is to use them as a source for CopyResource
* and CopySubresourceRegion as well as just waiting for them to become
* buffer 0 again.
* Buffer n - 1 is always displayed on screen.
* When you call Present(), the contents of the buffers are rotated, so that buffer 0
* goes to buffer n - 1, and is thus displayed, and buffer 1 goes to buffer 0, becomes
* the accessible back buffer.
* The resources themselves are NOT rotated, so that you can still render on the
* same ID3D11Texture2D*, and views based on it, that you got before Present().
*
* Present seems to happen by either copying the relevant buffer into the window,
* or alternatively making it the current one, either by programming the CRTC or
* by sending the resource name to the DWM compositor.
*
* Hence, you can call GetBuffer(0) once and keep using the same ID3D11Texture2D*
* and ID3D11RenderTargetView* (and other views if needed) you got from it.
*
* If the window gets resized, DXGI will then "emulate" all successive presentations,
* by using a stretched blit automatically.
* Thus, you should handle WM_SIZE and call ResizeBuffers to update the DXGI
* swapchain buffers size to the new window size.
* Doing so requires you to release all GetBuffers() results and anything referencing
* them, including views and Direct3D11 deferred context command lists (this is
* documented).
*
* How does Microsoft implement the rotation behavior?
* It turns out that it does it by calling RotateResourceIdentitiesDXGI in the user-mode
* DDI driver.
* This will rotate the kernel buffer handle, or possibly rotate the GPU virtual memory
* mappings.
*
* The reason this is done by driver instead of by the runtime appears to be that
* this is necessary to support driver-provided command list support, since otherwise
* the command list would not always target the current backbuffer, since it would
* be done at the driver level, while only the runtime knows about the rotation.
*
* OK, so how do we implement this in Gallium?
*
* There are three strategies:
* 1. Use a single buffer, and always copy it to a window system provided buffer, or
* just give the buffer to the window system if it supports that
* 2. Rotate the buffers in the D3D1x implementation, and recreate and rebind the views.
* Don't support driver-provided command lists
* 3. Add this rotation functionality to the Gallium driver, with the idea that it would rotate
* remap GPU virtual memory, so that virtual address are unchanged, but the physical
* ones are rotated (so that pushbuffers remain valid).
* If the driver does not support this, either fall back to (1), or have a layer doing this,
* putting a deferred context layer over this intermediate layer.
*
* (2) is not acceptable since it prevents an optimal implementation.
* (3) is the ideal solution, but it is complicated.
*
* Hence, we implement (1) for now, and will switch to (3) later.
*
* Note that (1) doesn't really work for DXGI_SWAP_EFFECT_SEQUENTIAL with more
* than one buffer, so we just pretend we got asked for a single buffer in that case
* Fortunately, no one seems to rely on that, so we'll just not implement it at first, and
* later perform the rotation with blits.
* Once we switch to (3), we'll just use real rotation to do it..
*
* DXGI_SWAP_EFFECT_SEQUENTIAL with more than one buffer is of dubious use
* anyway, since you can only render or write to buffer 0, and other buffers can apparently
* be used only as sources for copies.
* I was unable to find any code using it either in DirectX SDK examples, or on the web.
*
* It seems the only reason you would use it is to not have to redraw from scratch, while
* also possibly avoid a copy compared to buffer_count == 1, assuming that your
* application is OK with having to redraw starting not from the last frame, but from
* one/two/more frames behind it.
*
* A better design would forbid the user specifying buffer_count explicitly, and
* would instead let the application give an upper bound on how old the buffer can
* become after presentation, with "infinite" being equivalent to discard.
* The runtime would then tell the application with frame number the buffer switched to
* after present.
* In addition, in a better design, the application would be allowed to specify the
* number of buffers available, having all them usable for rendering, so that things
* like video players could efficiently decode frames in parallel.
* Present would in such a better design gain a way to specify the number of buffers
* to present.
*
* Other miscellaneous info:
* DXGI_PRESENT_DO_NOT_SEQUENCE causes DXGI to hold the frame for another
* vblank interval without rotating the resource data.
*
* References:
* "DXGI Overview" in MSDN
* IDXGISwapChain documentation on MSDN
* "RotateResourceIdentitiesDXGI" on MSDN
* http://forums.xna.com/forums/p/42362/266016.aspx
*/
static float quad_data[] = {
-1, -1, 0, 0,
-1, 1, 0, 1,
1, 1, 1, 1,
1, -1, 1, 0,
};
struct dxgi_blitter
{
pipe_context* pipe;
bool normalized;
void* fs;
void* vs;
void* sampler[2];
void* elements;
void* blend;
void* rasterizer;
void* zsa;
struct pipe_clip_state clip;
struct pipe_vertex_buffer vbuf;
struct pipe_draw_info draw;
dxgi_blitter(pipe_context* pipe)
: pipe(pipe)
{
//normalized = !!pipe->screen->get_param(pipe, PIPE_CAP_NPOT_TEXTURES);
// TODO: need to update buffer in unnormalized case
normalized = true;
struct pipe_rasterizer_state rs_state;
memset(&rs_state, 0, sizeof(rs_state));
rs_state.cull_face = PIPE_FACE_NONE;
rs_state.gl_rasterization_rules = 1;
rs_state.depth_clip = 1;
rs_state.flatshade = 1;
rasterizer = pipe->create_rasterizer_state(pipe, &rs_state);
struct pipe_blend_state blendd;
memset(&blendd, 0, sizeof(blendd));
blendd.rt[0].colormask = PIPE_MASK_RGBA;
blend = pipe->create_blend_state(pipe, &blendd);
struct pipe_depth_stencil_alpha_state zsad;
memset(&zsad, 0, sizeof(zsad));
zsa = pipe->create_depth_stencil_alpha_state(pipe, &zsad);
struct pipe_vertex_element velem[2];
memset(&velem[0], 0, sizeof(velem[0]) * 2);
velem[0].src_offset = 0;
velem[0].src_format = PIPE_FORMAT_R32G32_FLOAT;
velem[1].src_offset = 8;
velem[1].src_format = PIPE_FORMAT_R32G32_FLOAT;
elements = pipe->create_vertex_elements_state(pipe, 2, &velem[0]);
for(unsigned stretch = 0; stretch < 2; ++stretch)
{
struct pipe_sampler_state sampler_state;
memset(&sampler_state, 0, sizeof(sampler_state));
sampler_state.min_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST;
sampler_state.mag_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST;
sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
sampler_state.normalized_coords = normalized;
sampler[stretch] = pipe->create_sampler_state(pipe, &sampler_state);
}
fs = util_make_fragment_tex_shader(pipe, normalized ? TGSI_TEXTURE_2D : TGSI_TEXTURE_RECT, TGSI_INTERPOLATE_LINEAR);
const unsigned semantic_names[] = { TGSI_SEMANTIC_POSITION, TGSI_SEMANTIC_GENERIC };
const unsigned semantic_indices[] = { 0, 0 };
vs = util_make_vertex_passthrough_shader(pipe, 2, semantic_names, semantic_indices);
vbuf.buffer = pipe_buffer_create(pipe->screen, PIPE_BIND_VERTEX_BUFFER,
PIPE_USAGE_STREAM, sizeof(quad_data));
vbuf.buffer_offset = 0;
vbuf.stride = 4 * sizeof(float);
pipe_buffer_write(pipe, vbuf.buffer, 0, sizeof(quad_data), quad_data);
memset(&clip, 0, sizeof(clip));
memset(&draw, 0, sizeof(draw));
draw.mode = PIPE_PRIM_QUADS;
draw.count = 4;
draw.instance_count = 1;
draw.max_index = ~0;
}
void blit(struct pipe_surface* surf, struct pipe_sampler_view* view, unsigned x, unsigned y, unsigned w, unsigned h)
{
struct pipe_framebuffer_state fb;
memset(&fb, 0, sizeof(fb));
fb.nr_cbufs = 1;
fb.cbufs[0] = surf;
fb.width = surf->width;
fb.height = surf->height;
struct pipe_viewport_state viewport;
float half_width = w * 0.5f;
float half_height = h * 0.5f;
viewport.scale[0] = half_width;
viewport.scale[1] = half_height;
viewport.scale[2] = 1.0f;
viewport.scale[3] = 1.0f;
viewport.translate[0] = x + half_width;
viewport.translate[1] = y + half_height;
viewport.translate[2] = 0.0f;
viewport.translate[3] = 1.0f;
bool stretch = view->texture->width0 != w || view->texture->height0 != h;
if(pipe->render_condition)
pipe->render_condition(pipe, 0, 0);
pipe->set_framebuffer_state(pipe, &fb);
pipe->bind_fragment_sampler_states(pipe, 1, &sampler[stretch]);
pipe->set_viewport_state(pipe, &viewport);
pipe->set_clip_state(pipe, &clip);
pipe->bind_rasterizer_state(pipe, rasterizer);
pipe->bind_depth_stencil_alpha_state(pipe, zsa);
pipe->bind_blend_state(pipe, blend);
pipe->bind_vertex_elements_state(pipe, elements);
pipe->set_vertex_buffers(pipe, 1, &vbuf);
pipe->bind_fs_state(pipe, fs);
pipe->bind_vs_state(pipe, vs);
if(pipe->bind_gs_state)
pipe->bind_gs_state(pipe, 0);
if(pipe->set_stream_output_targets)
pipe->set_stream_output_targets(pipe, 0, NULL, 0);
pipe->set_fragment_sampler_views(pipe, 1, &view);
pipe->draw_vbo(pipe, &draw);
}
~dxgi_blitter()
{
pipe->delete_blend_state(pipe, blend);
pipe->delete_rasterizer_state(pipe, rasterizer);
pipe->delete_depth_stencil_alpha_state(pipe, zsa);
pipe->delete_sampler_state(pipe, sampler[0]);
pipe->delete_sampler_state(pipe, sampler[1]);
pipe->delete_vertex_elements_state(pipe, elements);
pipe->delete_vs_state(pipe, vs);
pipe->delete_fs_state(pipe, fs);
pipe->screen->resource_destroy(pipe->screen, vbuf.buffer);
}
};
struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>
{
ComPtr<IDXGIDevice>dxgi_device;
ComPtr<IGalliumDevice>gallium_device;
ComPtr<GalliumDXGIAdapter> adapter;
ComPtr<IDXGIOutput> target;
DXGI_SWAP_CHAIN_DESC desc;
struct native_surface* surface;
const struct native_config* config;
void* window;
struct pipe_resource* resources[NUM_NATIVE_ATTACHMENTS];
int width;
int height;
unsigned seq_num;
bool ever_validated;
bool needs_validation;
unsigned present_count;
ComPtr<IDXGISurface> buffer0;
struct pipe_resource* gallium_buffer0;
struct pipe_sampler_view* gallium_buffer0_view;
struct pipe_context* pipe;
bool owns_pipe;
BOOL fullscreen;
std::auto_ptr<dxgi_blitter> blitter;
bool formats_compatible;
GalliumDXGISwapChain(GalliumDXGIFactory* factory, IUnknown* p_device, const DXGI_SWAP_CHAIN_DESC& p_desc)
: GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>(factory), desc(p_desc), surface(0)
{
HRESULT hr;
hr = p_device->QueryInterface(IID_IGalliumDevice, (void**)&gallium_device);
if(!SUCCEEDED(hr))
throw hr;
hr = p_device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_device);
if(!SUCCEEDED(hr))
throw hr;
hr = dxgi_device->GetAdapter((IDXGIAdapter**)&adapter);
if(!SUCCEEDED(hr))
throw hr;
memset(resources, 0, sizeof(resources));
if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL && desc.BufferCount != 1)
{
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;
// change the returned desc, so that the application might perhaps notice what we did and react well
desc.BufferCount = 1;
}
pipe = gallium_device->GetGalliumContext();
owns_pipe = false;
if(!pipe)
{
pipe = adapter->display->screen->context_create(adapter->display->screen, 0);
owns_pipe = true;
}
blitter.reset(new dxgi_blitter(pipe));
window = 0;
hr = resolve_zero_width_height(true);
if(!SUCCEEDED(hr))
throw hr;
}
void init_for_window()
{
if(surface)
{
surface->destroy(surface);
surface = 0;
}
unsigned config_num;
if(!strcmp(parent->platform->name, "X11"))
{
XWindowAttributes xwa;
XGetWindowAttributes((Display*)parent->display, (Window)window, &xwa);
assert(adapter->configs_by_native_visual_id.count(xwa.visual->visualid));
config_num = adapter->configs_by_native_visual_id[xwa.visual->visualid];
}
else
{
enum pipe_format format = dxgi_to_pipe_format[desc.BufferDesc.Format];
if(!adapter->configs_by_pipe_format.count(format))
{
if(adapter->configs_by_pipe_format.empty())
throw E_FAIL;
// TODO: choose the best match
format = (pipe_format)adapter->configs_by_pipe_format.begin()->first;
}
// TODO: choose the best config
config_num = adapter->configs_by_pipe_format.find(format)->second;
}
config = adapter->configs[config_num];
surface = adapter->display->create_window_surface(adapter->display, (EGLNativeWindowType)window, config);
surface->user_data = this;
width = 0;
height = 0;
seq_num = 0;
present_count = 0;
needs_validation = true;
ever_validated = false;
formats_compatible = util_is_format_compatible(
util_format_description(dxgi_to_pipe_format[desc.BufferDesc.Format]),
util_format_description(config->color_format));
}
~GalliumDXGISwapChain()
{
if(owns_pipe)
pipe->destroy(pipe);
}
virtual HRESULT STDMETHODCALLTYPE GetDevice(
REFIID riid,
void **pdevice)
{
return dxgi_device->QueryInterface(riid, pdevice);
}
HRESULT create_buffer0()
{
HRESULT hr;
ComPtr<IDXGISurface> new_buffer0;
DXGI_USAGE usage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
if(desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD)
usage |= DXGI_USAGE_DISCARD_ON_PRESENT;
// for our blitter
usage |= DXGI_USAGE_SHADER_INPUT;
DXGI_SURFACE_DESC surface_desc;
surface_desc.Format = desc.BufferDesc.Format;
surface_desc.Width = desc.BufferDesc.Width;
surface_desc.Height = desc.BufferDesc.Height;
surface_desc.SampleDesc = desc.SampleDesc;
hr = dxgi_device->CreateSurface(&surface_desc, 1, usage, 0, &new_buffer0);
if(!SUCCEEDED(hr))
return hr;
ComPtr<IGalliumResource> gallium_resource;
hr = new_buffer0->QueryInterface(IID_IGalliumResource, (void**)&gallium_resource);
if(!SUCCEEDED(hr))
return hr;
struct pipe_resource* new_gallium_buffer0 = gallium_resource->GetGalliumResource();
if(!new_gallium_buffer0)
return E_FAIL;
buffer0.reset(new_buffer0.steal());
gallium_buffer0 = new_gallium_buffer0;
struct pipe_sampler_view templat;
memset(&templat, 0, sizeof(templat));
templat.texture = gallium_buffer0;
templat.swizzle_r = 0;
templat.swizzle_g = 1;
templat.swizzle_b = 2;
templat.swizzle_a = 3;
templat.format = gallium_buffer0->format;
gallium_buffer0_view = pipe->create_sampler_view(pipe, gallium_buffer0, &templat);
return S_OK;
}
bool validate()
{
unsigned new_seq_num;
needs_validation = false;
if(!surface->validate(surface, (1 << NATIVE_ATTACHMENT_BACK_LEFT) | (1 << NATIVE_ATTACHMENT_FRONT_LEFT), &new_seq_num, resources, &width, &height))
return false;
if(!ever_validated || seq_num != new_seq_num)
{
seq_num = new_seq_num;
ever_validated = true;
}
return true;
}
HRESULT resolve_zero_width_height(bool force = false)
{
if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height)
return S_OK;
unsigned width, height;
HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height);
if(!SUCCEEDED(hr))
return hr;
// On Windows, 8 is used, and a debug message saying so gets printed
if(!width)
width = 8;
if(!height)
height = 8;
if(!desc.BufferDesc.Width)
desc.BufferDesc.Width = width;
if(!desc.BufferDesc.Height)
desc.BufferDesc.Height = height;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Present(
UINT sync_interval,
UINT flags)
{
HRESULT hr;
if(flags & DXGI_PRESENT_TEST)
return parent->backend->TestPresent(desc.OutputWindow);
if(!buffer0)
{
HRESULT hr = create_buffer0();
if(!SUCCEEDED(hr))
return hr;
}
void* cur_window = 0;
RECT rect;
RGNDATA* rgndata;
BOOL preserve_aspect_ratio;
unsigned dst_w, dst_h;
bool db;
struct pipe_resource* dst;
struct pipe_resource* src;
struct pipe_surface* dst_surface;
struct native_present_control ctrl;
void* present_cookie;
hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio);
if(hr != S_OK)
return hr;
if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom)
goto end_present;
if(cur_window != window)
{
window = cur_window;
init_for_window();
}
if(needs_validation)
{
if(!validate())
return DXGI_ERROR_DEVICE_REMOVED;
}
db = !!(config->buffer_mask & (1 << NATIVE_ATTACHMENT_BACK_LEFT));
dst = resources[db ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT];
src = gallium_buffer0;
dst_surface = 0;
assert(src);
assert(dst);
/* TODO: sharing the context for blitting won't work correctly if queries are active
* Hopefully no one is crazy enough to keep queries active while presenting, expecting
* sensible results.
* We could alternatively force using another context, but that might cause inefficiency issues
*/
if((unsigned)rect.right > dst->width0)
rect.right = dst->width0;
if((unsigned)rect.bottom > dst->height0)
rect.bottom = dst->height0;
if(rect.left > rect.right)
rect.left = rect.right;
if(rect.top > rect.bottom)
rect.top = rect.bottom;
if(rect.left >= rect.right && rect.top >= rect.bottom)
goto end_present;
dst_w = rect.right - rect.left;
dst_h = rect.bottom - rect.top;
// TODO: add support for rgndata
// if(preserve_aspect_ratio || !rgndata)
if(1)
{
unsigned blit_x, blit_y, blit_w, blit_h;
static const union pipe_color_union black = { { 0, 0, 0, 0 } };
if(!formats_compatible || src->width0 != dst_w || src->height0 != dst_h) {
struct pipe_surface templat;
templat.usage = PIPE_BIND_RENDER_TARGET;
templat.format = dst->format;
templat.u.tex.level = 0;
templat.u.tex.first_layer = 0;
templat.u.tex.last_layer = 0;
dst_surface = pipe->create_surface(pipe, dst, &templat);
}
if(preserve_aspect_ratio)
{
int delta = src->width0 * dst_h - dst_w * src->height0;
if(delta > 0)
{
blit_w = dst_w;
blit_h = dst_w * src->height0 / src->width0;
}
else if(delta < 0)
{
blit_w = dst_h * src->width0 / src->height0;
blit_h = dst_h;
}
else
{
blit_w = dst_w;
blit_h = dst_h;
}
blit_x = (dst_w - blit_w) >> 1;
blit_y = (dst_h - blit_h) >> 1;
}
else
{
blit_x = 0;
blit_y = 0;
blit_w = dst_w;
blit_h = dst_h;
}
if(blit_x)
pipe->clear_render_target(pipe, dst_surface, &black, rect.left, rect.top, blit_x, dst_h);
if(blit_y)
pipe->clear_render_target(pipe, dst_surface, &black, rect.left, rect.top, dst_w, blit_y);
if(formats_compatible && blit_w == src->width0 && blit_h == src->height0)
{
pipe_box box;
box.x = box.y = box.z = 0;
box.width = blit_w;
box.height = blit_h;
box.depth = 1;
pipe->resource_copy_region(pipe, dst, 0, rect.left, rect.top, 0, src, 0, &box);
}
else
{
blitter->blit(dst_surface, gallium_buffer0_view, rect.left + blit_x, rect.top + blit_y, blit_w, blit_h);
if(!owns_pipe)
gallium_device->RestoreGalliumState();
}
if(blit_w != dst_w)
pipe->clear_render_target(pipe, dst_surface, &black, rect.left + blit_x + blit_w, rect.top, dst_w - blit_x - blit_w, dst_h);
if(blit_h != dst_h)
pipe->clear_render_target(pipe, dst_surface, &black, rect.left, rect.top + blit_y + blit_h, dst_w, dst_h - blit_y - blit_h);
}
if(dst_surface)
pipe->surface_destroy(pipe, dst_surface);
pipe->flush(pipe, 0);
memset(&ctrl, 0, sizeof(ctrl));
ctrl.natt = (db) ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT;
if(!surface->present(surface, &ctrl))
return DXGI_ERROR_DEVICE_REMOVED;
end_present:
parent->backend->EndPresent(desc.OutputWindow, present_cookie);
++present_count;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetBuffer(
UINT Buffer,
REFIID riid,
void **ppSurface)
{
if(Buffer > 0)
{
if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL)
std::cerr << "DXGI unimplemented: GetBuffer(n) with n > 0 not supported, returning buffer 0 instead!" << std::endl;
else
std::cerr << "DXGI error: in GetBuffer(n), n must be 0 for DXGI_SWAP_EFFECT_DISCARD\n" << std::endl;
}
if(!buffer0)
{
HRESULT hr = create_buffer0();
if(!SUCCEEDED(hr))
return hr;
}
return buffer0->QueryInterface(riid, ppSurface);
}
/* TODO: implement somehow */
virtual HRESULT STDMETHODCALLTYPE SetFullscreenState(
BOOL fullscreen,
IDXGIOutput *target)
{
fullscreen = fullscreen;
target = target;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetFullscreenState(
BOOL *out_fullscreen,
IDXGIOutput **out_target)
{
if(out_fullscreen)
*out_fullscreen = fullscreen;
if(out_target)
*out_target = target.ref();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetDesc(
DXGI_SWAP_CHAIN_DESC *out_desc)
{
*out_desc = desc;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ResizeBuffers(
UINT buffer_count,
UINT width,
UINT height,
DXGI_FORMAT new_format,
UINT swap_chain_flags)
{
if(buffer0)
{
buffer0.p->AddRef();
ULONG v = buffer0.p->Release();
// we must fail if there are any references to buffer0 other than ours
if(v > 1)
return E_FAIL;
pipe_sampler_view_reference(&gallium_buffer0_view, 0);
buffer0 = (IUnknown*)NULL;
gallium_buffer0 = 0;
}
if(desc.SwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL)
desc.BufferCount = buffer_count;
desc.BufferDesc.Format = new_format;
desc.BufferDesc.Width = width;
desc.BufferDesc.Height = height;
desc.Flags = swap_chain_flags;
return resolve_zero_width_height();
}
virtual HRESULT STDMETHODCALLTYPE ResizeTarget(
const DXGI_MODE_DESC *out_new_target_parameters)
{
/* TODO: implement */
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetContainingOutput(
IDXGIOutput **out_output)
{
*out_output = adapter->outputs[0].ref();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
DXGI_FRAME_STATISTICS *out_stats)
{
memset(out_stats, 0, sizeof(*out_stats));
#ifdef _WIN32
QueryPerformanceCounter(&out_stats->SyncQPCTime);
#endif
out_stats->PresentCount = present_count;
out_stats->PresentRefreshCount = present_count;
out_stats->SyncRefreshCount = present_count;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount(
UINT *last_present_count)
{
*last_present_count = present_count;
return S_OK;
}
};
static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain)
{
((GalliumDXGISwapChain*)swap_chain)->needs_validation = true;
}
static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter)
{
try
{
*out_adapter = new GalliumDXGIAdapter(factory, platform, dpy);
return S_OK;
}
catch(HRESULT hr)
{
return hr;
}
}
static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output)
{
try
{
*out_output = new GalliumDXGIOutput(adapter, name, connector);
return S_OK;
}
catch(HRESULT hr)
{
return hr;
}
}
static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain)
{
try
{
*out_swap_chain = new GalliumDXGISwapChain(factory, device, desc);
return S_OK;
}
catch(HRESULT hr)
{
return hr;
}
}
struct dxgi_binding
{
const struct native_platform* platform;
void* display;
IGalliumDXGIBackend* backend;
};
static dxgi_binding dxgi_default_binding;
static __thread dxgi_binding dxgi_thread_binding;
static const struct native_event_handler dxgi_event_handler = {
GalliumDXGIAdapter::handle_invalid_surface,
dxgi_loader_create_drm_screen,
dxgi_loader_create_sw_screen
};
void STDMETHODCALLTYPE GalliumDXGIUseNothing()
{
dxgi_thread_binding.platform = 0;
dxgi_thread_binding.display = 0;
if(dxgi_thread_binding.backend)
dxgi_thread_binding.backend->Release();
dxgi_thread_binding.backend = 0;
}
#ifdef GALLIUM_DXGI_USE_X11
void STDMETHODCALLTYPE GalliumDXGIUseX11Display(Display* dpy, IGalliumDXGIBackend* backend)
{
GalliumDXGIUseNothing();
dxgi_thread_binding.platform = native_get_x11_platform(&dxgi_event_handler);
dxgi_thread_binding.display = dpy;
if(backend)
{
dxgi_thread_binding.backend = backend;
backend->AddRef();
}
}
#endif
/*
#ifdef GALLIUM_DXGI_USE_DRM
void STDMETHODCALLTYPE GalliumDXGIUseDRMCard(int fd)
{
GalliumDXGIUseNothing();
dxgi_thread_binding.platform = native_get_drm_platform(&dxgi_event_handler);
dxgi_thread_binding.display = (void*)fd;
dxgi_thread_binding.backend = 0;
}
#endif
#ifdef GALLIUM_DXGI_USE_FBDEV
void STDMETHODCALLTYPE GalliumDXGIUseFBDev(int fd)
{
GalliumDXGIUseNothing();
dxgi_thread_binding.platform = native_get_fbdev_platform(&dxgi_event_handler);
dxgi_thread_binding.display = (void*)fd;
dxgi_thread_binding.backend = 0;
}
#endif
#ifdef GALLIUM_DXGI_USE_GDI
void STDMETHODCALLTYPE GalliumDXGIUseHDC(HDC hdc, PFNHWNDRESOLVER resolver, void* resolver_cookie)
{
GalliumDXGIUseNothing();
dxgi_thread_binding.platform = native_get_gdi_platform(&dxgi_event_handler);
dxgi_thread_binding.display = (void*)hdc;
dxgi_thread_binding.backend = 0;
}
#endif
*/
void STDMETHODCALLTYPE GalliumDXGIMakeDefault()
{
if(dxgi_default_binding.backend)
dxgi_default_binding.backend->Release();
dxgi_default_binding = dxgi_thread_binding;
if(dxgi_default_binding.backend)
dxgi_default_binding.backend->AddRef();
}
/* TODO: why did Microsoft add this? should we do something different for DXGI 1.0 and 1.1?
* Or perhaps what they actually mean is "only create a single factory in your application"?
* TODO: should we use a singleton here, so we never have multiple DXGI objects for the same thing? */
HRESULT STDMETHODCALLTYPE CreateDXGIFactory1(
REFIID riid,
void **out_factory
)
{
GalliumDXGIFactory* factory;
*out_factory = 0;
if(dxgi_thread_binding.platform)
factory = new GalliumDXGIFactory(dxgi_thread_binding.platform, dxgi_thread_binding.display, dxgi_thread_binding.backend);
else if(dxgi_default_binding.platform)
factory = new GalliumDXGIFactory(dxgi_default_binding.platform, dxgi_default_binding.display, dxgi_default_binding.backend);
else
factory = new GalliumDXGIFactory(native_get_x11_platform(&dxgi_event_handler), NULL, NULL);
HRESULT hres = factory->QueryInterface(riid, out_factory);
factory->Release();
return hres;
}
HRESULT STDMETHODCALLTYPE CreateDXGIFactory(
REFIID riid,
void **out_factor
)
{
return CreateDXGIFactory1(riid, out_factor);
}