2
0

BsD3D11HardwareBuffer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsD3D11HardwareBuffer.h"
  4. #include "BsD3D11Mappings.h"
  5. #include "BsD3D11Device.h"
  6. #include "Error/BsException.h"
  7. #include "Debug/BsDebug.h"
  8. #include "BsD3D11CommandBuffer.h"
  9. namespace bs { namespace ct
  10. {
  11. D3D11HardwareBuffer::D3D11HardwareBuffer(BufferType btype, GpuBufferUsage usage, UINT32 elementCount, UINT32 elementSize,
  12. D3D11Device& device, bool useSystemMem, bool streamOut, bool randomGpuWrite, bool useCounter)
  13. : HardwareBuffer(elementCount * elementSize), mD3DBuffer(nullptr), mpTempStagingBuffer(nullptr)
  14. , mUseTempStagingBuffer(false), mBufferType(btype), mDevice(device), mElementCount(elementCount)
  15. , mElementSize(elementSize), mUsage(usage), mRandomGpuWrite(randomGpuWrite), mUseCounter(useCounter)
  16. {
  17. assert((!streamOut || btype == BT_VERTEX) && "Stream out flag is only supported on vertex buffers.");
  18. assert(!randomGpuWrite || (btype & BT_GROUP_GENERIC) != 0 && "randomGpuWrite flag can only be enabled with standard, append/consume, indirect argument, structured or raw buffers.");
  19. assert(btype != BT_APPENDCONSUME || randomGpuWrite && "Append/Consume buffer must be created with randomGpuWrite enabled.");
  20. assert(!useCounter || btype == BT_STRUCTURED && "Counter can only be used with a structured buffer.");
  21. assert(!useCounter || randomGpuWrite && "Counter can only be used with buffers that have randomGpuWrite enabled.");
  22. assert(!randomGpuWrite || !useSystemMem && "randomGpuWrite and useSystemMem cannot be used together.");
  23. assert(!(useSystemMem && streamOut) && "useSystemMem and streamOut cannot be used together.");
  24. mDesc.ByteWidth = getSize();
  25. mDesc.MiscFlags = 0;
  26. mDesc.StructureByteStride = 0;
  27. if (useSystemMem)
  28. {
  29. mDesc.Usage = D3D11_USAGE_STAGING;
  30. mDesc.BindFlags = 0;
  31. mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
  32. }
  33. else if(randomGpuWrite)
  34. {
  35. mDesc.Usage = D3D11_USAGE_DEFAULT;
  36. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
  37. mDesc.CPUAccessFlags = 0;
  38. switch (btype)
  39. {
  40. case BT_STRUCTURED:
  41. case BT_APPENDCONSUME:
  42. mDesc.StructureByteStride = elementSize;
  43. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
  44. break;
  45. case BT_RAW:
  46. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
  47. break;
  48. case BT_INDIRECTARGUMENT:
  49. mDesc.MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
  50. break;
  51. }
  52. }
  53. else
  54. {
  55. mDesc.Usage = D3D11Mappings::getUsage(usage);
  56. mDesc.CPUAccessFlags = D3D11Mappings::getAccessFlags(usage);
  57. switch(btype)
  58. {
  59. case BT_STANDARD:
  60. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  61. break;
  62. case BT_VERTEX:
  63. mDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  64. if (streamOut)
  65. mDesc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
  66. break;
  67. case BT_INDEX:
  68. mDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
  69. break;
  70. case BT_CONSTANT:
  71. mDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
  72. break;
  73. case BT_STRUCTURED:
  74. case BT_APPENDCONSUME:
  75. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  76. mDesc.StructureByteStride = elementSize;
  77. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
  78. break;
  79. case BT_RAW:
  80. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  81. mDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
  82. break;
  83. case BT_INDIRECTARGUMENT:
  84. mDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  85. mDesc.MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
  86. break;
  87. }
  88. }
  89. HRESULT hr = device.getD3D11Device()->CreateBuffer(&mDesc, nullptr, &mD3DBuffer);
  90. if (FAILED(hr) || mDevice.hasError())
  91. {
  92. String msg = device.getErrorDescription();
  93. BS_EXCEPT(RenderingAPIException, "Cannot create D3D11 buffer: " + msg);
  94. }
  95. }
  96. D3D11HardwareBuffer::~D3D11HardwareBuffer()
  97. {
  98. SAFE_RELEASE(mD3DBuffer);
  99. if(mpTempStagingBuffer != nullptr)
  100. bs_delete(mpTempStagingBuffer);
  101. }
  102. void* D3D11HardwareBuffer::map(UINT32 offset, UINT32 length, GpuLockOptions options, UINT32 deviceIdx, UINT32 queueIdx)
  103. {
  104. if (length > mSize)
  105. BS_EXCEPT(RenderingAPIException, "Provided length " + toString(length) + " larger than the buffer " + toString(mSize) + ".");
  106. // Use direct (and faster) Map/Unmap if dynamic write, or a staging read/write
  107. if((mDesc.Usage == D3D11_USAGE_DYNAMIC && options != GBL_READ_ONLY) || mDesc.Usage == D3D11_USAGE_STAGING)
  108. {
  109. D3D11_MAP mapType;
  110. switch(options)
  111. {
  112. case GBL_WRITE_ONLY_DISCARD:
  113. if (mUsage & GBU_DYNAMIC)
  114. {
  115. mapType = D3D11_MAP_WRITE_DISCARD;
  116. }
  117. else
  118. {
  119. // Map cannot be called with MAP_WRITE_DISCARD access, because the Resource was not created as
  120. // D3D11_USAGE_DYNAMIC. D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
  121. // or MAP_WRITE_NO_OVERWRITE with Map.
  122. mapType = D3D11_MAP_WRITE;
  123. }
  124. break;
  125. case GBL_WRITE_ONLY_NO_OVERWRITE:
  126. if(mBufferType == BT_INDEX || mBufferType == BT_VERTEX)
  127. mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
  128. else
  129. {
  130. // Note supported on anything but index/vertex buffers in DX11 (this restriction was dropped in 11.1)
  131. mapType = D3D11_MAP_WRITE;
  132. }
  133. break;
  134. case GBL_WRITE_ONLY:
  135. mapType = D3D11_MAP_WRITE;
  136. break;
  137. case GBL_READ_WRITE:
  138. if ((mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) != 0 &&
  139. (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) != 0)
  140. {
  141. mapType = D3D11_MAP_READ_WRITE;
  142. }
  143. else if(mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)
  144. {
  145. mapType = D3D11_MAP_WRITE;
  146. }
  147. else
  148. {
  149. mapType = D3D11_MAP_READ;
  150. }
  151. break;
  152. case GBL_READ_ONLY:
  153. mapType = D3D11_MAP_READ;
  154. break;
  155. }
  156. if(D3D11Mappings::isMappingRead(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) == 0)
  157. LOGERR("Trying to read a buffer, but buffer wasn't created with a read access flag.");
  158. if(D3D11Mappings::isMappingWrite(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) == 0)
  159. LOGERR("Trying to write to a buffer, but buffer wasn't created with a write access flag.");
  160. void * pRet = NULL;
  161. D3D11_MAPPED_SUBRESOURCE mappedSubResource;
  162. mappedSubResource.pData = NULL;
  163. mDevice.clearErrors();
  164. HRESULT hr = mDevice.getImmediateContext()->Map(mD3DBuffer, 0, mapType, 0, &mappedSubResource);
  165. if (FAILED(hr) || mDevice.hasError())
  166. {
  167. String msg = mDevice.getErrorDescription();
  168. BS_EXCEPT(RenderingAPIException, "Error calling Map: " + msg);
  169. }
  170. pRet = static_cast<void*>(static_cast<char*>(mappedSubResource.pData) + offset);
  171. return pRet;
  172. }
  173. else // Otherwise create a staging buffer to do all read/write operations on. Usually try to avoid this.
  174. {
  175. mUseTempStagingBuffer = true;
  176. if (!mpTempStagingBuffer)
  177. {
  178. // Create another buffer instance but use system memory
  179. mpTempStagingBuffer = bs_new<D3D11HardwareBuffer>(mBufferType, mUsage, 1, mSize, std::ref(mDevice), true);
  180. }
  181. // Schedule a copy to the staging
  182. if (options == GBL_READ_ONLY || options == GBL_READ_WRITE)
  183. mpTempStagingBuffer->copyData(*this, 0, 0, mSize, true);
  184. // Register whether we'll need to upload on unlock
  185. mStagingUploadNeeded = (options != GBL_READ_ONLY);
  186. return mpTempStagingBuffer->lock(offset, length, options);
  187. }
  188. }
  189. void D3D11HardwareBuffer::unmap()
  190. {
  191. if (mUseTempStagingBuffer)
  192. {
  193. mUseTempStagingBuffer = false;
  194. mpTempStagingBuffer->unlock();
  195. if (mStagingUploadNeeded)
  196. copyData(*mpTempStagingBuffer, 0, 0, mSize, true);
  197. if(mpTempStagingBuffer != nullptr)
  198. {
  199. bs_delete(mpTempStagingBuffer);
  200. mpTempStagingBuffer = nullptr;
  201. }
  202. }
  203. else
  204. {
  205. mDevice.getImmediateContext()->Unmap(mD3DBuffer, 0);
  206. }
  207. }
  208. void D3D11HardwareBuffer::copyData(HardwareBuffer& srcBuffer, UINT32 srcOffset,
  209. UINT32 dstOffset, UINT32 length, bool discardWholeBuffer, const SPtr<ct::CommandBuffer>& commandBuffer)
  210. {
  211. auto executeRef = [this](HardwareBuffer& srcBuffer, UINT32 srcOffset, UINT32 dstOffset, UINT32 length)
  212. {
  213. // If we're copying same-size buffers in their entirety
  214. if (srcOffset == 0 && dstOffset == 0 &&
  215. length == mSize && mSize == srcBuffer.getSize())
  216. {
  217. mDevice.getImmediateContext()->CopyResource(mD3DBuffer,
  218. static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer());
  219. if (mDevice.hasError())
  220. {
  221. String errorDescription = mDevice.getErrorDescription();
  222. BS_EXCEPT(RenderingAPIException, "Cannot copy D3D11 resource\nError Description:" + errorDescription);
  223. }
  224. }
  225. else
  226. {
  227. // Copy subregion
  228. D3D11_BOX srcBox;
  229. srcBox.left = (UINT)srcOffset;
  230. srcBox.right = (UINT)srcOffset + length;
  231. srcBox.top = 0;
  232. srcBox.bottom = 1;
  233. srcBox.front = 0;
  234. srcBox.back = 1;
  235. mDevice.getImmediateContext()->CopySubresourceRegion(mD3DBuffer, 0, (UINT)dstOffset, 0, 0,
  236. static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);
  237. if (mDevice.hasError())
  238. {
  239. String errorDescription = mDevice.getErrorDescription();
  240. BS_EXCEPT(RenderingAPIException, "Cannot copy D3D11 subresource region\nError Description:" +
  241. errorDescription);
  242. }
  243. }
  244. };
  245. if (commandBuffer == nullptr)
  246. executeRef(srcBuffer, srcOffset, dstOffset, length);
  247. else
  248. {
  249. auto execute = [&]() { executeRef(srcBuffer, srcOffset, dstOffset, length); };
  250. SPtr<D3D11CommandBuffer> cb = std::static_pointer_cast<D3D11CommandBuffer>(commandBuffer);
  251. cb->queueCommand(execute);
  252. }
  253. }
  254. void D3D11HardwareBuffer::readData(UINT32 offset, UINT32 length, void* dest, UINT32 deviceIdx, UINT32 queueIdx)
  255. {
  256. // There is no functional interface in D3D, just do via manual lock, copy & unlock
  257. void* pSrc = this->lock(offset, length, GBL_READ_ONLY);
  258. memcpy(dest, pSrc, length);
  259. this->unlock();
  260. }
  261. void D3D11HardwareBuffer::writeData(UINT32 offset, UINT32 length, const void* pSource, BufferWriteType writeFlags,
  262. UINT32 queueIdx)
  263. {
  264. if(mDesc.Usage == D3D11_USAGE_DYNAMIC || mDesc.Usage == D3D11_USAGE_STAGING)
  265. {
  266. GpuLockOptions lockOption = GBL_WRITE_ONLY;
  267. if(writeFlags == BWT_DISCARD)
  268. lockOption = GBL_WRITE_ONLY_DISCARD;
  269. else if(writeFlags == BTW_NO_OVERWRITE)
  270. lockOption = GBL_WRITE_ONLY_NO_OVERWRITE;
  271. void* pDst = this->lock(offset, length, lockOption);
  272. memcpy(pDst, pSource, length);
  273. this->unlock();
  274. }
  275. else if(mDesc.Usage == D3D11_USAGE_DEFAULT)
  276. {
  277. if (mBufferType == BT_CONSTANT)
  278. {
  279. assert(offset == 0);
  280. // Constant buffer cannot be updated partially using UpdateSubresource
  281. mDevice.getImmediateContext()->UpdateSubresource(mD3DBuffer, 0, nullptr, pSource, 0, 0);
  282. }
  283. else
  284. {
  285. D3D11_BOX dstBox;
  286. dstBox.left = (UINT)offset;
  287. dstBox.right = (UINT)offset + length;
  288. dstBox.top = 0;
  289. dstBox.bottom = 1;
  290. dstBox.front = 0;
  291. dstBox.back = 1;
  292. mDevice.getImmediateContext()->UpdateSubresource(mD3DBuffer, 0, &dstBox, pSource, 0, 0);
  293. }
  294. }
  295. else
  296. {
  297. LOGERR("Trying to write into a buffer with unsupported usage: " + toString(mDesc.Usage));
  298. }
  299. }
  300. }}