Clarification about shaders and Directx11

Started by
11 comments, last by Federico Barlotti 7 years, 11 months ago

I've seen written all over the place, since DX10 the FFP is no longer supported and you need to set up shaders, otherwise there is no way to draw things. In basically every tutorials, in fact, you have to set up the input layout, fill in the "IA", etc.

But... I tried to not do any of that, and just create a vertex buffer, fill it with 3 (custom declared) "VERTEX" objects, and draw it. No shaders are being loaded, compiled, created nor set as active anywhere in the code, yet it renders a perfect moving triangle on screen.

Am I missing something very obvious here?

Advertisement

Yeah that should not work. Without a vertex shader, there's no way that D3D knows where those vertices should be placed on the screen.

What's your code look like?

I set up the wievport, swapchain, DX11 device, etc., then create a vertex buffer and set it as active


D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));

bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(VERTEX) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = dev->CreateBuffer(&bd, NULL, &pVBuffer);

UINT stride = sizeof(VERTEX);
UINT offset = 0;
devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);

Then, in the main frame function, create and update an array of three vertices...


devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));

static float r = 0.5;
static float h = 0;

h += 0.001;

if (h >= 2 * D3DX_PI)
{
    h = 0;
}

vertices[0] = { r*cos(-(D3DX_PI / 6) + h), r*sin(-(D3DX_PI / 6) + h), 0.0, D3DXCOLOR(1.0, 0.0, 0.0, 1.0) };
vertices[1] = { r*cos((D3DX_PI / 2) + h), r*sin((D3DX_PI / 2) + h), 0.0, D3DXCOLOR(0.0, 1.0, 0.0, 1.0) };
vertices[2] = { r*cos(7 * (D3DX_PI / 6) + h), r*sin(7 * (D3DX_PI / 6) + h), 0.0, D3DXCOLOR(0.0, 0.0, 1.0, 1.0) };

And fill the buffer, which is already active


fillBuffer<VERTEX[]>(dev, devcon, &pVBuffer, vertices, sizeof(VERTEX)*3);

...

template <typename T> HRESULT fillBuffer(ID3D11Device *dev, ID3D11DeviceContext *devcon, ID3D11Buffer **ppOut, T pIn, UINT vSize)
{
	HRESULT hr;

	D3D11_MAPPED_SUBRESOURCE ms;
	hr = devcon->Map(*ppOut, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
	if (FAILED(hr))
		return hr;
	memcpy(ms.pData, pIn, vSize);
	devcon->Unmap(*ppOut, NULL);

	return S_OK;
}

Then I draw the triangle and present the frame


devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
devcon->Draw(3, 0);

return swapchain->Present(0, 0);

And voilà.

Admittedly, if I try to use the shader I was working on, the output image is exactly the same, but I tried cleaning every part of the code where the shader is used or set up and it still works. I even renamed the .hlsl file and recompiled everything to be sure. Could it be a caching/partial compilation issue?

Excluding the possibility that you did set a shader earlier and didn't unset it, or some 3rd party dll did (such as Direct2D), 3D APIs are like web browsers: when you do something the docs specifically tell you not to do but it still works on your machine, it doesn't mean it will work on other machines.

Hmm, is it a general statement or did I do something very bad in my code?

I am including the FBX SDK .dll since I would have tried and tackled it in a later time, though I'm not doing anything with it. Could that be the problem?

Just call ID3D11DeviceContext::VSGetShader or ID3D11DeviceContext::PSGetShader to see what shader is bound.

They both return a null pointer, and for some reason now the GPU sometimes crashes when I attempt to start the program or resume it after pausing the debug... I'm not gonna attempt to run it without a shader anymore though, since the GPU apparently can't recover if it crashes a second time (thus requiring me to reboot). Maybe my GPU is just weird, I dunno.

Now, onto new and painful issues, since I just can't get the matrix buffer to be accepted by the shader...

Yes, the documentation for all of the *SetShader calls states:

Passing in NULL disables the shader for this pipeline stage.

However, the Shader Stages page also states:

The vertex shader stage must always be active for the pipeline to execute.

So by running with a NULL vertex shader, one of two things is happening: either your vertex shader isn't actually NULL, or your driver has a bug that is allowing the graphics pipeline to execute without a VS.

Either way you're relying on undocumented, undefined or erroneous behaviour. Instead you should familiarise yourself with the way things are supposed to work according to the documentation, and rely on that instead.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

There is no SetShader call active anywhere in the code, so it's most probably the second one.

And I know that isn't the way it's supposed to be done, I was just wondering why it just so happened to work magically like that.

Try specifying D3D11_CREATE_DEVICE_DEBUG when you create the device and see if it splits out any warnings/errors for you.

This topic is closed to new replies.

Advertisement