DirectX11, làm cách nào để quản lý và cập nhật nhiều bộ đệm liên tục shader?


13

Được rồi, tôi đang gặp khó khăn trong việc nắm bắt các bộ đệm liên tục bị ràng buộc với giai đoạn đường ống và được cập nhật. Tôi hiểu rằng DirectX11 có thể có tới 15 bộ đệm liên tục shader mỗi giai đoạn và mỗi bộ đệm có thể chứa tới 4096 hằng số. Tuy nhiên, tôi không hiểu liệu ID3D11Buffer COM được sử dụng để tương tác với các bộ đệm không đổi chỉ là một cơ chế (hoặc tay cầm) được sử dụng để lấp đầy các vị trí bộ đệm này hoặc nếu đối tượng thực sự tham chiếu một trường hợp cụ thể của dữ liệu bộ đệm được đẩy qua lại giữa GPU và CPU.

Tôi nghĩ rằng sự nhầm lẫn của tôi về chủ đề này là nguyên nhân của một vấn đề tôi đang sử dụng hai bộ đệm liên tục khác nhau.

Dưới đây là một số ví dụ mã shader.

cbuffer PerFrame : register(b0) {
    float4x4 view;
};

cbuffer PerObject : register(b1) {
    float4x4 scale;
    float4x4 rotation;
    float4x4 translation;
};

Cách thức tổ chức mã của tôi, máy ảnh sẽ xử lý cập nhật dữ liệu liên quan trên mỗi khung hình và GameObject sẽ tự cập nhật dữ liệu của từng đối tượng. Cả hai lớp đều có ID3D11Buffer riêng được sử dụng để thực hiện việc này (Sử dụng kiến ​​trúc trung tâm, do đó, một lớp GameObject sẽ xử lý kết xuất tất cả các GameObject được kích hoạt trên thế giới).

Vấn đề là tôi chỉ có thể nhận được một bản cập nhật tại một thời điểm, tùy thuộc vào vị trí và tôi cho rằng thứ tự cập nhật một bộ đệm được điền trong khi bộ đệm khác bị xóa.

Đây thực chất là mã của tôi. Cả hai lớp sử dụng logic cập nhật giống hệt nhau.

static PerObjectShaderBuffer _updatedBuffer; // PerFrameShaderBuffer if Camera class
_updatedBuffer.scale       = _rScale;
_updatedBuffer.rotation    = _rRotation;
_updatedBuffer.translation = _rTranslation;
pDeviceContext->UpdateSubresource(pShaderBuffer, 0 , 0, &_updatedBuffer, 0, 0);

pDeviceContext->VSSetShader(pVShader->GetShaderPtr(), 0, 0);
pDeviceContext->PSSetShader(pPShader->GetShaderPtr(), 0, 0);
pDeviceContext->VSSetConstantBuffers(1, 1, &pShaderBuffer);
pDeviceContext->IASetVertexBuffers(0, 1, &pVertexBuffer, &vStride, &_offset );
pDeviceContext->IASetPrimitiveTopology(topologyType);
pDeviceContext->Draw(bufSize, 0);

Câu hỏi chính của tôi là -

  • Tôi có cần phải thiết lập hoặc liên kết ShaderBuffer để cập nhật nó với lệnh gọi UpdateSubresource không? (Có nghĩa là chỉ thao tác với nó khi nó đang trong đường ống) Hoặc đó là một đốm dữ liệu sẽ được gửi bằng lệnh gọi VSSetConstantBuffer? (Có nghĩa là thứ tự ràng buộc và cập nhật dữ liệu không thành vấn đề, tôi có thể cập nhật nó trong đường ống hoặc bằng cách nào đó trên cpu)
  • Khi cài đặt hoặc liên kết bộ đệm, tôi có cần tham chiếu vị trí 0 để cập nhật bộ đệm PerFrame và vị trí 1 để cập nhật bộ đệm PerObject không? Có thể một số loại nhầm lẫn với cuộc gọi này trong mã của tôi khiến tất cả các bộ đệm bị ghi đè?
  • Làm thế nào để D3D11 biết bộ đệm nào tôi muốn cập nhật hoặc bản đồ? Nó có biết từ COM ID3D11Buffer được sử dụng không?

Biên tập -

Thay đổi các thẻ đăng ký bộ đệm không đổi trong ví dụ trên. Việc sử dụng (cb #) thay vì (b #) đã ảnh hưởng đến bộ đệm cập nhật chính xác vì một số lý do. Không chắc chắn nơi tôi đã chọn cú pháp ban đầu hoặc nếu nó hợp lệ, nhưng nó dường như là vấn đề chính của tôi.

Câu trả lời:


18

ID3D11Buffer tham chiếu một đoạn bộ nhớ thực tế chứa dữ liệu của bạn, cho dù đó là bộ đệm đỉnh, bộ đệm không đổi hay bất cứ thứ gì.

Bộ đệm liên tục hoạt động tương tự như bộ đệm đỉnh và các loại bộ đệm khác. Cụ thể, dữ liệu trong chúng không được GPU truy cập cho đến khi nó thực sự kết xuất lại khung, do đó, bộ đệm phải duy trì hiệu lực cho đến khi GPU được thực hiện với nó. Bạn nên đệm đôi mỗi bộ đệm không đổi, để bạn có một bản sao để cập nhật cho khung tiếp theo và một bản sao cho GPU để đọc trong khi hiển thị khung hiện tại. Điều này tương tự với cách bạn sẽ thực hiện bộ đệm đỉnh động cho một hệ thống hạt hoặc tương tự.

Các register(cb0), register(cb1)thiết lập trong tương ứng HLSL với các khe trong VSSetConstantBuffers. Khi bạn cập nhật các hằng số trên mỗi khung bạn sẽ làm VSSetConstantBuffers(0, 1, &pBuffer)để đặt CB0 và khi bạn cập nhật các hằng số cho từng đối tượng bạn sẽ làm VSSetConstantBuffers(1, 1, &pBuffer)để đặt CB1. Mỗi cuộc gọi chỉ cập nhật các bộ đệm được tham chiếu bởi các tham số bắt đầu / đếm và không chạm vào các cuộc gọi khác.

Bạn không cần phải liên kết bộ đệm để cập nhật nó với UpdateSubresource. Trên thực tế, nó không nên bị ràng buộc khi bạn cập nhật nó, hoặc điều này có thể buộc trình điều khiển tạo thêm các bản sao bộ nhớ trong (xem trang MSDN cho UpdateSubresource, đáng chú ý là các nhận xét về sự tranh chấp về một trang xuống).

Tôi không chắc ý của bạn là gì khi "Làm thế nào để D3D11 biết bộ đệm nào tôi muốn cập nhật hoặc bản đồ?" Nó cập nhật hoặc ánh xạ một con trỏ mà bạn đã truyền vào.


3

Dường như có rất nhiều nhầm lẫn xung quanh chủ đề cần liên kết lại bộ đệm liên tục sau khi cập nhật chúng. Khi tôi đang tìm hiểu về điều này, tôi đã thấy rất nhiều chủ đề và thảo luận với những ý kiến ​​trái chiều về điều này. Cụ thể là câu trả lời tốt nhất ở đây, khuyên bạn nên gọi XXSetConstantBufferssau khi bạn cập nhật qua UpdateSubresourcehoặc Map/Unmap.

Ngoài ra, một số mẫu và tài liệu MSDN D3D dường như sử dụng mẫu này, ràng buộc (gọi XXSetConstantBuffers) trên mỗi khung hoặc thậm chí trên mỗi cơ sở đối tượng được vẽ, mặc dù chúng chỉ cập nhật bộ đệm hiện có và không thay đổi vị trí cụ thể với bộ đệm hoàn toàn khác .

Tôi nghĩ rằng quan niệm sai lầm tồi tệ nhất là XXSetConstantBuffersthực sự "gửi dữ liệu bạn đã cập nhật trước đó cho GPU hoặc thông báo về bản cập nhật, để nó nhận các giá trị mới - dường như là hoàn toàn sai.

Thật vậy, khi sử dụng UpdateSubresourcehoặc Map/Unmap, tài liệu nói rằng GPU có thể tạo ra nhiều bản sao bên trong nếu nó vẫn cần dữ liệu cũ, nhưng điều này không gây lo ngại cho người dùng API khi cập nhật bộ đệm đã bị ràng buộc. Do đó, nhu cầu rõ ràng không ràng buộc dường như không cần thiết.

Trong quá trình thử nghiệm của tôi, tôi đã đi đến kết luận rằng không cần thiết phải liên kết lại bộ đệm thông qua XXSetConstantBufferssau khi cập nhật chúng, trừ khi chúng chưa bị ràng buộc! Miễn là bạn sử dụng cùng một bộ đệm (được chia sẻ giữa các shader, các giai đoạn đường ống đồng đều) đã từng bị ràng buộc (ví dụ ở giai đoạn khởi động), bạn không cần phải liên kết lại chúng - chỉ cần cập nhật chúng.

Một số mã để thể hiện tốt hơn bản chất thử nghiệm của tôi:

// Memory double of the buffer (static)
ConstBuffer* ShaderBase::CBuffer = (ConstBuffer*)_aligned_malloc(sizeof(ConstBuffer), 16);
// Hardware resource pointer (static)
ID3D11Buffer* ShaderBase::m_HwBuffer = nullptr;

void ShaderBase::Buffer_init()
{
     // Prepare buffer description etc.
     // Create one global buffer shared across shaders
     result = device->CreateBuffer(&cBufferDesc, NULL, &m_HwBuffer);
     BindConstBuffer();
}

...

void ShaderBase::BindConstBuffer()
{
     // Bind buffer to both VS and PS stages since it's a big global one
     deviceContext->VSSetConstantBuffers(0, 1, &m_HwBuffer);
     deviceContext->PSSetConstantBuffers(0, 1, &m_HwBuffer);
}

...
bool ShaderBase::UpdateConstBuffers()
{
    ...
    D3D11_MAPPED_SUBRESOURCE mappedResource;

    // Lock the constant buffer so it can be written to.
    deviceContext->Map(m_HwBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

    // Get a pointer to the data in the constant buffer.
    ConstBuffer* dataPtr = (ConstBuffer*)mappedResource.pData;
    memcpy(dataPtr, CBuffer, sizeof(ConstBuffer));

    // Unlock the constant buffer.
    deviceContext->Unmap(m_HwBuffer, 0);
    return true;
}

// May be called multiple times per frame (multiple render passes)
void DrawObjects()
{
    // Simplified version
    for each Mesh _m to be drawn
    {
        // Some changes are per frame - but since we have only one global buffer to which we 
        // write with write-discard we need to set all of the values again when we update per-object
        ShaderBase::CBuffer->view = view;
        ShaderBase::CBuffer->projection = projection;
        ShaderBase::CBuffer->cameraPosition = m_Camera->GetPosition();

        ... 

        ShaderBase::CBuffer->lightDirection = m_Light->GetDirection();

        ShaderBase::CBuffer->lightView = lightView;
        ShaderBase::CBuffer->lightProjection = lightProjection;
        ShaderBase::CBuffer->world = worldTransform;

        // Only update! No rebind!
        if (ShaderBase::UpdateConstBuffers() == false)
            return false;

        _m->LoadIABuffers(); // Set the vertex and index buffers for the mesh
        deviceContext->DrawIndexed(_m->indexCount, 0, 0);
    }
}

Dưới đây là một số chủ đề từ internet (diễn đàn gamedev) dường như chấp nhận và đề xuất phương pháp này: http://www.gamedev.net/topic/649410-set-constant-buffers-every-frame/?view=findpost&p=5105032http://www.gamedev.net/topic/647203-updating-constant-buffers/#entry5090000

Để kết luận, có vẻ như không cần ràng buộc trừ khi bạn thay đổi hoàn toàn bộ đệm nhưng miễn là bạn chia sẻ bộ đệm và cách bố trí giữa các shader (khuyến nghị thực hành) nên được thực hiện trong những trường hợp sau:

  • Khi khởi động - ràng buộc ban đầu - sau khi tạo bộ đệm chẳng hạn.
  • Nếu bạn cần / đã thiết kế để sử dụng nhiều bộ đệm được liên kết với một vị trí cụ thể của một hoặc nhiều giai đoạn.
  • Sau khi xóa trạng thái của thiết bịContext (khi thay đổi kích thước bộ đệm / cửa sổ)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.