BsD3D11InputLayoutManager.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #include "BsD3D11InputLayoutManager.h"
  2. #include "BsD3D11Mappings.h"
  3. #include "BsD3D11RenderSystem.h"
  4. #include "BsD3D11Device.h"
  5. #include "BsD3D11GpuProgram.h"
  6. #include "BsHardwareBufferManager.h"
  7. #include "BsRenderStats.h"
  8. #include "BsDebug.h"
  9. #include "BsUtil.h"
  10. namespace BansheeEngine
  11. {
  12. size_t D3D11InputLayoutManager::HashFunc::operator()
  13. (const D3D11InputLayoutManager::VertexDeclarationKey &key) const
  14. {
  15. size_t hash = 0;
  16. hash_combine(hash, key.vertxDeclId);
  17. hash_combine(hash, key.vertexProgramId);
  18. return hash;
  19. }
  20. bool D3D11InputLayoutManager::EqualFunc::operator()
  21. (const D3D11InputLayoutManager::VertexDeclarationKey &a, const D3D11InputLayoutManager::VertexDeclarationKey &b) const
  22. {
  23. if (a.vertxDeclId != b.vertxDeclId)
  24. return false;
  25. if(a.vertexProgramId != b.vertexProgramId)
  26. return false;
  27. return true;
  28. }
  29. D3D11InputLayoutManager::D3D11InputLayoutManager()
  30. :mLastUsedCounter(0), mWarningShown(false)
  31. {
  32. }
  33. D3D11InputLayoutManager::~D3D11InputLayoutManager()
  34. {
  35. while(mInputLayoutMap.begin() != mInputLayoutMap.end())
  36. {
  37. auto firstElem = mInputLayoutMap.begin();
  38. SAFE_RELEASE(firstElem->second->inputLayout);
  39. bs_delete<PoolAlloc>(firstElem->second);
  40. mInputLayoutMap.erase(firstElem);
  41. BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_InputLayout);
  42. }
  43. }
  44. ID3D11InputLayout* D3D11InputLayoutManager::retrieveInputLayout(VertexDeclarationPtr vertexShaderDecl, VertexDeclarationPtr vertexBufferDecl, D3D11GpuProgram& vertexProgram)
  45. {
  46. VertexDeclarationKey pair;
  47. pair.vertxDeclId = vertexBufferDecl->getInternalID();
  48. pair.vertexProgramId = vertexProgram.getProgramId();
  49. auto iterFind = mInputLayoutMap.find(pair);
  50. if(iterFind == mInputLayoutMap.end())
  51. {
  52. if(mInputLayoutMap.size() >= DECLARATION_BUFFER_SIZE)
  53. removeLeastUsed(); // Prune so the buffer doesn't just infinitely grow
  54. addNewInputLayout(vertexShaderDecl, vertexBufferDecl, vertexProgram);
  55. iterFind = mInputLayoutMap.find(pair);
  56. if(iterFind == mInputLayoutMap.end()) // We failed to create input layout
  57. return nullptr;
  58. }
  59. iterFind->second->lastUsedIdx = ++mLastUsedCounter;
  60. return iterFind->second->inputLayout;
  61. }
  62. void D3D11InputLayoutManager::addNewInputLayout(VertexDeclarationPtr vertexShaderDecl, VertexDeclarationPtr vertexBufferDecl, D3D11GpuProgram& vertexProgram)
  63. {
  64. if(!areCompatible(vertexShaderDecl, vertexBufferDecl))
  65. return; // Error was already reported, so just quit here
  66. UINT32 numElements = vertexBufferDecl->getElementCount();
  67. D3D11_INPUT_ELEMENT_DESC* declElements = bs_newN<D3D11_INPUT_ELEMENT_DESC, ScratchAlloc>(numElements);
  68. ZeroMemory(declElements, sizeof(D3D11_INPUT_ELEMENT_DESC) * numElements);
  69. unsigned int idx = 0;
  70. for(auto iter = vertexBufferDecl->getElements().begin(); iter != vertexBufferDecl->getElements().end(); ++iter)
  71. {
  72. declElements[idx].SemanticName = D3D11Mappings::get(iter->getSemantic());
  73. declElements[idx].SemanticIndex = iter->getSemanticIdx();
  74. declElements[idx].Format = D3D11Mappings::get(iter->getType());
  75. declElements[idx].InputSlot = iter->getStreamIdx();
  76. declElements[idx].AlignedByteOffset = static_cast<WORD>(iter->getOffset());
  77. declElements[idx].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
  78. declElements[idx].InstanceDataStepRate = 0;
  79. idx++;
  80. }
  81. D3D11RenderSystem* d3d11rs = static_cast<D3D11RenderSystem*>(RenderSystem::instancePtr());
  82. D3D11Device& device = d3d11rs->getPrimaryDevice();
  83. const HLSLMicroCode& microcode = vertexProgram.getMicroCode();
  84. InputLayoutEntry* newEntry = bs_new<InputLayoutEntry, PoolAlloc>();
  85. newEntry->lastUsedIdx = ++mLastUsedCounter;
  86. newEntry->inputLayout = nullptr;
  87. HRESULT hr = device.getD3D11Device()->CreateInputLayout(
  88. declElements,
  89. numElements,
  90. &microcode[0],
  91. microcode.size(),
  92. &newEntry->inputLayout);
  93. bs_deleteN<ScratchAlloc>(declElements, numElements);
  94. if (FAILED(hr)|| device.hasError())
  95. BS_EXCEPT(RenderingAPIException, "Unable to set D3D11 vertex declaration" + device.getErrorDescription());
  96. // Create key and add to the layout map
  97. VertexDeclarationKey pair;
  98. pair.vertxDeclId = vertexBufferDecl->getInternalID();
  99. pair.vertexProgramId = vertexProgram.getProgramId();
  100. mInputLayoutMap[pair] = newEntry;
  101. BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_InputLayout);
  102. }
  103. void D3D11InputLayoutManager::removeLeastUsed()
  104. {
  105. if(!mWarningShown)
  106. {
  107. LOGWRN("Input layout buffer is full, pruning last " + toString(NUM_ELEMENTS_TO_PRUNE) + " elements. This is probably okay unless you are creating a massive amount of input layouts" \
  108. " as they will get re-created every frame. In that case you should increase the layout buffer size. This warning won't be shown again.");
  109. mWarningShown = true;
  110. }
  111. Map<UINT32, VertexDeclarationKey> leastFrequentlyUsedMap;
  112. for(auto iter = mInputLayoutMap.begin(); iter != mInputLayoutMap.end(); ++iter)
  113. leastFrequentlyUsedMap[iter->second->lastUsedIdx] = iter->first;
  114. UINT32 elemsRemoved = 0;
  115. for(auto iter = leastFrequentlyUsedMap.begin(); iter != leastFrequentlyUsedMap.end(); ++iter)
  116. {
  117. auto inputLayoutIter = mInputLayoutMap.find(iter->second);
  118. SAFE_RELEASE(inputLayoutIter->second->inputLayout);
  119. bs_delete<PoolAlloc>(inputLayoutIter->second);
  120. mInputLayoutMap.erase(inputLayoutIter);
  121. BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_InputLayout);
  122. elemsRemoved++;
  123. if(elemsRemoved >= NUM_ELEMENTS_TO_PRUNE)
  124. break;
  125. }
  126. }
  127. bool D3D11InputLayoutManager::areCompatible(VertexDeclarationPtr vertexShaderDecl, VertexDeclarationPtr vertexBufferDecl)
  128. {
  129. for(auto shaderIter = vertexShaderDecl->getElements().begin(); shaderIter != vertexShaderDecl->getElements().end(); ++shaderIter)
  130. {
  131. const VertexElement* foundElement = nullptr;
  132. for(auto bufferIter = vertexBufferDecl->getElements().begin(); bufferIter != vertexBufferDecl->getElements().end(); ++bufferIter)
  133. {
  134. if(shaderIter->getSemantic() == bufferIter->getSemantic() && shaderIter->getSemanticIdx() == bufferIter->getSemanticIdx())
  135. {
  136. foundElement = &(*bufferIter);
  137. break;
  138. }
  139. }
  140. if(foundElement == nullptr)
  141. {
  142. LOGWRN("Provided vertex buffer doesn't have a required input attribute: " + toString(shaderIter->getSemantic()) + toString(shaderIter->getSemanticIdx()));
  143. return false;
  144. }
  145. }
  146. return true;
  147. }
  148. }