mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-22 21:55:52 +00:00
Function to compute uniform blocks from DXIL unstructured control flow
New files dxil_controlflow.{cpp,h}
This commit is contained in:
@@ -0,0 +1,383 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "dxil_controlflow.h"
|
||||
|
||||
/*
|
||||
|
||||
Inputs are links of blocks : from -> to (can be forwards or backwards links)
|
||||
Output is a list of uniform control flow blocks which all possible flows go through (not diverged)
|
||||
|
||||
The algorithm is:
|
||||
|
||||
1. Setup
|
||||
* Compute all possible known blocks.
|
||||
* For each block generate a list of "to" blocks from the input links
|
||||
* Any block without links in the input are set to link to the end sentinel (PATH_END)
|
||||
|
||||
2. Generate all possible paths
|
||||
* Paths can terminate at the end block (PATH_END)
|
||||
* Paths can also terminate at a block before the end, if that block has had all its possible paths
|
||||
already computed
|
||||
|
||||
3. Find Uniform Blocks
|
||||
* Generate a list of path indexes for each block in the paths
|
||||
* uniform block is defined by any block which appears in all paths
|
||||
* all paths includes walking any paths linked at the end node of the path being walked
|
||||
*/
|
||||
|
||||
namespace DXIL
|
||||
{
|
||||
struct ControlFlow
|
||||
{
|
||||
public:
|
||||
ControlFlow(const rdcarray<rdcpair<uint32_t, uint32_t>> &links);
|
||||
void FindUniformBlocks(rdcarray<uint32_t> &uniformBlocks);
|
||||
|
||||
private:
|
||||
typedef rdcarray<uint32_t> BlockPath;
|
||||
|
||||
bool TraceBlockFlow(const uint32_t from, BlockPath &path);
|
||||
bool BlockInPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx);
|
||||
|
||||
const uint32_t PATH_END = ~0U;
|
||||
|
||||
std::set<uint32_t> m_Blocks;
|
||||
std::map<uint32_t, BlockPath> m_BlockLinks;
|
||||
|
||||
std::map<uint32_t, rdcarray<uint32_t>> m_BlockPathLinks;
|
||||
std::set<uint32_t> m_TracedBlocks;
|
||||
std::set<uint32_t> m_CheckedPaths;
|
||||
rdcarray<BlockPath> m_Paths;
|
||||
};
|
||||
|
||||
ControlFlow::ControlFlow(const rdcarray<rdcpair<uint32_t, uint32_t>> &links)
|
||||
{
|
||||
// 1. Setup
|
||||
// Compute all possible known blocks
|
||||
for(const auto &link : links)
|
||||
{
|
||||
uint32_t from = link.first;
|
||||
uint32_t to = link.second;
|
||||
m_Blocks.insert(from);
|
||||
m_Blocks.insert(to);
|
||||
}
|
||||
|
||||
// For each block a list of "to" blocks
|
||||
for(const auto &link : links)
|
||||
{
|
||||
uint32_t from = link.first;
|
||||
uint32_t to = link.second;
|
||||
m_BlockLinks[from].push_back(to);
|
||||
}
|
||||
|
||||
// Any block without links in the input are set to link to the end sentinel (PATH_END)
|
||||
for(uint32_t b : m_Blocks)
|
||||
{
|
||||
if(m_BlockLinks.count(b) == 0)
|
||||
m_BlockLinks[b].push_back(PATH_END);
|
||||
}
|
||||
|
||||
// 2. Generate all possible paths
|
||||
|
||||
// Paths can terminate at the end block (PATH_END)
|
||||
// Paths can also terminate at a block before the end, if that block has had all its possible paths already computed
|
||||
for(const auto &it : m_BlockLinks)
|
||||
{
|
||||
uint32_t from = it.first;
|
||||
if(m_TracedBlocks.count(from) != 0)
|
||||
continue;
|
||||
BlockPath path;
|
||||
path.push_back(from);
|
||||
TraceBlockFlow(from, path);
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlFlow::TraceBlockFlow(const uint32_t from, BlockPath &path)
|
||||
{
|
||||
if(m_BlockLinks.count(from) == 0)
|
||||
{
|
||||
m_Paths.push_back(path);
|
||||
return true;
|
||||
}
|
||||
if(m_TracedBlocks.count(from) != 0)
|
||||
{
|
||||
m_Paths.push_back(path);
|
||||
return true;
|
||||
}
|
||||
m_TracedBlocks.insert(from);
|
||||
rdcarray<uint32_t> newPath = path;
|
||||
const BlockPath &gotos = m_BlockLinks.at(from);
|
||||
for(uint32_t to : gotos)
|
||||
{
|
||||
newPath.push_back(to);
|
||||
if(TraceBlockFlow(to, newPath))
|
||||
newPath = path;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControlFlow::BlockInPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx)
|
||||
{
|
||||
const rdcarray<uint32_t> &path = m_Paths[pathIdx];
|
||||
if(path.size() == 0)
|
||||
return false;
|
||||
|
||||
// Check the current path
|
||||
for(uint32_t i = startIdx; i < path.size(); ++i)
|
||||
{
|
||||
if(block == path[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
m_CheckedPaths.insert(pathIdx);
|
||||
uint32_t endNode = path[path.size() - 1];
|
||||
if(endNode == PATH_END)
|
||||
return false;
|
||||
|
||||
// Check any paths linked to by the end node of the current path
|
||||
const rdcarray<uint32_t> &childPathsToCheck = m_BlockPathLinks[endNode];
|
||||
for(uint32_t childPathIdx : childPathsToCheck)
|
||||
{
|
||||
if(m_CheckedPaths.count(childPathIdx) != 0)
|
||||
continue;
|
||||
|
||||
m_CheckedPaths.insert(childPathIdx);
|
||||
const rdcarray<uint32_t> &childPath = m_Paths[childPathIdx];
|
||||
uint32_t childPartStartIdx = ~0U;
|
||||
for(childPartStartIdx = 0; childPartStartIdx < childPath.size(); ++childPartStartIdx)
|
||||
{
|
||||
if(childPath[childPartStartIdx] == endNode)
|
||||
break;
|
||||
}
|
||||
if(!BlockInPath(block, childPathIdx, childPartStartIdx))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControlFlow::FindUniformBlocks(rdcarray<uint32_t> &uniformBlocks)
|
||||
{
|
||||
// 3. Find Uniform Blocks
|
||||
for(uint32_t b : m_Blocks)
|
||||
m_BlockPathLinks[b].clear();
|
||||
|
||||
// Generate a list of path indexes for each block in the paths
|
||||
for(uint32_t pathIdx = 0; pathIdx < m_Paths.size(); ++pathIdx)
|
||||
{
|
||||
for(uint32_t block : m_Paths[pathIdx])
|
||||
{
|
||||
if(block == PATH_END)
|
||||
break;
|
||||
m_BlockPathLinks[block].push_back(pathIdx);
|
||||
}
|
||||
}
|
||||
|
||||
uniformBlocks.clear();
|
||||
|
||||
// A uniform block is defined by any block which appears in all paths
|
||||
// all paths includes walking any paths linked at the end node of the path being walked
|
||||
for(uint32_t block : m_Blocks)
|
||||
{
|
||||
bool uniform = true;
|
||||
for(uint32_t pathIdx = 0; pathIdx < m_Paths.size(); ++pathIdx)
|
||||
{
|
||||
m_CheckedPaths.clear();
|
||||
// BlockInPath will also check all paths linked to from the end node of the path
|
||||
if(!BlockInPath(block, pathIdx, 0))
|
||||
{
|
||||
uniform = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(uniform)
|
||||
uniformBlocks.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
void FindUniformBlocks(const rdcarray<BlockLink> &links, rdcarray<uint32_t> &uniformBlocks)
|
||||
{
|
||||
ControlFlow controlFlow(links);
|
||||
|
||||
controlFlow.FindUniformBlocks(uniformBlocks);
|
||||
}
|
||||
|
||||
}; // namespace DXIL
|
||||
|
||||
#if ENABLED(ENABLE_UNIT_TESTS)
|
||||
|
||||
#include <limits>
|
||||
#include "catch/catch.hpp"
|
||||
|
||||
using namespace DXIL;
|
||||
|
||||
TEST_CASE("DXIL Control Flow", "[dxil]")
|
||||
{
|
||||
SECTION("FindUniformBlocks")
|
||||
{
|
||||
rdcarray<uint32_t> outputs;
|
||||
{
|
||||
// Degenerate case
|
||||
rdcarray<BlockLink> inputs;
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(0 == outputs.count());
|
||||
}
|
||||
{
|
||||
// Only uniform flow is the start and end
|
||||
// 0 -> 1
|
||||
rdcarray<BlockLink> inputs;
|
||||
inputs.push_back({0, 1});
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(2 == outputs.count());
|
||||
REQUIRE(outputs.contains(0U));
|
||||
REQUIRE(outputs.contains(1U));
|
||||
}
|
||||
|
||||
{
|
||||
// Single uniform flow between start and end
|
||||
// 0 -> 1 -> 3
|
||||
// 0 -> 2 -> 3
|
||||
// 3 -> 4
|
||||
rdcarray<BlockLink> inputs;
|
||||
inputs.push_back({0, 1});
|
||||
inputs.push_back({1, 3});
|
||||
inputs.push_back({0, 2});
|
||||
inputs.push_back({2, 3});
|
||||
inputs.push_back({3, 4});
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(3 == outputs.count());
|
||||
REQUIRE(outputs.contains(0U));
|
||||
REQUIRE(outputs.contains(3U));
|
||||
REQUIRE(outputs.contains(4U));
|
||||
}
|
||||
|
||||
{
|
||||
// Finite Loop (3 -> 4 -> 5 -> 3)
|
||||
// 0 -> 1 -> 3
|
||||
// 0 -> 2 -> 3
|
||||
// 3 -> 4 -> 5
|
||||
// 4 -> 6
|
||||
// 5 -> 3
|
||||
// 5 -> 6
|
||||
rdcarray<BlockLink> inputs;
|
||||
inputs.push_back({0, 1});
|
||||
inputs.push_back({1, 3});
|
||||
inputs.push_back({0, 2});
|
||||
inputs.push_back({2, 3});
|
||||
inputs.push_back({3, 4});
|
||||
inputs.push_back({4, 5});
|
||||
inputs.push_back({4, 6});
|
||||
inputs.push_back({5, 3});
|
||||
inputs.push_back({5, 6});
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(4 == outputs.count());
|
||||
REQUIRE(outputs.contains(0U));
|
||||
REQUIRE(outputs.contains(3U));
|
||||
REQUIRE(outputs.contains(4U));
|
||||
REQUIRE(outputs.contains(6U));
|
||||
}
|
||||
|
||||
{
|
||||
// Infinite loop which never converges (3 -> 4 -> 3)
|
||||
// 0 -> 1 -> 3
|
||||
// 0 -> 2 -> 3
|
||||
// 3 -> 4
|
||||
// 4 -> 3
|
||||
// 1 -> 6
|
||||
// 2 -> 6
|
||||
rdcarray<BlockLink> inputs;
|
||||
inputs.push_back({0, 1});
|
||||
inputs.push_back({1, 3});
|
||||
inputs.push_back({0, 2});
|
||||
inputs.push_back({2, 3});
|
||||
inputs.push_back({3, 4});
|
||||
inputs.push_back({4, 3});
|
||||
inputs.push_back({1, 6});
|
||||
inputs.push_back({2, 6});
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(2 == outputs.count());
|
||||
REQUIRE(outputs.contains(0U));
|
||||
REQUIRE(outputs.contains(6U));
|
||||
}
|
||||
|
||||
{
|
||||
// Complex case with multiple loops
|
||||
rdcarray<BlockLink> inputs;
|
||||
inputs.push_back({0, 1});
|
||||
inputs.push_back({0, 2});
|
||||
inputs.push_back({2, 3});
|
||||
inputs.push_back({1, 3});
|
||||
inputs.push_back({3, 4});
|
||||
inputs.push_back({4, 5});
|
||||
inputs.push_back({3, 5});
|
||||
inputs.push_back({5, 6});
|
||||
inputs.push_back({9, 7});
|
||||
inputs.push_back({6, 7});
|
||||
inputs.push_back({7, 8});
|
||||
inputs.push_back({7, 9});
|
||||
inputs.push_back({9, 10});
|
||||
inputs.push_back({10, 11});
|
||||
inputs.push_back({8, 11});
|
||||
inputs.push_back({5, 11});
|
||||
inputs.push_back({11, 12});
|
||||
inputs.push_back({15, 13});
|
||||
inputs.push_back({12, 13});
|
||||
inputs.push_back({13, 14});
|
||||
inputs.push_back({13, 15});
|
||||
inputs.push_back({15, 16});
|
||||
inputs.push_back({16, 17});
|
||||
inputs.push_back({14, 17});
|
||||
inputs.push_back({11, 17});
|
||||
inputs.push_back({17, 18});
|
||||
inputs.push_back({18, 19});
|
||||
inputs.push_back({17, 19});
|
||||
inputs.push_back({19, 20});
|
||||
inputs.push_back({20, 21});
|
||||
inputs.push_back({19, 21});
|
||||
inputs.push_back({21, 22});
|
||||
inputs.push_back({22, 23});
|
||||
inputs.push_back({22, 24});
|
||||
inputs.push_back({24, 25});
|
||||
inputs.push_back({25, 26});
|
||||
inputs.push_back({24, 26});
|
||||
inputs.push_back({23, 26});
|
||||
inputs.push_back({21, 26});
|
||||
DXIL::FindUniformBlocks(inputs, outputs);
|
||||
REQUIRE(8 == outputs.count());
|
||||
REQUIRE(outputs.contains(0U));
|
||||
REQUIRE(outputs.contains(3U));
|
||||
REQUIRE(outputs.contains(5U));
|
||||
REQUIRE(outputs.contains(11U));
|
||||
REQUIRE(outputs.contains(17U));
|
||||
REQUIRE(outputs.contains(19U));
|
||||
REQUIRE(outputs.contains(21U));
|
||||
REQUIRE(outputs.contains(26U));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif // ENABLED(ENABLE_UNIT_TESTS)
|
||||
@@ -0,0 +1,33 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace DXIL
|
||||
{
|
||||
typedef rdcpair<uint32_t, uint32_t> BlockLink;
|
||||
|
||||
void FindUniformBlocks(const rdcarray<BlockLink> &links, rdcarray<uint32_t> &uniformBlocks);
|
||||
|
||||
}; // namespace DXIL
|
||||
@@ -104,6 +104,7 @@
|
||||
<ClCompile Include="dxil_bytecode.cpp" />
|
||||
<ClCompile Include="dxil_bytecode_editor.cpp" />
|
||||
<ClCompile Include="dxil_common.cpp" />
|
||||
<ClCompile Include="dxil_controlflow.cpp" />
|
||||
<ClCompile Include="dxil_debug.cpp" />
|
||||
<ClCompile Include="dxil_debuginfo.cpp" />
|
||||
<ClCompile Include="dxil_disassemble.cpp" />
|
||||
@@ -120,6 +121,7 @@
|
||||
<ClInclude Include="dxil_bytecode.h" />
|
||||
<ClInclude Include="dxil_bytecode_editor.h" />
|
||||
<ClInclude Include="dxil_common.h" />
|
||||
<ClInclude Include="dxil_controlflow.h" />
|
||||
<ClInclude Include="dxil_debug.h" />
|
||||
<ClInclude Include="dxil_debuginfo.h" />
|
||||
<ClInclude Include="dxil_metadata.h" />
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<ClCompile Include="dxil_debug.cpp" />
|
||||
<ClCompile Include="dxil_stringise.cpp" />
|
||||
<ClCompile Include="dxil_metadata.cpp" />
|
||||
<ClCompile Include="dxil_controlflow.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="precompiled.h">
|
||||
@@ -31,6 +32,7 @@
|
||||
<ClInclude Include="llvm_common.h" />
|
||||
<ClInclude Include="dxil_debug.h" />
|
||||
<ClInclude Include="dxil_metadata.h" />
|
||||
<ClInclude Include="dxil_controlflow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="PCH">
|
||||
|
||||
Reference in New Issue
Block a user