BsD3D11HardwareBuffer.cpp 10.0 KB

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