D3D12 Dynamic indexing of textures with different formats - hlsl

In shader model 5.1 we can use dynamic indexing for textures like so:
Texture2D textures[5] : register(t0)
PixelShader(Input p) : SV_TARGET
{
float4 color = textures[0].Sample(someSampler, p.UV);
return color;
}
Here it's assumed that all textures have 4 channels (rgba). However, I have no idea how to sample when my texture array is a mix of different formats like BC3 (rgba), BC4 (single channel R), BC5 (dual channel RG). For example, in case of BC4 I could try
float R = textures[0].Sample(someSampler, p.UV).r;
But wouldn't this just skip over three texels?

HSLS Shader model 5.1 is quite confusing because you have a distinction between "texture array" and "texture array"...
The first meaning is the one that appeared with DX10, A single texture resource is made of several slices and a shader can index in the slices. The major limitation is that each slice have to share size and format.
The second meaning, introduced with API like DX12 or Vulkan is closer to "an array of textures". You can now group multiple resource objects into an array of descriptors. The shader can freely use any of them with dynamic indexing. The constraint of a texture array are lifted. The one limitation is the use of the NonUniformIndex intrinsic to let the driver fix up indexing limitation a GPU may have.
As for your original questionm, It is then up to you to know what texture are where, if you group texture with formats like BC4 and BC7, it is probably because one is an albedo while the other may be a gloss map. Your shader will give the semantic to what it read. But if you want a BC4 texture to expand as RRRR instead of the default R001, you can use the component mapping in the shader resource view.

This is not a 'texture array'. This is just a way to declare 5 textures bound individually, and the syntax lets you use indices to select t0 through t1. A 'texture array' is declared as follows:
Texture2DArray textures : register(t0);
Every texture in the texture array must be the same format (it's a single resource), and you use a float3 to index it for sampling.
float4 color = textures.Sample(someSampler, float3(p.UV,0) );
What you are doing above is basically the same thing as:
Texture2D texture0 : register(t0);
Texture2D texture1 : register(t1);
Texture2D texture2 : register(t2);
Texture2D texture3 : register(t3);
Texture2D texture4 : register(t4);
As such, the formats of each texture are completely independent, the code here:
float R = textures[0].Sample(someSampler, p.UV).r;
This just samples the texture in t0 as normal, returning just the red channel. For a BC4, this will cause the hardware to decompress the correct 4x4 block (or blocks depending on the UV and sampler mode), and return the red channel from the reconstruction.
If you are new to DirectX and HLSL, I strongly recommend not using DirectX 12 to start. It's a fairly unforgiving API designed for graphics experts, so you should consider starting with DirectX 11 instead. The APIs both drive the same hardware, they just do it with different programmer abstractions. DirectX 12 documentation also generally assumes you are already an expert with DirectX 11 anyhow and the HLSL usage is basically the same (with the addition of programmatic control over root signatures). See DirectX Tool Kit for DirectX 11 and DirectX 12.

Related

How to use swizzle .rrrg in directx11 shader

I have to create a texture with equivalent format of D3DFMT_A8L8 in directx9 to directx11. But the note in the documentation is not clear to me. Would someone explain what should be done? While creating input layout I am facing invalid parameter error.
D3DFMT_A8L8 => DXGI_FORMAT_R8G8_UNORM
Note: Use swizzle .rrrg in shader to duplicate red and move green to the alpha components to get Direct3D 9 behavior.
In Direct3D 9, the "Luminance" formats would automatically be read in the shader as RGB values (replicated) because they were greyscale formats. In Direct3D 10+, there are no "Luminance" format. There are one and two channel formats, and there's no "special behavior" with them.
Therefore, in a shader a DXGI_FORMAT_R8G8_UNORM texture will have the first channel in the 'r' channel and the second channel in the 'g' channel. The DXGI_FORMAT_R8G8_UNORM has the same memory foot-print as D3DFMT_A8L8, i.e. two 8-bit channels of unsigned normal integer data, but if you want the behavior in a modern shader that matches the old one you have to do it explicitly with shader swizzles:
Texture2D<float4> Texture : register(t0);
sampler Sampler : register(s0);
float4 color = Texture.Sample(Sampler, pin.TexCoord);
// Special-case for DXGI_FORMAT_R8G8_UNORM treated as D3DFMT_A8L8
color = color.rrrg;
This has nothing at all to do with input layouts. It's just the way a texture sampler works.

Pass more than 4-channel data to OpenGL or Vulkan

I would like to pass the following values as a texture to the fragment shader:
Base R
Base G
Base B
Material switch (metal/dielectric)
Normal x
Normal y
Normal z
IOR (Only for dielectric)
Roughness
That is a lot of stuff. It looks like this would require three different textures in OpenGL. Questions:
Are there any extensions to OpenGL that makes it possible to pass this as one texture?
From what I have understood about Vulkan, GPU memory is more easily accessible. Does this mean that you can use generalized texture formats?
Even in Vulkan, you can not have more than a four channels for a texture. However, both in OpenGL and Vulkan, you could use a 32 bits by channels texture, and use something like packUnorm( https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/packUnorm.xhtml ). But it works only with integer texture and you will have to perform filtering by yourself
Another way could be to use something like SSBO ou TBO. But I do not understand what the problem is by using 3 textures.

Editable Texture with OpenGL

I'm trying to take advantage of a gpu's parallelism to make an image proccessing application. I'm having a shader, which takes two textures, and based on some uniform variables, computes an output texture. But instead of transparency alpha value, each texture pixel needs an extra metadata byte, mandatory in computation:
So I consider running the shader twice each frame, once to compute the Dynamic Metadata as a single byte texture, and once to calculate the resulting Paint Texture, which I need to be 3 bytes (to limit memory usage, as there might be quite some such textures loaded at once).
I find the above problem a bit complicated, I've used opengl to paint to
the screen, but I need to paint to two different textures this time,
which I do not know how to do. Besides, gl_FragColor built-in variable's
type is vec4, but I need different output values.
So, to sum it up a little, is it possible for the fragment shader to output
anything other than a vec4?
Is it possible to save to two different textures with a single call?
Is it possible to make an editable texture to store changes, until the editing ends and the data have to be passed back to the cpu?
What openGL calls would be most usefull for the above?
Paint texture should also be able to be retrieved to be shown on the screen.
The above could very easily be done via blitting textures on the cpu.
I could keep all the relevant data on the cpu, do all the work 60 times/sec,
and update the relevant texture by passing the data from the cpu to the gpu.
For changing relatively small regions of a texture each frame
(about ~20% of the total scale of about 512x512 size textures), would you consider the above approach worth the trouble?
It depends on which version of OpenGL you use.
The latest OpenGL 4+ does not have a gl_FragColor variable, and instead lets you write any number (up to supported maximum) of output colors from the fragment shader, each sent to the corresponding framebuffer color attachment:
layout(location = 0) out vec4 OUT0;
layout(location = 1) out float OUT1;
That will write OUT0 to GL_COLOR_ATTACHMENT0 and OUT1 to GL_COLOR_ATTACHEMENT1 of the currently bound framebuffer.
However, considering that you use gl_FragColor, you use some old version of OpenGL. I'm not proficient in the legacy older OpenGL versions, but you can check out whether your implementation supports the GL_ARB_draw_buffers extension and/or gl_FragData[] output variable.
Also, as stated, it's unclear why can't you use a single RGBA texture and use its alpha channel for that metadata.

Sampling values in fragment shader from a R16_UINT-3D texture in Vulkan

I am using the texture tutorial from Sascha Willems and it works without any problems. Then I change the texture from a 2D texture to a 3D texture unsigned 16 bits.
I set the correct depth and then change these values:
VK_FORMAT_BC3_UNORM_BLOCK -> VK_FORMAT_R16_UINT
VK_IMAGE_TYPE_2D -> VK_IMAGE_TYPE_3D
and just one component -> view.components = { VK_COMPONENT_SWIZZLE_R };
In the shader:
sampler2D -> sampler3D
But all the values I get from texture(volumeTexture, textPos).r are now zero.
What I want to do is to upload a UINT16 image but sample it as float from the fragment shader.
If I open RenderDoc the texture looks good.
pastie.org/private/i7dqw7pm0snkbf40eyywg
This is what I did in opengl:
const auto& glType = GL_SHORT;
const auto& glFormat = GL_LUMINANCE;
const auto& glInternalFormat = GL_LUMINANCE16F_ARB;
const auto& glClampMode = GL_CLAMP_TO_EDGE;
This is what I did in opengl:
OpenGL's pixel transfer functionality is required to accept unpleasant or slow things. Vulkan's does not.
What you did in OpenGL was force the driver to convert every pixel from a 16-bit unsigned, normalized fixed-point value to a 16-bit IEEE-754 floating-point value. Almost certainly, this conversion was done on the CPU.
Vulkan doesn't have training wheels, and Vulkan drivers don't do your work for you. Vulkan has formats, and the specification states exactly what they mean. If your data does not match the format you eventually want to use, then its on you to do the conversion.
Can you please add some more info? By "read from texture" do you mean sampling in a fragment shader? Do you get any errors from the validation layers? Does your implementation support sampling from R16_UINT?
If RenderDoc displays your texture but it's not "visible" in your application you also may miss a proper image layout transition, at least on hardware that requires them. This also includes the correct subresourcerange.
And please take a look at the table provided at https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html#resources-image-views
And make sure that your image and view parameters fit for 3D textures.

What is the most efficient method of rendering sprites in DirectX 10?

I am currently experimenting with various ways of displaying 2D sprites in DirectX 10. I began by using the ID3DX10Sprite interface to batch draw my sprites in a single call. Eventually, however, I wanted a little more control over how my sprites were rendered, so I decided to look into quad-based sprite rendering (ie each sprite being represented by a quad with a texture applied).
I started out simple: I created a single vertex buffer consisting of 4 vertices that was applied once before the sprites were drawn. I then looped through my sprites, setting the appropriate properties to be passed into the shader, and making a draw call for each sprite, like so: d3dDevice->Draw( 4, 0);. Though it worked, the draw call for every sprite bugged me, so I looked for a more efficient method.
After searching about, I learned about object instancing, and decided to try it out. Everything went well until I tried implementing the most important part of sprites--textures. In short, though I had a texture array (declared at the top of my shader like so Texture2D textures[10];) that could be successfully sampled within my pixel shader using literals/constants as indexes, I could not figure out how to control which textures were applied to which instances via a texture index.
The idea would be for me to pass in a texture index per instance, that could then be used to sample the appropriate texture in the array within the pixel shader. However, after searching around more, I could not find an example of how it could be done (and found many things suggesting that it could not be done without moving to DirectX 11).
Is that to say that the only way to successfully render sprites via object instancing in DirectX 10 is to render them in batches based on texture? So, for example, if my scene consists of 100 sprites with 20 different textures (each texture referenced by 5 sprites), then it would take 20 separate draw calls to display the scene, and I would only be sending 5 sprites at a time.
In the end, I am rather at a loss. I have done a lot of searching, and seem to be coming up with conflicting information. For example, in this article in paragraph 6 it states:
Using DirectX 10, it is possible to apply different textures in the array to different instances of the same object, thus making them look different
In addition, on page 3 of this whitepaper, it mentions the option to:
Read a custom texture per instance from a texture array
However, I cannot seem to find a concrete example of how the shader can be setup to access a texture array using a per instance texture index.
In the end, the central question is: What is the most efficient method of rendering sprites using DirectX 10?
If the answer is instancing, then is it possible to control which texture is applied to each specific instance within the shader--thereby making it possible to send in much larger batches of sprites along with their appropriate texture index with only a single draw call? Or must I be content with only instancing sprites with the same texture at a time?
If the answer is returning to the use of the provided DX10 Sprite interface, then is there a way for me to have more control over how it is rendered?
As a side note, I have also looked into using a Geometry Shader to create the actual quad, so I would only have to pass in a series of points instead of managing a vertex and instance buffer. Again, though, unless there is a way to control which textures are applied to the generated quads, then I'm back to only batching sprites by textures.
There's a few ways (as usual) to do what you describe.
Please note that using
Texture2D textures[10];
will not allow you to use a variable index for lookup in Pixel Shader (since technically this declaration will allocate a slot per texture).
So what you need is to create a Texture2DArray instead. This is a bit like a volume texture, but the z component is a full number and there's no sampling on it.
You will need to generate this texture array though. Easy way is on startup you do one full screen quad draw call to draw each texture into a slice of the array (you can create a RenderTargetView for a specific slice). Shader will be a simple passtrough here.
To create a Texture Array (code is in SlimDX but, options are similar):
var texBufferDesc = new Texture2DDescription
{
ArraySize = TextureCount,
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
Format = format,
Height = h,
Width = w,
OptionFlags = ResourceOptionFlags.None,
SampleDescription = new SampleDescription(1,0),
Usage = ResourceUsage.Default,
};
Then shader resource view is like this:
ShaderResourceViewDescription srvd = new ShaderResourceViewDescription()
{
ArraySize = TextureCount,
FirstArraySlice = 0,
Dimension = ShaderResourceViewDimension.Texture2DArray,
Format = format,
MipLevels = 1,
MostDetailedMip = 0
};
Finally, to get a render target for a specific slice:
RenderTargetViewDescription rtd = new RenderTargetViewDescription()
{
ArraySize = 1,
FirstArraySlice = SliceIndex,
Dimension = RenderTargetViewDimension.Texture2DArray,
Format = this.Format
};
Bind that to your passtrough shader, set desired texture as input and slice as output and draw a full screen quad (or full screen triangle).
Please note that this texture can also be saved in dds format (so it saves you to regenerate every time you start your program).
Looking up your Texture is like:
Texture2DArray myarray;
In Pixel Shader:
myarray.Sample(mySampler, float2(uv,SliceIndex);
Now about rendering sprites, you also have the option of GS expansion.
So you create a vertex buffer containing only the position/size/textureindex/whatever else you need one one vertex per sprite.
Send a draw call with n sprites (Topology needs to be set to point list).
Passtrough the data from vertex shader to geometry shader.
Expand your point into quad in geometry shader, you can find an example which is ParticlesGS in Microsoft SDK doing that, it's a bit overkill for your case since you only need the rendering part for it, not the animation. If you need some cleaned code let me know I'll quickly make a dx10 compatible sample (In my case I use StructuredBuffers instead of VertexBuffer)
Doing a pre-made Quad and passing the above data in Per Instance VertexBuffer is also possible, but if you have a high number of sprites it will easily blow up your graphics card (by high I mean something like over 3 million particles, which is not much by nowadays standards, but if you're under half a million sprites you'll be totally fine ;)
Include the texture index within the instance buffer and use this to select the correct texture from the texture array per instance:
struct VS
{
float3 Position: POSITION;
float2 TexCoord: TEXCOORD0;
float TexIndex: TexIndex; // From the instance buffer not the vertex buffer
}
Then pass this value on through to the pixel shader
struct PS
{
float4 Positon: SV_POSITION;
float3 TexCoord: TEXCOORD0;
}
..
vout.TexCoord = float3(vin.TexCoord, vin.TexIndex);