On Tue, 1 Jun 2021 at 02:12, Matteo Bruni <matteo.mystral(a)gmail.com> wrote:
Does it even
make sense to use a persistently mapped GPU buffer for a
resource mapped/updated by a deferred context? Obligatory disclaimer
that I don't know anything about GPU-side performance, but obviously you
can't reuse an earlier mapping for UpdateSubResource() or for DISCARD
maps, and I'm failing to understand why an application would ever use
NOOVERWRITE on a deferred context.
I don't have all the details of deferred contexts down, but I'm pretty
sure that you can. All that you need to know is that the memory block
you return to the application is not in use (be it by wined3d or by
the underlying API / GPU) by the time the application will access it.
Whether the memory is backed by a buffer object, suballocated from a
larger GPU-side resource or a random chunk of system memory is an
implementation detail.
NOOVERWRITE also makes perfect sense with deferred contexts. The
general idea behind DISCARD and NOOVERWRITE is to gradually "fill" a
larger resource over a number of draws, without requiring any
synchronization between the draws.
This is probably largely or entirely obvious but I'll go through the
whole story to make sure we're on the same page.
The natural use case goes like this: the application wants to draw
some dynamic geometry (i.e. generated at runtime, with variable
primitive count). At initialization time it creates a couple large
buffers (e.g. 1 MB each), one for vertices and one for indices. Then
at runtime it maps both with DISCARD, fills them with whatever data is
necessary for the current draw (e.g. vertex and index data for 100
triangles), keeping track of how much memory is still available in
either buffer, unmaps and draws. After a while it decides that it
needs to draw some more dynamic stuff. Now it does something very
similar to the previous time, except if there is still some free space
available it will map the buffer with NOOVERWRITE and "append" to the
buffer, writing the new data after the area used by the first draw.
NOOVERWRITE is a promise to d3d to not touch the data in use by any
previous draw, which in practice means that the implementation can
return the same memory as the previous map, rather than blocking to
make sure that any pending draw using the same buffer is completed.
Eventually the application will run out of space from one buffer or
the other. No problem, at that point the application will map the
relevant buffer with DISCARD and restart filling the buffer from the
top. Here DISCARD is a different promise to d3d: the application tells
d3d that it won't try to make use of the old contents of the buffer.
What it implies for the implementation is that an entirely new
"physical" buffer can be made available to the application, which will
take the place of the previous "physical" buffer once pending draws
are done with.
Afterwards the application will resume mapping the buffer with
NOOVERWRITE for the following dynamic draws, until the buffer fills up
again and so on and so forth. Worth mentioning is that the "new"
buffer that the implementation can return on a DISCARD map might in
fact be an old buffer, for example coming from a buffer discarded some
time earlier, currently unused and sitting idle. Or it might be a
portion of a larger physical buffer that the implementation knows it's
not in use.
Of course applications can and often do find ways to use those
primitives more creatively, but this is the basic idea. Also, this
works just as well for immediate or for deferred contexts.
Although you could certainly argue that since deferred
contexts/command lists are created in advance, there should be no need
for NOOVERWRITE maps, and the application could simply fill the entire
resource (or at least, the part it's going to use) with the initial
DISCARD map. (The case for DISCARD maps vs UpdateSubResource() is
perhaps a little more subtle; when using GPU buffers, DISCARD maps
would allow you to avoid a copy.) I suspect it's mostly for
convenience; NOOVERWRITE maps on deferred contexts allow applications
to render in the same way to deferred contexts as they would to
immediate contexts.