BsD3D11HardwareBuffer.cpp 10 KB

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