Advertisement

Working on D3D11/OGL wrapper

Started by September 01, 2015 11:37 AM
5 comments, last by devdber 9 years, 3 months ago

To deepen my knowledge of these APIs.

Here I will post some misc questions on "cross-API" wrapper. They are supposed to be associated with each other, so I decided unite all in single topic (if moderators will accept this).

My main purpose is to get as much as possible of the features of both APIs using single aproach in my wrapper architecture.

At this moment im trying to find common things in OGL and D3D11 buffer representation.

First issue is binding point. In OGL, parameter "target" may be set as:

GL_ARRAY_BUFFER

GL_ELEMENT_ARRAY_BUFFER

GL_UNIFORM_BUFFER

GL_COPY_READ_BUFFER - what's proper equivalent in d3d11?

GL_COPY_WRITE_BUFFER - what's proper equivalent in d3d11?

GL_PIXEL_PACK_BUFFER - what's proper equivalent in d3d11?

GL_PIXEL_UNPACK_BUFFER - what's proper equivalent in d3d11?

GL_TRANSFORM_FEEDBACK_BUFFER - what's proper equivalent in d3d11? To simplify task I could use special transform feedback object in OGL 4.1+.

GL_TEXTURE_BUFFER - this we omit, it's used with special function glTexBuffer - am I right???

In D3D11 we have D3D11_BUFFER_DESC which has two elements with similar meaning - D3D11_BIND_FLAG and D3D11_RESOURCE_MISC_FLAG (I will get to this much much later, but still)

possible values of D3D11_BIND_FLAG are:

D3D11_BIND_VERTEX_BUFFER
D3D11_BIND_INDEX_BUFFER
D3D11_BIND_CONSTANT_BUFFER
D3D11_BIND_SHADER_RESOURCE
D3D11_BIND_STREAM_OUTPUT
D3D11_BIND_RENDER_TARGET
D3D11_BIND_DEPTH_STENCIL
D3D11_BIND_UNORDERED_ACCESS
D3D11_BIND_DECODER
D3D11_BIND_VIDEO_ENCODER

I ignore last three. They are seemed to be quite tricky and I dont know well how they are working.

Problem is that these flags can be combined, except D3D11_BIND_CONSTANT_BUFFER. But if we do this, we disallow GPU to do smart managing with this buffer. Can I afford this? Are there so many cases when someone prefer to use several flags?

Next thing about buffers is "usage".

D3D11_USAGE_DEFAULT - no problem with this, glBufferData
D3D11_USAGE_IMMUTABLE - this buffer cant be changed after creation. Does glBufferStorage with proper flags do the same? And do these flags really add to performance?
D3D11_USAGE_DYNAMIC - in OGL i would use GL_DYNAMIC_DRAW, however it can be ignored by OGL. Anything elese?
D3D11_USAGE_STAGING - could i use this for transform feedback?

To reach the same things in both APIs we need to find proper combination of binding ponts, buffer usage in OGL and appropriate combination of binding flags, and usage in D3D11. These combinations can be reached by different ways (because buffer binding in OGL is "dynamic", while with D3D11 we have to set it strictly at creation. Am right with this point?). The only way i see is to make single classes for uniform buffers, vertex buffers, index buffers and transform feedback buffers. In special cases we have immutable buffer, which cannot be transform feedback buffers as i suppose.
Please, answer questions in previous sections and suggest your approaches.

Sorry for my English.

First issue is binding point. In OGL, parameter "target" may be set as:

These are "binding locations" -- slots in the API which you're attaching resources to. The D3D equivalents are these binding functions:

GL_ARRAY_BUFFER -> IASetVertexBuffers
GL_ELEMENT_ARRAY_BUFFER -> IASetIndexBuffer
GL_UNIFORM_BUFFER -> *SSetConstantBuffers
GL_COPY_*_BUFFER -> CopyResource
GL_TEXTURE_BUFFER -> *SSetShaderResourceViews -- this function in D3D can bind "buffer views" and "texture views". In GL you've got these weird "texture buffers", which are a kind of API hack that lets you bind a buffer as if it was a texture.
GL_TRANSFORM_FEEDBACK_BUFFER -> SOSetTargets -- transform feedback is call stream output in D3D.

GL_PIXEL_PACK_BUFFER / GL_PIXEL_UNPACK_BUFFER -- IIRC, this is GL's way of asynchronously transferring data between the CPU/GPU. In D3D you'd use a dynamic texture for CPU->GPU, or a staging texture for GPU->CPU possibly along with the CopyResource function.

FBOs -> OMSetRenderTargets

D3D11_BIND_UNORDERED_ACCESS

D3D's UAV's are equivalent to GL's SSBO's, if you decide to look into them later. These are similar to a "texture buffer", or "buffer w/ shader resource view", but they can be arbitrarily read and written to by shaders.

Are there so many cases when someone prefer to use several flags?

Usually with transform-feedback/stream-output, you want the GPU to make use of the results. In that case, you might have a buffer that is a stream-output buffer, AND also a vertex-buffer, so that the GPU can read vertices from it later on.

I often create large buffer objects, which contain vertex data and index data at different sub-offsets, so these have both the VERTEX_BUFFER bind flag and the INDEX_BUFFER bind flag.

As above -- When used on a buffer, D3D11_BIND_SHADER_RESOURCE is the same as GL's "texture buffer" concept. It's a buffer that can be bound to a shader and read from arbitrarily.

D3D11_BIND_RENDER_TARGET & D3D11_BIND_DEPTH_STENCIL are use on renderable textures (not buffers), equivalent to GL FBO's.

D3D11_USAGE_DEFAULT - no problem with this, glBufferData
D3D11_USAGE_IMMUTABLE - this buffer cant be changed after creation. Does glBufferStorage with proper flags do the same? And do these flags really add to performance?
D3D11_USAGE_DYNAMIC - in OGL i would use GL_DYNAMIC_DRAW, however it can be ignored by OGL. Anything elese?
D3D11_USAGE_STAGING - could i use this for transform feedback?

Default is a regular GPU-RAM allocation.
Immutable similar to default, but is a hint for the driver to try and optimize, knowing you won't ever request to modify the data.
Dynamic is for CPU->GPU streaming -- if you'll be mapping/updating it constantly. The driver will pre-optimize for a resource-orphaning use-case.
Staging is basically a CPU-RAM (malloc/new) type memory allocation. You can use CopyResource to copy from a Staging resource into a Default resource for CPU->GPU streaming, or to copy from a Default resource to a Staging resource for GPU->CPU read-back.

Advertisement

D3D11_USAGE_DEFAULT - no problem with this, glBufferData

glBufferStorage( binding_point, size, 0, 0 );

D3D11_USAGE_IMMUTABLE - this buffer cant be changed after creation. Does glBufferStorage with proper flags do the same? And do these flags really add to performance?

glBufferStorage( binding_point, size, 0, 0 );
Note: there's no difference in GL.

D3D11_USAGE_DYNAMIC - in OGL i would use GL_DYNAMIC_DRAW, however it can be ignored by OGL. Anything elese?

There's no equivalent because the "equivalents" are horribly broken across GL driver implementations (technically yes, using GL_DYNAMIC_DRAW and glMapBufferRange with GL_MAP_INVALIDATE_BUFFER_BIT)
It's best to do:
glBufferStorage( GL_ARRAY_BUFFER, size, 0, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT );
void *dynamicPtr = glMapBufferRange( GL_ARRAY_BUFFER, 0, size,
                                       GL_MAP_WRITE_BIT |
                                       GL_MAP_FLUSH_EXPLICIT_BIT |
                                       GL_MAP_PERSISTENT_BIT );
//write to dynamicPtr...

//Notify the ranges you've writen to
glFlushMappedBufferRange( ... );
And write directly to it, synchronizing with fences. It's the equivalent of mapping the buffers with MAP_NO_OVERWRITE in D3D11 (although const and texture buffers in D3D11 couldn't be mapped with NO_OVERWRITE until D3D11.1 with Windows 8.1).

D3D11_USAGE_STAGING - could i use this for transform feedback?

No. And modern equivalent is:
glBufferStorage( GL_COPY_READ_BUFFER, stashSize, 0, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT );
void *stashPtr = glMapBufferRange( GL_COPY_READ_BUFFER, 0, stashSize,
                                       GL_MAP_WRITE_BIT |
                                       GL_MAP_FLUSH_EXPLICIT_BIT |
                                       GL_MAP_PERSISTENT_BIT );
Use fences and flushmappedbufferrange again for sync.

See apitest on how to use fences.

These are "binding locations" -- slots in the API which you're attaching resources to. The D3D equivalents are these binding functions:

GL_ARRAY_BUFFER -> IASetVertexBuffers
GL_ELEMENT_ARRAY_BUFFER -> IASetIndexBuffer
GL_UNIFORM_BUFFER -> *SSetConstantBuffers
GL_COPY_*_BUFFER -> CopyResource
GL_TEXTURE_BUFFER -> *SSetShaderResourceViews -- this function in D3D can bind "buffer views" and "texture views". In GL you've got these weird "texture buffers", which are a kind of API hack that lets you bind a buffer as if it was a texture.
GL_TRANSFORM_FEEDBACK_BUFFER -> SOSetTargets -- transform feedback is call stream output in D3D.

GL_PIXEL_PACK_BUFFER / GL_PIXEL_UNPACK_BUFFER -- IIRC, this is GL's way of asynchronously transferring data between the CPU/GPU. In D3D you'd use a dynamic texture for CPU->GPU, or a staging texture for GPU->CPU possibly along with the CopyResource function.

Well, no problem. OGL is more flexible in this case.


D3D's UAV's are equivalent to GL's SSBO's, if you decide to look into them later. These are similar to a "texture buffer", or "buffer w/ shader resource view", but they can be arbitrarily read and written to by shaders.

I thought D3D11's "SSBO" is RWBuffer/StructuredBuffer. What's about them?


Usually with transform-feedback/stream-output, you want the GPU to make use of the results. In that case, you might have a buffer that is a stream-output buffer, AND also a vertex-buffer, so that the GPU can read vertices from it later on.

I often create large buffer objects, which contain vertex data and index data at different sub-offsets, so these have both the VERTEX_BUFFER bind flag and the INDEX_BUFFER bind flag.

What if I decide to use VERTEX_BUFFER | INDEX_BUFFER | STREAM_OUTPUT | ... anything possible | ... ? Will be there horrible performance issues?


glBufferStorage( binding_point, size, 0, 0 );

Note: there's no difference in GL.

but I learned from MSDN that immutable buffers' content cant be changed and articles on ogl say that "immutability" results only in persistent buffers' size and (?) location, not affecting content modification.

Sorry for my English.

D3D's UAV's are equivalent to GL's SSBO's, if you decide to look into them later. These are similar to a "texture buffer", or "buffer w/ shader resource view", but they can be arbitrarily read and written to by shaders.

I thought D3D11's "SSBO" is RWBuffer/StructuredBuffer. What's about them?

"UAV" encompases a lot of stuff, including RWBuffer/StructuredBuffer.
In GL, to get all the functionality you can do with UAVs, you need SSBOs + imageStore/imageLoad

What if I decide to use VERTEX_BUFFER | INDEX_BUFFER | STREAM_OUTPUT | ... anything possible | ... ? Will be there horrible performance issues?

It will be fine, just don't bind overlapping regions to different types.

but I learned from MSDN that immutable buffers' content cant be changed and articles on ogl say that "immutability" results only in persistent buffers' size and (?) location, not affecting content modification.

There is no 1:1 relation here between D3D11 & GL.
In D3D11 immutable buffers means that data is uploaded to GPU on creation and never ever changed until destruction.
In GL glBufferStorage is the successor of glBufferData, and makes the handle immutable, which means you can't later change the size of the buffer (something you could never do in D3D11 anyway... also in GL internally specifying a new size in glBufferData would internally destroy the buffer and recreate it behind the scenes), but you can upload data to it at any point as often as you want. Its contents aren't immutable at all.

Thank you for replies.

Im trying to come up with common interface for stream output. This is the problem that raised as i decided to go deeper into buffer implementation.

Common way to get such info out of shader in d3d11 (please correct me if i got it wrong):

1. Create buffer with STREAM_OUTPUT flag

2. Fill in D3D11_SO_DECLARATION_ENTRY's and strides.

3. Create geometry shader with CreateGeometryShaderWithStreamOutput.

-----

4. Set geometry shader

5. Set SO target

6. Draw to buffer

7. Unset SO target

Via OGL we do this:

1. Create transform feedback object

2. Bind buffers with TRANSFORM_FEEDBACK binding point to it (or do it at rendering)

3. Create program with geometry shader (optional?)

4. Set transform feedback varyings

--------

5. Bind transform feedback object

6. Use program with GS

7. Begin TF

8. Draw to buffer

9. End TF

Problem is that ways of d3d11 and gl retrieve info for further feedback are different:

typedef struct D3D11_SO_DECLARATION_ENTRY {
UINT Stream;
LPCSTR SemanticName;
UINT SemanticIndex;
BYTE StartComponent;
BYTE ComponentCount;
BYTE OutputSlot;
} D3D11_SO_DECLARATION_ENTRY;
and OGL:
void glTransformFeedbackVaryings?(GLuint program?, GLsizei count?, const char **varyings?, GLenum bufferMode?);
With OGL we can easily set vars to be retrieved by name.
With D3D11 we have to state semantic name and semantic slot. Is there way to do as with OGL without D3DReflect?
As i learned, with OGL we can bind some buffer to particular "output slot" with BindBufferBase. But how we can do the same with d3d11, where we "bind" to "slots" not buffers (but this we do implicitly by SOSetTargets still ) but retrieved info?
What's Stream and which equivalent of this have OGL?

Sorry for my English.

Advertisement

Up!

Sorry for my English.

This topic is closed to new replies.

Advertisement