Screen-tearing, buffer overwrites and timing issues?

Started by
11 comments, last by KaiserJohan 4 years, 11 months ago

I have a renderer project that I recently upgraded from VS2015 x86 -> VS2019 x64 and got a new, more powerful computer aswell. I got maybe a 2-3x FPS increase. However I ran into a few problems:

  1. Screen-tearing. The less intense the gamescene, the more FPS, and the more visible tearing.
  2. One of my per-frame constant buffers appears to have garbage contents (more on that later)
  3. Inspecting the backbuffer in RenderDoc in captured sample it appears to have several different frames depending on at what point you inspect it (might be a renderdoc bug but maybe not)

When I enable VSync the screen tearing is less but not completely gone.

I believe the old compiler and/or my older computer hid these issues before. So I must be something wrong with my swapchain setup or somesuch.

My swapchain creation:

ZeroMemory(&mSwapchainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
mSwapchainDesc.BufferCount = 2;
mSwapchainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
mSwapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
mSwapchainDesc.OutputWindow = mWindowHandle;
mSwapchainDesc.SampleDesc.Count = 1;
mSwapchainDesc.Windowed = true;
DXCALL(D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, deviceFlags, &featureLevel, numFeatureLevels, D3D11_SDK_VERSION, &mSwapchainDesc, &mSwapchain, &mDevice, nullptr, &mContext));

I am using deferred rendering and use a light accumulation buffer as the RTV when doing shadows/lights. I then do tonemapping on it and output it to the backbuffer in sRGB format. Then I copy the backbuffer texture to use it as input to a FXAA shader which renders into the backbuffer again. After that I optionally do a bunch of debug passes directly into the backbuffer aswell before I call present().

(I also read the depth texture during a depth reduction pass but I don't think thats relevant to these problems)

On point 2) I have a very simple way to calculate tessellation factor like this:

float CalculateTessellationfactor( float3 worldPatchMidpoint )
{
    float cameraToPatchDistance = distance( gWorldEyePos, worldPatchMidpoint );
    float scaledDistance = ( cameraToPatchDistance - gMinZ ) / ( gMaxZ - gMinZ );
    scaledDistance = clamp( scaledDistance, 0.0f, 1.0f );
 
    return pow( 2, lerp( 6.0f, 2.0f, scaledDistance ) );
}

gMinZ/gMaxZ is min/far Z that is set once-per-frame in a constant buffer. When I inspect them in RenderDoc they have correct values (and it worked before), but apparently they do not when I render, but if I hardcode the values in the shader then it works. I assume they are overwritten repeatedly and garbage when I read them in the shader?

---------------------------------------------------------------------

All in all I guess I am rendering too fast and overwriting resources all the time. How would I go about solving this?

Advertisement

Sounds like you're trying to writing to a resource to generate one frame, while it's still being used to render another. You're using deferred rendering; can you describe your rendering pipeline, taking particular care to document in what order you perform each step?

RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
23 hours ago, Wyrframe said:

Sounds like you're trying to writing to a resource to generate one frame, while it's still being used to render another. You're using deferred rendering; can you describe your rendering pipeline, taking particular care to document in what order you perform each step?

Some more imagines to describe the problem:

example1.thumb.png.ceb76723f29279995b6023f90fa5a529.png

Notice the wierd mix of tessellated and completely non-tessellated patches in the image.

And then at the end frame, everything is non-tessellated:

example2.thumb.png.16a88f79e185492ca50809868da3e0fa.png

I am not sure how reliable RenderDoc is on the matter though... but the problem remains.

After some more testing, the tearing stops and everything seems to work if I hardcode the tessellation factor in the shader.

This is what it looks like without it, captured sample in renderdoc:


float CalculateTessellationfactor( float3 worldPatchMidpoint )
{
    //float cameraToPatchDistance = distance( gWorldEyePos, worldPatchMidpoint );
    float scaledDistance = 0.12f;// ( cameraToPatchDistance - gMinZ ) / ( gMaxZ - gMinZ );
    scaledDistance = clamp( scaledDistance, 0.0f, 1.0f );

    return pow( 2, lerp( 6.0f, 2.0f, scaledDistance ) );
}

And then working sample:

stable1.thumb.png.e8306ef12527c50b435e72b208ee62ff.png

This is just crazy wierd. Here is the complete hull-shader without the edit-fix:


#ifndef TERRAIN_HULL_HLSL
#define TERRAIN_HULL_HLSL

#include "TerrainCommon.hlsl"
#include "Common.hlsl"

float3 ComputePatchMidpoint( float3 corner1, float3 corner2, float3 corner3, float3 corner4 )
{
    return ( corner1 + corner2 + corner3 + corner4 ) / 4.0f;
}

float CalculateTessellationfactor( float3 worldPatchMidpoint )
{
    float cameraToPatchDistance = distance( gWorldEyePos, worldPatchMidpoint );
    float scaledDistance = ( cameraToPatchDistance - gMinZ ) / ( gMaxZ - gMinZ );
    scaledDistance = clamp( scaledDistance, 0.0f, 1.0f );

    return pow( 2, lerp( 6.0f, 2.0f, scaledDistance ) );
}

PatchTess PatchHS( InputPatch<VertexOut, 12> inputVertices )
{
    PatchTess patch;

    float3 midPatchMidpoint = ComputePatchMidpoint( inputVertices[ 0 ].mWorldPosition, inputVertices[ 1 ].mWorldPosition, inputVertices[ 2 ].mWorldPosition, inputVertices[ 3 ].mWorldPosition );
    float3 edgePatchMidpoint[] = {
        ComputePatchMidpoint( inputVertices[ 0 ].mWorldPosition, inputVertices[ 1 ].mWorldPosition, inputVertices[ 4 ].mWorldPosition, inputVertices[ 5 ].mWorldPosition ),
        ComputePatchMidpoint( inputVertices[ 1 ].mWorldPosition, inputVertices[ 2 ].mWorldPosition, inputVertices[ 6 ].mWorldPosition, inputVertices[ 7 ].mWorldPosition ),
        ComputePatchMidpoint( inputVertices[ 2 ].mWorldPosition, inputVertices[ 3 ].mWorldPosition, inputVertices[ 8 ].mWorldPosition, inputVertices[ 9 ].mWorldPosition ),
        ComputePatchMidpoint( inputVertices[ 3 ].mWorldPosition, inputVertices[ 0 ].mWorldPosition, inputVertices[ 10 ].mWorldPosition, inputVertices[ 11 ].mWorldPosition )
    };

    float midPatchTessFactor = CalculateTessellationfactor( midPatchMidpoint );
    float edgePatchTessFactors[] = {
        CalculateTessellationfactor( edgePatchMidpoint[ 3 ] ),
        CalculateTessellationfactor( edgePatchMidpoint[ 0 ] ),
        CalculateTessellationfactor( edgePatchMidpoint[ 1 ] ),
        CalculateTessellationfactor( edgePatchMidpoint[ 2 ] )
    };

    patch.mEdgeTess[ 0 ] = min( midPatchTessFactor, edgePatchTessFactors[ 0 ] );
    patch.mEdgeTess[ 1 ] = min( midPatchTessFactor, edgePatchTessFactors[ 1 ] );
    patch.mEdgeTess[ 2 ] = min( midPatchTessFactor, edgePatchTessFactors[ 2 ] );
    patch.mEdgeTess[ 3 ] = min( midPatchTessFactor, edgePatchTessFactors[ 3 ] );
    
    patch.mInsideTess[ 0 ] = midPatchTessFactor;
    patch.mInsideTess[ 1 ] = midPatchTessFactor;

    return patch;
}

[domain("quad")]
[partitioning("fractional_odd")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(4)]
[patchconstantfunc("PatchHS")]
HullOut hs_main( InputPatch<VertexOut, 12> verticeData, uint index : SV_OutputControlPointID )
{
    HullOut ret;

    ret.mWorldPosition = verticeData[ index ].mWorldPosition;
    ret.mTexcoord = verticeData[ index ].mTexcoord;

    return ret;
}

#endif

cbuffer PerFrameCB : register(CBUFFER_REGISTER_PER_FRAME)
{
    float4x4 gFrameViewProj;
    float4x4 gFrameView;
    float4x4 gFrameInvView;
    float4x4 gFrameInvProj;
    float3 gWorldEyePos;
    float gMinZ;
    float gMaxZ;
};

and inspecting the constant-buffer it looks like this:

constant_buffer.thumb.png.5ab4b71dda201cc77ae842ba257e3f94.png

The contents looks legit.

Again, the problem seems localized to this shader, but it might very well be a well-obscured problem.. it's bizare because I use constant buffers all the time and they work just fine. The same constant buffer contains other data like transformation matrices that work just fine.

Also for reference this is how I set my constant buffer data, which should work fine on x64 aswell


template <typename ContentType>
 DX11ConstantBuffer(ID3D11DevicePtr device, ID3D11DeviceContextPtr context, const CONSTANT_BUFFER_SLOT cbufferSlot) :
	mContext(context), mConstantBuffer(nullptr), mConstantBufferSlot(cbufferSlot)
{
	D3D11_BUFFER_DESC bufferDescription;
	ZeroMemory(&bufferDescription, sizeof(D3D11_BUFFER_DESC));
	bufferDescription.ByteWidth = static_cast<uint32_t>( sizeof(ContentType) );
	bufferDescription.Usage = D3D11_USAGE_DYNAMIC;
	bufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	DXCALL(device->CreateBuffer(&bufferDescription, NULL, &mConstantBuffer));
}

template <typename ContentType>
void SetData(const ContentType& content)
{
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));

    DXCALL(mContext->Map(mConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
    std::memcpy(mappedResource.pData, &content, sizeof(ContentType));
    mContext->Unmap(mConstantBuffer, 0);
}

 

I'm not familiar enough with DX11 to know offhand... are you using any given constant buffer only once per swap? Or do you know that you explicitly can re-use a constant buffer after issuing the draw call that uses it?

RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.

    float4x4 gFrameViewProj;
    float4x4 gFrameView;
    float4x4 gFrameInvView;
    float4x4 gFrameInvProj;
    float4 gWorldEyePos;
    float4 gMinZ_gMaxZ;

What happens if you do this..? Just get .x and .y from gMinZ_gMaxZ..

.:vinterberg:.

6 hours ago, vinterberg said:


    float4x4 gFrameViewProj;
    float4x4 gFrameView;
    float4x4 gFrameInvView;
    float4x4 gFrameInvProj;
    float4 gWorldEyePos;
    float4 gMinZ_gMaxZ;

What happens if you do this..? Just get .x and .y from gMinZ_gMaxZ..

No luck sadly. The struct that is normally mapped looks like this:


struct PerFrameCB
{
	Mat4 mViewProj;
	Mat4 mView;
	Mat4 mInvView;
	Mat4 mInvProj;
	Vec3 mWorldEyePos;
	float mMinZ;
	float mMaxZ;
	float __padding[ 3 ];

	PerFrameCB() {}
	PerFrameCB( const Mat4& viewProj, const Mat4& view, const Mat4& invView, const Mat4& invProj, const Vec3& worldEyePos, float minZ, float maxZ ) :
		mViewProj(viewProj),
		mView(view),
		mInvView(invView),
		mInvProj(invProj),
		mWorldEyePos( worldEyePos ),
		mMinZ( minZ ),
		mMaxZ( maxZ )
	{
	}
};

I tried your suggested changes to the shader and changed the struct accordingly:


struct PerFrameCB
{
	Mat4 mViewProj;
	Mat4 mView;
	Mat4 mInvView;
	Mat4 mInvProj;
	Vec4 mWorldEyePos;
	Vec4 mMinZ_mMaxZ;

	PerFrameCB() {}
	PerFrameCB( const Mat4& viewProj, const Mat4& view, const Mat4& invView, const Mat4& invProj, const Vec3& worldEyePos, float minZ, float maxZ ) :
		mViewProj(viewProj),
		mView(view),
		mInvView(invView),
		mInvProj(invProj),
		mWorldEyePos( worldEyePos, 0.0f ),
		mMinZ_mMaxZ( minZ, maxZ, 0.0f, 0.0f )
	{
	}
};

Still the same ?

 

10 hours ago, Wyrframe said:

I'm not familiar enough with DX11 to know offhand... are you using any given constant buffer only once per swap? Or do you know that you explicitly can re-use a constant buffer after issuing the draw call that uses it?

This CBuffer is set at the beginning of every frame only.

How about hardcoding gMin/MaxZ then, to see wether the issue is with those constants or cameraToPatchDistance is the culprit?
Maybe test some min/maxs that works, and then transfer them to the cbuffer values..

Could you post the relevant rendering code part(s)?

I'm still relatively sure that you need to pad cbuffer stuff though, and not just at the end but at each element (like my example)..?

.:vinterberg:.

Is swapchaindesc description in any disynchronization with device creation desciptor, as for window handle? Does the issue remain if you are fullscreen? In windowed mode OS composition comes to account, and you are compiled against many new library routines.

I'm gonna double-check so that the padding is correct (as per https://docs.microsoft.com/sv-se/windows/desktop/direct3dhlsl/dx-graphics-hlsl-packing-rules) for all the cbuffers ?

This topic is closed to new replies.

Advertisement