BsD3D11HardwareBuffer.cpp 9.9 KB

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