Questions about Map/UpdateSubResource

Started by
18 comments, last by 360GAMZ 12 years, 2 months ago

[quote name='Erik Rufelt' timestamp='1327189592' post='4904974']
You can also update just part of it with Map, if you use D3D11_MAP_WRITE instead of D3D11_MAP_WRITE_DISCARD. I'm not sure what restrictions apply to the usage and bind-flags for uncommon cases, but that is easy to look up or test.
http://msdn.microsof...v=vs.85%29.aspx
You probably don't want to do that for a constant-buffer, as they are usually relatively small. Doubtful it's ever worth it compared to overwriting the whole thing with a discarding map.
http://msdn.microsof...v=vs.85%29.aspx has additional discussion on dynamic usage.

Mapping with write will end up mapping the whole sub-resource too, as far as I know. There are some specialized cases where a vertex or index buffer can be partially updated with WRITE_DISCARD and WRITE_NO_OVERWRITE flags, but they are very special cases (building dynamic vertex / index buffers as opposed to dynamically updating the contents of the buffers).
[/quote]

You can also update just part of it with Map, if you use D3D11_MAP_WRITE instead of D3D11_MAP_WRITE_DISCARD. I'm not sure what restrictions apply to the usage and bind-flags for uncommon cases, but that is easy to look up or test.
http://msdn.microsof...v=vs.85%29.aspx
You probably don't want to do that for a constant-buffer, as they are usually relatively small. Doubtful it's ever worth it compared to overwriting the whole thing with a discarding map.
http://msdn.microsof...v=vs.85%29.aspx has additional discussion on dynamic usage.


So, I did some investigation (boy, I wish they'd document this stuff more explicitly) and I found this:
I -can- update a portion of the vertex buffer when I use UpdateSubResource by defining the left and right fields of the destination box. So there's that.

I can't seem to do it for Map though. I tried using the Write flag and the debug gave me this:
D3D11: ERROR: ID3D11DeviceContext::Map: Map cannot be called with MAP_WRITE access, because the Resource was created as D3D11_USAGE_DYNAMIC. D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD or MAP_WRITE_NO_OVERWRITE with Map. [ RESOURCE_MANIPULATION ERROR #2097210: RESOURCE_MAP_INVALIDMAPTYPE ]
So, I changed it to default, with no CPU write access (can't use CPU write with default resources) and I got this:
D3D11: ERROR: ID3D11DeviceContext::Map: Map cannot be called with MAP_WRITE access, because the Resource was not created with the D3D11_CPU_ACCESS_WRITE flag. [ RESOURCE_MANIPULATION ERROR #2097210: RESOURCE_MAP_INVALIDMAPTYPE ]

Anyway, this has been extremely helpful, so thank you all.

I do have another question though... what is this Subresource parameter I'm seeing? What's its purpose and how is it used?

Thanks!
Advertisement
Your first error is because your vertex buffer either didn't have a dynamic usage, or perhaps that you didn't indicate that you wanted write access with the CPU flags parameter. The second one makes sense, since you don't have any way to update a default resource with the CPU...

The sub-resource is a portion of a complete resource (obviously :) ) that is simply an index to point to that part of it. So for textures there is a separate subresource index for each mip level, each array slice, etc... For buffers, there is only one subresource - the whole buffer.

I dedicated quite a bit of time to researching each possibility and they are explicitly shown in our book (chapter 2, for each resource type) - but if you trawl through the online documentation you can also find the subresource information.

Your first error is because your vertex buffer either didn't have a dynamic usage, or perhaps that you didn't indicate that you wanted write access with the CPU flags parameter. The second one makes sense, since you don't have any way to update a default resource with the CPU...

The sub-resource is a portion of a complete resource (obviously smile.png ) that is simply an index to point to that part of it. So for textures there is a separate subresource index for each mip level, each array slice, etc... For buffers, there is only one subresource - the whole buffer.

I dedicated quite a bit of time to researching each possibility and they are explicitly shown in our book (chapter 2, for each resource type) - but if you trawl through the online documentation you can also find the subresource information.


Awesome.

As an additional thank you, I've purchased your book.
Great - I look forward to hearing what you think about it!
On a related note, D3D11_MAP_WRITE_NO_OVERWRITE is not allowed with Structured Buffers, Raw Buffers, or regular buffers. This is unfortunate when you want to dynamically append data to what you've already written. DX 11.1 fixes this, but not sure when it'll be released, whether it'll be available on Windows 7, and whether existing DX11 graphics cards will support it.

Assuming that DX 11.1 isn't a possibility, the only ways we have of updating a Structured Buffer are either D3D11_MAP_WRITE_DISCARD or UpdateSubresource. For our app, DISCARD won't work because it makes all previously written data unavailable to further draw calls, and our shaders need access to the entire buffer. So that leaves UpdateSubresource as our only option, I believe. I'm unclear as to whether our Structured Buffer should be created as DEFAULT or DYNAMIC for best performance with UpdateSubresource.

Structured Buffers created as DYNAMIC reside permanently in system memory. The data is streamed to the graphics card over the bus as the shader needs it. I assume that when UpdateResource is called, the driver makes a copy of the data in temp memory and then simply copies it to the buffer in system memory when it's safe to. Contrast this with D3D11_MAP_WRITE_NO_OVERWRITE on vertex buffers, where the app can write directly to the destination memory location without the driver making a copy.

StructuredBuffers created as DEFAULT reside in video memory with faster access by the shader. When UpdateResource is called, I assume the driver still needs to make a copy of the data in temp memory and then upload it to video memory at a safe time. Still a lot of copying.

If anyone has experience with the fastest way to incrementally update Structured Buffers, would love to hear it!

I'm unclear as to whether our Structured Buffer should be created as DEFAULT or DYNAMIC for best performance with UpdateSubresource.


Update: UpdateSubresource requires that the resource have been created as DEFAULT without CPU write access.
I've used MAP_WRITE_NO_OVERWRITE and MAP_WRITE_DISCARD and it doesn't seem to have had any adverse impact on performance (overall a good deal faster than D3D9, but I haven't benched this in isolation), but still ... it would be really nice to see some clear documentation or guidelines coming from MS on what you need to do in order to replicate the old NOOVERWRITE/DISCARD Locking pattern, because right now it's just as head-scratching as GL_ARB_vertex_buffer_object, and that's not a good thing.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

For vertex & index buffers, you should be able to use the NO_OVERWRITE/DISCARD locking pattern just as in the DX9 days. You'll just need to make sure you create the resources with DYNAMIC and CPU write access.

For Structured Buffers, Raw Buffers, and normal buffers, NO_OVERWRITE isn't supported which is very unfortunate. So, the only way to do incremental updating of one of these types of buffers is through UpdateSubresource(). In this case, the resource needs to be created with DEFAULT and no CPU write access.
The key difference however is: with the old way, when locking with no overwrite, you specify a start offset and size to lock. When mapping you don't - you get the entire buffer contents. And that is the source of confusion: what assurance have you that your no overwrite map is going to stand a chance of actually doing what you asked it to do (and not stalling the pipeline)?

OK, I know that the old locking flags were hints, not absolute assurances, but that doesn't make the difference go away - how do you hint to D3D11 that this is the behaviour you want?

So right now I'm just casting the pData member of D3D11_MAPPED_SUBRESOURCE to my vertex structure type, incrementing the pointer by the required amount, and writing in. Like I said, that seems to work and it doesn't cause any performance loss, but in the absence of any documented description of what's happening (and it could be as simple as "yeah, do this" and "no, don't do that") it feels an awful lot like throwing magic pixie dust in the air and seeing what comes down. That's not fun. D3D9 had a clear and well-documented approach for this buffer usage pattern (and let's leave out buffer types to which it doesn't apply), D3D11 doesn't.

So I guess what's needed is a D3D11 equivalent of usage style 2 in the old "Using Dynamic Vertex and Index Buffers" SDK entry.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


So right now I'm just casting the pData member of D3D11_MAPPED_SUBRESOURCE to my vertex structure type, incrementing the pointer by the required amount, and writing in. Like I said, that seems to work and it doesn't cause any performance loss, but in the absence of any documented description of what's happening (and it could be as simple as "yeah, do this" and "no, don't do that") it feels an awful lot like throwing magic pixie dust in the air and seeing what comes down. That's not fun. D3D9 had a clear and well-documented approach for this buffer usage pattern (and let's leave out buffer types to which it doesn't apply), D3D11 doesn't.


What you're describing sounds correct to me and consistent with the link provided by Erik above. The DX11 docs also suggest that you use NO_OVERWRITE along with DISCARD to insure you won't be overwriting any data the GPU may be using. Same as with DX9, but the DX9 docs explain it a lot better (i.e. use DISCARD when you reach the end of the buffer). As you pointed out, the difference between the DX9 and DX11 APIs are that you specified the region in DX9 whereas DX11 gives you a pointer to the start of the buffer, and then we must apply the offset afterwards. I agree that the docs could explain all of this better.

This topic is closed to new replies.

Advertisement