#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 "Direct3D11IndexBufferObserver.h" #include "../Direct3D11RasterizerResources.h" #include "../Direct3D11Converters.h" #include "Nuclex/Graphics/Rasterization/Usage.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Ensures that an index buffer is unmapped again class IndexBufferUnmapper { /// Initializes a new index buffer unmapper /// /// Device context through which the buffer will be unmapped /// /// Buffer that will be unmapped public: IndexBufferUnmapper( const Nuclex::Graphics::Rasterization::ID3D11DeviceContextNPtr &deviceContext, const Nuclex::Graphics::Rasterization::ID3D11BufferPtr &buffer ) : deviceContext(deviceContext), buffer(buffer) {} /// Unmaps the vertex buffer public: ~IndexBufferUnmapper() { this->deviceContext->Unmap(this->buffer, 0); } private: IndexBufferUnmapper(const IndexBufferUnmapper &); private: IndexBufferUnmapper &operator =(const IndexBufferUnmapper &); /// 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 { // ------------------------------------------------------------------------------------------- // Direct3D11IndexBufferObserver::Direct3D11IndexBufferObserver( Direct3D11RasterizerResources &resources, GenericIndexBuffer *indexBuffer ) : resources(resources), indexBuffer(indexBuffer), usedBytes(0) { createDirect3DIndexBuffer(); } // ------------------------------------------------------------------------------------------- // Direct3D11IndexBufferObserver::~Direct3D11IndexBufferObserver() { // Our smart pointers will take care of everything } // ------------------------------------------------------------------------------------------- // void Direct3D11IndexBufferObserver::Destroying() { this->resources.IndexBufferHolder.Evict(this->indexBuffer, this); // Do not do anything below Evict(). It will delete this instance! } // ------------------------------------------------------------------------------------------- // void Direct3D11IndexBufferObserver::Cleared() { this->usedBytes = static_cast(-1); } // ------------------------------------------------------------------------------------------- // void Direct3D11IndexBufferObserver::Changed(std::size_t offset, std::size_t count) { if(this->indexBuffer->GetUsage() == Usage::CpuToGpuTransfer) { 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. If the user follows // the procedure correctly, the buffer is always locked with the ideal flags. If not, // we'll be less efficient, but it will still work. 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; } else if(offset >= this->usedBytes) { // Is the change not touching existing data? lockType = D3D11_MAP_WRITE_NO_OVERWRITE; } else { // Write touches existing data, full recreation with all contents needed. lockType = D3D11_MAP_WRITE_DISCARD; offset = 0; count = this->indexBuffer->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->direct3DIndexBuffer, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not map Direct3D 11 vertex buffer into memory"); } IndexBufferUnmapper unmapScope(deviceContext, this->direct3DIndexBuffer); // Copy the contents of the agnostic constant buffer into the Direct3D buffer const std::uint8_t *source = this->indexBuffer->AccessMemory() + offset; std::uint8_t *destination = reinterpret_cast(mappedResource.pData); std::copy_n(source, count, destination); } else { // Resource cannot be modified and needs to be recreated //std::lock_guard guard(this->resources.IndexBufferHolder.GetMutex()); this->direct3DIndexBuffer = nullptr; createDirect3DIndexBuffer(); // TODO: Flag this observer as 'replaced' so the rasterizer knows to reselect it } } // ------------------------------------------------------------------------------------------- // void Direct3D11IndexBufferObserver::createDirect3DIndexBuffer() { D3D11_BUFFER_DESC bufferDescription; bufferDescription.ByteWidth = static_cast(this->indexBuffer->GetCapacityInBytes()); bufferDescription.Usage = direct3DUsageFromUsage(this->indexBuffer->GetUsage()); bufferDescription.BindFlags = D3D11_BIND_INDEX_BUFFER; bufferDescription.CPUAccessFlags = direct3DCpuAccessFromUsage(this->indexBuffer->GetUsage()); bufferDescription.MiscFlags = 0; if(indexBuffer->Is32BitIndexBuffer()) { bufferDescription.StructureByteStride = 4; } else { bufferDescription.StructureByteStride = 2; } D3D11_SUBRESOURCE_DATA initialData; initialData.pSysMem = indexBuffer->AccessMemory(); initialData.SysMemPitch = 0; initialData.SysMemSlicePitch = 0; HRESULT resultHandle = resources.GetDevice()->CreateBuffer( &bufferDescription, &initialData, &this->direct3DIndexBuffer ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 index buffer"); } } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization