diff options
author | Luca Barbieri <luca@luca-barbieri.com> | 2010-09-12 02:49:36 +0200 |
---|---|---|
committer | Luca Barbieri <luca@luca-barbieri.com> | 2010-09-21 10:58:17 +0200 |
commit | 92617aeac109481258f0c3863d09c1b8903d438b (patch) | |
tree | d85d6a04e87d227964386ad1d6b3d6ae6954e179 /src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h | |
parent | 894a307a91d6437ec418800952da2ec174e092f5 (diff) |
d3d1x: add new Direct3D 10/11 COM state tracker for Gallium
This is a new implementation of the Direct3D 11 COM API for Gallium.
Direct3D 10 and 10.1 implementations are also provided, which are
automatically generated with s/D3D11/D3D10/g plus a bunch of #ifs.
While this is an initial version, most of the code is there (limited
to what Gallium can express), and tri, gears and texturing demos
are working.
The primary goal is to realize Gallium's promise of multiple API
support, and provide an API that can be easily implemented with just
a very thin wrapper over Gallium, instead of the enormous amount of
complex code needed for OpenGL.
The secondary goal is to run Windows Direct3D 10/11 games on Linux
using Wine.
Wine dlls are currently not provided, but adding them should be
quite easy.
Fglrx and nvidia drivers can also be supported by writing a Gallium
driver that talks to them using OpenGL, which is a relatively easy
task.
Thanks to the great design of Direct3D 10/11 and closeness to Gallium,
this approach should not result in detectable overhead, and is the
most maintainable way to do it, providing a path to switch to the
open Gallium drivers once they are on par with the proprietary ones.
Currently Wine has a very limited Direct3D 10 implementation, and
completely lacks a Direct3D 11 implementation.
Note that Direct3D 10/11 are completely different from Direct3D 9
and earlier, and thus warrant a fully separate implementation.
The third goal is to provide a superior alternative to OpenGL for
graphics programming on non-Windows systems, particularly Linux
and other free and open systems.
Thanks to a very clean and well-though design done from scratch,
the Direct3D 10/11 APIs are vastly better than OpenGL and can be
supported with orders of magnitude less code and development time,
as you can see by comparing the lines of code of this commit and
those in the existing Mesa OpenGL implementation.
This would have been true for the Longs Peak proposal as well, but
unfortunately it was abandoned by Khronos, leaving the OpenGL
ecosystem without a graphics API with a modern design.
A binding of Direct3D 10/11 to EGL would solve this issue in the
most economical way possible, and this would be great to provide
in Mesa, since DXGI, the API used to bind Direct3D 10/11 to Windows,
is a bit suboptimal, especially on non-Windows platforms.
Finally, a mature Direct3D 10/11 implementation is intrinsically going
to be faster and more reliable than an OpenGL implementation, thanks
to the dramatically smaller API and the segregation of all nontrivial
work to object creation that the application must perform ahead of
time.
Currently, this commit contains:
- Independently created headers for Direct3D 10, 10.1, 11 and DXGI 1.1,
partially based on the existing Wine headers for D3D10 and DXGI 1.0
- A parser for Direct3D 10/11 DXBC and TokenizedProgramFormat (TPF)
- A shader translator from TokenizedProgramFormat to TGSI
- Implementation of the Direct3D 11 core interfaces
- Automatically generated implementation of Direct3D 10 and 10.1
- Implementation of DXGI using the "native" framework of the EGL st
- Demos, usable either on Windows or on this implementation
- d3d11tri, a clone of tri
- d3d11tex, a (multi)texturing demo
- d3d11gears, an improved version of glxgears
- d3d11spikysphere, a D3D11 tessellation demo (currently Windows-only)
- A downloader for the Microsoft HLSL compiler, needed to recompile
the shaders (compiled shader bytecode is also included)
To compile this, configure at least with these options:
--with-state-trackers=egl,d3d1x --with-egl-platforms=x11
plus some gallium drivers (such as softpipe with --enable-gallium-swrast)
The Wine headers (usually from a wine-dev or wine-devel package) must
be installed.
Only x86-32 has been tested.
You may need to run "make" in the subdirectories of src/gallium/winsys/sw
and you may need to manually run "sudo make install" in
src/gallium/targets/egl
To test it, run the demos in the "progs" directory.
Windows binaries are included to find out how demos should work, and to
test Wine integration when it will be done.
Enjoy, and let me know if you manage to compile and run this, or
which issues you are facing if not.
Using softpipe is recommended for now, and your mileage with hardware
drivers may vary.
However, getting this to work on hardware drivers is also obviously very
important.
Note that currently llvmpipe is buggy and causes all 3 gears to be
drawn with the same color.
Use export GALLIUM_DRIVER=softpipe to avoid this.
Thanks to all the Gallium contributors and especially the VMware
team, whose work made it possible to implement Direct3D 10/11 much
more easily than it would have been otherwise.
Diffstat (limited to 'src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h')
-rw-r--r-- | src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h | 1038 |
1 files changed, 1038 insertions, 0 deletions
diff --git a/src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h b/src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h new file mode 100644 index 0000000000..a9260acdba --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/d3d1xstutil/include/d3d1xstutil.h @@ -0,0 +1,1038 @@ +/************************************************************************** + * + * 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. + * + **************************************************************************/ + +#ifndef D3D1XSTUTIL_H_ +#define D3D1XSTUTIL_H_ + +#ifdef _MSC_VER +#include <unordered_map> +#include <unordered_set> +#else +#include <tr1/unordered_map> +#include <tr1/unordered_set> +namespace std +{ + using namespace tr1; +} +#endif +#include <map> +#include <utility> + +#define WIN32_LEAN_AND_MEAN +#include <objbase.h> +#include <guiddef.h> +#include <specstrings.h> + +#ifdef __GNUC__ +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#else +#define ATTRIBUTE_UNUSED +#endif + +// just replicate GUIDs in every object file to avoid the hassle of having to pull in a library for them +#undef DEFINE_GUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + static const GUID name ATTRIBUTE_UNUSED = \ + { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#include "galliumdxgi.h" + +extern "C" +{ +#include <util/u_atomic.h> +#include <pipe/p_format.h> +#include <os/os_thread.h> +} + +#include <assert.h> +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + +/* NOTE: this _depends_ on the vtable layout of the C++ compiler to be + * binary compatible with Windows. + * Furthermore some absurd vtable layout likely won't work at all, since + * we perform some casts which are probably not safe by the C++ standard. + * + * In particular, the GNU/Linux/Itanium/clang ABI and Microsoft ABIs will work, + * but others may not. + * If in doubt, just switch to the latest version of a widely used C++ compiler. + * + * DESIGN of the Gallium COM implementation + * + * This state tracker uses somewhat unusual C++ coding patterns, + * to implement the COM interfaces required by Direct3D. + * + * While it may seem complicated, the effect is that the result + * generally behaves as intuitively as possible: in particular pointer + * casts very rarely change the pointer value (only for secondary + * DXGI/Gallium interfaces) + * + * Implementing COM is on first sight very easy: after all, it just + * consists of a reference count, and a dynamic_cast<> equivalent. + * + * However, implementing objects with multiple interfaces is actually + * quite tricky. + * The issue is that the interface pointers can't be equal, since this + * would place incompatible constraints on the vtable layout and thus + * multiple inheritance (and the subobjects the C++ compiler creates + * with it) must be correctly used. + * + * Furthermore, we must have a single reference count, which means + * that a naive implementation won't work, and it's necessary to either + * use virtual inheritance, or the "mixin inheritance" model we use. + * + * This solution aims to achieve the following object layout: + * 0: pointer to vtable for primary interface + * 1: reference count + * ... main class + * ... vtable pointers for secondary interfaces + * ... implementation of subclasses assuming secondary interfaces + * + * This allows us to cast pointers by just reinterpreting the value in + * almost all cases. + * + * To achieve this, *all* non-leaf classes must have their parent + * or the base COM interface as a template parameter, since derived + * classes may need to change that to support an interface derived + * from the one implemented by the superclass. + * + * Note however, that you can cast without regard to the template + * parameter, because only the vtable layout depends on it, since + * interfaces have no data members. + * + * For this to work, DON'T USE VIRTUAL FUNCTIONS except to implement + * interfaces, since the vtable layouts would otherwise be mismatched. + * An exception are virtual functions called only from other virtual functions, + * which is currently only used for the virtual destructor. + * + * The base class is GalliumComObject<IFoo>, which implements the + * IUnknown interface, and inherits IFoo. + * + * To support multiple inheritance, we insert GalliumMultiComObject, + * which redirects the secondary interfaces to the GalliumComObject + * superclass. + * + * Gallium(Multi)PrivateDataComObject is like ComObject but also + * implements the Get/SetPrivateData functions present on several + * D3D/DXGI interfaces. + * + * Example class hierarchy: + * + * IUnknown + * (pure interface) + * | + * V + * IAnimal + * (pure interface) + * | + * V + * IDuck + * (pure interface) + * | + * V + * GalliumComObject<IDuck> + * (non-instantiable, only implements IUnknown) + * | + * V + * GalliumAnimal<IDuck> + * (non-instantiable, only implements IAnimal) + * | + * V + * GalliumDuck + * (concrete) + * | + * V + * GalliumMultiComObject<GalliumDuck, IWheeledVehicle> <- IWheeledVehicle <- IVehicle <- IUnknown (second version) + * (non-instantiable, only implements IDuck and the IUnknown of IWheeledVehicle) + * | + * V + * GalliumDuckOnWheels + * (concrete) + * + * This will produce the desired layout. + * Note that GalliumAnimal<IFoo>* is safely castable to GalliumAnimal<IBar>* + * by reinterpreting, as long as non-interface virtual functions are not used, + * and that you only call interface functions for the superinterface of IBar + * that the object actually implements. + * + * Instead, if GalliumDuck where to inherit both from GalliumAnimal + * and IDuck, then (IDuck*)gallium_duck and (IAnimal*)gallium_duck would + * have different pointer values, which the "base class as template parameter" + * trick avoids. + * + * The price we pay is that you MUST NOT have virtual functions other than those + * implementing interfaces (except for leaf classes) since the position of these + * would depend on the base interface. + * As mentioned above, virtual functions only called from interface functions + * are an exception, currently used only for the virtual destructor. + * If you want virtual functions anyway , put them in a separate interface class, + * multiply inherit from that and cast the pointer to that interface. + * + * You CAN however have virtual functions on any class which does not specify + * his base as a template parameter, or where you don't need to change the + * template base interface parameter by casting. + * + * --- The magic QueryInterface "delete this" trick --- + * + * When the reference count drops to 0, we must delete the class. + * The problem is, that we must call the right virtual destructor (i.e. on the right class). + * However, we would like to be able to call release() and nonatomic_release() + * non-virtually for performance (also, the latter cannot be called virtually at all, since + * IUnknown does not offer it). + * + * The naive solution would be to just add a virtual destructor and rely on it. + * However, this doesn't work due to the fact that as described above we perform casets + * with are unsafe regarding vtable layout. + * In particular, consider the case where we try to delete GalliumComObject<ID3D11Texture2D> + * with a pointer to GalliumComObject<ID3D11Resource>. + * Since we think that this is a GalliumComObject<ID3D11Resource>, we'll look for the + * destructor in the vtable slot immediately after the ID3D11Resource vtable, but this is + * actually an ID3D11Texture2D function implemented by the object! + * + * So, we must put the destructor somewhere else. + * We could add it as a data member, but it would be awkward and it would bloat the + * class. + * Thus, we use this trick: we reuse the vtable slot for QueryInterface, which is always at the + * same position. + * To do so, we define a special value for the first pointer argument, that triggers a + * "delete this". + * In addition to that, we add a virtual destructor to GalliumComObject. + * That virtual destructor will be called by QueryInterface, and since that is a virtual + * function, it will know the correct place for the virtual destructor. + * + * QueryInterface is already slow due to the need to compare several GUIDs, so the + * additional pointer test should not be significant. + * + * Of course the ideal solution would be telling the C++ compiler to put the + * destructor it in a negative vtable slot, but unfortunately GCC doesn't support that + * yet, and this method is almost as good as that. + */ + +template<typename T> +struct com_traits; + +#define COM_INTERFACE(intf, base) \ +template<> \ +struct com_traits<intf> \ +{ \ + static REFIID iid() {return IID_##intf;} \ + static inline bool is_self_or_ancestor(REFIID riid) {return riid == iid() || com_traits<base>::is_self_or_ancestor(riid);} \ +}; + +template<> +struct com_traits<IUnknown> +{ + static REFIID iid() {return IID_IUnknown;} + static inline bool is_self_or_ancestor(REFIID riid) {return riid == iid();} +}; + +#ifndef _MSC_VER +#define __uuidof(T) (com_traits<T>::iid()) +#endif + +struct refcnt_t +{ + uint32_t refcnt; + + refcnt_t(unsigned v = 1) + : refcnt(v) + {} + + unsigned add_ref() + { + p_atomic_inc((int32_t*)&refcnt); + return refcnt; + } + + unsigned release() + { + if(p_atomic_dec_zero((int32_t*)&refcnt)) + return 0; + return refcnt; + } + + void nonatomic_add_ref() + { + p_atomic_inc((int32_t*)&refcnt); + } + + unsigned nonatomic_release() + { + if(p_atomic_dec_zero((int32_t*)&refcnt)) + return 0; + else + return 1; + } +}; + +#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) +/* this should be safe because atomic ops are full memory barriers, and thus a sequence that does: + * ++one_refcnt; + * --other_refcnt; + * should never be reorderable (as seen from another CPU) to: + * --other_refcnt + * ++one_refcnt + * + * since one of the ops is atomic. + * If this weren't the case, a CPU could incorrectly destroy an object manipulated in that way by another one. + */ +struct dual_refcnt_t +{ + union + { + uint64_t refcnt; + struct + { + uint32_t atomic_refcnt; + uint32_t nonatomic_refcnt; + }; + }; + + dual_refcnt_t(unsigned v = 1) + { + atomic_refcnt = v; + nonatomic_refcnt = 0; + } + + bool is_zero() + { + if(sizeof(void*) == 8) + return *(volatile uint64_t*)&refcnt == 0ULL; + else + { + uint64_t v; + do + { + v = refcnt; + } + while(!__sync_bool_compare_and_swap(&refcnt, v, v)); + return v == 0ULL; + } + } + + unsigned add_ref() + { + //printf("%p add_ref at %u %u\n", this, atomic_refcnt, nonatomic_refcnt); + p_atomic_inc((int32_t*)&atomic_refcnt); + return atomic_refcnt + nonatomic_refcnt; + } + + unsigned release() + { + //printf("%p release at %u %u\n", this, atomic_refcnt, nonatomic_refcnt); + if(p_atomic_dec_zero((int32_t*)&atomic_refcnt) && !nonatomic_refcnt && is_zero()) + return 0; + unsigned v = atomic_refcnt + nonatomic_refcnt; + return v ? v : 1; + } + + void nonatomic_add_ref() + { + //printf("%p nonatomic_add_ref at %u %u\n", this, atomic_refcnt, nonatomic_refcnt); + ++nonatomic_refcnt; + } + + unsigned nonatomic_release() + { + //printf("%p nonatomic_release at %u %u\n", this, atomic_refcnt, nonatomic_refcnt); + if(!--nonatomic_refcnt && !atomic_refcnt && is_zero()) + return 0; + return 1; + } +}; +#else +// this will result in atomic operations being used while they could have been avoided +#ifdef __i386__ +#warning Compile for 586+ using GCC to improve the performance of the Direct3D 10/11 state tracker +#endif +typedef refcnt_t dual_refcnt_t; +#endif + +#define IID_MAGIC_DELETE_THIS (*(const IID*)((intptr_t)-(int)(sizeof(IID) - 1))) + +template<typename Base = IUnknown, typename RefCnt = refcnt_t> +struct GalliumComObject : public Base +{ + RefCnt refcnt; + + GalliumComObject() + {} + + /* DO NOT CALL this from externally called non-virtual functions in derived classes, since + * the vtable position depends on the COM interface being implemented + */ + virtual ~GalliumComObject() + {} + + inline ULONG add_ref() + { + return refcnt.add_ref(); + } + + inline ULONG release() + { + ULONG v = refcnt.release(); + if(!v) + { + /* this will call execute "delete this", using the correct vtable slot for the destructor */ + /* see the initial comment for an explaination of this magic trick */ + this->QueryInterface(IID_MAGIC_DELETE_THIS, 0); + return 0; + } + return v; + } + + inline void nonatomic_add_ref() + { + refcnt.nonatomic_add_ref(); + } + + inline void nonatomic_release() + { + if(!refcnt.nonatomic_release()) + { + /* this will execute "delete this", using the correct vtable slot for the destructor */ + /* see the initial comment for an explaination of this magic trick */ + this->QueryInterface(IID_MAGIC_DELETE_THIS, 0); + } + } + + inline HRESULT query_interface(REFIID riid, void **ppvObject) + { + if(com_traits<Base>::is_self_or_ancestor(riid)) + { + // must be the virtual AddRef, since it is overridden by some classes + this->AddRef(); + *ppvObject = this; + return S_OK; + } + else + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return add_ref(); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + return release(); + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject) + { + /* see the initial comment for an explaination of this magic trick */ + if(&riid == &IID_MAGIC_DELETE_THIS) + { + delete this; + return 0; + } + if(!this) + return E_INVALIDARG; + if(!ppvObject) + return E_POINTER; + return query_interface(riid, ppvObject); + } +}; + +template<typename BaseClass, typename SecondaryInterface> +struct GalliumMultiComObject : public BaseClass, SecondaryInterface +{ + // we could avoid this duplication, but the increased complexity to do so isn't worth it + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return BaseClass::add_ref(); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + return BaseClass::release(); + } + + inline HRESULT query_interface(REFIID riid, void **ppvObject) + { + HRESULT hr = BaseClass::query_interface(riid, ppvObject); + if(SUCCEEDED(hr)) + return hr; + if(com_traits<SecondaryInterface>::is_self_or_ancestor(riid)) + { + // must be the virtual AddRef, since it is overridden by some classes + this->AddRef(); + *ppvObject = (SecondaryInterface*)this; + return S_OK; + } + else + return E_NOINTERFACE; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject) + { + /* see the initial comment for an explaination of this magic trick */ + if(&riid == &IID_MAGIC_DELETE_THIS) + { + delete this; + return 0; + } + if(!this) + return E_INVALIDARG; + if(!ppvObject) + return E_POINTER; + return query_interface(riid, ppvObject); + } +}; + +template<typename T, typename Traits> +struct refcnt_ptr +{ + T* p; + + refcnt_ptr() + : p(0) + {} + + void add_ref() {Traits::add_ref(p);} + void release() {Traits::release(p);} + + template<typename U, typename UTraits> + refcnt_ptr(const refcnt_ptr<U, UTraits>& c) + { + *this = static_cast<U*>(c.ref()); + } + + ~refcnt_ptr() + { + release(); + } + + void reset(T* q) + { + release(); + p = q; + } + + template<typename U, typename UTraits> + refcnt_ptr& operator =(const refcnt_ptr<U, UTraits>& q) + { + return *this = q.p; + } + + template<typename U> + refcnt_ptr& operator =(U* q) + { + release(); + p = static_cast<T*>(q); + add_ref(); + return *this; + } + + T* ref() + { + add_ref(); + return p; + } + + T* steal() + { + T* ret = p; + p = 0; + return ret; + } + + T* operator ->() + { + return p; + } + + const T* operator ->() const + { + return p; + } + + T** operator &() + { + assert(!p); + return &p; + } + + bool operator !() const + { + return !p; + } + + typedef T* refcnt_ptr::*unspecified_bool_type; + + operator unspecified_bool_type() const + { + return p ? &refcnt_ptr::p : 0; + } +}; + +struct simple_ptr_traits +{ + static void add_ref(void* p) {} + static void release(void* p) {} +}; + +struct com_ptr_traits +{ + static void add_ref(void* p) + { + if(p) + ((IUnknown*)p)->AddRef(); + } + + static void release(void* p) + { + if(p) + ((IUnknown*)p)->Release(); + } +}; + +template<typename T> +struct ComPtr : public refcnt_ptr<T, com_ptr_traits> +{ + template<typename U, typename UTraits> + ComPtr& operator =(const refcnt_ptr<U, UTraits>& q) + { + return *this = q.p; + } + + template<typename U> + ComPtr& operator =(U* q) + { + this->release(); + this->p = static_cast<T*>(q); + this->add_ref(); + return *this; + } +}; + +template<typename T, typename TTraits, typename U, typename UTraits> +bool operator ==(const refcnt_ptr<T, TTraits>& a, const refcnt_ptr<U, UTraits>& b) +{ + return a.p == b.p; +} + +template<typename T, typename TTraits, typename U> +bool operator ==(const refcnt_ptr<T, TTraits>& a, U* b) +{ + return a.p == b; +} + +template<typename T, typename TTraits, typename U> +bool operator ==(U* b, const refcnt_ptr<T, TTraits>& a) +{ + return a.p == b; +} + +template<typename T, typename TTraits, typename U, typename UTraits> +bool operator !=(const refcnt_ptr<T, TTraits>& a, const refcnt_ptr<U, UTraits>& b) +{ + return a.p != b.p; +} + +template<typename T, typename TTraits, typename U> +bool operator !=(const refcnt_ptr<T, TTraits>& a, U* b) +{ + return a.p != b; +} + +template<typename T, typename TTraits, typename U> +bool operator !=(U* b, const refcnt_ptr<T, TTraits>& a) +{ + return a.p != b; +} + +template<bool threadsafe> +struct maybe_mutex_t; + +template<> +struct maybe_mutex_t<true> +{ + pipe_mutex mutex; + + void lock() + { + pipe_mutex_lock(mutex); + } + + void unlock() + { + pipe_mutex_unlock(mutex); + } +}; + +template<> +struct maybe_mutex_t<false> +{ + void lock() + { + } + + void unlock() + { + } +}; + +typedef maybe_mutex_t<true> mutex_t; + +template<typename T> +struct lock_t +{ + T& mutex; + lock_t(T& mutex) + : mutex(mutex) + { + mutex.lock(); + } + + ~lock_t() + { + mutex.unlock(); + } +}; + +struct c_string +{ + const char* p; + c_string(const char* p) + : p(p) + {} + + operator const char*() const + { + return p; + } +}; + +static inline bool operator ==(const c_string& a, const c_string& b) +{ + return !strcmp(a.p, b.p); +} + +static inline bool operator !=(const c_string& a, const c_string& b) +{ + return strcmp(a.p, b.p); +} + +#ifdef __GLIBCXX__ +namespace std +{ + namespace tr1 + { + template<> + inline size_t hash<GUID>::operator()(GUID __val) const + { + return _Fnv_hash::hash(__val); + } + + template<> + inline size_t hash<c_string>::operator()(c_string __val) const + { + return _Fnv_hash::hash(__val.p, strlen(__val.p)); + } + + template<typename T, typename U> + struct hash<std::pair<T, U> > : public std::unary_function<std::pair<T, U>, size_t> + { + size_t operator()(std::pair<T, U> __val) const; + }; + + template<typename T, typename U> + inline size_t hash<std::pair<T, U> >::operator()(std::pair<T, U> __val) const + { + std::pair<size_t, size_t> p; + p.first = hash<T>()(__val.first); + p.second = hash<U>()(__val.second); + return _Fnv_hash::hash(p); + } + } +} +#else +#warning "You probably need to add a pair, C string and GUID hash implementation for your C++ library" +#endif + +template<typename Base, typename RefCnt = refcnt_t> +struct GalliumPrivateDataComObject : public GalliumComObject<Base, RefCnt> +{ + typedef std::unordered_map<GUID, std::pair<void*, unsigned> > private_data_map_t; + private_data_map_t private_data_map; + mutex_t private_data_mutex; + + ~GalliumPrivateDataComObject() + { + for(private_data_map_t::iterator i = private_data_map.begin(), e = private_data_map.end(); i != e; ++i) + { + if(i->second.second == ~0u) + ((IUnknown*)i->second.first)->Release(); + else + free(i->second.first); + } + } + + HRESULT get_private_data( + __in REFGUID guid, + __inout UINT *pDataSize, + __out_bcount_opt(*pDataSize) void *pData) + { + lock_t<mutex_t> lock(private_data_mutex); + private_data_map_t::iterator i = private_data_map.find(guid); + *pDataSize = 0; + if(i == private_data_map.end()) + return DXGI_ERROR_NOT_FOUND; + if(i->second.second == ~0u) + { + /* TODO: is GetPrivateData on interface data supposed to do this? */ + if(*pDataSize < sizeof(void*)) + return E_INVALIDARG; + if(pData) + { + memcpy(pData, &i->second.first, sizeof(void*)); + ((IUnknown*)i->second.first)->AddRef(); + } + *pDataSize = sizeof(void*); + } + else + { + unsigned size = std::min(*pDataSize, i->second.second); + if(pData) + memcpy(pData, i->second.first, size); + *pDataSize = size; + } + return S_OK; + } + + HRESULT set_private_data( + __in REFGUID guid, + __in UINT DataSize, + __in_bcount_opt( DataSize ) const void *pData) + { + void* p = 0; + + if(DataSize && pData) + { + p = malloc(DataSize); + if(!p) + return E_OUTOFMEMORY; + } + + lock_t<mutex_t> lock(private_data_mutex); + std::pair<void*, unsigned>& v = private_data_map[guid]; + if(v.first) + { + if(v.second == ~0u) + ((IUnknown*)v.first)->Release(); + else + free(v.first); + } + if(DataSize && pData) + { + memcpy(p, pData, DataSize); + v.first = p; + v.second = DataSize; + } + else + private_data_map.erase(guid); + return S_OK; + } + + HRESULT set_private_data_interface( + __in REFGUID guid, + __in_opt const IUnknown *pData) + { + lock_t<mutex_t> lock(private_data_mutex); + std::pair<void*, unsigned>& v = private_data_map[guid]; + if(v.first) + { + if(v.second == ~0u) + ((IUnknown*)v.first)->Release(); + else + free(v.first); + } + if(pData) + { + ((IUnknown*)pData)->AddRef(); + v.first = (void*)pData; + v.second = ~0; + } + else + private_data_map.erase(guid); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetPrivateData( + __in REFGUID guid, + __inout UINT *pDataSize, + __out_bcount_opt(*pDataSize) void *pData) + { + return get_private_data(guid, pDataSize, pData); + } + + virtual HRESULT STDMETHODCALLTYPE SetPrivateData( + __in REFGUID guid, + __in UINT DataSize, + __in_bcount_opt( DataSize ) const void *pData) + { + return set_private_data(guid, DataSize, pData); + } + + virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( + __in REFGUID guid, + __in_opt const IUnknown *pData) + { + return set_private_data_interface(guid, pData); + } +}; + +template<typename BaseClass, typename SecondaryInterface> +struct GalliumMultiPrivateDataComObject : public GalliumMultiComObject<BaseClass, SecondaryInterface> +{ + // we could avoid this duplication, but the increased complexity to do so isn't worth it + virtual HRESULT STDMETHODCALLTYPE GetPrivateData( + __in REFGUID guid, + __inout UINT *pDataSize, + __out_bcount_opt(*pDataSize) void *pData) + { + return BaseClass::get_private_data(guid, pDataSize, pData); + } + + virtual HRESULT STDMETHODCALLTYPE SetPrivateData( + __in REFGUID guid, + __in UINT DataSize, + __in_bcount_opt( DataSize ) const void *pData) + { + return BaseClass::set_private_data(guid, DataSize, pData); + } + + virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( + __in REFGUID guid, + __in_opt const IUnknown *pData) + { + return BaseClass::set_private_data_interface(guid, pData); + } +}; + +#define DXGI_FORMAT_COUNT 100 +extern pipe_format dxgi_to_pipe_format[DXGI_FORMAT_COUNT]; +extern DXGI_FORMAT pipe_to_dxgi_format[PIPE_FORMAT_COUNT]; + +void init_pipe_to_dxgi_format(); + +COM_INTERFACE(IGalliumDevice, IUnknown); +COM_INTERFACE(IGalliumAdapter, IUnknown); +COM_INTERFACE(IGalliumResource, IUnknown); + +// used to make QueryInterface know the IIDs of the interface and its ancestors +COM_INTERFACE(IDXGIObject, IUnknown) +COM_INTERFACE(IDXGIDeviceSubObject, IDXGIObject) +COM_INTERFACE(IDXGISurface, IDXGIDeviceSubObject) +COM_INTERFACE(IDXGIOutput, IDXGIObject) +COM_INTERFACE(IDXGIAdapter, IDXGIObject) +COM_INTERFACE(IDXGISwapChain, IDXGIDeviceSubObject) +COM_INTERFACE(IDXGIFactory, IDXGIObject) +COM_INTERFACE(IDXGIDevice, IDXGIObject) +COM_INTERFACE(IDXGIResource, IDXGIDeviceSubObject) +COM_INTERFACE(IDXGISurface1, IDXGISurface) +COM_INTERFACE(IDXGIDevice1, IDXGIDevice) +COM_INTERFACE(IDXGIAdapter1, IDXGIAdapter) +COM_INTERFACE(IDXGIFactory1, IDXGIFactory) + +template<typename Base> +struct GalliumDXGIDevice : public GalliumMultiPrivateDataComObject<Base, IDXGIDevice> +{ + ComPtr<IDXGIAdapter> adapter; + int priority; + unsigned max_latency; + + GalliumDXGIDevice(IDXGIAdapter* p_adapter) + { + adapter = p_adapter; + } + + virtual HRESULT STDMETHODCALLTYPE GetParent( + __in REFIID riid, + __out void **ppParent) + { + return adapter.p->QueryInterface(riid, ppParent); + } + + virtual HRESULT STDMETHODCALLTYPE GetAdapter( + __out IDXGIAdapter **pAdapter) + { + *pAdapter = adapter.ref(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE QueryResourceResidency( + __in_ecount(NumResources) IUnknown *const *ppResources, + __out_ecount(NumResources) DXGI_RESIDENCY *pResidencyStatus, + UINT NumResources) + { + for(unsigned i = 0; i < NumResources; ++i) + pResidencyStatus[i] = DXGI_RESIDENCY_FULLY_RESIDENT; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE SetGPUThreadPriority( + INT Priority) + { + priority = Priority; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetGPUThreadPriority( + __out INT *pPriority) + { + *pPriority = priority; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency( + UINT *pMaxLatency + ) + { + *pMaxLatency = max_latency; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency( + UINT MaxLatency) + { + max_latency = MaxLatency; + return S_OK; + } +}; + +#endif /* D3D1XSTUTIL_H_ */ |