#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_GRAPHICS_SOURCE 1 #include "Direct3D11ConstantBufferObserver.h" #include "../Direct3D11RasterizerResources.h" #include "../Direct3D11Converters.h" #include "Nuclex/Graphics/Rasterization/Usage.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Ensures that a constant buffer is unmapped again class ConstantBufferUnmapper { /// Initializes a new constant buffer unmapper /// /// Device context through which the buffer will be unmapped /// /// Buffer that will be unmapped public: ConstantBufferUnmapper( const Nuclex::Graphics::Rasterization::ID3D11DeviceContextNPtr &deviceContext, const Nuclex::Graphics::Rasterization::ID3D11BufferPtr &buffer ) : deviceContext(deviceContext), buffer(buffer) {} /// Unmaps the vertex buffer public: ~ConstantBufferUnmapper() { this->deviceContext->Unmap(this->buffer, 0); } private: ConstantBufferUnmapper(const ConstantBufferUnmapper &); private: ConstantBufferUnmapper &operator =(const ConstantBufferUnmapper &); /// Device context through which the buffer will be unmapped private: const Nuclex::Graphics::Rasterization::ID3D11DeviceContextNPtr &deviceContext; /// Buffer that will be unmapped private: const Nuclex::Graphics::Rasterization::ID3D11BufferPtr &buffer; }; // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // Direct3D11ConstantBufferObserver::Direct3D11ConstantBufferObserver( Direct3D11RasterizerResources &resources, GenericConstantBuffer *constantBuffer ) : resources(resources), constantBuffer(constantBuffer), usedBytes(0) { createDirect3DConstantBuffer(); #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) // Yep, we do this for each constant buffer. There aren't that many of them and this is // an exceptionally quick method since it's not accessing the GPU in any way. D3D11_FEATURE_DATA_D3D11_OPTIONS featureDataOptions; HRESULT resultHandle = this->resources.GetDevice()->CheckFeatureSupport( D3D11_FEATURE_D3D11_OPTIONS, &featureDataOptions, sizeof(featureDataOptions) ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not query Direct3D 11.1 device for feature data"); } // Windows' weird BOOL that is an int... this->noOverwriteLockSupported = ( featureDataOptions.MapNoOverwriteOnDynamicConstantBuffer == TRUE ); #endif } // ------------------------------------------------------------------------------------------- // Direct3D11ConstantBufferObserver::~Direct3D11ConstantBufferObserver() { // Our smart pointers will take care of everything } // ------------------------------------------------------------------------------------------- // void Direct3D11ConstantBufferObserver::Destroying() { this->resources.ConstantBufferHolder.Evict(this->constantBuffer, this); // Do not do anything below Evict(). It will delete this instance! } // ------------------------------------------------------------------------------------------- // void Direct3D11ConstantBufferObserver::Cleared() { this->usedBytes = static_cast(-1); } // ------------------------------------------------------------------------------------------- // void Direct3D11ConstantBufferObserver::Changed(std::size_t offset, std::size_t count) { const ID3D11DeviceContextNPtr &deviceContext = this->resources.GetDeviceContext(); // Try to use a NO_OVERWRITE lock if possible (if change doesn't touch any memory // that was used before), otherwise fall back to a DISCARD lock. For constant buffers // DISCARD locks are likely always the case. This is okay. D3D11_MAP lockType; // If the buffer was cleared since we last accessed it, a minimal recreate is okay. if(this->usedBytes == static_cast(-1)) { lockType = D3D11_MAP_WRITE_DISCARD; #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) } else if(this->noOverwriteLockSupported && (offset >= this->usedBytes)) { lockType = D3D11_MAP_WRITE_NO_OVERWRITE; #endif } else { // Write touches existing data, full recreation with all contents needed. lockType = D3D11_MAP_WRITE_DISCARD; offset = 0; count = this->constantBuffer->CountUsedBytes(); } // Update the internal counter used to keep track of which part of the buffer has // been exposed to the graphics card already. this->usedBytes = offset + count; // Map the buffer into system memory D3D11_MAPPED_SUBRESOURCE mappedResource; HRESULT resultHandle = deviceContext->Map( this->direct3DConstantBuffer, 0, lockType, 0, &mappedResource ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not map Direct3D 11 vertex buffer into memory"); } ConstantBufferUnmapper unmapScope(deviceContext, this->direct3DConstantBuffer); // Copy the contents of the agnostic constant buffer into the Direct3D buffer const std::uint8_t *source = this->constantBuffer->AccessMemory() + offset; std::uint8_t *destination = reinterpret_cast(mappedResource.pData); std::copy_n(source, count, destination); } // ------------------------------------------------------------------------------------------- // void Direct3D11ConstantBufferObserver::createDirect3DConstantBuffer() { D3D11_BUFFER_DESC bufferDescription; bufferDescription.ByteWidth = static_cast(this->constantBuffer->GetCapacityInBytes()); bufferDescription.Usage = D3D11_USAGE_DYNAMIC; bufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDescription.MiscFlags = 0; bufferDescription.StructureByteStride = 0; // Not necessary for constant buffers D3D11_SUBRESOURCE_DATA initialData; initialData.pSysMem = constantBuffer->AccessMemory(); initialData.SysMemPitch = 0; initialData.SysMemSlicePitch = 0; HRESULT resultHandle = resources.GetDevice()->CreateBuffer( &bufferDescription, &initialData, &this->direct3DConstantBuffer ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 constant buffer"); } this->usedBytes = this->constantBuffer->CountUsedBytes(); } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization