输入汇编程序 - 顶点着色器链接错误

Input Assembler - Vertex Shader Linkage error

我正在尝试使用 SpriteFontSpriteBatch 在我的 DX11 项目中渲染文本。

在添加代码之前一切正常,但是当取消注释代码时,出现以下错误并且没有任何渲染。

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. Semantic 'TEXCOORD' is defined for mismatched hardware registers between the output stage and input stage. [ EXECUTION ERROR #343: DEVICE_SHADER_LINKAGE_REGISTERINDEX]
D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. The input stage requires Semantic/Index (POSITION,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]
D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. The input stage requires Semantic/Index (NORMAL,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]

我已经尝试更改 DeviceContext 和 Device,但仍然出现相同的错误。

    //Draw Text
    g_spriteBatch = std::make_unique<SpriteBatch>(g_pImmediateContext);
    g_spriteFont = std::make_unique<SpriteFont>(g_pd3dDevice1, L"Data\game_font.spritefont");

    //Render Text
    g_spriteBatch->Begin();
    //g_spriteFont->DrawString(g_spriteBatch.get(), L"Score: 0", XMFLOAT2(0,0), DirectX::Colors::White);
    g_spriteBatch->End();

我试过使用不同的设备和 DeviceContext,但仍然出现相同的错误。


我一直在尝试许多不同的方法来让它工作。 这就是我的渲染函数目前的样子

更新时间:18/02/2019

void Render() {

    float backgroundColour[] = { 0.0f, 0.1f, 0.5f, 0.0f };
    g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, backgroundColour);
    g_pImmediateContext->ClearDepthStencilView(g_depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

    g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
    g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    g_pImmediateContext->RSSetState(g_rasterState);
    g_pImmediateContext->OMSetDepthStencilState(g_depthStencilState, 0);
    g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
    g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);

    UINT stride = sizeof(SimpleVertex);
    UINT offset = 0;

    g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
    g_pImmediateContext->Draw(3, 0);
    //g_pImmediateContext->DrawIndexed(36, 0, 0);

    //Draw Text
    spriteBatch->Begin();
    spriteFont->DrawString(spriteBatch.get(), L"Test", DirectX::XMFLOAT2(0, 0), DirectX::Colors::White);
    spriteBatch->End();

    //Present our back buffer to the front buffer
    g_pSwapChain->Present(0, NULL);

}

当运行项目时,出现如下错误 D3D11 ERROR: ID3D11DeviceContext::IASetVertexBuffers: A Buffer trying to be bound to slot 0 did not have the appropriate bind flag set at creation time to allow the Buffer to be bound as a VertexBuffer. [ STATE_SETTING ERROR #238: IASETVERTEXBUFFERS_INVALIDBUFFER]

这是为 tri 存储数据并设置 index/vertex 缓冲区的代码

    SimpleVertex vertices[] =
    {           
        SimpleVertex(-0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f),
        SimpleVertex( 0.0f,  0.5f, 1.0f, 1.0f, 0.0f, 0.0f),
        SimpleVertex( 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f),
    };

    D3D11_BUFFER_DESC bd = {};
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( SimpleVertex ) * ARRAYSIZE(vertices);
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bd.CPUAccessFlags = 0;
    bd.MiscFlags = 0;

    D3D11_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = vertices;
    hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
    if (FAILED(hr))
    {
        printf("Failed ro Create Vertex Buffer.");
        return hr;
    }

我把D3D11_BIND_INDEX_BUFFER改成了D3D11_BIND_VERTEX_BUFFER,还是出现同样的错误。

在此之后,还会产生以下警告。

D3D11 WARNING: ID3D11DeviceContext::Draw: Vertex Buffer at the input vertex slot 0 is not big enough for what the Draw*() call expects to traverse. This is OK, as reading off the end of the Buffer is defined to return 0. However the developer probably did not intend to make use of this behavior.  [ EXECUTION WARNING #356: DEVICE_DRAW_VERTEX_BUFFER_TOO_SMALL]

D3D11 WARNING: ID3D11DeviceContext::Draw: The size of the Constant Buffer at slot 0 of the Vertex Shader unit is too small (64 bytes provided, 192 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects.  [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL]

D3D11 WARNING: ID3D11DeviceContext::Draw: Input vertex slot 0 has stride 24 which is less than the minimum stride logically expected from the current Input Layout (32 bytes). This is OK, as hardware is perfectly capable of reading overlapping data. However the developer probably did not intend to make use of this behavior.  [ EXECUTION WARNING #355: DEVICE_DRAW_VERTEX_BUFFER_STRIDE_TOO_SMALL]

TL;DR: 在调用 Draw.

之前,您需要在每一帧设置立方体的输入布局、顶点着色器和像素着色器

具体来说,您忘记设置顶点缓冲区,因为您将代码注释掉了:

// >>> Set vertex buffer
//g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);

Your comment includes the Debug Device error you get that caused you to comment it out in the first place. It is pretty clear that your bug is actually where you call CreateBuffer for a VB without setting D3D11_BUFFER_DESC.BindFlags to D3D11_BIND_VERTEX_BUFFER.

在每次调用 Draw* 之前,您需要设置该抽奖所需的任何状态。

通过调用 SpriteFont::Begin/End 更改的确切状态记录在 wiki 中。特别是,它同时更改了输入布局和顶点着色器。您的 "cube drawing" 代码依赖于这些状态在每一帧之间保持不变,这实际上只适用于微不足道的情况。

在每个渲染帧的开始,您应该:

  • 清除渲染目标and/or深度模板缓冲区
  • 调用 OMSetRenderTargets 设置您的主要渲染目标
  • 调用 RSSetViewports 设置视口状态

然后在每次调用之前Draw*设置你使用的所有状态。对于简单的 3D 立方体绘图,这至少包括:

  • 渲染目标视图、深度模板视图、视口(在简单情况下可以在帧开始时设置一次)
  • BlendState、DepthStencilState、RasterizerState
  • 输入布局
  • 任何常量缓冲区、像素着色器、顶点着色器和任何所需的 SamplerState 或着色器资源。

DirectX Tool Kit 包含许多帮助程序,例如 CommonStateEffects 以使其更易于管理,但您仍然必须确保 all 您依赖的状态已设置。

状态管理

状态管理是 Direct3D 编程的基本要求,对性能尤为重要。在您调用 Draw* 的任何给定时间,都需要在渲染管道上设置该绘制所需的所有状态,但您确实希望尽量减少更改状态的次数以提高性能。

DirectX 工具包:

等实用程序库中确实存在三种基本的状态管理策略
  1. 捕获并恢复所有状态。这就是已弃用的 D3DX9 库中遗留的 Direct3D 9 时代 ID3DXFont 代码所做的。在 Direct3D 9 中,这最初是通过运行时跟踪当前状态这一事实来实现的。 "PURE" 设备是专门为消除这种开销而引入的,因此添加了状态块作为尝试使其工作的一种方式。 Direct3D 11 中的状态向量也相当大,运行时不支持同样的 'state blocks'。总的来说,这种方法非常容易使用,但速度不快。

  2. 设置所有需要的状态,之后清除使用的状态。另一种选择是每次使用 SpriteBatch::Begin 等辅助函数时设置所有状态,然后在调用 SpriteBatch::End 时清除它使用的任何状态值。这种方法在概念上也很简单,因为没有挥之不去的副作用,但在实践中也很慢,因为有很多额外的调用来设置空状态。

  3. 设置需要的状态,让后面的绘制清理状态。这是 DirectX Tool Kit 使用的选项。我设置了我需要绘制的状态,然后不管它。如果在帧结束之前调用我的代码后还有更多绘图要做,则需要设置它们。在实践中,DirectX 工具包 中的助手仅使用基本上所有绘图通用的标准状态,但如果您没有意识到这种行为,您肯定会得到一些奇怪的交互。例如,如果您在调用 SpriteBatch 之前设置了几何着色器,那么当您调用 End 时会发生奇怪的事情。我假设如果你使用了超出通常 VS/PS 组合的东西,你在调用我的代码之前清除了它。我在 wiki 上记录了每个助手使用的所有状态,在实践中,这是性能和可预测行为的良好平衡,尽管它有时会让人感到困惑。

Direct3D 11 与 Direct3D 12 注释

在 Direct3D 11 中,当您调用 Present 时在一帧结束时设置的任何状态仍会在下一帧开始时设置,因此您会看到一些较旧的教程只设置一次状态在开始渲染循环之前假设保持不变。这对于 Direct3D 12 不是 是正确的,其中状态被 Present 完全重置。因此,最好养成每帧都设置所有状态的习惯。验证您是否正确执行此操作的一种方法是在每帧 Present 之后立即调用 ClearState,尽管您可能只想在调试版本中执行此操作,因为这会浪费时间,否则在正确编写的程序中.

在 Direct3D 11 中,调用 RSSetViewports 来设置视口也会将剪裁矩形初始化为相同的值。在 Direct3D 12 中,您需要通过调用 RSSetScissorRects 明确地执行此操作,否则您最终会得到一个 (0,0,0,0) 剪切矩形,它会剪切掉所有内容。

有关这些基本渲染循环的一些示例,请参阅 GitHub