1 /**************************************************************************
3 * Copyright 2010 Luca Barbieri
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:
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.
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.
25 **************************************************************************/
27 #ifndef D3D1XSTUTIL_H_
28 #define D3D1XSTUTIL_H_
31 #include <unordered_map>
32 #include <unordered_set>
34 #include <tr1/unordered_map>
35 #include <tr1/unordered_set>
44 #define WIN32_LEAN_AND_MEAN
47 #include "galliumdxgi.h"
48 #include <d3dcommon.h>
52 #include "util/u_atomic.h"
53 #include "pipe/p_format.h"
54 #include "os/os_thread.h"
65 #define D3D_PRIMITIVE_TOPOLOGY_COUNT 65
66 extern unsigned d3d_to_pipe_prim[D3D_PRIMITIVE_TOPOLOGY_COUNT];
68 #define D3D_PRIMITIVE_COUNT 40
69 extern unsigned d3d_to_pipe_prim_type[D3D_PRIMITIVE_COUNT];
71 /* NOTE: this _depends_ on the vtable layout of the C++ compiler to be
72 * binary compatible with Windows.
73 * Furthermore some absurd vtable layout likely won't work at all, since
74 * we perform some casts which are probably not safe by the C++ standard.
76 * In particular, the GNU/Linux/Itanium/clang ABI and Microsoft ABIs will work,
78 * If in doubt, just switch to the latest version of a widely used C++ compiler.
80 * DESIGN of the Gallium COM implementation
82 * This state tracker uses somewhat unusual C++ coding patterns,
83 * to implement the COM interfaces required by Direct3D.
85 * While it may seem complicated, the effect is that the result
86 * generally behaves as intuitively as possible: in particular pointer
87 * casts very rarely change the pointer value (only for secondary
88 * DXGI/Gallium interfaces)
90 * Implementing COM is on first sight very easy: after all, it just
91 * consists of a reference count, and a dynamic_cast<> equivalent.
93 * However, implementing objects with multiple interfaces is actually
95 * The issue is that the interface pointers can't be equal, since this
96 * would place incompatible constraints on the vtable layout and thus
97 * multiple inheritance (and the subobjects the C++ compiler creates
98 * with it) must be correctly used.
100 * Furthermore, we must have a single reference count, which means
101 * that a naive implementation won't work, and it's necessary to either
102 * use virtual inheritance, or the "mixin inheritance" model we use.
104 * This solution aims to achieve the following object layout:
105 * 0: pointer to vtable for primary interface
108 * ... vtable pointers for secondary interfaces
109 * ... implementation of subclasses assuming secondary interfaces
111 * This allows us to cast pointers by just reinterpreting the value in
114 * To achieve this, *all* non-leaf classes must have their parent
115 * or the base COM interface as a template parameter, since derived
116 * classes may need to change that to support an interface derived
117 * from the one implemented by the superclass.
119 * Note however, that you can cast without regard to the template
120 * parameter, because only the vtable layout depends on it, since
121 * interfaces have no data members.
123 * For this to work, DON'T USE VIRTUAL FUNCTIONS except to implement
124 * interfaces, since the vtable layouts would otherwise be mismatched.
125 * An exception are virtual functions called only from other virtual functions,
126 * which is currently only used for the virtual destructor.
128 * The base class is GalliumComObject<IFoo>, which implements the
129 * IUnknown interface, and inherits IFoo.
131 * To support multiple inheritance, we insert GalliumMultiComObject,
132 * which redirects the secondary interfaces to the GalliumComObject
135 * Gallium(Multi)PrivateDataComObject is like ComObject but also
136 * implements the Get/SetPrivateData functions present on several
137 * D3D/DXGI interfaces.
139 * Example class hierarchy:
153 * GalliumComObject<IDuck>
154 * (non-instantiable, only implements IUnknown)
157 * GalliumAnimal<IDuck>
158 * (non-instantiable, only implements IAnimal)
165 * GalliumMultiComObject<GalliumDuck, IWheeledVehicle> <- IWheeledVehicle <- IVehicle <- IUnknown (second version)
166 * (non-instantiable, only implements IDuck and the IUnknown of IWheeledVehicle)
169 * GalliumDuckOnWheels
172 * This will produce the desired layout.
173 * Note that GalliumAnimal<IFoo>* is safely castable to GalliumAnimal<IBar>*
174 * by reinterpreting, as long as non-interface virtual functions are not used,
175 * and that you only call interface functions for the superinterface of IBar
176 * that the object actually implements.
178 * Instead, if GalliumDuck where to inherit both from GalliumAnimal
179 * and IDuck, then (IDuck*)gallium_duck and (IAnimal*)gallium_duck would
180 * have different pointer values, which the "base class as template parameter"
183 * The price we pay is that you MUST NOT have virtual functions other than those
184 * implementing interfaces (except for leaf classes) since the position of these
185 * would depend on the base interface.
186 * As mentioned above, virtual functions only called from interface functions
187 * are an exception, currently used only for the virtual destructor.
188 * If you want virtual functions anyway , put them in a separate interface class,
189 * multiply inherit from that and cast the pointer to that interface.
191 * You CAN however have virtual functions on any class which does not specify
192 * his base as a template parameter, or where you don't need to change the
193 * template base interface parameter by casting.
195 * --- The magic QueryInterface "delete this" trick ---
197 * When the reference count drops to 0, we must delete the class.
198 * The problem is, that we must call the right virtual destructor (i.e. on the right class).
199 * However, we would like to be able to call release() and nonatomic_release()
200 * non-virtually for performance (also, the latter cannot be called virtually at all, since
201 * IUnknown does not offer it).
203 * The naive solution would be to just add a virtual destructor and rely on it.
204 * However, this doesn't work due to the fact that as described above we perform casets
205 * with are unsafe regarding vtable layout.
206 * In particular, consider the case where we try to delete GalliumComObject<ID3D11Texture2D>
207 * with a pointer to GalliumComObject<ID3D11Resource>.
208 * Since we think that this is a GalliumComObject<ID3D11Resource>, we'll look for the
209 * destructor in the vtable slot immediately after the ID3D11Resource vtable, but this is
210 * actually an ID3D11Texture2D function implemented by the object!
212 * So, we must put the destructor somewhere else.
213 * We could add it as a data member, but it would be awkward and it would bloat the
215 * Thus, we use this trick: we reuse the vtable slot for QueryInterface, which is always at the
217 * To do so, we define a special value for the first pointer argument, that triggers a
219 * In addition to that, we add a virtual destructor to GalliumComObject.
220 * That virtual destructor will be called by QueryInterface, and since that is a virtual
221 * function, it will know the correct place for the virtual destructor.
223 * QueryInterface is already slow due to the need to compare several GUIDs, so the
224 * additional pointer test should not be significant.
226 * Of course the ideal solution would be telling the C++ compiler to put the
227 * destructor it in a negative vtable slot, but unfortunately GCC doesn't support that
228 * yet, and this method is almost as good as that.
234 #define COM_INTERFACE(intf, base) \
236 struct com_traits<intf> \
238 static REFIID iid() {return IID_##intf;} \
239 static inline bool is_self_or_ancestor(REFIID riid) {return riid == iid() || com_traits<base>::is_self_or_ancestor(riid);} \
243 struct com_traits<IUnknown>
245 static REFIID iid() {return IID_IUnknown;}
246 static inline bool is_self_or_ancestor(REFIID riid) {return riid == iid();}
250 #define __uuidof(T) (com_traits<T>::iid())
257 refcnt_t(unsigned v = 1)
263 p_atomic_inc((int32_t*)&refcnt);
269 if(p_atomic_dec_zero((int32_t*)&refcnt))
274 void nonatomic_add_ref()
276 p_atomic_inc((int32_t*)&refcnt);
279 unsigned nonatomic_release()
281 if(p_atomic_dec_zero((int32_t*)&refcnt))
288 #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
289 /* this should be safe because atomic ops are full memory barriers, and thus a sequence that does:
292 * should never be reorderable (as seen from another CPU) to:
296 * since one of the ops is atomic.
297 * If this weren't the case, a CPU could incorrectly destroy an object manipulated in that way by another one.
306 uint32_t atomic_refcnt;
307 uint32_t nonatomic_refcnt;
311 dual_refcnt_t(unsigned v = 1)
314 nonatomic_refcnt = 0;
319 if(sizeof(void*) == 8)
320 return *(volatile uint64_t*)&refcnt == 0ULL;
328 while(!__sync_bool_compare_and_swap(&refcnt, v, v));
335 //printf("%p add_ref at %u %u\n", this, atomic_refcnt, nonatomic_refcnt);
336 p_atomic_inc((int32_t*)&atomic_refcnt);
337 return atomic_refcnt + nonatomic_refcnt;
342 //printf("%p release at %u %u\n", this, atomic_refcnt, nonatomic_refcnt);
343 if(p_atomic_dec_zero((int32_t*)&atomic_refcnt) && !nonatomic_refcnt && is_zero())
345 unsigned v = atomic_refcnt + nonatomic_refcnt;
349 void nonatomic_add_ref()
351 //printf("%p nonatomic_add_ref at %u %u\n", this, atomic_refcnt, nonatomic_refcnt);
355 unsigned nonatomic_release()
357 //printf("%p nonatomic_release at %u %u\n", this, atomic_refcnt, nonatomic_refcnt);
358 if(!--nonatomic_refcnt)
360 __sync_synchronize();
361 if(!atomic_refcnt && is_zero())
368 // this will result in atomic operations being used while they could have been avoided
370 #warning Compile for 586+ using GCC to improve the performance of the Direct3D 10/11 state tracker
372 typedef refcnt_t dual_refcnt_t;
375 #define IID_MAGIC_DELETE_THIS (*(const IID*)((intptr_t)-(int)(sizeof(IID) - 1)))
377 template<typename Base = IUnknown, typename RefCnt = refcnt_t>
378 struct GalliumComObject : public Base
385 /* DO NOT CALL this from externally called non-virtual functions in derived classes, since
386 * the vtable position depends on the COM interface being implemented
388 virtual ~GalliumComObject()
391 inline ULONG add_ref()
393 return refcnt.add_ref();
396 inline ULONG release()
398 ULONG v = refcnt.release();
401 /* this will call execute "delete this", using the correct vtable slot for the destructor */
402 /* see the initial comment for an explaination of this magic trick */
403 this->QueryInterface(IID_MAGIC_DELETE_THIS, 0);
409 inline void nonatomic_add_ref()
411 refcnt.nonatomic_add_ref();
414 inline void nonatomic_release()
416 if(!refcnt.nonatomic_release())
418 /* this will execute "delete this", using the correct vtable slot for the destructor */
419 /* see the initial comment for an explaination of this magic trick */
420 this->QueryInterface(IID_MAGIC_DELETE_THIS, 0);
424 inline HRESULT query_interface(REFIID riid, void **ppvObject)
426 if(com_traits<Base>::is_self_or_ancestor(riid))
428 // must be the virtual AddRef, since it is overridden by some classes
434 return E_NOINTERFACE;
437 virtual ULONG STDMETHODCALLTYPE AddRef()
442 virtual ULONG STDMETHODCALLTYPE Release()
447 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
451 /* see the initial comment for an explaination of this magic trick */
452 if(&riid == &IID_MAGIC_DELETE_THIS)
461 return query_interface(riid, ppvObject);
465 template<typename BaseClass, typename SecondaryInterface>
466 struct GalliumMultiComObject : public BaseClass, SecondaryInterface
468 // we could avoid this duplication, but the increased complexity to do so isn't worth it
469 virtual ULONG STDMETHODCALLTYPE AddRef()
471 return BaseClass::add_ref();
474 virtual ULONG STDMETHODCALLTYPE Release()
476 return BaseClass::release();
479 inline HRESULT query_interface(REFIID riid, void **ppvObject)
481 HRESULT hr = BaseClass::query_interface(riid, ppvObject);
484 if(com_traits<SecondaryInterface>::is_self_or_ancestor(riid))
486 // must be the virtual AddRef, since it is overridden by some classes
488 *ppvObject = (SecondaryInterface*)this;
492 return E_NOINTERFACE;
495 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
499 /* see the initial comment for an explaination of this magic trick */
500 if(&riid == &IID_MAGIC_DELETE_THIS)
509 return query_interface(riid, ppvObject);
513 template<typename T, typename Traits>
522 void add_ref() {Traits::add_ref(p);}
523 void release() {Traits::release(p);}
525 template<typename U, typename UTraits>
526 refcnt_ptr(const refcnt_ptr<U, UTraits>& c)
528 *this = static_cast<U*>(c.ref());
542 template<typename U, typename UTraits>
543 refcnt_ptr& operator =(const refcnt_ptr<U, UTraits>& q)
549 refcnt_ptr& operator =(U* q)
552 p = static_cast<T*>(q);
575 const T* operator ->() const
586 bool operator !() const
591 typedef T* refcnt_ptr::*unspecified_bool_type;
593 operator unspecified_bool_type() const
595 return p ? &refcnt_ptr::p : 0;
599 struct simple_ptr_traits
601 static void add_ref(void* p) {}
602 static void release(void* p) {}
605 struct com_ptr_traits
607 static void add_ref(void* p)
610 ((IUnknown*)p)->AddRef();
613 static void release(void* p)
616 ((IUnknown*)p)->Release();
621 struct ComPtr : public refcnt_ptr<T, com_ptr_traits>
623 template<typename U, typename UTraits>
624 ComPtr& operator =(const refcnt_ptr<U, UTraits>& q)
630 ComPtr& operator =(U* q)
633 this->p = static_cast<T*>(q);
639 template<typename T, typename TTraits, typename U, typename UTraits>
640 bool operator ==(const refcnt_ptr<T, TTraits>& a, const refcnt_ptr<U, UTraits>& b)
645 template<typename T, typename TTraits, typename U>
646 bool operator ==(const refcnt_ptr<T, TTraits>& a, U* b)
651 template<typename T, typename TTraits, typename U>
652 bool operator ==(U* b, const refcnt_ptr<T, TTraits>& a)
657 template<typename T, typename TTraits, typename U, typename UTraits>
658 bool operator !=(const refcnt_ptr<T, TTraits>& a, const refcnt_ptr<U, UTraits>& b)
663 template<typename T, typename TTraits, typename U>
664 bool operator !=(const refcnt_ptr<T, TTraits>& a, U* b)
669 template<typename T, typename TTraits, typename U>
670 bool operator !=(U* b, const refcnt_ptr<T, TTraits>& a)
675 template<bool threadsafe>
676 struct maybe_mutex_t;
679 struct maybe_mutex_t<true>
685 pipe_mutex_init(mutex);
690 pipe_mutex_lock(mutex);
695 pipe_mutex_unlock(mutex);
700 struct maybe_mutex_t<false>
711 typedef maybe_mutex_t<true> mutex_t;
732 c_string(const char* p)
736 operator const char*() const
742 static inline bool operator ==(const c_string& a, const c_string& b)
744 return !strcmp(a.p, b.p);
747 static inline bool operator !=(const c_string& a, const c_string& b)
749 return strcmp(a.p, b.p);
752 static inline size_t raw_hash(const char* p, size_t size)
755 if(sizeof(size_t) >= 8)
756 res = (size_t)14695981039346656037ULL;
758 res = (size_t)2166136261UL;
759 const char* end = p + size;
763 if(sizeof(size_t) >= 8)
764 res *= (size_t)1099511628211ULL;
766 res *= (size_t)16777619UL;
772 static inline size_t raw_hash(const T& t)
774 return raw_hash((const char*)&t, sizeof(t));
777 // TODO: only tested with the gcc libstdc++, might not work elsewhere
785 struct hash<GUID> : public std::unary_function<GUID, size_t>
787 inline size_t operator()(GUID __val) const;
790 inline size_t hash<GUID>::operator()(GUID __val) const
792 return raw_hash(__val);
796 struct hash<c_string> : public std::unary_function<c_string, size_t>
798 inline size_t operator()(c_string __val) const;
801 inline size_t hash<c_string>::operator()(c_string __val) const
803 return raw_hash(__val.p, strlen(__val.p));
806 template<typename T, typename U>
807 struct hash<std::pair<T, U> > : public std::unary_function<std::pair<T, U>, size_t>
809 inline size_t operator()(std::pair<T, U> __val) const;
812 template<typename T, typename U>
813 inline size_t hash<std::pair<T, U> >::operator()(std::pair<T, U> __val) const
815 std::pair<size_t, size_t> p;
816 p.first = hash<T>()(__val.first);
817 p.second = hash<U>()(__val.second);
825 template<typename Base, typename RefCnt = refcnt_t>
826 struct GalliumPrivateDataComObject : public GalliumComObject<Base, RefCnt>
828 typedef std::unordered_map<GUID, std::pair<void*, unsigned> > private_data_map_t;
829 private_data_map_t private_data_map;
830 mutex_t private_data_mutex;
832 ~GalliumPrivateDataComObject()
834 for(private_data_map_t::iterator i = private_data_map.begin(), e = private_data_map.end(); i != e; ++i)
836 if(i->second.second == ~0u)
837 ((IUnknown*)i->second.first)->Release();
839 free(i->second.first);
843 HRESULT get_private_data(
848 lock_t<mutex_t> lock(private_data_mutex);
849 private_data_map_t::iterator i = private_data_map.find(guid);
851 if(i == private_data_map.end())
852 return DXGI_ERROR_NOT_FOUND;
853 if(i->second.second == ~0u)
855 /* TODO: is GetPrivateData on interface data supposed to do this? */
856 if(*pDataSize < sizeof(void*))
860 memcpy(pData, &i->second.first, sizeof(void*));
861 ((IUnknown*)i->second.first)->AddRef();
863 *pDataSize = sizeof(void*);
867 unsigned size = std::min(*pDataSize, i->second.second);
869 memcpy(pData, i->second.first, size);
875 HRESULT set_private_data(
882 if(DataSize && pData)
884 p = malloc(DataSize);
886 return E_OUTOFMEMORY;
889 lock_t<mutex_t> lock(private_data_mutex);
890 std::pair<void*, unsigned>& v = private_data_map[guid];
894 ((IUnknown*)v.first)->Release();
898 if(DataSize && pData)
900 memcpy(p, pData, DataSize);
905 private_data_map.erase(guid);
909 HRESULT set_private_data_interface(
911 const IUnknown *pData)
913 lock_t<mutex_t> lock(private_data_mutex);
914 std::pair<void*, unsigned>& v = private_data_map[guid];
918 ((IUnknown*)v.first)->Release();
924 ((IUnknown*)pData)->AddRef();
925 v.first = (void*)pData;
929 private_data_map.erase(guid);
933 virtual HRESULT STDMETHODCALLTYPE GetPrivateData(
938 return get_private_data(guid, pDataSize, pData);
941 virtual HRESULT STDMETHODCALLTYPE SetPrivateData(
946 return set_private_data(guid, DataSize, pData);
949 virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
951 const IUnknown *pData)
953 return set_private_data_interface(guid, pData);
957 template<typename BaseClass, typename SecondaryInterface>
958 struct GalliumMultiPrivateDataComObject : public GalliumMultiComObject<BaseClass, SecondaryInterface>
960 // we could avoid this duplication, but the increased complexity to do so isn't worth it
961 virtual HRESULT STDMETHODCALLTYPE GetPrivateData(
966 return BaseClass::get_private_data(guid, pDataSize, pData);
969 virtual HRESULT STDMETHODCALLTYPE SetPrivateData(
974 return BaseClass::set_private_data(guid, DataSize, pData);
977 virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
979 const IUnknown *pData)
981 return BaseClass::set_private_data_interface(guid, pData);
985 #define DXGI_FORMAT_COUNT 116
986 extern pipe_format dxgi_to_pipe_format[DXGI_FORMAT_COUNT];
987 extern DXGI_FORMAT pipe_to_dxgi_format[PIPE_FORMAT_COUNT];
989 void init_pipe_to_dxgi_format();
991 COM_INTERFACE(IGalliumDevice, IUnknown);
992 COM_INTERFACE(IGalliumAdapter, IUnknown);
993 COM_INTERFACE(IGalliumResource, IUnknown);
995 // used to make QueryInterface know the IIDs of the interface and its ancestors
996 COM_INTERFACE(IDXGIObject, IUnknown)
997 COM_INTERFACE(IDXGIDeviceSubObject, IDXGIObject)
998 COM_INTERFACE(IDXGISurface, IDXGIDeviceSubObject)
999 COM_INTERFACE(IDXGIOutput, IDXGIObject)
1000 COM_INTERFACE(IDXGIAdapter, IDXGIObject)
1001 COM_INTERFACE(IDXGISwapChain, IDXGIDeviceSubObject)
1002 COM_INTERFACE(IDXGIFactory, IDXGIObject)
1003 COM_INTERFACE(IDXGIDevice, IDXGIObject)
1004 COM_INTERFACE(IDXGIResource, IDXGIDeviceSubObject)
1005 COM_INTERFACE(IDXGISurface1, IDXGISurface)
1006 COM_INTERFACE(IDXGIDevice1, IDXGIDevice)
1007 COM_INTERFACE(IDXGIAdapter1, IDXGIAdapter)
1008 COM_INTERFACE(IDXGIFactory1, IDXGIFactory)
1010 template<typename Base>
1011 struct GalliumDXGIDevice : public GalliumMultiPrivateDataComObject<Base, IDXGIDevice1>
1013 ComPtr<IDXGIAdapter> adapter;
1015 unsigned max_latency;
1017 GalliumDXGIDevice(IDXGIAdapter* p_adapter)
1019 adapter = p_adapter;
1022 virtual HRESULT STDMETHODCALLTYPE GetParent(
1026 return adapter.p->QueryInterface(riid, ppParent);
1029 virtual HRESULT STDMETHODCALLTYPE GetAdapter(
1030 IDXGIAdapter **pAdapter)
1032 *pAdapter = adapter.ref();
1036 virtual HRESULT STDMETHODCALLTYPE QueryResourceResidency(
1037 IUnknown *const *ppResources,
1038 DXGI_RESIDENCY *pResidencyStatus,
1041 for(unsigned i = 0; i < NumResources; ++i)
1042 pResidencyStatus[i] = DXGI_RESIDENCY_FULLY_RESIDENT;
1046 virtual HRESULT STDMETHODCALLTYPE SetGPUThreadPriority(
1049 priority = Priority;
1053 virtual HRESULT STDMETHODCALLTYPE GetGPUThreadPriority(
1056 *pPriority = priority;
1060 HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(
1064 *pMaxLatency = max_latency;
1068 virtual HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(
1071 max_latency = MaxLatency;
1076 COM_INTERFACE(ID3D10Blob, IUnknown);
1078 /* NOTE: ID3DBlob implementations may come from a Microsoft native DLL
1079 * (e.g. d3dcompiler), or perhaps even from the application itself.
1081 * Hence, never try to access the data/size members directly, which is why they are private.
1082 * In internal code, use std::pair<void*, size_t> instead of this class.
1084 class GalliumD3DBlob : public GalliumComObject<ID3DBlob>
1090 GalliumD3DBlob(void* data, size_t size)
1091 : data(data), size(size)
1099 virtual LPVOID STDMETHODCALLTYPE GetBufferPointer()
1104 virtual SIZE_T STDMETHODCALLTYPE GetBufferSize()
1110 #endif /* D3D1XSTUTIL_H_ */