r2d2rigo / MyFirstDirect2D-SharpDX

A simple tutorial on how to draw basic 2D shapes in a Windows Store DirectX app using SharpDX and Direct2D.
MIT License
10 stars 3 forks source link

[SharpDX] D2D + D3DPresenter.Resize(...) exception #1

Closed QuantumDeveloper closed 9 years ago

QuantumDeveloper commented 9 years ago

Hello. I don`t know would you be so kind to help me understand this issue, but I ask for your help.

I faced with issue that when I use D2D on D3D backbuffer and resize swapchainpanel, resizing fails because it always says DXGI ERROR: IDXGISwapChain::ResizeBuffers: Swapchain cannot be resized unless all outstanding buffer references have been released. [ MISCELLANEOUS ERROR #19: ]

But only resource I am using is D2D render target and I dispose it on begin device change event, but inside GraphicsDevice itself during resizing it just crashes on Resize() method. If I dont use D2D, resizing goes well, so I think this is definately because of D2D, but I dont know how to fix it. Here I upload my small test class to demonstrate this issue: https://gist.github.com/469db5ea0270d4524d38.git

Did you face with this error or at least know how to workaround it?

Will be apprechiate for your help.

r2d2rigo commented 9 years ago

Could you fix the gist link? It takes me to a blank webpage.

I had a problem similar to that one in a Windows Phone Silverlight 8.1 app and managed to solve it, but without you specific case I can't help you further.

QuantumDeveloper commented 9 years ago

Sorry, something strange with that gists. https://gist.github.com/QuantumDeveloper/469db5ea0270d4524d38

I found out that presenter instance in d2dservice is locking resizing and if I delete if, it would resize, but in that case I need to recreate my presenter each time and it will cause outofmemory exception....

QuantumDeveloper commented 9 years ago

@r2d2rigo do you have any ideas, or you didn`t look yet?

r2d2rigo commented 9 years ago

@QuantumDeveloper Yes, I have taken a look at it and have some suggestions:

QuantumDeveloper commented 9 years ago

Check thoroughly that you are disposing everything that you are creating.

Everything you mean even d2d device with context and resources (brushes and other?)

r2d2rigo commented 9 years ago

That was just a guideline for preventing leaks, as in my experience leaking memory with SharpDX is quite easy.

I don't know if you will need to dispose the D2D device/context too, but if releasing the brushes and images isn't enough try doing it too.

QuantumDeveloper commented 9 years ago

I tried to delete everything - it works partly (begin resizing), but still present messages about resizing error and it still crashes because of memory with message: First-chance exception at 0x76E01D4D in App6.exe: Microsoft C++ exception: _com_error at memory location 0x062AC34C.

Here is the link to my updated project https://gist.github.com/QuantumDeveloper/7f3535ea316a73d4cc6f

r2d2rigo commented 9 years ago

I had no luck trying to preserver the D2D device/context, so I have commented your first gist with a version that destroys all D2D resources when the device starts changing and recreates them when it ends. Hope this works for you.

QuantumDeveloper commented 9 years ago

@r2d2rigo Thanks for hint. I tried to do this - _disposeCollector.DisposeAndClear(); and that works - no memory exceptions anymore! (At last). But I have questions regarding memory leaks: when I should use ToDispose() and ToDIsposeContent()? Or just use disposecollector class instead all of them? I didn`t find answer on this questions anywhere, so maybe you know something about this aspect of sharpdx?

r2d2rigo commented 9 years ago

ToDispose and ToDisposeContent should be used for objects instantiated inside your Game class. The difference is that ToDispose is called when the Game class is being disposed, while the ToDisposeContent is called when UnloadContent happens.

For non-Game classes, use the DisposeCollector and add the created objects there.

QuantumDeveloper commented 9 years ago

OK, now I understood it better. Thanks a lot for help!!!!

r2d2rigo commented 9 years ago

@QuantumDeveloper are you targetting Windows 8.1 exclusively by any chance?

QuantumDeveloper commented 9 years ago

@r2d2rigo I think yes, because I am sick from old platfom limitations (WinAPI, WinForms, WPF). I hope that Windows 9 will improve that was done in 8.1, make modernUI run in window and some other improvements, but for now I am happy with that WIn 8.1 gives.

r2d2rigo commented 9 years ago

@QuantumDeveloper Oh, just remembered that you are using SharpDX.Toolkit, so what I was thinking about won't work in your case :(

I was going to suggest that if you are creating your SwapChain from scratch, you could try settings its SourceSize instead of resizing it with the new APIs added in DirectX 11.2: http://msdn.microsoft.com/en-us/library/windows/desktop/dn280408(v=vs.85).aspx

QuantumDeveloper commented 9 years ago

@r2d2rigo I am using toolkit, so it created automatically. But anyway, thanks for hint

QuantumDeveloper commented 9 years ago

@r2d2rigo Could I ask you about help one more time? Now its about render to texture instead of backbuffer and copy result to back buffer

r2d2rigo commented 9 years ago

Yes, of course. You have a repro project/code?

-----Original Message----- From: "QuantumDeveloper" notifications@github.com Sent: ‎07/‎09/‎2014 15:14 To: "r2d2rigo/MyFirstDirect2D" MyFirstDirect2D@noreply.github.com Cc: "Rodrigo Diaz" r2d2rigo@gmail.com Subject: Re: [MyFirstDirect2D] [SharpDX] D2D + D3DPresenter.Resize(...)exception (#1)

@r2d2rigo Could I ask you about help one more time? Now its about render to texture instead of backbuffer and copy result to back buffer — Reply to this email directly or view it on GitHub.=

QuantumDeveloper commented 9 years ago

Sure. I already created my depth buffer and rendertarget view (I want to enable MSAA in WinRT) but the problem is in rendering part. I am not sure how to correctly render to texture and then copy to backbuffer. My original project is quite big, so i will make simple test project.

QuantumDeveloper commented 9 years ago

Here is the link to my one drive storage: https://onedrive.live.com/?cid=650F345C1F75A314&id=650F345C1F75A314!105 project named App8. I made as simple as possible. Method "SetCustomRenderTarget()" create multisampled depthbuffer and rendertargetview with texture.

Now I draw to backbuffer directly, but I wish to render to my texture and then copy it to backbuffer. I comment non working code in Draw() method.

QuantumDeveloper commented 9 years ago

@r2d2rigo Did you try my demo app?

r2d2rigo commented 9 years ago

Sorry for the delay, my current PC isn't DX11 compatible and had to make some minor changes to your projects. I have tested the following code and it works for rendering to a separate render target:

            if (_modelLoaded)
            {
                Texture2D rtTex = Texture2D.New(GraphicsDevice, new Texture2DDescription()
                    {
                        Width = 100,
                        Height = 100,
                        Format = Format.R8G8B8A8_UNorm,
                        BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
                        SampleDescription = new SampleDescription(1,0),
                        Usage = ResourceUsage.Default,
                        CpuAccessFlags = CpuAccessFlags.None,
                        OptionFlags = ResourceOptionFlags.None,
                        ArraySize = 1,
                        MipLevels = 1,
                    });
                RenderTarget2D rt = RenderTarget2D.New(GraphicsDevice, (SharpDX.Direct3D11.Texture2D)rtTex);

                DepthStencilView oldDepth;
                RenderTargetView[] oldTargets = GraphicsDevice.GetRenderTargets(out oldDepth);

                GraphicsDevice.SetRenderTargets(rt);
                GraphicsDevice.Clear(Color.CornflowerBlue);

                GraphicsDevice.SetVertexBuffer(vertices);
                GraphicsDevice.SetIndexBuffer(indices, true);

                GraphicsDevice.SetVertexInputLayout(inputLayout);
                customEffect.CurrentTechnique.Passes["Pass0"].Apply();

                GraphicsDevice.DrawIndexed(PrimitiveType.TriangleList, indices.ElementCount);

                GraphicsDevice.SetRenderTargets(oldDepth, oldTargets);

                spriteBatch.Begin();
                spriteBatch.Draw(rtTex, new RectangleF(0, 0, 300, 300), Color.White);
                spriteBatch.End();
            }

            base.Draw(gameTime);                
        }
QuantumDeveloper commented 9 years ago

Thanks a lot, but I have a few questions:

r2d2rigo commented 9 years ago
QuantumDeveloper commented 9 years ago

Thanks. And what about my last question?

r2d2rigo commented 9 years ago

Well, it depends. If you use the SpriteBatch approach for drawing the render target, you have to keep switching them. However, browsing the MSDN docs, if you use ResolveSubresource (http://msdn.microsoft.com/en-us/library/windows/apps/dn458384.aspx) for copying the contents of the render target to the back buffer, you don't have to do it:

                GraphicsDevice.DrawIndexed(PrimitiveType.TriangleList, indices.ElementCount);

                ((SharpDX.Direct3D11.DeviceContext)GraphicsDevice).ResolveSubresource(rt, 0, GraphicsDevice.BackBuffer, 0, Format.R8G8B8A8_UNorm);
QuantumDeveloper commented 9 years ago

Thnaks a lot for your help. I will test it at home and write the result here.

QuantumDeveloper commented 9 years ago

@r2d2rigo One small addidtion and small question: You must set also Depthstencilbuffer to avoid troubles with z-buffer as I found out.

And now I cant get work my D2D. I send it created rendertarget2d, but it don`t render to it anything.

In output window I see the following error:

D3D11 ERROR: ID3D11DeviceContext::ResolveSubresource: The formats of each Resource are not compatible each other. Source Resource format is (0x1c, R8G8B8A8_UNORM). Destination Resource format is (0x57, B8G8R8A8_UNORM). [ RESOURCE_MANIPULATION ERROR #292: DEVICE_RESOLVESUBRESOURCE_SOURCE_INVALID]

Maybe this is the reason? But I can`t understand why it is so? I rendered text to simple texture and it displays on backbuffer. What I am missing?

r2d2rigo commented 9 years ago

Take a look at the error message: your backbuffer is in BGRA format so it works with Direct2D, but the rendertarget I created in the sample code is RGBA. Since the color formats don't match, it is failing.

Change the format of the off-screen render target to BGRA and it should work.

QuantumDeveloper commented 9 years ago

@r2d2rigo Already tried:

rendertoTextureDescription.Format = Format.B8G8R8A8_UNorm;
...
device.ResolveSubresource(rt, 0, GraphicsDevice.BackBuffer, 0, Format.B8G8R8A8_UNorm);

Message: D3D11 ERROR: ID3D11DeviceContext::ResolveSubresource: The formats of each Resource are not compatible each other. Source Resource format is (0x57, B8G8R8A8_UNORM). Destination Resource format is (0x1c, R8G8B8A8_UNORM). [ RESOURCE_MANIPULATION ERROR #292: DEVICE_RESOLVESUBRESOURCE_SOURCE_INVALID]

Interesting detail I see the first frame with text and on second fram It begins to show this message. Maybe resizing do something with it?

r2d2rigo commented 9 years ago

Yes, apparently the second time PreparingDeviceSettings is called, it ignores to set the back buffer to BGRA based solely on e.GraphicsDeviceInformation.DeviceCreationFlags = DeviceCreationFlags.BgraSupport. You can fix it by specifying the pixel format by yourself there: graphicsDeviceManager.PreferredBackBufferFormat = PixelFormat.B8G8R8A8.UNorm;.

QuantumDeveloper commented 9 years ago

You will laugh, but I solved this problem: Texture format must be this: rendertoTextureDescription.Format = Format.R8G8B8A8_UNorm; but resolvesubresorce must be this:

device.ResolveSubresource(rt, 0, GraphicsDevice.BackBuffer, 0, GraphicsDevice.BackBuffer.Format);

And now its working.

But I have one more question regarding to apllying texture only to the part of back buffer. For ex, for displaying minimap. I have texture with 200*150 px and I want to place it in the bottom corner of the screen. I found method device.CopySubresourceRegion(); but It not working with multisampled texture.

r2d2rigo commented 9 years ago

I'm afraid you will need to resort to draw the texture over everything with an orthographic projection for that. SpriteBatch should do the trick.

QuantumDeveloper commented 9 years ago

image its working for me, but... always but Does sprite butch support multisampling? Because when I render with spritebatch without updatesubresource - texture was completely without multisampling

r2d2rigo commented 9 years ago

What size is the texture you are drawing on the down right part of the screen? Because if it is bigger than the size in pixels of the area you are drawing to, it will be downscaled and you will lose the quality introduced by multisampling.

QuantumDeveloper commented 9 years ago

Texture is 1920*1080 and I tested it by applying to back buffer. ANd I see that quality is awful, but with subresource the same texture is drawing perfect.

QuantumDeveloper commented 9 years ago

BTW, do you know something about next WinRT version? Will it swapchin support multisampling from the box? I hope so, because now its not very comfortable to render that way. At least it will be a little easy to handle drawing ro backbuffer if not rendering to texture.

r2d2rigo commented 9 years ago

I think you have two options:

No, I don't know much about what's coming next, specially with all the changes that Threshold will supposedly have.

By the way, why don't you try using any of the shader-based antialiasing techniques?

QuantumDeveloper commented 9 years ago

I also want to try that techiques, but I dont know mush about shaders, so for now I am using very-very basic shaders. Shaders itself its black spot for me yet. I begin to understand them, but many things I don`t understand for now.

And about spritebatch - does it has good performance? I tried to make simple 2d particle and see that if I generate more than 500 small particles at onle, by FPS descend to 30 or lower. It is very interesting, because using particle with shaders and D3D I can generate at least 50 000 particle without performance loss.

r2d2rigo commented 9 years ago

AFAIK Toolkit's SpriteBatch is adapted from the one in DirectXTK, so it should perform similarly to XNA. Maybe you have more texture/state changes? Looking at it with a profiler would show what is making it go so slow.

QuantumDeveloper commented 9 years ago

OK, I will do that to be sure what I am doing wrong, But it seems that with shaders and D3D I can do more effecient particles system anyway. More that 10000 particles with spritebatch I couldnt do in any case.

QuantumDeveloper commented 9 years ago

@r2d2rigo Hello again) May I disturb you a little?

Mentioning yesterday issues. I found that using spritebatch I receive the following error: D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: The Shader Resource View dimension declared in the shader code (TEXTURE2D) does not match the view type bound to slot 0 of the Pixel Shader unit (TEXTURE2DMS). This mismatch is invalid if the shader actually uses the view (e.g. it is not skipped due to shader code branching). [ EXECUTION ERROR #354: DEVICE_DRAW_VIEW_DIMENSION_MISMATCH]

As I understand this happens because I use in shader Texture2d instead Texture2DMS, but if I not use spritebatch, there is no such error. So, maybe I am doing something wrong with spritebatch? (Interesting that in the same time it displays texture correct)

Here is how I am using spritebatch class

d2DService.Context.BeginDraw();
                        d2DService.Context.DrawText(D2DText, d2DService.TextFormat, d2DService.RectangleF,
                            d2DService.TextBrush);
                        d2DService.Context.EndDraw();
                        device.ResolveSubresource(rt, 0, GraphicsDevice.BackBuffer, 0, GraphicsDevice.BackBuffer.Format);
                        GraphicsDevice.SetRenderTargets(oldDepth, oldTargets);
                        basic.CurrentTechnique.Passes[0].Apply();
                        spriteBatch.Begin();
                        spriteBatch.Draw(rt, new RectangleF((int)SwapChainPanel.ActualWidth - 500, (int)SwapChainPanel.ActualHeight - 350, 500, 350), Color.White);
                        spriteBatch.End();

DId you faced with such issue?

r2d2rigo commented 9 years ago

You are trying to draw a multisampled texture while the code for the shader only supports standard 2D textures. You will have to draw a non-multisampled texture or roll your own SpriteBatch shader that fixes it.

QuantumDeveloper commented 9 years ago

I am trying to find some sample code to find out how to use MS texture in pixel shader, but for now I coudnt. As I understand I need only pixel shader, but what I must calculate in it? Everything already calculated... Or I can create new technique and define new pixel shader with texture2DMS, but still cant understand how to work with that texture in shader.

Simple texture is:

float4 LightPixelShader(PixelInputType input) : SV_TARGET
{
    float4 textureColor;
    float3 lightDir = 0;
    float lightIntensity;
    float4 color;

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTexture.Sample(sampleType, input.tex);

    // Invert the light direction for calculations.
    color = AmbientColor * AmbientIntensity;

    // Calculate the amount of light on this pixel.
    lightIntensity = saturate(dot(input.normal, lightDirection));

    // Determine the final amount of diffuse color based on the diffuse color combined with the light intensity.
    color = saturate(diffuseColor * lightIntensity);

    // Multiply the texture pixel and the final diffuse color to get the final pixel color result.
    color = color + textureColor;

    return color;
}

With MS texture I need do something like?

float4 LightPixelShader(PixelInputType input) : SV_TARGET
{
    float4 textureColor;
    float3 lightDir = 0;
    float lightIntensity;
    float4 color;
MScount = 4;
for (int i = 0; i < MScount; i++)
{
    //what here?
}
    return color;
}
r2d2rigo commented 9 years ago

You can copy the one provided by the Toolkit: https://github.com/sharpdx/SharpDX/blob/master/Source/Toolkit/SharpDX.Toolkit.Graphics/StockEffects/HLSL/SpriteEffect.fx

Copy and paste the definitions used from Macros.fxh and change all Texture2D types to Texture2DMS. You don't have to sample manually the neighbouring pixels, the Sample instruction will do it for you.

QuantumDeveloper commented 9 years ago

I feel myself like a complete idiot. I cant understand what is happening there. Need I vertex shader or not? Here I found sample from hierogliph engine, but still not completely understand it. https://searchcode.com/codesearch/view/10329980/

When I use it after rendering I thinhk I dont need vertex shader at all, but in that case what I must write in pixel shader. I just don`t know...

QuantumDeveloper commented 9 years ago

@r2d2rigo Did you not tired to help me yet? Hehe)

r2d2rigo commented 9 years ago

@QuantumDeveloper Sure not! :) Did you solve the shader problem?

QuantumDeveloper commented 9 years ago

Yes. I solved it with additional render target. I resolve my texture to 2 subresources and then its render without errors. For I now I don`t understand how to work correctly with multisampled texture, so I leave it as is and start working with skybox. For the current moment skybox is load and displays correctly, but I have one issue - faces disappearing when camera look on them from some angle. Like in the picture below. skybox

This was caused by logarithmic buffer in shader code. Here is the skybox effect sample:

matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;

float3 CameraPosition;

float zNear;
float zFar;
TextureCube skyboxTexture;
SamplerState skyBoxSampler;

struct VertexShaderInput
{
    float4 Position : SV_POSITION0;
};

struct VertexShaderOutput
{
    float4 Position : SV_POSITION0;
    float3 TextureCoordinate : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4 worldPosition = mul(input.Position, worldMatrix);
    float4 viewPosition = mul(worldPosition, viewMatrix);
    output.Position = mul(viewPosition, projectionMatrix);
    //this line caused that error
    output.Position.z = log(zNear*output.Position.z + 1) / log(zNear*zFar + 1) * output.Position.w;

    float4 VertexPosition = mul(input.Position, worldMatrix);         
    output.TextureCoordinate = VertexPosition - CameraPosition;

    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : SV_TARGET
{
    return skyboxTexture.Sample(skyBoxSampler, input.TextureCoordinate);
}

technique Skybox
{
    pass Pass0
    {
        Profile = 11.0;
        VertexShader = VertexShaderFunction;
        PixelShader = PixelShaderFunction;
    }
}

Problem is that if I remove that line and far away from some object - skybox cube began flickering. Logarithmic depth buffer solved that issue, but caused side effect described above. Is there any way to solve this issue?

r2d2rigo commented 9 years ago

How are you drawing the skybox? You should draw it with z-read only so no depth artifacts show.

QuantumDeveloper commented 9 years ago

I render like this:

GraphicsDevice.SetRenderTargets(depthStencilBuffer, rt);
                    GraphicsDevice.Clear(Color.CornflowerBlue);
                    skyboxEffect.Techniques["Skybox"].Passes[0].Apply();
                    GraphicsDevice.SetRasterizerState(GraphicsDevice.RasterizerStates.CullNone);
                    GraphicsDevice.SetDepthStencilState(GraphicsDevice.DepthStencilStates.DepthRead);

                    primitive.Draw();
                    GraphicsDevice.SetDepthStencilState(GraphicsDevice.DepthStencilStates.Default);
                    customEffect.CurrentTechnique.Passes[0].Apply();

And it works... but without my logarothmic z-buffer. So, I cannot do it infinit size.....