CmD3D11HardwareBuffer.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #include "CmD3D11HardwareBuffer.h"
  2. #include "CmD3D11Mappings.h"
  3. #include "CmD3D11Device.h"
  4. #include "CmException.h"
  5. #include "CmDebug.h"
  6. #include "CmProfiler.h"
  7. namespace CamelotFramework
  8. {
  9. D3D11HardwareBuffer::D3D11HardwareBuffer(BufferType btype, GpuBufferUsage usage, UINT32 elementCount, UINT32 elementSize,
  10. D3D11Device& device, bool useSystemMem, bool streamOut, bool randomGpuWrite, bool useCounter)
  11. : HardwareBuffer(usage, useSystemMem),
  12. mD3DBuffer(0),
  13. mpTempStagingBuffer(0),
  14. mUseTempStagingBuffer(false),
  15. mBufferType(btype),
  16. mDevice(device),
  17. mElementCount(elementCount),
  18. mElementSize(elementSize),
  19. mRandomGpuWrite(randomGpuWrite),
  20. mUseCounter(useCounter)
  21. {
  22. assert((!streamOut || btype == BT_VERTEX) && "Stream out flag is only supported on vertex buffers");
  23. assert(!randomGpuWrite || (btype & BT_GROUP_GENERIC) != 0 && "randomGpuWrite flag can only be enabled with append/consume, indirect argument, structured or raw buffers");
  24. assert(btype != BT_APPENDCONSUME || randomGpuWrite && "Append/Consume buffer must be created with randomGpuWrite enabled.");
  25. assert(!useCounter || btype == BT_STRUCTURED && "Counter can only be used with a structured buffer.");
  26. assert(!useCounter || randomGpuWrite && "Counter can only be used with buffers that have randomGpuWrite enabled.");
  27. assert(!randomGpuWrite || !useSystemMem && "randomGpuWrite and useSystemMem cannot be used together.");
  28. assert(!(useSystemMem && streamOut) && "useSystemMem and streamOut cannot be used together.");
  29. mSizeInBytes = elementCount * elementSize;
  30. mDesc.ByteWidth = mSizeInBytes;
  31. mDesc.MiscFlags = 0;
  32. mDesc.StructureByteStride = 0;
  33. if (useSystemMem)
  34. {
  35. mDesc.Usage = D3D11_USAGE_STAGING;
  36. mDesc.BindFlags = 0;
  37. mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ ;
  38. }
  39. else if(randomGpuWrite)
  40. {
  41. mDesc.Usage = D3D11_USAGE_DEFAULT;
  42. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
  43. mDesc.CPUAccessFlags = 0;
  44. }
  45. else
  46. {
  47. mDesc.Usage = D3D11Mappings::_getUsage(mUsage);
  48. mDesc.CPUAccessFlags = D3D11Mappings::_getAccessFlags(mUsage);
  49. switch(btype)
  50. {
  51. case BT_VERTEX:
  52. mDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  53. if (streamOut)
  54. mDesc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
  55. break;
  56. case BT_INDEX:
  57. mDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
  58. break;
  59. case BT_CONSTANT:
  60. mDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
  61. break;
  62. case BT_STRUCTURED:
  63. case BT_APPENDCONSUME:
  64. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  65. mDesc.StructureByteStride = elementSize;
  66. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
  67. break;
  68. case BT_RAW:
  69. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  70. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
  71. break;
  72. case BT_INDIRECTARGUMENT:
  73. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  74. mDesc.MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
  75. break;
  76. }
  77. }
  78. HRESULT hr = device.getD3D11Device()->CreateBuffer( &mDesc, nullptr, &mD3DBuffer );
  79. if (FAILED(hr) || mDevice.hasError())
  80. {
  81. String msg = device.getErrorDescription();
  82. CM_EXCEPT(RenderingAPIException, "Cannot create D3D11 buffer: " + msg);
  83. }
  84. }
  85. D3D11HardwareBuffer::~D3D11HardwareBuffer()
  86. {
  87. SAFE_RELEASE(mD3DBuffer);
  88. if(mpTempStagingBuffer != nullptr)
  89. cm_delete<PoolAlloc>(mpTempStagingBuffer);
  90. }
  91. void* D3D11HardwareBuffer::lockImpl(UINT32 offset,
  92. UINT32 length, GpuLockOptions options)
  93. {
  94. if (length > mSizeInBytes)
  95. CM_EXCEPT(RenderingAPIException, "Provided length " + toString(length) + " larger than the buffer " + toString(mSizeInBytes) + ".");
  96. // Use direct (and faster) Map/Unmap if dynamic write, or a staging read/write
  97. if((mDesc.Usage == D3D11_USAGE_DYNAMIC && options != GBL_READ_ONLY) || mDesc.Usage == D3D11_USAGE_STAGING)
  98. {
  99. D3D11_MAP mapType;
  100. switch(options)
  101. {
  102. case GBL_WRITE_ONLY_DISCARD:
  103. if (mUsage & GBU_DYNAMIC)
  104. {
  105. // Map cannot be called with MAP_WRITE access,
  106. // because the Resource was created as D3D11_USAGE_DYNAMIC.
  107. // D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
  108. // or MAP_WRITE_NO_OVERWRITE with Map.
  109. mapType = D3D11_MAP_WRITE_DISCARD;
  110. }
  111. else
  112. {
  113. // Map cannot be called with MAP_WRITE_DISCARD access,
  114. // because the Resource was not created as D3D11_USAGE_DYNAMIC.
  115. // D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
  116. // or MAP_WRITE_NO_OVERWRITE with Map.
  117. mapType = D3D11_MAP_WRITE;
  118. LOGWRN("DISCARD lock is only available on dynamic buffers. Falling back to normal write.");
  119. }
  120. break;
  121. case GBL_WRITE_ONLY_NO_OVERWRITE:
  122. if(mBufferType == BT_INDEX || mBufferType == BT_VERTEX)
  123. mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
  124. else
  125. {
  126. mapType = D3D11_MAP_WRITE;
  127. LOGWRN("NO_OVERWRITE lock is not available on this (" + toString(mBufferType) + ") buffer type. Falling back to normal write.");
  128. }
  129. break;
  130. case GBL_WRITE_ONLY:
  131. mapType = D3D11_MAP_WRITE;
  132. break;
  133. case GBL_READ_WRITE:
  134. if ((mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) != 0 &&
  135. (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) != 0)
  136. {
  137. mapType = D3D11_MAP_READ_WRITE;
  138. }
  139. else if(mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)
  140. {
  141. mapType = D3D11_MAP_WRITE;
  142. }
  143. else
  144. {
  145. mapType = D3D11_MAP_READ;
  146. }
  147. break;
  148. case GBL_READ_ONLY:
  149. mapType = D3D11_MAP_READ;
  150. break;
  151. }
  152. if(D3D11Mappings::isMappingRead(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) == 0)
  153. CM_EXCEPT(RenderingAPIException, "Trying to read a buffer, but buffer wasn't created with a read access flag.");
  154. if(D3D11Mappings::isMappingWrite(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) == 0)
  155. CM_EXCEPT(RenderingAPIException, "Trying to write to a buffer, but buffer wasn't created with a write access flag.");
  156. void * pRet = NULL;
  157. D3D11_MAPPED_SUBRESOURCE mappedSubResource;
  158. mappedSubResource.pData = NULL;
  159. mDevice.clearErrors();
  160. gProfiler().beginSample("Map");
  161. HRESULT hr = mDevice.getImmediateContext()->Map(mD3DBuffer, 0, mapType, 0, &mappedSubResource);
  162. if (FAILED(hr) || mDevice.hasError())
  163. {
  164. String msg = mDevice.getErrorDescription();
  165. CM_EXCEPT(RenderingAPIException, "Error calling Map: " + msg);
  166. }
  167. gProfiler().endSample("Map");
  168. pRet = static_cast<void*>(static_cast<char*>(mappedSubResource.pData) + offset);
  169. return pRet;
  170. }
  171. else // Otherwise create a staging buffer to do all read/write operations on. Usually try to avoid this.
  172. {
  173. mUseTempStagingBuffer = true;
  174. if (!mpTempStagingBuffer)
  175. {
  176. // create another buffer instance but use system memory
  177. mpTempStagingBuffer = cm_new<D3D11HardwareBuffer, PoolAlloc>(mBufferType, mUsage, 1, mSizeInBytes, std::ref(mDevice), true);
  178. }
  179. // schedule a copy to the staging
  180. if (options != GBL_WRITE_ONLY_DISCARD)
  181. mpTempStagingBuffer->copyData(*this, 0, 0, mSizeInBytes, true);
  182. // register whether we'll need to upload on unlock
  183. mStagingUploadNeeded = (options != GBL_READ_ONLY);
  184. return mpTempStagingBuffer->lock(offset, length, options);
  185. }
  186. }
  187. void D3D11HardwareBuffer::unlockImpl(void)
  188. {
  189. if (mUseTempStagingBuffer)
  190. {
  191. mUseTempStagingBuffer = false;
  192. // ok, we locked the staging buffer
  193. mpTempStagingBuffer->unlock();
  194. // copy data if needed
  195. // this is async but driver should keep reference
  196. if (mStagingUploadNeeded)
  197. copyData(*mpTempStagingBuffer, 0, 0, mSizeInBytes, true);
  198. // delete
  199. // not that efficient, but we should not be locking often
  200. if(mpTempStagingBuffer != nullptr)
  201. {
  202. cm_delete<PoolAlloc>(mpTempStagingBuffer);
  203. mpTempStagingBuffer = nullptr;
  204. }
  205. }
  206. else
  207. {
  208. // unmap
  209. mDevice.getImmediateContext()->Unmap(mD3DBuffer, 0);
  210. }
  211. }
  212. void D3D11HardwareBuffer::copyData(HardwareBuffer& srcBuffer, UINT32 srcOffset,
  213. UINT32 dstOffset, UINT32 length, bool discardWholeBuffer)
  214. {
  215. // If we're copying same-size buffers in their entirety...
  216. if (srcOffset == 0 && dstOffset == 0 &&
  217. length == mSizeInBytes && mSizeInBytes == srcBuffer.getSizeInBytes())
  218. {
  219. // schedule hardware buffer copy
  220. mDevice.getImmediateContext()->CopyResource(mD3DBuffer, static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer());
  221. if (mDevice.hasError())
  222. {
  223. String errorDescription = mDevice.getErrorDescription();
  224. CM_EXCEPT(RenderingAPIException, "Cannot copy D3D11 resource\nError Description:" + errorDescription);
  225. }
  226. }
  227. else
  228. {
  229. // copy subregion
  230. D3D11_BOX srcBox;
  231. srcBox.left = (UINT)srcOffset;
  232. srcBox.right = (UINT)srcOffset + length;
  233. srcBox.top = 0;
  234. srcBox.bottom = 1;
  235. srcBox.front = 0;
  236. srcBox.back = 1;
  237. mDevice.getImmediateContext()->CopySubresourceRegion(mD3DBuffer, 0, (UINT)dstOffset, 0, 0,
  238. static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);
  239. if (mDevice.hasError())
  240. {
  241. String errorDescription = mDevice.getErrorDescription();
  242. CM_EXCEPT(RenderingAPIException, "Cannot copy D3D11 subresource region\nError Description:" + errorDescription);
  243. }
  244. }
  245. }
  246. void D3D11HardwareBuffer::readData(UINT32 offset, UINT32 length, void* pDest)
  247. {
  248. // There is no functional interface in D3D, just do via manual
  249. // lock, copy & unlock
  250. void* pSrc = this->lock(offset, length, GBL_READ_ONLY);
  251. memcpy(pDest, pSrc, length);
  252. this->unlock();
  253. }
  254. void D3D11HardwareBuffer::writeData(UINT32 offset, UINT32 length,
  255. const void* pSource, bool discardWholeBuffer)
  256. {
  257. if(mDesc.Usage == D3D11_USAGE_DYNAMIC || mDesc.Usage == D3D11_USAGE_STAGING)
  258. {
  259. void* pDst = this->lock(offset, length,
  260. discardWholeBuffer ? GBL_WRITE_ONLY_DISCARD : GBL_WRITE_ONLY);
  261. memcpy(pDst, pSource, length);
  262. this->unlock();
  263. }
  264. else if(mDesc.Usage == D3D11_USAGE_DEFAULT)
  265. {
  266. mDevice.getImmediateContext()->UpdateSubresource(mD3DBuffer, 0, nullptr, pSource, offset, length);
  267. }
  268. else
  269. {
  270. CM_EXCEPT(RenderingAPIException, "Trying to write into a buffer with unsupported usage: " + toString(mDesc.Usage));
  271. }
  272. }
  273. }