Sign in to follow this  

SetRenderState(D3DRS_CLIPPING, FALSE) does not disable clipping

This topic is 4165 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, I have an application where the viewport is smaller than the entire render target, but I don't want the geometry to be clipped to the viewport. I assumed SetRenderState(D3DRS_CLIPPING, FALSE) would do the trick but it still clips to the viewport. So how do I really disable clipping, and what is D3DRS_CLIPPING actually used for? Thanks, c0d1f1ed

Share this post


Link to post
Share on other sites
The viewport, by definition, is the active partition of your render target. Vertices are translated and scaled from the projection(screen) space coordinates ([-1,1] in x/y) to screen(device/display) space coordinates, which is a rectangular area of the total render target. So if you set the viewport to,say, the upper right half of the screen, the rendered image will be scaled to fit the rectangle. This translation and scaling occurs independent whether you use fixed or programmable pipeline.

Edit:
D3DRS_CLIPPING on the other hand, is used to enable/disable geometry clipping planes defined in world space. Primitives behind the plane(s) will not be rendered. I mixed this with D3DRS_CLIPPLANEENABLE, which is the world-space clipping stuff. However, neither have anything to do with viewports.

It seems you want to move the center of the view while still rendering to the whole render target area. This can be done either by altering the view or projection matrices with custom translation. This sounds like a good time to brush up your projection matrix math algebra.

Share this post


Link to post
Share on other sites
Read up on Guard-band clipping. It might also be an issue with a scissor rect.

Does this happen when you run with the Reference Rasterizer?

Share this post


Link to post
Share on other sites
Quote:
Original post by clb
The viewport, by definition, is the active partition of your render target. Vertices are translated and scaled from the projection(screen) space coordinates ([-1,1] in x/y) to screen(device/display) space coordinates, which is a rectangular area of the total render target. So if you set the viewport to,say, the upper right half of the screen, the rendered image will be scaled to fit the rectangle. This translation and scaling occurs independent whether you use fixed or programmable pipeline.

I know. But it's just a scaling of (x, y) from [-1, 1][-1, 1] to the viewport dimensions in screen space, so when clipping is disabled (which prevents coordinates to go outside [-1, 1][-1, 1] before the viewport transform) it should be possible to render outside the viewport.
Quote:
Edit:
D3DRS_CLIPPING on the other hand, is used to enable/disable geometry clipping planes defined in world space. Primitives behind the plane(s) will not be rendered. I mixed this with D3DRS_CLIPPLANEENABLE, which is the world-space clipping stuff. However, neither have anything to do with viewports.

So what does D3DRS_CLIPPING really do then? Whether I enable or disable it, my geometry still gets clipped to the viewport.
Quote:
It seems you want to...

I'm just exploring the API...

Thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by don
Read up on Guard-band clipping. It might also be an issue with a scissor rect.

Guard band clipping is an implementation optimization, it doesn't have anything to do with the actual clipping behaviour of the API. I'm not using any scissor rectangles.
Quote:
Does this happen when you run with the Reference Rasterizer?

Yes.

Share this post


Link to post
Share on other sites
Here's a small test application illustrating the behaviour:

#include <d3d9.h>
#include <d3dx9.h>

HWND hwnd = 0;
LPDIRECT3D9 d3d = 0;
LPDIRECT3DDEVICE9 device = 0;
LPDIRECT3DVERTEXBUFFER9 vertexBuffer = 0;
LPDIRECT3DINDEXBUFFER9 indexBuffer = 0;

float spinX = 0.0f;
float spinY = 0.0f;

struct Vertex
{
float x;
float y;
float z;
DWORD color;

enum FVF
{
FVF_Flags = D3DFVF_XYZ | D3DFVF_DIFFUSE
};
};

Vertex cubeVertices[] =
{
{-1.0f, 1.0f,-1.0f, D3DCOLOR_COLORVALUE(1.0, 0.0, 0.0, 1.0) }, // 0
{ 1.0f, 1.0f,-1.0f, D3DCOLOR_COLORVALUE(0.0, 1.0, 0.0, 1.0) }, // 1
{-1.0f,-1.0f,-1.0f, D3DCOLOR_COLORVALUE(0.0, 0.0, 1.0, 1.0) }, // 2
{ 1.0f,-1.0f,-1.0f, D3DCOLOR_COLORVALUE(1.0, 1.0, 0.0, 1.0) }, // 3
{-1.0f, 1.0f, 1.0f, D3DCOLOR_COLORVALUE(1.0, 0.0, 1.0, 1.0) }, // 4
{-1.0f,-1.0f, 1.0f, D3DCOLOR_COLORVALUE(0.0, 1.0, 1.0, 1.0) }, // 5
{ 1.0f, 1.0f, 1.0f, D3DCOLOR_COLORVALUE(1.0, 1.0, 1.0, 1.0) }, // 6
{ 1.0f,-1.0f, 1.0f, D3DCOLOR_COLORVALUE(1.0, 0.0, 0.0, 1.0) } // 7
};

WORD cubeIndices[] =
{
0, 1, 2, 3, // Quad 0
4, 5, 6, 7, // Quad 1
4, 6, 0, 1, // Quad 2
5, 2, 7, 3, // Quad 3
1, 6, 3, 7, // Quad 4
0, 2, 4, 5 // Quad 5
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void init(void);
void shutDown(void);
void render(void);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX windowClass;
MSG message;

memset(&message, 0, sizeof(message));

windowClass.lpszClassName = "MY_WINDOWS_CLASS";
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hInstance;
windowClass.hIcon = 0;
windowClass.hIconSm = 0;
windowClass.hCursor = LoadCursor(0, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.lpszMenuName = 0;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;

if(!RegisterClassEx(&windowClass)) return E_FAIL;

hwnd = CreateWindowEx(0, "MY_WINDOWS_CLASS", "Viewport test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, 0);

if(hwnd == 0) return E_FAIL;

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

init();

while(message.message != WM_QUIT)
{
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
else
{
render();
}
}

shutDown();

UnregisterClass("MY_WINDOWS_CLASS", windowClass.hInstance);

return message.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static POINT previousMousePosition;
static POINT currentMousePosition;
static bool mousing;

switch(msg)
{
case WM_KEYDOWN:
if(wParam == VK_ESCAPE)
{
PostQuitMessage(0);
}
break;
case WM_LBUTTONDOWN:
previousMousePosition.x = currentMousePosition.x = LOWORD(lParam);
previousMousePosition.y = currentMousePosition.y = HIWORD(lParam);
mousing = true;
break;
case WM_LBUTTONUP:
mousing = false;
break;
case WM_MOUSEMOVE:
currentMousePosition.x = LOWORD(lParam);
currentMousePosition.y = HIWORD(lParam);

if(mousing)
{
spinX -= (currentMousePosition.x - previousMousePosition.x);
spinY -= (currentMousePosition.y - previousMousePosition.y);
}

previousMousePosition.x = currentMousePosition.x;
previousMousePosition.y = currentMousePosition.y;
break;
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}

return 0;
}

void init(void)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);

D3DDISPLAYMODE d3ddm;

d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));

d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &device);

device->SetRenderState(D3DRS_LIGHTING, FALSE);
device->SetRenderState(D3DRS_ZENABLE, TRUE);
device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);

device->CreateVertexBuffer(8*sizeof(Vertex), D3DUSAGE_WRITEONLY | D3DUSAGE_DONOTCLIP, Vertex::FVF_Flags, D3DPOOL_DEFAULT, &vertexBuffer, 0);

void *vertices = 0;

vertexBuffer->Lock(0, sizeof(cubeVertices), &vertices, 0);
memcpy(vertices, cubeVertices, sizeof(cubeVertices));
vertexBuffer->Unlock();

device->CreateIndexBuffer(24 * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &indexBuffer, 0);

void *indices = 0;
indexBuffer->Lock(0, sizeof(cubeIndices), &indices, 0);
memcpy(indices, cubeIndices, sizeof(cubeIndices));
indexBuffer->Unlock();
}

void shutDown(void)
{
if(vertexBuffer != 0) vertexBuffer->Release();
if(indexBuffer != 0) indexBuffer->Release();
if(device != 0) device->Release();
if(d3d != 0) d3d->Release();
}

void render(void)
{
D3DVIEWPORT9 viewport;

viewport.X = 0;
viewport.Y = 0;
viewport.Width = 320;
viewport.Height = 480;
viewport.MinZ = 0;
viewport.MaxZ = 1;

device->SetViewport(&viewport);
device->SetRenderState(D3DRS_CLIPPING, FALSE);

D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH(&matProj, 45.0f, 640.0f / 480.0f, 0.1f, 100.0f);
// Scale and translate back to the center of the window
matProj._11 *= 2;
matProj._31 = 1;
device->SetTransform(D3DTS_PROJECTION, &matProj);

device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0);

D3DXMATRIX matTrans;
D3DXMATRIX matRot;
D3DXMATRIX matWorld;

D3DXMatrixTranslation(&matTrans, 0.0f, 0.0f, 4.0f);

D3DXMatrixRotationYawPitchRoll(&matRot, D3DXToRadian(spinX), D3DXToRadian(spinY), 0.0f);
matWorld = matRot * matTrans;
device->SetTransform(D3DTS_WORLD, &matWorld);

device->BeginScene();

device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex));
device->SetIndices(indexBuffer);
device->SetFVF(Vertex::FVF_Flags);

device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 0, 2);
device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 4, 2);
device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 8, 2);
device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 12, 2);
device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 16, 2);
device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 20, 2);

device->EndScene();
device->Present(0, 0, 0, 0);
}


You can spin the cube with the mouse. In render(), the viewport is set to the left half of the window, and clipping is disabled. The perspective transform positions the cube back to the center of the window, but because clipping is somehow still active you can only see the left half of it.

Share this post


Link to post
Share on other sites
I dont have an installed SDK to hand, so I might be wrong... but I think you'll need to check out the other D3DXMatrixPerspective**() functions.

I've done this before to implement different (but similar in implementation) effects to the one you describe. Post projection transform your geometry will be in -1...+1 coordinates, the viewport matrix will then remap these to your 0..w and 0..h rasterization coordinates.

Off the top of my head, the D3DXMatrixPerspectiveOffCenterLH() (or similar) allows you to specify a top-left and bottom-right rather than just a height/width.

The SDK documentation should have details on the viewport matrix so you can see the mathemagical details if needs be.

hth
Jack

Share this post


Link to post
Share on other sites
The guard band was a feature that showed up on pre-TNL hardware that allowed apps to disable software clipping and throw the polys that crossed a viewport edge down to the hardware for rasterization and not have to worry about them being clipped.

As you're probably aware, what you want to do is resize your viewport to the render target rect and then draw your polygons. That will give you the result you seem to want.

Share this post


Link to post
Share on other sites
Quote:
Original post by donAs you're probably aware, what you want to do is resize your viewport to the render target rect and then draw your polygons. That will give you the result you seem to want.

I'm sorry but I don't want a result other than understanding the interaction between viewports and clipping for the Direct3D API. More specifically, I want to know what D3DRS_CLIPPING does or doesn't do. So far it doesn't seem to do anything, while the theory and the API documentation tell me it should allow the geometry to be rendered outside the viewport.

Share this post


Link to post
Share on other sites
I think I figured it out... D3DRS_CLIPPING is a legacy render state from the times when clipping was still mostly done in software. On modern hardware it's simply ignored and clipping is always enabled. I think this is what you were trying to tell me with the guard-band clipping, but I didn't know that it was always implicitely enabled.

I think it's very odd that they've kept this in Direct3D 9. I don't think there's any hardware with a DX9 driver that can't do hardware clipping. Even if it does exist, I doubt there's any application still doing its own clipping...

Share this post


Link to post
Share on other sites
Nick,

you only care for this bit if you're doing software vertex processing.
Modern hardware are just able to handle clipping at full speed (it's hardly ever the bottleneck. At least on Nvidia hardware.), so the clipping renderstate can be ignored. They also clip the transformed and lit vertices.

On your end if you're trying to improve your software renderer, you have to assume that all geometry needs to be clipped to the viewport. The only case where you could skip this is if the current vertex buffer was declared with the DONOTCLIP usage (meaning that the data inside it is safe and falls inside the frustum), and the D3DRS_CLIPPING disabled (both usage and renderstate must match).

LeGreg
Edit: well i guess you figured that out..

Share this post


Link to post
Share on other sites
OK, I'll try to explain it in another way.

Imagine that you're in 1998 and most 3D cards are rasterization-only devices. The transformation and lighting is still being performed by the D3D runtime or in the application. Then one day the hardware vendors say to MS, "You know, we've got the capability to cull pixels that are drawn outside the viewport in our hardware for free, so give us caps to expose that to ISVs so that they won't have to bother with slower software clipping performed by the runtime just to keep pixels from extending beyond the viewport rect."

So the guard band clipping extents were added to the caps bits and IHVs were evangelizing this new feature to ISVs (search the IHV websites for "guard band" some of the docs are still there). They told ISVs that there's no point in using the slower D3D runtime frustum clipping if you know your poly is between the front and back planes, and you know that it won't extend beyond the guard band limits exposed by the caps.

So ISVs began testing meshes against the frustum and if they were completely outside, they would reject the mesh. If they were completely inside, or partially inside, but completely inside the guard band they would accept them and send them to the hardware (disabling the D3D clipping renderstate).

Now shift forward to 2006. What do you think should happen if you run one of these older apps that disabled clipping and instead relied on the automatic guard band clipping provided by the hardware to cull pixels that extended beyond the viewport rect? Those apps still must continue to work as they did in the past.

In hindsight, perhaps MS should have added a D3DRS_GUARDBANDCLIPPING render state (if the hdwe had the capability of disabling this) but it's too late for that now.

I hope that helps explain things a bit.

Share this post


Link to post
Share on other sites
Quote:
Original post by LeGreg
Modern hardware are just able to handle clipping at full speed (it's hardly ever the bottleneck. At least on Nvidia hardware.), so the clipping renderstate can be ignored.

Yeah, but it's still a bit odd that they changed API behaviour because of this. I mean, it used to be possible to render outside the viewport by simply disabling clipping. And that's also the theoretically 'correct' behaviour. I sort of expected them to automatically clip to the render target boundaries (just to avoid writing outside memory) when frustum clipping is disabled.

I wonder what OpenGL does...
Quote:
On your end if you're trying to improve your software renderer, you have to assume that all geometry needs to be clipped to the viewport.

That's already the case. :-) In fact I already ignore the D3DRS_CLIPPING state (I remember it caused trouble with Max Payne). But I always thought that was a hack and I was missing something else...
Quote:
The only case where you could skip this is if the current vertex buffer was declared with the DONOTCLIP usage (meaning that the data inside it is safe and falls inside the frustum), and the D3DRS_CLIPPING disabled (both usage and renderstate must match).

Both HAL and REF appear to still clip to the viewport in this case.
Quote:
Edit: well i guess you figured that out..

Still, thanks for confirming it!

Share this post


Link to post
Share on other sites
Quote:
Original post by C0D1F1ED
Quote:
The only case where you could skip this is if the current vertex buffer was declared with the DONOTCLIP usage (meaning that the data inside it is safe and falls inside the frustum), and the D3DRS_CLIPPING disabled (both usage and renderstate must match).

Both HAL and REF appear to still clip to the viewport in this case.


Sure they do, but it's an option that you have.

LeGreg

Share this post


Link to post
Share on other sites
Quote:
Original post by LeGreg
Sure they do, but it's an option that you have.

Not really. If both HAL and REF do it then it's likely that people rely on it without checking whether it works when the device doesn't support it... Besides, it doesn't really matter. Clipping is quite fast and I rather haver one consistent design. I even doubt any application actually uses it, since modern hardware gets no benefit from it.

Anyway, thanks all for the information!

Share this post


Link to post
Share on other sites

This topic is 4165 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this