CmD3D11HardwareBuffer.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #include "CmD3D11HardwareBuffer.h"
  2. #include "CmD3D11Mappings.h"
  3. #include "CmD3D11Device.h"
  4. #include "CmException.h"
  5. #include "CmDebug.h"
  6. namespace CamelotEngine
  7. {
  8. D3D11HardwareBuffer::D3D11HardwareBuffer(BufferType btype, UINT32 sizeBytes, GpuBufferUsage usage,
  9. D3D11Device& device, bool useSystemMemory, bool streamOut)
  10. : HardwareBuffer(usage, useSystemMemory),
  11. mD3DBuffer(0),
  12. mpTempStagingBuffer(0),
  13. mUseTempStagingBuffer(false),
  14. mBufferType(btype),
  15. mDevice(device)
  16. {
  17. if(streamOut && btype != VERTEX_BUFFER)
  18. CM_EXCEPT(InvalidParametersException, "Stream out flag is only supported on vertex buffers");
  19. mSizeInBytes = sizeBytes;
  20. mDesc.ByteWidth = static_cast<UINT>(sizeBytes);
  21. mDesc.MiscFlags = 0;
  22. if (useSystemMemory)
  23. {
  24. mDesc.Usage = D3D11_USAGE_STAGING;
  25. mDesc.BindFlags = 0;
  26. mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ ;// A D3D11_USAGE_STAGING Resource must have at least one CPUAccessFlag bit set.
  27. }
  28. else
  29. {
  30. mDesc.Usage = D3D11Mappings::_getUsage(mUsage);
  31. mDesc.BindFlags = btype == VERTEX_BUFFER ? D3D11_BIND_VERTEX_BUFFER :
  32. btype == INDEX_BUFFER ? D3D11_BIND_INDEX_BUFFER :
  33. D3D11_BIND_CONSTANT_BUFFER;
  34. mDesc.CPUAccessFlags = D3D11Mappings::_getAccessFlags(mUsage);
  35. }
  36. // Check of stream out flag
  37. if (streamOut)
  38. {
  39. mDesc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
  40. }
  41. // TODO: we can explicitly initialise the buffer contents here if we like
  42. // not doing this since OGRE doesn't support this model yet
  43. HRESULT hr = device.getD3D11Device()->CreateBuffer( &mDesc, nullptr, &mD3DBuffer );
  44. if (FAILED(hr) || mDevice.hasError())
  45. {
  46. String msg = device.getErrorDescription();
  47. CM_EXCEPT(RenderingAPIException, "Cannot create D3D11 buffer: " + msg);
  48. }
  49. }
  50. D3D11HardwareBuffer::~D3D11HardwareBuffer()
  51. {
  52. SAFE_RELEASE(mD3DBuffer);
  53. SAFE_DELETE(mpTempStagingBuffer); // should never be nonzero unless destroyed while locked
  54. }
  55. void* D3D11HardwareBuffer::lockImpl(UINT32 offset,
  56. UINT32 length, GpuLockOptions options)
  57. {
  58. if (length > mSizeInBytes)
  59. CM_EXCEPT(RenderingAPIException, "Provided length " + toString(length) + " larger than the buffer " + toString(mSizeInBytes) + ".");
  60. // Use direct (and faster) Map/Unmap if dynamic write, or a staging read/write
  61. if((mDesc.Usage == D3D11_USAGE_DYNAMIC && options != GBL_READ_ONLY) || mDesc.Usage == D3D11_USAGE_STAGING)
  62. {
  63. D3D11_MAP mapType;
  64. switch(options)
  65. {
  66. case GBL_WRITE_ONLY_DISCARD:
  67. if (mUsage & GBU_DYNAMIC)
  68. {
  69. // Map cannot be called with MAP_WRITE access,
  70. // because the Resource was created as D3D11_USAGE_DYNAMIC.
  71. // D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
  72. // or MAP_WRITE_NO_OVERWRITE with Map.
  73. mapType = D3D11_MAP_WRITE_DISCARD;
  74. }
  75. else
  76. {
  77. // Map cannot be called with MAP_WRITE_DISCARD access,
  78. // because the Resource was not created as D3D11_USAGE_DYNAMIC.
  79. // D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
  80. // or MAP_WRITE_NO_OVERWRITE with Map.
  81. mapType = D3D11_MAP_WRITE;
  82. LOGWRN("DISCARD lock is only available on dynamic buffers. Falling back to normal write.");
  83. }
  84. break;
  85. case GBL_WRITE_ONLY_NO_OVERWRITE:
  86. if(mBufferType == INDEX_BUFFER || mBufferType == VERTEX_BUFFER)
  87. mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
  88. else
  89. {
  90. mapType = D3D11_MAP_WRITE;
  91. LOGWRN("NO_OVERWRITE lock is not available on this (" + toString(mBufferType) + ") buffer type. Falling back to normal write.");
  92. }
  93. break;
  94. case GBL_READ_WRITE:
  95. if ((mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) != 0 &&
  96. (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) != 0)
  97. {
  98. mapType = D3D11_MAP_READ_WRITE;
  99. }
  100. else if(mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)
  101. {
  102. mapType = D3D11_MAP_WRITE;
  103. }
  104. else
  105. {
  106. mapType = D3D11_MAP_READ;
  107. }
  108. break;
  109. case GBL_READ_ONLY:
  110. mapType = D3D11_MAP_READ;
  111. break;
  112. }
  113. if(D3D11Mappings::isMappingRead(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) == 0)
  114. CM_EXCEPT(RenderingAPIException, "Trying to read a buffer, but buffer wasn't created with a read access flag.");
  115. if(D3D11Mappings::isMappingWrite(mapType) && (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) == 0)
  116. CM_EXCEPT(RenderingAPIException, "Trying to write to a buffer, but buffer wasn't created with a write access flag.");
  117. void * pRet = NULL;
  118. D3D11_MAPPED_SUBRESOURCE mappedSubResource;
  119. mappedSubResource.pData = NULL;
  120. mDevice.clearErrors();
  121. HRESULT hr = mDevice.getImmediateContext()->Map(mD3DBuffer, 0, mapType, 0, &mappedSubResource);
  122. if (FAILED(hr) || mDevice.hasError())
  123. {
  124. String msg = mDevice.getErrorDescription();
  125. CM_EXCEPT(RenderingAPIException, "Error calling Map: " + msg);
  126. }
  127. pRet = static_cast<void*>(static_cast<char*>(mappedSubResource.pData) + offset);
  128. return pRet;
  129. }
  130. else // Otherwise create a staging buffer to do all read/write operations on. Usually try to avoid this.
  131. {
  132. mUseTempStagingBuffer = true;
  133. if (!mpTempStagingBuffer)
  134. {
  135. // create another buffer instance but use system memory
  136. mpTempStagingBuffer = new D3D11HardwareBuffer(mBufferType,
  137. mSizeInBytes, mUsage, mDevice, true, false);
  138. }
  139. // schedule a copy to the staging
  140. if (options != GBL_WRITE_ONLY_DISCARD)
  141. mpTempStagingBuffer->copyData(*this, 0, 0, mSizeInBytes, true);
  142. // register whether we'll need to upload on unlock
  143. mStagingUploadNeeded = (options != GBL_READ_ONLY);
  144. return mpTempStagingBuffer->lock(offset, length, options);
  145. }
  146. }
  147. void D3D11HardwareBuffer::unlockImpl(void)
  148. {
  149. if (mUseTempStagingBuffer)
  150. {
  151. mUseTempStagingBuffer = false;
  152. // ok, we locked the staging buffer
  153. mpTempStagingBuffer->unlock();
  154. // copy data if needed
  155. // this is async but driver should keep reference
  156. if (mStagingUploadNeeded)
  157. copyData(*mpTempStagingBuffer, 0, 0, mSizeInBytes, true);
  158. // delete
  159. // not that efficient, but we should not be locking often
  160. SAFE_DELETE(mpTempStagingBuffer);
  161. }
  162. else
  163. {
  164. // unmap
  165. mDevice.getImmediateContext()->Unmap(mD3DBuffer, 0);
  166. }
  167. }
  168. void D3D11HardwareBuffer::copyData(HardwareBuffer& srcBuffer, UINT32 srcOffset,
  169. UINT32 dstOffset, UINT32 length, bool discardWholeBuffer)
  170. {
  171. // If we're copying same-size buffers in their entirety...
  172. if (srcOffset == 0 && dstOffset == 0 &&
  173. length == mSizeInBytes && mSizeInBytes == srcBuffer.getSizeInBytes())
  174. {
  175. // schedule hardware buffer copy
  176. mDevice.getImmediateContext()->CopyResource(mD3DBuffer, static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer());
  177. if (mDevice.hasError())
  178. {
  179. String errorDescription = mDevice.getErrorDescription();
  180. CM_EXCEPT(RenderingAPIException, "Cannot copy D3D11 resource\nError Description:" + errorDescription);
  181. }
  182. }
  183. else
  184. {
  185. // copy subregion
  186. D3D11_BOX srcBox;
  187. srcBox.left = (UINT)srcOffset;
  188. srcBox.right = (UINT)srcOffset + length;
  189. srcBox.top = 0;
  190. srcBox.bottom = 1;
  191. srcBox.front = 0;
  192. srcBox.back = 1;
  193. mDevice.getImmediateContext()->CopySubresourceRegion(mD3DBuffer, 0, (UINT)dstOffset, 0, 0,
  194. static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);
  195. if (mDevice.hasError())
  196. {
  197. String errorDescription = mDevice.getErrorDescription();
  198. CM_EXCEPT(RenderingAPIException, "Cannot copy D3D11 subresource region\nError Description:" + errorDescription);
  199. }
  200. }
  201. }
  202. void D3D11HardwareBuffer::readData(UINT32 offset, UINT32 length, void* pDest)
  203. {
  204. // There is no functional interface in D3D, just do via manual
  205. // lock, copy & unlock
  206. void* pSrc = this->lock(offset, length, GBL_READ_ONLY);
  207. memcpy(pDest, pSrc, length);
  208. this->unlock();
  209. }
  210. void D3D11HardwareBuffer::writeData(UINT32 offset, UINT32 length,
  211. const void* pSource, bool discardWholeBuffer)
  212. {
  213. if(mDesc.Usage == D3D11_USAGE_DYNAMIC || mDesc.Usage == D3D11_USAGE_STAGING)
  214. {
  215. void* pDst = this->lock(offset, length,
  216. discardWholeBuffer ? GBL_WRITE_ONLY_DISCARD : GBL_READ_WRITE);
  217. memcpy(pDst, pSource, length);
  218. this->unlock();
  219. }
  220. else if(mDesc.Usage == D3D11_USAGE_DEFAULT)
  221. {
  222. mDevice.getImmediateContext()->UpdateSubresource(mD3DBuffer, 0, nullptr, pSource, offset, length);
  223. }
  224. else
  225. {
  226. CM_EXCEPT(RenderingAPIException, "Trying to write into a buffer with unsupported usage: " + toString(mDesc.Usage));
  227. }
  228. }
  229. }