#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 "Direct3D11VertexBufferObserver.h" #include "../Direct3D11RasterizerResources.h" #include "../Direct3D11Converters.h" #include "Nuclex/Graphics/Rasterization/Usage.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Ensures that a vertex buffer is unmapped again class VertexBufferUnmapper { /// Initializes a new vertex buffer unmapper /// /// Device context through which the buffer will be unmapped /// /// Buffer that will be unmapped public: VertexBufferUnmapper( const Nuclex::Graphics::Rasterization::ID3D11DeviceContextNPtr &deviceContext, const Nuclex::Graphics::Rasterization::ID3D11BufferPtr &buffer ) : deviceContext(deviceContext), buffer(buffer) {} /// Unmaps the vertex buffer public: ~VertexBufferUnmapper() { this->deviceContext->Unmap(this->buffer, 0); } private: VertexBufferUnmapper(const VertexBufferUnmapper &); private: VertexBufferUnmapper &operator =(const VertexBufferUnmapper &); /// 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 { // ------------------------------------------------------------------------------------------- // Direct3D11VertexBufferObserver::Direct3D11VertexBufferObserver( Direct3D11RasterizerResources &resources, GenericVertexBuffer *vertexBuffer ) : resources(resources), vertexBuffer(vertexBuffer), usedBytes(0) { createDirect3DVertexBuffer(); } // ------------------------------------------------------------------------------------------- // Direct3D11VertexBufferObserver::~Direct3D11VertexBufferObserver() { // Our smart pointers take care of everything } // ------------------------------------------------------------------------------------------- // void Direct3D11VertexBufferObserver::Destroying() { this->resources.VertexBufferHolder.Evict(this->vertexBuffer, this); // Do not do anything below Evict(). It will delete this instance! } // ------------------------------------------------------------------------------------------- // void Direct3D11VertexBufferObserver::Cleared() { this->usedBytes = static_cast(-1); } // ------------------------------------------------------------------------------------------- // void Direct3D11VertexBufferObserver::Changed(std::size_t offset, std::size_t count) { if(this->vertexBuffer->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->vertexBuffer->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->direct3DVertexBuffer, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not map Direct3D 11 vertex buffer into memory"); } VertexBufferUnmapper unmapScope(deviceContext, this->direct3DVertexBuffer); // Copy the contents of the agnostic constant buffer into the Direct3D buffer const std::uint8_t *source = this->vertexBuffer->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 this->direct3DVertexBuffer = nullptr; createDirect3DVertexBuffer(); // TODO: Flag this observer as 'replaced' so the rasterizer knows to reselect it } } // ------------------------------------------------------------------------------------------- // void Direct3D11VertexBufferObserver::createDirect3DVertexBuffer() { D3D11_BUFFER_DESC bufferDescription; bufferDescription.ByteWidth = static_cast(vertexBuffer->GetCapacityInBytes()); bufferDescription.Usage = direct3DUsageFromUsage(vertexBuffer->GetUsage()); bufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDescription.CPUAccessFlags = direct3DCpuAccessFromUsage(vertexBuffer->GetUsage()); bufferDescription.MiscFlags = 0; bufferDescription.StructureByteStride = static_cast(vertexBuffer->GetStride()); D3D11_SUBRESOURCE_DATA initialData; initialData.pSysMem = vertexBuffer->AccessMemory(); initialData.SysMemPitch = 0; initialData.SysMemSlicePitch = 0; HRESULT resultHandle = resources.GetDevice()->CreateBuffer( &bufferDescription, &initialData, &this->direct3DVertexBuffer ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 vertex buffer"); } } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization