#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 "Direct3D11RasterizerState.h" #include "Direct3D11RasterizerResources.h" #include "Direct3D11InputLayoutManager.h" #include "Direct3D11Converters.h" #include "Nuclex/Graphics/Rasterization/Limits.h" #include "Observers/Direct3D11ConstantBufferObserver.h" #include "Observers/Direct3D11VertexBufferObserver.h" #include "Observers/Direct3D11IndexBufferObserver.h" #include "Observers/Direct3D11VertexShaderObserver.h" #include "Observers/Direct3D11PixelShaderObserver.h" #include "Observers/Direct3D11RenderTarget2Observer.h" #include "Observers/Direct3D11Texture2Observer.h" namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // Direct3D11RasterizerState::Direct3D11RasterizerState() : topology(Topology::TriangleList), topologyChanged(true), viewportChanged(true), isUsingDefaultViewport(true), backBufferRenderTargetChanged(true), highestChangedVertexBufferIndex(0), vertexBuffers(new std::shared_ptr[VertexBufferSlotCount]), pinnedVertexBuffers(new std::shared_ptr[VertexBufferSlotCount]), pinnedVertexShaderConstantBuffers( new std::shared_ptr[ConstantBufferSlotCount] ), pinnedVertexShaderTextures(new std::shared_ptr[TextureSlotCount]), pinnedPixelShaderConstantBuffers( new std::shared_ptr[ConstantBufferSlotCount] ), pinnedPixelShaderTextures(new std::shared_ptr[TextureSlotCount]) {} // ------------------------------------------------------------------------------------------- // Direct3D11RasterizerState::~Direct3D11RasterizerState() { for(std::size_t index = 0; index < ConstantBufferSlotCount; ++index) { GenericConstantBuffer *buffer = this->pinnedPixelShaderConstantBuffers[index].get(); if(buffer != nullptr) { this->resources->ConstantBufferHolder.Unpin(buffer); } } for(std::size_t index = 0; index < TextureSlotCount; ++index) { Texture *texture = this->pinnedPixelShaderTextures[index].get(); if(texture != nullptr) { this->resources->Texture2Holder.Unpin( static_cast(texture) // TODO: Hard-coded assumption on 2D textures ); } } for(std::size_t index = 0; index < ConstantBufferSlotCount; ++index) { GenericConstantBuffer *buffer = this->pinnedVertexShaderConstantBuffers[index].get(); if(buffer != nullptr) { this->resources->ConstantBufferHolder.Unpin(buffer); } } for(std::size_t index = 0; index < TextureSlotCount; ++index) { Texture *texture = this->pinnedVertexShaderTextures[index].get(); if(texture != nullptr) { this->resources->Texture2Holder.Unpin( static_cast(texture) // TODO: Hard-coded assumption on 2D textures ); } } GenericIndexBuffer *pinnedIndexBuffer = this->pinnedIndexBuffer.get(); if(pinnedIndexBuffer != nullptr) { this->resources->IndexBufferHolder.Unpin(pinnedIndexBuffer); } for(std::size_t index = 0; index < VertexBufferSlotCount; ++index) { GenericVertexBuffer *pinnedVertexBuffer = this->pinnedVertexBuffers[index].get(); if(pinnedVertexBuffer != nullptr) { this->resources->VertexBufferHolder.Unpin(pinnedVertexBuffer); } } VertexShader *pinnedVertexShader = this->pinnedVertexShader.get(); if(pinnedVertexShader != nullptr) { this->resources->VertexShaderHolder.Unpin(pinnedVertexShader); } PixelShader *pinnedPixelShader = this->pinnedPixelShader.get(); if(pinnedPixelShader != nullptr) { this->resources->PixelShaderHolder.Unpin(pinnedPixelShader); } if(this->pinnedRenderTarget != this->backBufferRenderTarget) { RenderTarget2 *pinnedRenderTarget = this->pinnedRenderTarget.get(); if(pinnedRenderTarget != nullptr) { this->resources->RenderTarget2Holder.Unpin(pinnedRenderTarget); } } // Kill the special swap chain render target auto observer = this->backBufferRenderTarget->GetObserver( &this->resources->RenderTarget2Holder ); this->backBufferRenderTarget->DetachObserver( &this->resources->RenderTarget2Holder ); delete observer; } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::CreateBackBufferRenderTarget( const IDXGISwapChainNPtr &swapChain ) { DXGI_SWAP_CHAIN_DESC swapChainDescription; HRESULT resultHandle = swapChain->GetDesc(&swapChainDescription); if(FAILED(resultHandle)) { throw std::runtime_error("Could not retrieve swap chain description"); } this->backBufferRenderTarget.reset( new RenderTarget2( swapChainDescription.BufferDesc.Width, swapChainDescription.BufferDesc.Height, PixelFormat::R8_G8_B8_A8_Unsigned, Usage::GpuChangeable ) ); this->viewport.Region.Right = this->backBufferRenderTarget->GetWidth(); this->viewport.Region.Bottom = this->backBufferRenderTarget->GetHeight(); std::unique_ptr observer( new Direct3D11RenderTarget2Observer( *this->resources, this->backBufferRenderTarget.get(), swapChain ) ); this->backBufferRenderTarget->AttachObserver( &this->resources->RenderTarget2Holder, observer.get() ); observer.release(); this->resources->RenderTarget2Holder.PrepareAndPin(this->backBufferRenderTarget.get()); this->renderTarget = this->backBufferRenderTarget; } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::ShutdownForSwapChainRecreation() { auto observer = this->backBufferRenderTarget->GetObserver( &this->resources->RenderTarget2Holder ); static_cast(observer)->ShutdownForSwapChainRecreation(); } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::RestoreAfterSwapChainRecreation( std::size_t width, std::size_t height ) { // TODO: Use special means to update size of backbuffer render target // Or alternative, allow all render targets to be resized auto observer = this->backBufferRenderTarget->GetObserver( &this->resources->RenderTarget2Holder ); static_cast(observer)->RestoreAfterSwapChainRecreation( width, height ); this->backBufferRenderTarget->Resize(width, height); this->backBufferRenderTargetChanged = true; if(this->isUsingDefaultViewport) { this->viewport.Region.Right = width; this->viewport.Region.Bottom = height; this->viewportChanged = true; } } // ------------------------------------------------------------------------------------------- // std::size_t Direct3D11RasterizerState::CountAvailableVertexStreams() const { return VertexBufferSlotCount; } // ------------------------------------------------------------------------------------------- // const std::shared_ptr &Direct3D11RasterizerState::GetVertexBuffer( std::size_t index ) const { if(index >= VertexBufferSlotCount) { throw std::runtime_error("Invalid vertex buffer index"); } return this->vertexBuffers[index]; } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::SetVertexBuffer( std::size_t index, const std::shared_ptr &vertexBuffer ) { if(index >= VertexBufferSlotCount) { throw std::runtime_error("Invalid vertex buffer index"); } this->vertexBuffers[index] = vertexBuffer; this->highestChangedVertexBufferIndex = index + 1; } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignTopologyIfChanged() { if(this->topologyChanged) { this->context->IASetPrimitiveTopology( direct3DTopologyFromTopology(this->topology) ); this->topologyChanged = false; } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignViewportIfChanged() { if(this->viewportChanged) { D3D11_VIEWPORT viewport; viewport.TopLeftX = static_cast(this->viewport.Region.Left); viewport.TopLeftY = static_cast(this->viewport.Region.Top); viewport.Width = static_cast(this->viewport.Region.GetWidth()); viewport.Height = static_cast(this->viewport.Region.GetHeight()); viewport.MinDepth = this->viewport.Near; viewport.MaxDepth = this->viewport.Far; // TODO: Aren't these the near and far clip planes? this->context->RSSetViewports(1, &viewport); this->viewportChanged = false; } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignIndexBufferIfChanged() { GenericIndexBuffer *pinnedIndexBuffer = this->pinnedIndexBuffer.get(); GenericIndexBuffer *selectedIndexBuffer = this->indexBuffer.get(); if(selectedIndexBuffer != pinnedIndexBuffer) { if(pinnedIndexBuffer != nullptr) { this->resources->IndexBufferHolder.Unpin(pinnedIndexBuffer); this->pinnedIndexBuffer.reset(); } if(selectedIndexBuffer == nullptr) { this->context->IASetIndexBuffer(nullptr, DXGI_FORMAT_UNKNOWN, 0); } else { Direct3D11IndexBufferObserver *observer = this->resources->IndexBufferHolder.PrepareAndPin(selectedIndexBuffer); this->pinnedIndexBuffer = this->indexBuffer; if(selectedIndexBuffer->Is32BitIndexBuffer()) { this->context->IASetIndexBuffer( observer->GetDirect3DIndexBuffer(), DXGI_FORMAT_R32_UINT, 0 ); } else { this->context->IASetIndexBuffer( observer->GetDirect3DIndexBuffer(), DXGI_FORMAT_R16_UINT, 0 ); } } } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignVertexBufferIfChanged() { if(this->highestChangedVertexBufferIndex == 0) { return; } ID3D11Buffer *vertexBuffers[VertexBufferSlotCount]; UINT strides[VertexBufferSlotCount]; UINT offsets[VertexBufferSlotCount]; for(std::size_t index = 0; index < this->highestChangedVertexBufferIndex; ++index) { offsets[index] = 0; GenericVertexBuffer *pinnedVertexBuffer = this->pinnedVertexBuffers[index].get(); GenericVertexBuffer *selectedVertexBuffer = this->vertexBuffers[index].get(); if(selectedVertexBuffer == pinnedVertexBuffer) { if(selectedVertexBuffer == nullptr) { vertexBuffers[index] = nullptr; strides[index] = 0; } else { Direct3D11VertexBufferObserver *observer = this->resources->VertexBufferHolder.Get( pinnedVertexBuffer ); vertexBuffers[index] = observer->GetDirect3DVertexBuffer(); strides[index] = static_cast(pinnedVertexBuffer->GetStride()); } } else { if(pinnedVertexBuffer != nullptr) { this->resources->VertexBufferHolder.Unpin(pinnedVertexBuffer); this->pinnedVertexBuffers[index].reset(); } if(selectedVertexBuffer == nullptr) { vertexBuffers[index] = nullptr; strides[index] = 0; } else { Direct3D11VertexBufferObserver *observer = this->resources->VertexBufferHolder.PrepareAndPin(selectedVertexBuffer); this->pinnedVertexBuffers[index] = this->vertexBuffers[index]; vertexBuffers[index] = observer->GetDirect3DVertexBuffer(); strides[index] = static_cast(selectedVertexBuffer->GetStride()); } } } this->context->IASetVertexBuffers( 0, static_cast(this->highestChangedVertexBufferIndex), vertexBuffers, strides, offsets ); this->highestChangedVertexBufferIndex = 0; } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignVertexShaderIfChanged() { VertexShader *pinnedVertexShader = this->pinnedVertexShader.get(); VertexShader *selectedVertexShader = this->vertexShader.get(); if(selectedVertexShader != pinnedVertexShader) { if(pinnedVertexShader != nullptr) { this->resources->VertexShaderHolder.Unpin(pinnedVertexShader); this->pinnedVertexShader.reset(); } if(selectedVertexShader == nullptr) { this->context->VSSetShader(nullptr, nullptr, 0); } else { Direct3D11VertexShaderObserver *observer = this->resources->VertexShaderHolder.PrepareAndPin(selectedVertexShader); this->pinnedVertexShader = this->vertexShader; // Get or create the input layout for this vertex shader. This is done by // comparing the vertex elements of the currently selected vertex buffers with // the inputs the vertex shader expects. ID3D11InputLayoutPtr inputLayout = this->inputLayouts->GetInputLayout( selectedVertexShader, this->vertexBuffers.get(), VertexBufferSlotCount ); this->context->IASetInputLayout(inputLayout); this->context->VSSetShader(observer->GetDirect3DVertexShader(), nullptr, 0); } } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignPixelShaderIfChanged() { PixelShader *pinnedPixelShader = this->pinnedPixelShader.get(); PixelShader *selectedPixelShader = this->pixelShader.get(); if(selectedPixelShader != pinnedPixelShader) { if(pinnedPixelShader != nullptr) { this->resources->PixelShaderHolder.Unpin(pinnedPixelShader); this->pinnedPixelShader.reset(); } if(selectedPixelShader == nullptr) { this->context->PSSetShader(nullptr, nullptr, 0); } else { Direct3D11PixelShaderObserver *observer = this->resources->PixelShaderHolder.PrepareAndPin(selectedPixelShader); this->pinnedPixelShader = this->pixelShader; this->context->PSSetShader(observer->GetDirect3DPixelShader(), nullptr, 0); } } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignRenderTargetIfChanged() { RenderTarget2 *pinnedRenderTarget = this->pinnedRenderTarget.get(); RenderTarget2 *selectedRenderTarget = this->renderTarget.get(); if((selectedRenderTarget != pinnedRenderTarget) || this->backBufferRenderTargetChanged) { if(pinnedRenderTarget != nullptr) { if(pinnedRenderTarget != this->backBufferRenderTarget.get()) { this->resources->RenderTarget2Holder.Unpin(pinnedRenderTarget); } this->pinnedRenderTarget.reset(); } if(selectedRenderTarget == nullptr) { this->context->OMSetRenderTargets(0, nullptr, nullptr); } else { Direct3D11RenderTarget2Observer *observer = this->resources->RenderTarget2Holder.PrepareAndPin(selectedRenderTarget); this->pinnedRenderTarget = this->renderTarget; ID3D11RenderTargetView *renderTargetView = observer->GetDirect3DRenderTargetView(); ID3D11DepthStencilView *depthStencilView = observer->GetDirect3DDepthStencilView(); this->context->OMSetRenderTargets(1, &renderTargetView, depthStencilView); } this->backBufferRenderTargetChanged = false; } } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignVertexShaderConstantBufferIfChanged() { VertexShader *vertexShader = this->vertexShader.get(); if(vertexShader == nullptr) { return; } ID3D11Buffer *constantBuffers[ConstantBufferSlotCount]; for( std::size_t index = 0; index < ConstantBufferSlotCount; ++index ) { GenericConstantBuffer *pinned = this->pinnedVertexShaderConstantBuffers[index].get(); std::shared_ptr selected(vertexShader->GetConstantBuffer(index)); if(selected.get() == pinned) { if(pinned == nullptr) { constantBuffers[index] = nullptr; } else { Direct3D11ConstantBufferObserver *observer = this->resources->ConstantBufferHolder.Get( pinned ); constantBuffers[index] = observer->GetDirect3DConstantBuffer(); } } else { if(pinned != nullptr) { this->resources->ConstantBufferHolder.Unpin(pinned); this->pinnedVertexShaderConstantBuffers[index].reset(); } if(selected.get() == nullptr) { constantBuffers[index] = nullptr; } else { Direct3D11ConstantBufferObserver *observer = this->resources->ConstantBufferHolder.PrepareAndPin(selected.get()); this->pinnedVertexShaderConstantBuffers[index] = selected; constantBuffers[index] = observer->GetDirect3DConstantBuffer(); } } } this->context->VSSetConstantBuffers( 0, static_cast(ConstantBufferSlotCount), constantBuffers ); } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignPixelShaderConstantBufferIfChanged() { PixelShader *pixelShader = this->pixelShader.get(); if(pixelShader == nullptr) { return; } ID3D11Buffer *constantBuffers[ConstantBufferSlotCount]; for( std::size_t index = 0; index < ConstantBufferSlotCount; ++index ) { GenericConstantBuffer *pinned = this->pinnedPixelShaderConstantBuffers[index].get(); std::shared_ptr selected(pixelShader->GetConstantBuffer(index)); if(selected.get() == pinned) { if(pinned == nullptr) { constantBuffers[index] = nullptr; } else { Direct3D11ConstantBufferObserver *observer = this->resources->ConstantBufferHolder.Get( pinned ); constantBuffers[index] = observer->GetDirect3DConstantBuffer(); } } else { if(pinned != nullptr) { this->resources->ConstantBufferHolder.Unpin(pinned); this->pinnedPixelShaderConstantBuffers[index].reset(); } if(selected.get() == nullptr) { constantBuffers[index] = nullptr; } else { Direct3D11ConstantBufferObserver *observer = this->resources->ConstantBufferHolder.PrepareAndPin(selected.get()); this->pinnedPixelShaderConstantBuffers[index] = selected; constantBuffers[index] = observer->GetDirect3DConstantBuffer(); } } } this->context->PSSetConstantBuffers( 0, static_cast(ConstantBufferSlotCount), constantBuffers ); } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignVertexShaderTextureIfChanged() { VertexShader *vertexShader = this->vertexShader.get(); if(vertexShader == nullptr) { return; } ID3D11ShaderResourceView *textures[TextureSlotCount]; for(std::size_t index = 0; index < TextureSlotCount; ++index) { Texture *pinned = this->pinnedVertexShaderTextures[index].get(); std::shared_ptr selected(vertexShader->GetTexture(index)); if(selected.get() == pinned) { if(pinned == nullptr) { textures[index] = nullptr; } else { // TODO: Create a TextureObserver base class for all dimensionalities Direct3D11TextureObserver *observer = this->resources->Texture2Holder.Get( static_cast(pinned) // TODO: Hard-coded assumption on 2D textures ); textures[index] = observer->GetResourceView(); } } else { if(pinned != nullptr) { this->resources->Texture2Holder.Unpin( static_cast(pinned) // TODO: Hard-coded assumption on 2D textures ); this->pinnedVertexShaderTextures[index].reset(); } if(selected.get() == nullptr) { textures[index] = nullptr; } else { Direct3D11TextureObserver *observer = this->resources->Texture2Holder.PrepareAndPin( static_cast(selected.get()) // TODO: Hard-coded assumption on 2D textures ); this->pinnedVertexShaderTextures[index] = selected; textures[index] = observer->GetResourceView(); } } } this->context->VSSetShaderResources(0, static_cast(TextureSlotCount), textures); } // ------------------------------------------------------------------------------------------- // void Direct3D11RasterizerState::assignPixelShaderTextureIfChanged() { PixelShader *pixelShader = this->pixelShader.get(); if(pixelShader == nullptr) { return; } ID3D11ShaderResourceView *textures[TextureSlotCount]; for(std::size_t index = 0; index < TextureSlotCount; ++index) { Texture *pinned = this->pinnedPixelShaderTextures[index].get(); std::shared_ptr selected(pixelShader->GetTexture(index)); if(selected.get() == pinned) { if(pinned == nullptr) { textures[index] = nullptr; } else { Direct3D11TextureObserver *observer = this->resources->Texture2Holder.Get( static_cast(pinned) // TODO: Hard-coded assumption on 2D textures ); textures[index] = observer->GetResourceView(); } } else { if(pinned != nullptr) { this->resources->Texture2Holder.Unpin( static_cast(pinned) // TODO: Hard-coded assumption on 2D textures ); this->pinnedPixelShaderTextures[index].reset(); } if(selected.get() == nullptr) { textures[index] = nullptr; } else { Direct3D11Texture2Observer *observer = this->resources->Texture2Holder.PrepareAndPin( static_cast(selected.get()) // TODO: Hard-coded assumption on 2D textures ); this->pinnedPixelShaderTextures[index] = selected; textures[index] = observer->GetResourceView(); } } } this->context->PSSetShaderResources(0, static_cast(TextureSlotCount), textures); } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization