#include "stdafx.h" #include #include "CommandBuffer.h" CommandBuffer::CommandBuffer(bool argCommandOwer, bool argChildBufferOwner) : m_CommandOwer(argCommandOwer) , m_ChildBufferOwner(argChildBufferOwner) , m_OptimizedCommandBuffer(NULL) , m_ParentCommandBuffer(NULL) { } CommandBuffer::~CommandBuffer() { delete m_OptimizedCommandBuffer; } void CommandBuffer::Clear() { if (!m_CommandOwer) m_CommandList.clear(); else { while (!m_CommandList.empty()) { delete m_CommandList[0]; m_CommandList.erase(m_CommandList.begin()); } } if (!m_ChildBufferOwner) { while (!m_ChildCommandBuffers.empty()) this->RemoveChildCommandBuffer(*m_ChildCommandBuffers.back()); } else { while (!m_ChildCommandBuffers.empty()) { delete m_ChildCommandBuffers[0]; m_ChildCommandBuffers.erase(m_ChildCommandBuffers.begin()); } } while (!m_GatheredCommandChains.empty()) { delete m_GatheredCommandChains[0]; m_GatheredCommandChains.erase(m_GatheredCommandChains.begin()); } if (m_OptimizedCommandBuffer != NULL) m_OptimizedCommandBuffer->Clear(); } void CommandBuffer::AddCommand( Command& argCommand ) { m_CommandList.push_back(&argCommand); } void CommandBuffer::Perform( bool argUseOptimizedList ) { if (argUseOptimizedList) { if (m_OptimizedCommandBuffer != NULL) m_OptimizedCommandBuffer->Perform(false); } else { int subChainStartCommandIndex = -1; for (uint32 i = 0; i < m_CommandList.size();) { if (m_CommandList[i]->Flags & CommandFlags::SubChainStart) subChainStartCommandIndex = i; switch (m_CommandList[i]->Owner->ExecuteCommand(m_CommandList[i]->Type, *const_cast(this), i)) { case CommandExecuteResult::None: if (m_CommandList[i]->Flags & CommandFlags::SubChainEnd) subChainStartCommandIndex = -1; i++; break; case CommandExecuteResult::RetrySubChain: if (subChainStartCommandIndex == -1) std::wcout << std::red << "Failed to restart sub command chain." << std::white << std::endl; else i = subChainStartCommandIndex; break; case CommandExecuteResult::RetrySubChainSkipHead: if (subChainStartCommandIndex == -1) std::wcout << std::red << "Failed to restart headless sub command chain." << std::white << std::endl; else i = subChainStartCommandIndex + 1; break; } } } } void CommandBuffer::Optimize() { m_HashToCommandChain.clear(); if (m_OptimizedCommandBuffer == NULL) m_OptimizedCommandBuffer = new CommandBuffer(false, true); else m_OptimizedCommandBuffer->Clear(); std::vector tempChain; this->SearchDrawableCommandChain(this, tempChain); this->FlushGatheredCommandChains(); } void CommandBuffer::AddChildCommandBuffer( ICommandBuffer& argCommandBuffer ) { m_ChildCommandBuffers.push_back(&argCommandBuffer); argCommandBuffer.set_ParentBuffer(this); } void CommandBuffer::RemoveChildCommandBuffer( ICommandBuffer& argCommandBuffer ) { std::vector::iterator iter = std::find(m_ChildCommandBuffers.begin(), m_ChildCommandBuffers.end(), &argCommandBuffer); if (iter != m_ChildCommandBuffers.end()) { argCommandBuffer.set_ParentBuffer(NULL); m_ChildCommandBuffers.erase(iter); } } std::vector& CommandBuffer::get_BufferedCommands() { return m_CommandList; } const std::vector& CommandBuffer::get_ChildCommandBuffers() const { return m_ChildCommandBuffers; } ICommandBuffer* CommandBuffer::get_ParentBuffer() const { return m_ParentCommandBuffer; } void CommandBuffer::set_ParentBuffer( ICommandBuffer* ar_CommandBuffer_ ) { m_ParentCommandBuffer = ar_CommandBuffer_; } void CommandBuffer::SearchDrawableCommandChain(ICommandBuffer* ar_Buffer_, std::vector& argCurrentChain) { for (uint32 i = 0; i < ar_Buffer_->get_BufferedCommands().size();) { Command& currentCommand = *ar_Buffer_->get_BufferedCommands()[i]; UINT32 commandFlags = currentCommand.Flags; // A command is start AND end of a command chain, this indicates that a serious state change will come, // so we will need to flush the current command chains. if ((commandFlags & CommandFlags::FlushChain) != 0) { this->FlushGatheredCommandChains(); argCurrentChain.clear(); } // Old way commented out due problems with transformations before shaders // A command was found which does not belong to a build up current chain (due no chain is building herself up..) // so we drop it, we simply do not care about some run away /*if (argCurrentChain.empty() && (commandFlags & CommandFlags::StartChain) == 0 && (commandFlags & CommandFlags::EndChain) == 0) { ++i; continue; }*/ // New way is to search the list for a start chain command, then we put this command after the start chain command if (argCurrentChain.empty() && (commandFlags & CommandFlags::StartChain) == 0 && (commandFlags & CommandFlags::EndChain) == 0) { bool foundPlace = false; std::vector& commands = ar_Buffer_->get_BufferedCommands(); for (uint32 k = i + 1; k < commands.size(); ++k) if (commands[k]->Flags & CommandFlags::StartChain) { commands.insert(commands.begin() + k + 1, ¤tCommand); commands.erase(commands.begin() + i); foundPlace = true; break; } if (!foundPlace) ++i; continue; } // A new command chain should be initialized, clear our current temp chain and add this command into it. // All old commands remaining in the chain are already used or where completely useless (SetShader -> SetParameter -> NextPass). else if ((commandFlags & CommandFlags::StartChain) != 0 && !argCurrentChain.empty()) { argCurrentChain.clear(); argCurrentChain.push_back(¤tCommand); ++i; } // We should end our chain, now we need to optimize this chain and add this into the gathered chain list, // at the next flush command we will put this list into our optimized list, with some more optimizations. else if ((commandFlags & CommandFlags::EndChain) != 0) { // We have a filled chain (at least 1 operation following by this operation) if (!argCurrentChain.empty()) { CommandChainInternal* chain = new CommandChainInternal(); chain->m_Chain = argCurrentChain; chain->m_Chain.push_back(¤tCommand); std::sort(chain->m_Chain.begin(), chain->m_Chain.end(), CommandChainInternal::CommandSort()); //m_OptimizedCommandBuffer->get_BufferedCommands().insert(m_OptimizedCommandBuffer->get_BufferedCommands().end(), chain.begin(), chain.end()); m_GatheredCommandChains.push_back(chain); // Search for the SubChainEnd flagged command , this should be found right after the SubChainStart flagged commands // both commands will mark the beginning of our chain -> delete everything behind this command will ensure // that we will use a fresh new command chain, only filled with what we need (StartSubChain -> EndSubChain) for (uint32 k = 0; k < argCurrentChain.size(); ++k) { if (argCurrentChain[k]->Flags & CommandFlags::SubChainEnd) { argCurrentChain.erase(argCurrentChain.begin() + k + 1, argCurrentChain.end()); break; } } } // Our chain was empty, this means a solo command was found, put it into our optimized list and continue, // there is not much to do here. else { m_OptimizedCommandBuffer->get_BufferedCommands().push_back(¤tCommand); } ++i; } // No start and no end of the command chain, just put this command into our chain, // commands of this type are for example, SetVertexBuffer, SetIndexBuffer, SetTransformation,... else { argCurrentChain.push_back(¤tCommand); ++i; } } // We may have child buffers, continue our search there. for (uint32 i = 0; i < ar_Buffer_->get_ChildCommandBuffers().size(); ++i) { std::vector currentChain = argCurrentChain; this->SearchDrawableCommandChain(ar_Buffer_->get_ChildCommandBuffers()[i], argCurrentChain); argCurrentChain = currentChain; } } void CommandBuffer::FlushGatheredCommandChains() { std::vector flushedCommands; for (uint32 i = 0; i < m_GatheredCommandChains.size(); ++i) m_GatheredCommandChains[i]->UpdatePriority(); while (!m_GatheredCommandChains.empty()) { std::sort(m_GatheredCommandChains.begin(), m_GatheredCommandChains.end(), CommandChainInternal::CommandChainSort()); uint32 currentPriority = 0; std::vector checkables; for (uint32 i = 0; i < m_GatheredCommandChains.size();) { if (m_GatheredCommandChains[i]->m_Chain.empty()) m_GatheredCommandChains.erase(m_GatheredCommandChains.begin() + i); else if (checkables.empty() || currentPriority == m_GatheredCommandChains[i]->m_Priority) { currentPriority = m_GatheredCommandChains[i]->m_Priority; checkables.push_back(i); ++i; } else break; } if (checkables.size() == 1) { uint32 thisIndex = checkables.front(); flushedCommands.insert( flushedCommands.end(), m_GatheredCommandChains[thisIndex]->m_Chain.begin(), m_GatheredCommandChains[thisIndex]->m_Chain.end()); m_GatheredCommandChains.erase(m_GatheredCommandChains.begin() + thisIndex); if (m_GatheredCommandChains.empty()) break; } std::vector optimizedCommandChain; Command* lastCommand = NULL; while (checkables.size() > 1) { if (!checkables.empty() && checkables.front() != 0) break; for (uint32 i = 0; i < checkables.size();) { uint32 thisIndex = checkables[i]; CommandChainInternal* currentCommandChainInternal = m_GatheredCommandChains[thisIndex]; Command* thisCommand = *currentCommandChainInternal->m_Chain.begin(); if (i == 0 || (thisCommand->CommandInfo == lastCommand->CommandInfo && thisCommand->Owner == lastCommand->Owner)) { // we found a similarity (or the first element), put this into our optimized temp list lastCommand = thisCommand; if (i == 0 || (thisCommand->Flags & CommandFlags::Unique) != 0) optimizedCommandChain.push_back(thisCommand); currentCommandChainInternal->m_Chain.erase(currentCommandChainInternal->m_Chain.begin()); if (currentCommandChainInternal->m_Chain.empty()) { checkables.erase(checkables.begin() + i); break; } else ++i; } else { // do not check this list further, a difference was detected checkables.erase(checkables.begin() + i); } } // resort our m_GatheredCommandChains so that we could use the remaining content of a list properly, only do that if the checkable list has no direct preceeding elements ({0,2,3} instead {0,1,2}) if (!checkables.empty()) for (uint32 i = checkables.front() + 1; i < checkables.size(); ++i) if (checkables[i] != i) { std::swap(m_GatheredCommandChains[i], m_GatheredCommandChains[checkables[i]]); checkables[i] = i; } } flushedCommands.insert(flushedCommands.end(), optimizedCommandChain.begin(), optimizedCommandChain.end()); } // remove unnecessary SubChainEnd commands bool openPass = false; for (int i = flushedCommands.size() - 1; i >= 0; --i) { if (flushedCommands[i]->Flags == CommandFlags::SubChainEnd) { if (!openPass) openPass = true; else flushedCommands.erase(flushedCommands.begin() + i); } else if (flushedCommands[i]->Flags == CommandFlags::SubChainStart) openPass = false; } m_OptimizedCommandBuffer->get_BufferedCommands().insert(m_OptimizedCommandBuffer->get_BufferedCommands().end(), flushedCommands.begin(), flushedCommands.end()); }