kodi
d3dxGlobal.h
1 //--------------------------------------------------------------------------------------
2 // File: D3DXGlobal.h
3 //
4 // Direct3D 11 Effects helper defines and data structures
5 //
6 // Copyright (c) Microsoft Corporation.
7 // Licensed under the MIT License.
8 //
9 // http://go.microsoft.com/fwlink/p/?LinkId=271568
10 //--------------------------------------------------------------------------------------
11 
12 #pragma once
13 
14 #include <assert.h>
15 #include <string.h>
16 
17 namespace D3DX11Debug
18 {
19  // Helper sets a D3D resource name string (used by PIX and debug layer leak reporting).
20  inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char *name )
21  {
22  #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
23  resource->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast<UINT>(strlen(name)), name );
24  #else
25  UNREFERENCED_PARAMETER(resource);
26  UNREFERENCED_PARAMETER(name);
27  #endif
28  }
29 
30  template<UINT TNameLength>
31  inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char (&name)[TNameLength])
32  {
33  #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
34  resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name);
35  #else
36  UNREFERENCED_PARAMETER(resource);
37  UNREFERENCED_PARAMETER(name);
38  #endif
39  }
40 }
41 
42 using namespace D3DX11Debug;
43 
44 #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }
45 #define SAFE_ADDREF(p) { if (p) { (p)->AddRef(); } }
46 
47 #define SAFE_DELETE_ARRAY(p) { delete [](p); p = nullptr; }
48 #define SAFE_DELETE(p) { delete (p); p = nullptr; }
49 
50 #if FXDEBUG
51 #define __BREAK_ON_FAIL { __debugbreak(); }
52 #else
53 #define __BREAK_ON_FAIL
54 #endif
55 
56 #define VA(x, action) { hr = (x); if (FAILED(hr)) { action; __BREAK_ON_FAIL; return hr; } }
57 #define VNA(x,action) { if (!(x)) { action; __BREAK_ON_FAIL; hr = E_OUTOFMEMORY; goto lExit; } }
58 #define VBA(x,action) { if (!(x)) { action; __BREAK_ON_FAIL; hr = E_FAIL; goto lExit; } }
59 #define VHA(x,action) { hr = (x); if (FAILED(hr)) { action; __BREAK_ON_FAIL; goto lExit; } }
60 
61 #define V(x) { VA (x, 0) }
62 #define VN(x) { VNA(x, 0) }
63 #define VB(x) { VBA(x, 0) }
64 #define VH(x) { VHA(x, 0) }
65 
66 #define VBD(x,str) { VBA(x, DPF(1,str)) }
67 #define VHD(x,str) { VHA(x, DPF(1,str)) }
68 
69 #define VEASSERT(x) { hr = (x); if (FAILED(hr)) { __BREAK_ON_FAIL; assert(!#x); goto lExit; } }
70 #define VNASSERT(x) { if (!(x)) { __BREAK_ON_FAIL; assert(!#x); hr = E_OUTOFMEMORY; goto lExit; } }
71 
72 #define D3DX11FLTASSIGN(a,b) { *reinterpret_cast< UINT32* >(&(a)) = *reinterpret_cast< UINT32* >(&(b)); }
73 
74 // Preferred data alignment -- must be a power of 2!
75 static const uint32_t c_DataAlignment = sizeof(UINT_PTR);
76 
77 inline uint32_t AlignToPowerOf2(uint32_t Value, uint32_t Alignment)
78 {
79  assert((Alignment & (Alignment - 1)) == 0);
80  // to align to 2^N, add 2^N - 1 and AND with all but lowest N bits set
81  _Analysis_assume_(Alignment > 0 && Value < MAXDWORD - Alignment);
82  return (Value + Alignment - 1) & (~(Alignment - 1));
83 }
84 
85 inline void * AlignToPowerOf2(void *pValue, UINT_PTR Alignment)
86 {
87  assert((Alignment & (Alignment - 1)) == 0);
88  // to align to 2^N, add 2^N - 1 and AND with all but lowest N bits set
89  return (void *)(((UINT_PTR)pValue + Alignment - 1) & (~((UINT_PTR)Alignment - 1)));
90 }
91 
92 namespace D3DX11Core
93 {
94 
96 // CMemoryStream - A class to simplify reading binary data
99 {
100  uint8_t *m_pData;
101  size_t m_cbData;
102  size_t m_readPtr;
103 
104 public:
105  HRESULT SetData(_In_reads_bytes_(size) const void *pData, _In_ size_t size);
106 
107  HRESULT Read(_Out_ uint32_t *pUint);
108  HRESULT Read(_Outptr_result_buffer_(size) void **ppData, _In_ size_t size);
109  HRESULT Read(_Outptr_ LPCSTR *ppString);
110 
111  HRESULT ReadAtOffset(_In_ size_t offset, _In_ size_t size, _Outptr_result_buffer_(size) void **ppData);
112  HRESULT ReadAtOffset(_In_ size_t offset, _Outptr_result_z_ LPCSTR *ppString);
113 
114  size_t GetPosition();
115  HRESULT Seek(_In_ size_t offset);
116 
117  CMemoryStream() noexcept;
118  ~CMemoryStream();
119 };
120 
121 }
122 
123 #if defined(_DEBUG) && !defined(_M_X64) && !defined(_M_ARM64)
124 
125 namespace D3DX11Debug
126 {
127 
128 // This variable indicates how many ticks to go before rolling over
129 // all of the timer variables in the FX system.
130 // It is read from the system registry in debug builds; in retail the high bit is simply tested.
131 
132 _declspec(selectany) unsigned int g_TimerRolloverCount = 0x80000000;
133 }
134 
135 #endif // _DEBUG && !_M_X64
136 
137 
139 // CEffectVector - A vector implementation
141 
142 template<class T> class CEffectVector
143 {
144 protected:
145 #if _DEBUG
146  T *m_pCastData; // makes debugging easier to have a casted version of the data
147 #endif // _DEBUG
148 
149  uint8_t *m_pData;
150  uint32_t m_MaxSize;
151  uint32_t m_CurSize;
152 
153  HRESULT Grow()
154  {
155  return Reserve(m_CurSize + 1);
156  }
157 
158  HRESULT Reserve(_In_ uint32_t DesiredSize)
159  {
160  if (DesiredSize > m_MaxSize)
161  {
162  uint8_t *pNewData;
163  uint32_t newSize = std::max(m_MaxSize * 2, DesiredSize);
164 
165  if (newSize < 16)
166  newSize = 16;
167 
168  if ((newSize < m_MaxSize) || (newSize < m_CurSize) || (newSize >= UINT_MAX / sizeof(T)))
169  {
170  m_hLastError = E_OUTOFMEMORY;
171  return m_hLastError;
172  }
173 
174  pNewData = new uint8_t[newSize * sizeof(T)];
175  if (pNewData == nullptr)
176  {
177  m_hLastError = E_OUTOFMEMORY;
178  return m_hLastError;
179  }
180 
181  if (m_pData)
182  {
183  memcpy(pNewData, m_pData, m_CurSize * sizeof(T));
184  delete []m_pData;
185  }
186 
187  m_pData = pNewData;
188  m_MaxSize = newSize;
189  }
190 #if _DEBUG
191  m_pCastData = (T*) m_pData;
192 #endif // _DEBUG
193  return S_OK;
194  }
195 
196 public:
197  HRESULT m_hLastError;
198 
199  CEffectVector<T>() noexcept :
200 #if _DEBUG
201  m_pCastData(nullptr),
202 #endif
203  m_pData(nullptr),
204  m_CurSize(0),
205  m_MaxSize(0),
206  m_hLastError(S_OK)
207  {
208  }
209 
211  {
212  Clear();
213  }
214 
215  // cleanly swaps two vectors -- useful for when you want
216  // to reallocate a vector and copy data over, then swap them back
217  void SwapVector(_Out_ CEffectVector<T> &vOther)
218  {
219  uint8_t tempData[sizeof(*this)];
220 
221  memcpy(tempData, this, sizeof(*this));
222  memcpy(this, &vOther, sizeof(*this));
223  memcpy(&vOther, tempData, sizeof(*this));
224  }
225 
226  HRESULT CopyFrom(_In_ const CEffectVector<T> &vOther)
227  {
228  HRESULT hr = S_OK;
229  Clear();
230  VN( m_pData = new uint8_t[vOther.m_MaxSize * sizeof(T)] );
231 
232  m_CurSize = vOther.m_CurSize;
233  m_MaxSize = vOther.m_MaxSize;
234  m_hLastError = vOther.m_hLastError;
235 
236  for (size_t i = 0; i < m_CurSize; ++ i)
237  {
238  ((T*)m_pData)[i] = ((T*)vOther.m_pData)[i];
239  }
240 
241 lExit:
242 
243 #if _DEBUG
244  m_pCastData = (T*) m_pData;
245 #endif // _DEBUG
246 
247  return hr;
248  }
249 
250  void Clear()
251  {
252  Empty();
253  SAFE_DELETE_ARRAY(m_pData);
254  m_MaxSize = 0;
255 #if _DEBUG
256  m_pCastData = nullptr;
257 #endif // _DEBUG
258  }
259 
260  void ClearWithoutDestructor()
261  {
262  m_CurSize = 0;
263  m_hLastError = S_OK;
264  SAFE_DELETE_ARRAY(m_pData);
265  m_MaxSize = 0;
266 
267 #if _DEBUG
268  m_pCastData = nullptr;
269 #endif // _DEBUG
270  }
271 
272  void Empty()
273  {
274 
275  // manually invoke destructor on all elements
276  for (size_t i = 0; i < m_CurSize; ++ i)
277  {
278  ((T*)m_pData + i)->~T();
279  }
280  m_CurSize = 0;
281  m_hLastError = S_OK;
282  }
283 
284  T* Add()
285  {
286  if (FAILED(Grow()))
287  return nullptr;
288 
289  // placement new
290  return new((T*)m_pData + (m_CurSize ++)) T;
291  }
292 
293  T* AddRange(_In_ uint32_t count)
294  {
295  if (m_CurSize + count < m_CurSize)
296  {
297  m_hLastError = E_OUTOFMEMORY;
298  return nullptr;
299  }
300 
301  if (FAILED(Reserve(m_CurSize + count)))
302  return nullptr;
303 
304  T *pData = (T*)m_pData + m_CurSize;
305  for (size_t i = 0; i < count; ++ i)
306  {
307  new(pData + i) T;
308  }
309  m_CurSize += count;
310  return pData;
311  }
312 
313  HRESULT Add(_In_ const T& var)
314  {
315  if (FAILED(Grow()))
316  return m_hLastError;
317 
318  memcpy((T*)m_pData + m_CurSize, &var, sizeof(T));
319  m_CurSize++;
320 
321  return S_OK;
322  }
323 
324  HRESULT AddRange(_In_reads_(count) const T *pVar, _In_ uint32_t count)
325  {
326  if (m_CurSize + count < m_CurSize)
327  {
328  m_hLastError = E_OUTOFMEMORY;
329  return m_hLastError;
330  }
331 
332  if (FAILED(Reserve(m_CurSize + count)))
333  return m_hLastError;
334 
335  memcpy((T*)m_pData + m_CurSize, pVar, count * sizeof(T));
336  m_CurSize += count;
337 
338  return S_OK;
339  }
340 
341  HRESULT Insert(_In_ const T& var, _In_ uint32_t index)
342  {
343  assert(index < m_CurSize);
344 
345  if (FAILED(Grow()))
346  return m_hLastError;
347 
348  memmove((T*)m_pData + index + 1, (T*)m_pData + index, (m_CurSize - index) * sizeof(T));
349  memcpy((T*)m_pData + index, &var, sizeof(T));
350  m_CurSize++;
351 
352  return S_OK;
353  }
354 
355  HRESULT InsertRange(_In_reads_(count) const T *pVar, _In_ uint32_t index, _In_ uint32_t count)
356  {
357  assert(index < m_CurSize);
358 
359  if (m_CurSize + count < m_CurSize)
360  {
361  m_hLastError = E_OUTOFMEMORY;
362  return m_hLastError;
363  }
364 
365  if (FAILED(Reserve(m_CurSize + count)))
366  return m_hLastError;
367 
368  memmove((T*)m_pData + index + count, (T*)m_pData + index, (m_CurSize - index) * sizeof(T));
369  memcpy((T*)m_pData + index, pVar, count * sizeof(T));
370  m_CurSize += count;
371 
372  return S_OK;
373  }
374 
375  inline T& operator[](_In_ size_t index)
376  {
377  assert(index < m_CurSize);
378  return ((T*)m_pData)[index];
379  }
380 
381  // Deletes element at index and shifts all other values down
382  void Delete(_In_ uint32_t index)
383  {
384  assert(index < m_CurSize);
385 
386  -- m_CurSize;
387  memmove((T*)m_pData + index, (T*)m_pData + index + 1, (m_CurSize - index) * sizeof(T));
388  }
389 
390  // Deletes element at index and moves the last element into its place
391  void QuickDelete(_In_ uint32_t index)
392  {
393  assert(index < m_CurSize);
394 
395  -- m_CurSize;
396  memcpy((T*)m_pData + index, (T*)m_pData + m_CurSize, sizeof(T));
397  }
398 
399  inline uint32_t GetSize() const
400  {
401  return m_CurSize;
402  }
403 
404  inline T* GetData() const
405  {
406  return (T*)m_pData;
407  }
408 
409  uint32_t FindIndexOf(_In_ const void *pEntry) const
410  {
411  for (size_t i = 0; i < m_CurSize; ++ i)
412  {
413  if (((T*)m_pData + i) == pEntry)
414  return i;
415  }
416 
417  return -1;
418  }
419 
420  void Sort(int (__cdecl *pfnCompare)(const void *pElem1, const void *pElem2))
421  {
422  qsort(m_pData, m_CurSize, sizeof(T), pfnCompare);
423  }
424 };
425 
427 // CEffectVectorOwner - implements a vector of ptrs to objects. The vector owns the objects.
429 template<class T> class CEffectVectorOwner : public CEffectVector<T*>
430 {
431 public:
433  {
434  Clear();
435 
436  for (size_t i=0; i<m_CurSize; i++)
437  SAFE_DELETE(((T**)m_pData)[i]);
438 
439  SAFE_DELETE_ARRAY(m_pData);
440  }
441 
442  void Clear()
443  {
444  Empty();
445  SAFE_DELETE_ARRAY(m_pData);
446  m_MaxSize = 0;
447  }
448 
449  void Empty()
450  {
451  // manually invoke destructor on all elements
452  for (size_t i = 0; i < m_CurSize; ++ i)
453  {
454  SAFE_DELETE(((T**)m_pData)[i]);
455  }
456  m_CurSize = 0;
457  m_hLastError = S_OK;
458  }
459 
460  void Delete(_In_ uint32_t index)
461  {
462  assert(index < m_CurSize);
463 
464  SAFE_DELETE(((T**)m_pData)[index]);
465 
467  }
468 };
469 
471 // Checked uint32_t, uint64_t
472 // Use CheckedNumber only with uint32_t and uint64_t
474 template <class T, T MaxValue> class CheckedNumber
475 {
476  T m_Value;
477  bool m_bValid;
478 
479 public:
480  CheckedNumber<T, MaxValue>() noexcept :
481  m_Value(0),
482  m_bValid(true)
483  {
484  }
485 
486  CheckedNumber<T, MaxValue>(const T &value) : m_Value(value), m_bValid(true)
487  {
488  }
489 
490  CheckedNumber<T, MaxValue>(const CheckedNumber<T, MaxValue> &value) : m_bValid(value.m_bValid), m_Value(value.m_Value)
491  {
492  }
493 
495  {
496  CheckedNumber<T, MaxValue> Res(*this);
497  return Res+=other;
498  }
499 
501  {
502  if (!other.m_bValid)
503  {
504  m_bValid = false;
505  }
506  else
507  {
508  m_Value += other.m_Value;
509 
510  if (m_Value < other.m_Value)
511  m_bValid = false;
512  }
513 
514  return *this;
515  }
516 
518  {
519  CheckedNumber<T, MaxValue> Res(*this);
520  return Res*=other;
521  }
522 
524  {
525  if (!other.m_bValid)
526  {
527  m_bValid = false;
528  }
529  else
530  {
531  if (other.m_Value != 0)
532  {
533  if (m_Value > MaxValue / other.m_Value)
534  {
535  m_bValid = false;
536  }
537  }
538  m_Value *= other.m_Value;
539  }
540 
541  return *this;
542  }
543 
544  HRESULT GetValue(_Out_ T *pValue)
545  {
546  if (!m_bValid)
547  {
548  *pValue = uint32_t(-1);
549  return E_FAIL;
550  }
551 
552  *pValue = m_Value;
553  return S_OK;
554  }
555 };
556 
559 
560 
562 // Data Block Store - A linked list of allocations
564 
566 {
567 protected:
568  uint32_t m_size;
569  uint32_t m_maxSize;
570  uint8_t *m_pData;
571  CDataBlock *m_pNext;
572 
573  bool m_IsAligned; // Whether or not to align the data to c_DataAlignment
574 
575 public:
576  // AddData appends an existing use buffer to the data block
577  HRESULT AddData(_In_reads_bytes_(bufferSize) const void *pNewData, _In_ uint32_t bufferSize, _Outptr_ CDataBlock **ppBlock);
578 
579  // Allocate reserves bufferSize bytes of contiguous memory and returns a pointer to the user
580  _Success_(return != nullptr)
581  void* Allocate(_In_ uint32_t bufferSize, _Outptr_ CDataBlock **ppBlock);
582 
583  void EnableAlignment();
584 
585  CDataBlock() noexcept;
586  ~CDataBlock();
587 
588  friend class CDataBlockStore;
589 };
590 
591 
593 {
594 protected:
595  CDataBlock *m_pFirst;
596  CDataBlock *m_pLast;
597  uint32_t m_Size;
598  uint32_t m_Offset; // m_Offset gets added to offsets returned from AddData & AddString. Use this to set a global for the entire string block
599  bool m_IsAligned; // Whether or not to align the data to c_DataAlignment
600 
601 public:
602 #if _DEBUG
603  uint32_t m_cAllocations;
604 #endif
605 
606 public:
607  HRESULT AddString(_In_z_ LPCSTR pString, _Inout_ uint32_t *pOffset);
608  // Writes a null-terminated string to buffer
609 
610  HRESULT AddData(_In_reads_bytes_(bufferSize) const void *pNewData, _In_ uint32_t bufferSize, _Inout_ uint32_t *pOffset);
611  // Writes data block to buffer
612 
613  // Memory allocator support
614  void* Allocate(_In_ uint32_t bufferSize);
615  uint32_t GetSize();
616  void EnableAlignment();
617 
618  CDataBlockStore() noexcept;
619  ~CDataBlockStore();
620 };
621 
622 // Custom allocator that uses CDataBlockStore
623 // The trick is that we never free, so we don't have to keep as much state around
624 // Use PRIVATENEW in CEffectLoader
625 
626 inline void* __cdecl operator new(_In_ size_t s, _In_ CDataBlockStore &pAllocator)
627 {
628 #ifdef _M_X64
629  assert( s <= 0xffffffff );
630 #endif
631  return pAllocator.Allocate( (uint32_t)s );
632 }
633 
634 inline void __cdecl operator delete(_In_opt_ void* p, _In_ CDataBlockStore &pAllocator)
635 {
636  UNREFERENCED_PARAMETER(p);
637  UNREFERENCED_PARAMETER(pAllocator);
638 }
639 
640 
642 // Hash table
644 
645 #define HASH_MIX(a,b,c) \
646 { \
647  a -= b; a -= c; a ^= (c>>13); \
648  b -= c; b -= a; b ^= (a<<8); \
649  c -= a; c -= b; c ^= (b>>13); \
650  a -= b; a -= c; a ^= (c>>12); \
651  b -= c; b -= a; b ^= (a<<16); \
652  c -= a; c -= b; c ^= (b>>5); \
653  a -= b; a -= c; a ^= (c>>3); \
654  b -= c; b -= a; b ^= (a<<10); \
655  c -= a; c -= b; c ^= (b>>15); \
656 }
657 
658 static uint32_t ComputeHash(_In_reads_bytes_(cbToHash) const uint8_t *pb, _In_ uint32_t cbToHash)
659 {
660  uint32_t cbLeft = cbToHash;
661 
662  uint32_t a;
663  uint32_t b;
664  a = b = 0x9e3779b9; // the golden ratio; an arbitrary value
665 
666  uint32_t c = 0;
667 
668  while (cbLeft >= 12)
669  {
670  const uint32_t *pdw = reinterpret_cast<const uint32_t *>(pb);
671 
672  a += pdw[0];
673  b += pdw[1];
674  c += pdw[2];
675 
676  HASH_MIX(a,b,c);
677  pb += 12;
678  cbLeft -= 12;
679  }
680 
681  c += cbToHash;
682 
683  switch(cbLeft) // all the case statements fall through
684  {
685  case 11: c+=((uint32_t) pb[10] << 24);
686  case 10: c+=((uint32_t) pb[9] << 16);
687  case 9 : c+=((uint32_t) pb[8] << 8);
688  // the first byte of c is reserved for the length
689  case 8 : b+=((uint32_t) pb[7] << 24);
690  case 7 : b+=((uint32_t) pb[6] << 16);
691  case 6 : b+=((uint32_t) pb[5] << 8);
692  case 5 : b+=pb[4];
693  case 4 : a+=((uint32_t) pb[3] << 24);
694  case 3 : a+=((uint32_t) pb[2] << 16);
695  case 2 : a+=((uint32_t) pb[1] << 8);
696  case 1 : a+=pb[0];
697  }
698 
699  HASH_MIX(a,b,c);
700 
701  return c;
702 }
703 
704 static uint32_t ComputeHashLower(_In_reads_bytes_(cbToHash) const uint8_t *pb, _In_ uint32_t cbToHash)
705 {
706  uint32_t cbLeft = cbToHash;
707 
708  uint32_t a;
709  uint32_t b;
710  a = b = 0x9e3779b9; // the golden ratio; an arbitrary value
711  uint32_t c = 0;
712 
713  while (cbLeft >= 12)
714  {
715  uint8_t pbT[12];
716  for( size_t i = 0; i < 12; i++ )
717  pbT[i] = (uint8_t)tolower(pb[i]);
718 
719  uint32_t *pdw = reinterpret_cast<uint32_t *>(pbT);
720 
721  a += pdw[0];
722  b += pdw[1];
723  c += pdw[2];
724 
725  HASH_MIX(a,b,c);
726  pb += 12;
727  cbLeft -= 12;
728  }
729 
730  c += cbToHash;
731 
732  uint8_t pbT[12];
733  for( size_t i = 0; i < cbLeft; i++ )
734  pbT[i] = (uint8_t)tolower(pb[i]);
735 
736  switch(cbLeft) // all the case statements fall through
737  {
738  case 11: c+=((uint32_t) pbT[10] << 24);
739  case 10: c+=((uint32_t) pbT[9] << 16);
740  case 9 : c+=((uint32_t) pbT[8] << 8);
741  // the first byte of c is reserved for the length
742  case 8 : b+=((uint32_t) pbT[7] << 24);
743  case 7 : b+=((uint32_t) pbT[6] << 16);
744  case 6 : b+=((uint32_t) pbT[5] << 8);
745  case 5 : b+=pbT[4];
746  case 4 : a+=((uint32_t) pbT[3] << 24);
747  case 3 : a+=((uint32_t) pbT[2] << 16);
748  case 2 : a+=((uint32_t) pbT[1] << 8);
749  case 1 : a+=pbT[0];
750  }
751 
752  HASH_MIX(a,b,c);
753 
754  return c;
755 }
756 
757 static uint32_t ComputeHash(_In_z_ LPCSTR pString)
758 {
759  return ComputeHash(reinterpret_cast<const uint8_t*>(pString), (uint32_t)strlen(pString));
760 }
761 
762 
763 // 1) these numbers are prime
764 // 2) each is slightly less than double the last
765 // 4) each is roughly in between two powers of 2;
766 // (2^n hash table sizes are VERY BAD; they effectively truncate your
767 // precision down to the n least significant bits of the hash)
768 static const uint32_t c_PrimeSizes[] =
769 {
770  11,
771  23,
772  53,
773  97,
774  193,
775  389,
776  769,
777  1543,
778  3079,
779  6151,
780  12289,
781  24593,
782  49157,
783  98317,
784  196613,
785  393241,
786  786433,
787  1572869,
788  3145739,
789  6291469,
790  12582917,
791  25165843,
792  50331653,
793  100663319,
794  201326611,
795  402653189,
796  805306457,
797  1610612741,
798 };
799 
800 template<typename T, bool (*pfnIsEqual)(const T &Data1, const T &Data2)>
802 {
803 protected:
804 
805  struct SHashEntry
806  {
807  uint32_t Hash;
808  T Data;
809  SHashEntry *pNext;
810  };
811 
812  // Array of hash entries
813  SHashEntry **m_rgpHashEntries;
814  uint32_t m_NumHashSlots;
815  uint32_t m_NumEntries;
816  bool m_bOwnHashEntryArray;
817 
818 public:
819  class CIterator
820  {
821  friend class CEffectHashTable;
822 
823  protected:
824  SHashEntry **ppHashSlot;
825  SHashEntry *pHashEntry;
826 
827  public:
828  T GetData()
829  {
830  assert(pHashEntry != 0);
831  _Analysis_assume_(pHashEntry != 0);
832  return pHashEntry->Data;
833  }
834 
835  uint32_t GetHash()
836  {
837  assert(pHashEntry != 0);
838  _Analysis_assume_(pHashEntry != 0);
839  return pHashEntry->Hash;
840  }
841  };
842 
843  CEffectHashTable() noexcept :
844  m_rgpHashEntries(nullptr),
845  m_NumHashSlots(0),
846  m_NumEntries(0),
847  m_bOwnHashEntryArray(false)
848  {
849  }
850 
851  HRESULT Initialize(_In_ const CEffectHashTable *pOther)
852  {
853  HRESULT hr = S_OK;
854  SHashEntry **rgpNewHashEntries = nullptr;
855  uint32_t valuesMigrated = 0;
856  uint32_t actualSize;
857 
858  Cleanup();
859 
860  actualSize = pOther->m_NumHashSlots;
861  VN( rgpNewHashEntries = new SHashEntry*[actualSize] );
862 
863  ZeroMemory(rgpNewHashEntries, sizeof(SHashEntry*) * actualSize);
864 
865  // Expensive operation: rebuild the hash table
866  CIterator iter, nextIter;
867  pOther->GetFirstEntry(&iter);
868  while (!pOther->PastEnd(&iter))
869  {
870  uint32_t index = iter.GetHash() % actualSize;
871 
872  // we need to advance to the next element
873  // before we seize control of this element and move
874  // it to the new table
875  nextIter = iter;
876  pOther->GetNextEntry(&nextIter);
877 
878  // seize this hash entry, migrate it to the new table
879  SHashEntry *pNewEntry;
880  VN( pNewEntry = new SHashEntry );
881 
882  pNewEntry->pNext = rgpNewHashEntries[index];
883  pNewEntry->Data = iter.pHashEntry->Data;
884  pNewEntry->Hash = iter.pHashEntry->Hash;
885  rgpNewHashEntries[index] = pNewEntry;
886 
887  iter = nextIter;
888  ++ valuesMigrated;
889  }
890 
891  assert(valuesMigrated == pOther->m_NumEntries);
892 
893  m_rgpHashEntries = rgpNewHashEntries;
894  m_NumHashSlots = actualSize;
895  m_NumEntries = pOther->m_NumEntries;
896  m_bOwnHashEntryArray = true;
897  rgpNewHashEntries = nullptr;
898 
899 lExit:
900  SAFE_DELETE_ARRAY( rgpNewHashEntries );
901  return hr;
902  }
903 
904 protected:
905  void CleanArray()
906  {
907  if (m_bOwnHashEntryArray)
908  {
909  SAFE_DELETE_ARRAY(m_rgpHashEntries);
910  m_bOwnHashEntryArray = false;
911  }
912  }
913 
914 public:
915  void Cleanup()
916  {
917  for (size_t i = 0; i < m_NumHashSlots; ++ i)
918  {
919  SHashEntry *pCurrentEntry = m_rgpHashEntries[i];
920  SHashEntry *pTempEntry;
921  while (nullptr != pCurrentEntry)
922  {
923  pTempEntry = pCurrentEntry->pNext;
924  SAFE_DELETE(pCurrentEntry);
925  pCurrentEntry = pTempEntry;
926  -- m_NumEntries;
927  }
928  }
929  CleanArray();
930  m_NumHashSlots = 0;
931  assert(m_NumEntries == 0);
932  }
933 
935  {
936  Cleanup();
937  }
938 
939  static uint32_t GetNextHashTableSize(_In_ uint32_t DesiredSize)
940  {
941  // figure out the next logical size to use
942  for (size_t i = 0; i < _countof(c_PrimeSizes); ++i )
943  {
944  if (c_PrimeSizes[i] >= DesiredSize)
945  {
946  return c_PrimeSizes[i];
947  }
948  }
949 
950  return DesiredSize;
951  }
952 
953  // O(n) function
954  // Grows to the next suitable size (based off of the prime number table)
955  // DesiredSize is merely a suggestion
956  HRESULT Grow(_In_ uint32_t DesiredSize,
957  _In_ uint32_t ProvidedArraySize = 0,
958  _In_reads_opt_(ProvidedArraySize) void** ProvidedArray = nullptr,
959  _In_ bool OwnProvidedArray = false)
960  {
961  HRESULT hr = S_OK;
962  SHashEntry **rgpNewHashEntries = nullptr;
963  uint32_t valuesMigrated = 0;
964  uint32_t actualSize;
965 
966  VB( DesiredSize > m_NumHashSlots );
967 
968  actualSize = GetNextHashTableSize(DesiredSize);
969 
970  if (ProvidedArray &&
971  ProvidedArraySize >= actualSize)
972  {
973  rgpNewHashEntries = reinterpret_cast<SHashEntry**>(ProvidedArray);
974  }
975  else
976  {
977  OwnProvidedArray = true;
978 
979  VN( rgpNewHashEntries = new SHashEntry*[actualSize] );
980  }
981 
982  ZeroMemory(rgpNewHashEntries, sizeof(SHashEntry*) * actualSize);
983 
984  // Expensive operation: rebuild the hash table
985  CIterator iter, nextIter;
986  GetFirstEntry(&iter);
987  while (!PastEnd(&iter))
988  {
989  uint32_t index = iter.GetHash() % actualSize;
990 
991  // we need to advance to the next element
992  // before we seize control of this element and move
993  // it to the new table
994  nextIter = iter;
995  GetNextEntry(&nextIter);
996 
997  // seize this hash entry, migrate it to the new table
998  iter.pHashEntry->pNext = rgpNewHashEntries[index];
999  rgpNewHashEntries[index] = iter.pHashEntry;
1000 
1001  iter = nextIter;
1002  ++ valuesMigrated;
1003  }
1004 
1005  assert(valuesMigrated == m_NumEntries);
1006 
1007  CleanArray();
1008  m_rgpHashEntries = rgpNewHashEntries;
1009  m_NumHashSlots = actualSize;
1010  m_bOwnHashEntryArray = OwnProvidedArray;
1011 
1012 lExit:
1013  return hr;
1014  }
1015 
1016  HRESULT AutoGrow()
1017  {
1018  // arbitrary heuristic -- grow if 1:1
1019  if (m_NumEntries >= m_NumHashSlots)
1020  {
1021  // grows this hash table so that it is roughly 50% full
1022  return Grow(m_NumEntries * 2 + 1);
1023  }
1024  return S_OK;
1025  }
1026 
1027 #if _DEBUG
1028  void PrintHashTableStats()
1029  {
1030  if (m_NumHashSlots == 0)
1031  {
1032  DPF(0, "Uninitialized hash table!");
1033  return;
1034  }
1035 
1036  float variance = 0.0f;
1037  float mean = (float)m_NumEntries / (float)m_NumHashSlots;
1038  uint32_t unusedSlots = 0;
1039 
1040  DPF(0, "Hash table slots: %d, Entries in table: %d", m_NumHashSlots, m_NumEntries);
1041 
1042  for (size_t i = 0; i < m_NumHashSlots; ++ i)
1043  {
1044  uint32_t entries = 0;
1045  SHashEntry *pCurrentEntry = m_rgpHashEntries[i];
1046 
1047  while (nullptr != pCurrentEntry)
1048  {
1049  SHashEntry *pCurrentEntry2 = m_rgpHashEntries[i];
1050 
1051  // check other hash entries in this slot for hash collisions or duplications
1052  while (pCurrentEntry2 != pCurrentEntry)
1053  {
1054  if (pCurrentEntry->Hash == pCurrentEntry2->Hash)
1055  {
1056  if (pfnIsEqual(pCurrentEntry->Data, pCurrentEntry2->Data))
1057  {
1058  assert(0);
1059  DPF(0, "Duplicate entry (identical hash, identical data) found!");
1060  }
1061  else
1062  {
1063  DPF(0, "Hash collision (hash: %d)", pCurrentEntry->Hash);
1064  }
1065  }
1066  pCurrentEntry2 = pCurrentEntry2->pNext;
1067  }
1068 
1069  pCurrentEntry = pCurrentEntry->pNext;
1070  ++ entries;
1071  }
1072 
1073  if (0 == entries)
1074  {
1075  ++ unusedSlots;
1076  }
1077 
1078  // mean must be greater than 0 at this point
1079  variance += (float)entries * (float)entries / mean;
1080  }
1081 
1082  variance /= std::max(1.0f, (m_NumHashSlots - 1));
1083  variance -= (mean * mean);
1084 
1085  DPF(0, "Mean number of entries per slot: %f, Standard deviation: %f, Unused slots; %d", mean, variance, unusedSlots);
1086  }
1087 #endif // _DEBUG
1088 
1089  // S_OK if element is found, E_FAIL otherwise
1090  HRESULT FindValueWithHash(_In_ T Data, _In_ uint32_t Hash, _Out_ CIterator *pIterator)
1091  {
1092  assert(m_NumHashSlots > 0);
1093 
1094  uint32_t index = Hash % m_NumHashSlots;
1095  SHashEntry *pEntry = m_rgpHashEntries[index];
1096  while (nullptr != pEntry)
1097  {
1098  if (Hash == pEntry->Hash && pfnIsEqual(pEntry->Data, Data))
1099  {
1100  pIterator->ppHashSlot = m_rgpHashEntries + index;
1101  pIterator->pHashEntry = pEntry;
1102  return S_OK;
1103  }
1104  pEntry = pEntry->pNext;
1105  }
1106  return E_FAIL;
1107  }
1108 
1109  // S_OK if element is found, E_FAIL otherwise
1110  HRESULT FindFirstMatchingValue(_In_ uint32_t Hash, _Out_ CIterator *pIterator)
1111  {
1112  assert(m_NumHashSlots > 0);
1113 
1114  uint32_t index = Hash % m_NumHashSlots;
1115  SHashEntry *pEntry = m_rgpHashEntries[index];
1116  while (nullptr != pEntry)
1117  {
1118  if (Hash == pEntry->Hash)
1119  {
1120  pIterator->ppHashSlot = m_rgpHashEntries + index;
1121  pIterator->pHashEntry = pEntry;
1122  return S_OK;
1123  }
1124  pEntry = pEntry->pNext;
1125  }
1126  return E_FAIL;
1127  }
1128 
1129  // Adds data at the specified hash slot without checking for existence
1130  HRESULT AddValueWithHash(_In_ T Data, _In_ uint32_t Hash)
1131  {
1132  HRESULT hr = S_OK;
1133 
1134  assert(m_NumHashSlots > 0);
1135 
1136  SHashEntry *pHashEntry;
1137  uint32_t index = Hash % m_NumHashSlots;
1138 
1139  VN( pHashEntry = new SHashEntry );
1140  pHashEntry->pNext = m_rgpHashEntries[index];
1141  pHashEntry->Data = Data;
1142  pHashEntry->Hash = Hash;
1143  m_rgpHashEntries[index] = pHashEntry;
1144 
1145  ++ m_NumEntries;
1146 
1147 lExit:
1148  return hr;
1149  }
1150 
1151  // Iterator code:
1152  //
1153  // CMyHashTable::CIterator myIt;
1154  // for (myTable.GetFirstEntry(&myIt); !myTable.PastEnd(&myIt); myTable.GetNextEntry(&myIt)
1155  // { myTable.GetData(&myIt); }
1156  void GetFirstEntry(_Out_ CIterator *pIterator)
1157  {
1158  SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
1159  pIterator->ppHashSlot = m_rgpHashEntries;
1160  while (pIterator->ppHashSlot < ppEnd)
1161  {
1162  if (nullptr != *(pIterator->ppHashSlot))
1163  {
1164  pIterator->pHashEntry = *(pIterator->ppHashSlot);
1165  return;
1166  }
1167  ++ pIterator->ppHashSlot;
1168  }
1169  }
1170 
1171  bool PastEnd(_Inout_ CIterator *pIterator)
1172  {
1173  SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
1174  assert(pIterator->ppHashSlot >= m_rgpHashEntries && pIterator->ppHashSlot <= ppEnd);
1175  return (pIterator->ppHashSlot == ppEnd);
1176  }
1177 
1178  void GetNextEntry(_Inout_ CIterator *pIterator)
1179  {
1180  SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
1181  assert(pIterator->ppHashSlot >= m_rgpHashEntries && pIterator->ppHashSlot <= ppEnd);
1182  assert(pIterator->pHashEntry != 0);
1183  _Analysis_assume_(pIterator->pHashEntry != 0);
1184 
1185  pIterator->pHashEntry = pIterator->pHashEntry->pNext;
1186  if (nullptr != pIterator->pHashEntry)
1187  {
1188  return;
1189  }
1190 
1191  ++ pIterator->ppHashSlot;
1192  while (pIterator->ppHashSlot < ppEnd)
1193  {
1194  pIterator->pHashEntry = *(pIterator->ppHashSlot);
1195  if (nullptr != pIterator->pHashEntry)
1196  {
1197  return;
1198  }
1199  ++ pIterator->ppHashSlot;
1200  }
1201  // hit the end of the list, ppHashSlot == ppEnd
1202  }
1203 
1204  void RemoveEntry(_Inout_ CIterator *pIterator)
1205  {
1206  SHashEntry *pTemp;
1207  SHashEntry **ppPrev;
1208  SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
1209 
1210  assert(pIterator && !PastEnd(pIterator));
1211  ppPrev = pIterator->ppHashSlot;
1212  pTemp = *ppPrev;
1213  while (pTemp)
1214  {
1215  if (pTemp == pIterator->pHashEntry)
1216  {
1217  *ppPrev = pTemp->pNext;
1218  pIterator->ppHashSlot = ppEnd;
1219  delete pTemp;
1220  return;
1221  }
1222  ppPrev = &pTemp->pNext;
1223  pTemp = pTemp->pNext;
1224  }
1225 
1226  // Should never get here
1227  assert(0);
1228  }
1229 
1230 };
1231 
1232 // Allocates the hash slots on the regular heap (since the
1233 // hash table can grow), but all hash entries are allocated on
1234 // a private heap
1235 
1236 template<typename T, bool (*pfnIsEqual)(const T &Data1, const T &Data2)>
1238 {
1239 protected:
1240  CDataBlockStore *m_pPrivateHeap;
1241 
1242 public:
1243  CEffectHashTableWithPrivateHeap() noexcept :
1244  m_pPrivateHeap(nullptr)
1245  {
1246  }
1247 
1248  void Cleanup()
1249  {
1250  CleanArray();
1251  m_NumHashSlots = 0;
1252  m_NumEntries = 0;
1253  }
1254 
1256  {
1257  Cleanup();
1258  }
1259 
1260  // Call this only once
1261  void SetPrivateHeap(_In_ CDataBlockStore *pPrivateHeap)
1262  {
1263  assert(nullptr == m_pPrivateHeap);
1264  m_pPrivateHeap = pPrivateHeap;
1265  }
1266 
1267  // Adds data at the specified hash slot without checking for existence
1268  HRESULT AddValueWithHash(_In_ T Data, _In_ uint32_t Hash)
1269  {
1270  HRESULT hr = S_OK;
1271 
1272  assert(m_pPrivateHeap);
1273  _Analysis_assume_(m_pPrivateHeap);
1274  assert(m_NumHashSlots > 0);
1275 
1276  SHashEntry *pHashEntry;
1277  uint32_t index = Hash % m_NumHashSlots;
1278 
1279  VN( pHashEntry = new(*m_pPrivateHeap) SHashEntry );
1280  pHashEntry->pNext = m_rgpHashEntries[index];
1281  pHashEntry->Data = Data;
1282  pHashEntry->Hash = Hash;
1283  m_rgpHashEntries[index] = pHashEntry;
1284 
1285  ++ m_NumEntries;
1286 
1287 lExit:
1288  return hr;
1289  }
1290 };
Definition: d3dxGlobal.h:17
Definition: d3dxGlobal.h:142
Definition: d3dxGlobal.h:592
Definition: d3dxGlobal.h:429
Definition: d3dxGlobal.h:565
Definition: d3dxGlobal.cpp:19
Definition: d3dxGlobal.h:98
Definition: d3dxGlobal.h:819
Definition: d3dxGlobal.h:801
Definition: d3dxGlobal.h:1237
Definition: d3dxGlobal.h:474
Definition: d3dxGlobal.h:805