Hello guys, thank you for your interest in this topic.
To begin with, i must say that if any of you don't understand the problem then it is very easy to reproduce. Simply grab the most basic example in the D3D12 SDK, the "HelloWindow" example and move line 162 (a call to the function to populate the command list) to line 151 (at the end of the function to load assets). What you're doing here is recording the command list at initialization once and then executing it at every frame. If you compile and execute the program it is going to run, clean the first frame correctly but then in the next frame the command list will reference the previous backbuffer and it will crash. I've attached to this reply a ZIP file with the C++ source file and the compiled program, try it.
Now i'll answer some fragments of this topic:
I am not really sure why your concerned with resetting in the first place. I highly doubt you're getting much performance gain
I'm not resetting my lists because i have too many commands and by doing the prerecording model i'm saving CPU time. This may not gain performance in the GPU side as you say but will compensate when doing heavy work in the CPU.
Yeah bundles is what you want.
Bundles have no effect different to direct command lists regarding the backbuffer index issue. The problem persists even with bundles.
Alternatively, you can use an intermediate and issue a final copy to the back buffer (which is essentially how D3D11 handled swapchains with one buffer).
This is a good idea. Create a "ID3D12Resource" and a handle to it, use it as a render target for all my commands and then copy the whole region to the current backbuffer. It sounds great, sure it will require memory for the frame buffer but its just a routine worth the sacrifice (and not that much memory anyway, depends on the resolution, 4k omg). Entire frame buffer copies are expensive but again are dependent on resolution, i wonder how the performance will be affected and how it will be scaled based on resolution. I'll have to elaborate more on the subject as i made an implementation for it. Thanks for the advice, i'll have to try this.
You can triple buffer your command lists with back buffer references, and have one per buffer
I also thought about creating a command list for each backbuffer but that would be 100+ commands per list for each buffer. This would completely solve the execution problem and it would allow me to write directly to the backbuffer but it would introduce memory usage by a lot (seriously, i'm precaching too many commands across many lists). To counter the memory usage i was thinking about branching my command lists using linked lists. The structure used for the linked list can specify if my command lists are "normal" type or a "backbuffer reference" type. The normal types would only utilize one command list and the other type would use FRAME_COUNT command lists (which can be optimized by creating them as bundles). This way when composing the final array of command lists that are going to be submitted to the command queue i can create an infinite branch of mixed normal and backbuffer reference types. This is my concept:
struct CommandLink
{
uint8_t type = 0; // 0 = normal (use m_command_list[0]), 1 = backbuffer reference (use m_command_list[0 to FRAME_COUNT - 1]).
ComPtr<ID3D12GraphicsCommandList> m_command_list[FRAME_COUNT];
CommandLink* next = nullptr;
// Note that this structure can be extended or optimized using unions.
};
And this can be an example branch:
1 - Normal [0]
|
2 - Backbuffer reference [0-(FRAME_COUNT - 1)]
|
3 - Normal [0]
|
4 - Normal [0] (i can do this but two normal types can be merged together for better performance)
|
5 - Backbuffer reference [0-(FRAME_COUNT - 1)]
|
6 - Normal [0]
|
7 - Backbuffer reference [0-(FRAME_COUNT - 1)]
EDIT: Actually this is more like serializing command lists rather than branching them. Also this can be done with arrays instead of linked lists.
This could sound like an overthought concept but i'm guessing that it will have low memory usage and good performance compared to the intermediate RTV solution. I'll also have to code something like this to see how it goes.
Well, this has gone long enough. I'll try to post my results for the 2 solutions but i'll need some time. Also this has somehow turned to something fun to me. I'm really liking D3D12 a lot, it is flexible enough allowing you to do anything you want, even crash your program on purpose.
Cheers guys, take care.