mirror of
https://gitlab.winehq.org/wine/vkd3d.git
synced 2025-01-28 13:05:02 -08:00
vkd3d-shader/ir: Keep loops contiguous in the topological sort.
This commit is contained in:
parent
cf9a023566
commit
b940d79aa8
Notes:
Alexandre Julliard
2024-03-08 23:47:19 +01:00
Approved-by: Conor McCarthy (@cmccarthy) Approved-by: Giovanni Mascellani (@giomasce) Approved-by: Henri Verbeet (@hverbeet) Approved-by: Alexandre Julliard (@julliard) Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/672
@ -3121,6 +3121,11 @@ static void vsir_block_list_sort(struct vsir_block_list *list)
|
|||||||
qsort(list->blocks, list->count, sizeof(*list->blocks), block_compare);
|
qsort(list->blocks, list->count, sizeof(*list->blocks), block_compare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool vsir_block_list_search(struct vsir_block_list *list, struct vsir_block *block)
|
||||||
|
{
|
||||||
|
return !!bsearch(&block, list->blocks, list->count, sizeof(*list->blocks), block_compare);
|
||||||
|
}
|
||||||
|
|
||||||
struct vsir_cfg
|
struct vsir_cfg
|
||||||
{
|
{
|
||||||
struct vkd3d_shader_message_context *message_context;
|
struct vkd3d_shader_message_context *message_context;
|
||||||
@ -3487,16 +3492,37 @@ static enum vkd3d_result vsir_cfg_compute_loops(struct vsir_cfg *cfg)
|
|||||||
struct vsir_cfg_node_sorter
|
struct vsir_cfg_node_sorter
|
||||||
{
|
{
|
||||||
struct vsir_cfg *cfg;
|
struct vsir_cfg *cfg;
|
||||||
|
struct vsir_cfg_node_sorter_stack_item
|
||||||
|
{
|
||||||
|
struct vsir_block_list *loop;
|
||||||
|
unsigned int seen_count;
|
||||||
|
} *stack;
|
||||||
|
size_t stack_count, stack_capacity;
|
||||||
struct vsir_block_list available_blocks;
|
struct vsir_block_list available_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum vkd3d_result vsir_cfg_node_sorter_make_node_available(struct vsir_cfg_node_sorter *sorter, struct vsir_block *block)
|
static enum vkd3d_result vsir_cfg_node_sorter_make_node_available(struct vsir_cfg_node_sorter *sorter, struct vsir_block *block)
|
||||||
{
|
{
|
||||||
|
struct vsir_block_list *loop = NULL;
|
||||||
|
struct vsir_cfg_node_sorter_stack_item *item;
|
||||||
enum vkd3d_result ret;
|
enum vkd3d_result ret;
|
||||||
|
|
||||||
|
if (sorter->cfg->loops_by_header[block->label - 1] != SIZE_MAX)
|
||||||
|
loop = &sorter->cfg->loops[sorter->cfg->loops_by_header[block->label - 1]];
|
||||||
|
|
||||||
if ((ret = vsir_block_list_add_checked(&sorter->available_blocks, block)) < 0)
|
if ((ret = vsir_block_list_add_checked(&sorter->available_blocks, block)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (!loop)
|
||||||
|
return VKD3D_OK;
|
||||||
|
|
||||||
|
if (!vkd3d_array_reserve((void **)&sorter->stack, &sorter->stack_capacity, sorter->stack_count + 1, sizeof(*sorter->stack)))
|
||||||
|
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
item = &sorter->stack[sorter->stack_count++];
|
||||||
|
item->loop = loop;
|
||||||
|
item->seen_count = 0;
|
||||||
|
|
||||||
return VKD3D_OK;
|
return VKD3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3507,10 +3533,23 @@ static enum vkd3d_result vsir_cfg_node_sorter_make_node_available(struct vsir_cf
|
|||||||
* available list of all the blocks whose incoming degree has reached
|
* available list of all the blocks whose incoming degree has reached
|
||||||
* zero. At each step we pick a block from the available list and
|
* zero. At each step we pick a block from the available list and
|
||||||
* strip it away from the graph, updating the incoming degrees and
|
* strip it away from the graph, updating the incoming degrees and
|
||||||
* available list. We prefer picking the most recently added block
|
* available list.
|
||||||
* (i.e., from the end of the available list) because it will keep
|
*
|
||||||
* together related blocks in the order, which allow us to generate
|
* In principle at each step we can pick whatever node we want from
|
||||||
* fewer control flow primitives. */
|
* the available list, and will get a topological sort
|
||||||
|
* anyway. However, we use these two criteria to give to the computed
|
||||||
|
* order additional properties:
|
||||||
|
*
|
||||||
|
* 1. we keep track of which loops we're into, and pick blocks
|
||||||
|
* belonging to the current innermost loop, so that loops are kept
|
||||||
|
* contiguous in the order; this can always be done when the input
|
||||||
|
* CFG is reducible;
|
||||||
|
*
|
||||||
|
* 2. subject to the requirement above, we always pick the most
|
||||||
|
* recently added block to the available list, because this tends
|
||||||
|
* to keep related blocks and require fewer control flow
|
||||||
|
* primitives.
|
||||||
|
*/
|
||||||
static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
|
static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
|
||||||
{
|
{
|
||||||
struct vsir_cfg_node_sorter sorter = { .cfg = cfg };
|
struct vsir_cfg_node_sorter sorter = { .cfg = cfg };
|
||||||
@ -3566,12 +3605,51 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
|
|||||||
|
|
||||||
while (sorter.available_blocks.count != 0)
|
while (sorter.available_blocks.count != 0)
|
||||||
{
|
{
|
||||||
struct vsir_block *block = sorter.available_blocks.blocks[sorter.available_blocks.count - 1];
|
struct vsir_cfg_node_sorter_stack_item *inner_stack_item = NULL;
|
||||||
|
struct vsir_block *block;
|
||||||
|
size_t new_seen_count;
|
||||||
|
|
||||||
vsir_block_list_remove_index(&sorter.available_blocks, sorter.available_blocks.count - 1);
|
if (sorter.stack_count != 0)
|
||||||
|
inner_stack_item = &sorter.stack[sorter.stack_count - 1];
|
||||||
|
|
||||||
|
for (i = sorter.available_blocks.count - 1; ; --i)
|
||||||
|
{
|
||||||
|
if (i == SIZE_MAX)
|
||||||
|
{
|
||||||
|
ERR("Couldn't find any viable next block, is the input CFG reducible?\n");
|
||||||
|
ret = VKD3D_ERROR_INVALID_SHADER;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
block = sorter.available_blocks.blocks[i];
|
||||||
|
|
||||||
|
if (!inner_stack_item || vsir_block_list_search(inner_stack_item->loop, block))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsir_block_list_remove_index(&sorter.available_blocks, i);
|
||||||
if ((ret = vsir_block_list_add_checked(&cfg->order, block)) < 0)
|
if ((ret = vsir_block_list_add_checked(&cfg->order, block)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
/* Close loops: since each loop is a strict subset of any
|
||||||
|
* outer loop, we just need to track how many blocks we've
|
||||||
|
* seen; when I close a loop I mark the same number of seen
|
||||||
|
* blocks for the next outer loop. */
|
||||||
|
new_seen_count = 1;
|
||||||
|
while (sorter.stack_count != 0)
|
||||||
|
{
|
||||||
|
inner_stack_item = &sorter.stack[sorter.stack_count - 1];
|
||||||
|
|
||||||
|
inner_stack_item->seen_count += new_seen_count;
|
||||||
|
|
||||||
|
assert(inner_stack_item->seen_count <= inner_stack_item->loop->count);
|
||||||
|
if (inner_stack_item->seen_count != inner_stack_item->loop->count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
new_seen_count = inner_stack_item->loop->count;
|
||||||
|
--sorter.stack_count;
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove (forward) edges and make new nodes available. */
|
/* Remove (forward) edges and make new nodes available. */
|
||||||
for (i = 0; i < block->successors.count; ++i)
|
for (i = 0; i < block->successors.count; ++i)
|
||||||
{
|
{
|
||||||
@ -3601,7 +3679,10 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(sorter.stack_count == 0);
|
||||||
|
|
||||||
vkd3d_free(in_degrees);
|
vkd3d_free(in_degrees);
|
||||||
|
vkd3d_free(sorter.stack);
|
||||||
vsir_block_list_cleanup(&sorter.available_blocks);
|
vsir_block_list_cleanup(&sorter.available_blocks);
|
||||||
|
|
||||||
if (TRACE_ON())
|
if (TRACE_ON())
|
||||||
@ -3619,6 +3700,7 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
vkd3d_free(in_degrees);
|
vkd3d_free(in_degrees);
|
||||||
|
vkd3d_free(sorter.stack);
|
||||||
vsir_block_list_cleanup(&sorter.available_blocks);
|
vsir_block_list_cleanup(&sorter.available_blocks);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user