3

I am trying to run a compute shader and get the resulting texture using SharpDX.

From what I understood, I need to: 1. Create a texture to set as an output to the shader. 2. Set the above texture as an unordered access view so I can write to it. 3. Run the shader 4. Copy the UAV texture to a staging texture so it can be accessed by the CPU 5. Read the staging texture to a Bitmap

The problem is that whatever I do, the result is a black bitmap. I don't think the bug is in the Texture2D -> Bitmap conversion code as printing the first pixel directly from the staging texture also gives me 0.

This is my shader code:

RWTexture2D<float4> Output : register(u0);

[numthreads(32, 32, 1)]
void main(uint3 id : SV_DispatchThreadID) {
    Output[id.xy] = float4(0, 1.0, 0, 1.0);
}

Using the MS DX11 docs and blogs, I pieced together this code to run the texture:

public class GPUScreenColor {
    private int adapterIndex = 0;

    private Adapter1 gpu;
    private Device device;
    private ComputeShader computeShader;

    private Texture2D texture;
    private Texture2D stagingTexture;
    private UnorderedAccessView view;

    public GPUScreenColor() {
        initializeDirectX();
    }

    private void initializeDirectX() {
        using (var factory = new Factory1()) {
            gpu = factory.GetAdapter1(adapterIndex);
        }

        device = new Device(gpu, DeviceCreationFlags.Debug, FeatureLevel.Level_11_1);

        var compilationResult = ShaderBytecode.CompileFromFile("test.hlsl", "main", "cs_5_0", ShaderFlags.Debug);
        computeShader = new ComputeShader(device, compilationResult.Bytecode);

        texture = new Texture2D(device, new Texture2DDescription() {
            BindFlags = BindFlags.UnorderedAccess | BindFlags.ShaderResource,
            Format = Format.R8G8B8A8_UNorm,
            Width = 1024,
            Height = 1024,
            OptionFlags = ResourceOptionFlags.None,
            MipLevels = 1,
            ArraySize = 1,
            SampleDescription = { Count = 1, Quality = 0 }
        });

        UnorderedAccessView view = new UnorderedAccessView(device, texture, new UnorderedAccessViewDescription() {
            Format = Format.R8G8B8A8_UNorm,
            Dimension = UnorderedAccessViewDimension.Texture2D,
            Texture2D = { MipSlice = 0 }
        });

        stagingTexture = new Texture2D(device, new Texture2DDescription {
            CpuAccessFlags = CpuAccessFlags.Read,
            BindFlags = BindFlags.None,
            Format = Format.R8G8B8A8_UNorm,
            Width = 1024,
            Height = 1024,
            OptionFlags = ResourceOptionFlags.None,
            MipLevels = 1,
            ArraySize = 1,
            SampleDescription = { Count = 1, Quality = 0 },
            Usage = ResourceUsage.Staging
        });
    }

    public Bitmap getBitmap() {
        device.ImmediateContext.ComputeShader.Set(computeShader);
        device.ImmediateContext.ComputeShader.SetUnorderedAccessView(0, view);

        device.ImmediateContext.Dispatch(32, 32, 1);
        device.ImmediateContext.CopyResource(texture, stagingTexture);
        var mapSource = device.ImmediateContext.MapSubresource(stagingTexture, 0, MapMode.Read, MapFlags.None);

        Console.WriteLine(Marshal.ReadInt32(IntPtr.Add(mapSource.DataPointer, 0)));

        try {
            // Copy pixels from screen capture Texture to GDI bitmap
            Bitmap bitmap = new Bitmap(1024, 1024, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            BitmapData mapDest = bitmap.LockBits(new Rectangle(0, 0, 1024, 1024), ImageLockMode.ReadWrite, bitmap.PixelFormat);

            try {
                var sourcePtr = mapSource.DataPointer;
                var destPtr = mapDest.Scan0;
                for (int y = 0; y < 1024; y++) {
                    // Copy a single line
                    Utilities.CopyMemory(destPtr, sourcePtr, 1024 * 4);

                    // Advance pointers
                    sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
                    destPtr = IntPtr.Add(destPtr, mapDest.Stride);
                }

                return bitmap;
            } finally {
                bitmap.UnlockBits(mapDest);
            }
        } finally {
            device.ImmediateContext.UnmapSubresource(stagingTexture, 0);
        }
    }
}

I am pretty new to shaders so it may be something obvious...

stormbreaker
  • 848
  • 13
  • 22
  • Have you run with D3D11 debug (and unsafe debug enabled, and checking the debug VS output windows) to check there is nothing wrong in your flow? Have you tried a graphics debugger? (like Renderdoc) – xoofx Jun 04 '17 at 13:27

1 Answers1

3

First thing, you create your UAV as a local :

UnorderedAccessView view = new UnorderedAccessView(....

So the field is then null, replacing by

view = new UnorderedAccessView(....

will solve the first issue.

Second, it's quite likely that the runtime will complain about types (debug will give you something like :

The resource return type for component 0 declared in the shader code (FLOAT) is not compatible with the resource type bound to Unordered Access View slot 0 of the Compute Shader unit (UNORM).

Some cards might do something (fix it silently), some might do nothing, some might crash :)

Problem is that RWTexture2D does not match UNORM format (as you specify flating point format here).

You need to enforce your RWTexture to be specifically of unorm format eg (yes runtime can be that picky):

RWTexture2D<unorm float4> Output : register(u0);

Then your whole setup should work (PS: I did not check the bitmap code, but I doubled checked that the shader is running without error and first pixel is matching)

mrvux
  • 8,523
  • 1
  • 27
  • 61
  • I was sure it was something stupid I missed. Thank you! Will double-check that this solves the issue when I get back home (end of the week) and will mark the answer. In the meantime I got it to work in a fresh project without discovering what the problem was here. – stormbreaker Jun 12 '17 at 11:35