DirectX11, मैं कई शेडर निरंतर बफ़र्स को कैसे प्रबंधित और अपडेट कर सकता हूं?


13

ठीक है, मैं एक कठिन समय दे रहा हूं कि कैसे निरंतर बफ़र्स एक पाइपलाइन चरण से बंधे हैं और अपडेट किए गए हैं। मैं समझता हूं कि DirectX11 में प्रति चरण 15 shader- स्थिर बफ़र्स हो सकते हैं और प्रत्येक बफ़र 4096 स्थिरांक तक पकड़ सकता है। हालाँकि, मुझे समझ नहीं आता कि ID3D11Buffer COM का उपयोग निरंतर बफ़र्स के साथ बातचीत करने के लिए किया जाता है, जो इन बफर स्लॉट्स को भरने के लिए उपयोग किया जाने वाला एक तंत्र (या हैंडल) है या यदि वस्तु वास्तव में बफर डेटा के किसी विशेष उदाहरण को संदर्भित करती है जिसे आगे और पीछे धकेला जाता है। GPU और CPU के बीच।

मुझे लगता है कि विषय पर मेरा भ्रम एक समस्या का कारण है जो मैं दो अलग-अलग निरंतर बफ़र्स का उपयोग कर रहा हूं।

यहाँ कुछ उदाहरण shader कोड है।

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

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

जिस तरह से मेरा कोड व्यवस्थित किया गया है, कैमरा प्रासंगिक प्रति फ्रेम डेटा को अपडेट करने का काम करेगा और गेमऑब्जेक्ट अपने स्वयं के ऑब्जेक्ट डेटा को अपडेट कर रहा होगा। दोनों वर्गों की अपनी ID3D11Buffer है जो इसे करने के लिए उपयोग की जाती है (हब आर्किटेक्चर का उपयोग करके, इसलिए एक गेमऑबजेक्ट क्लास दुनिया के सभी इंस्टेंट गेमऑब्जेक्ट्स के प्रतिपादन को संभाल लेगा)।

समस्या यह है कि मैं केवल एक समय में एक अद्यतन प्राप्त कर सकता हूं, स्लॉट के आधार पर और मैं अद्यतन आदेश मानता हूं कि एक बफर भरा जाता है जबकि दूसरा शून्य से बाहर हो जाता है।

यह अनिवार्य रूप से मेरा कोड है। दोनों वर्ग समान अद्यतन तर्क का उपयोग करते हैं।

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);

मेरे मुख्य प्रश्न हैं -

  • क्या मुझे UpdateSubresource कॉल के साथ इसे अपडेट करने के लिए ShaderBuffer को सेट या बाइंड करने की आवश्यकता है? (इसका मतलब यह है कि यह पाइपलाइन में होने पर ही हेरफेर करें) या क्या यह डेटा का एक ब्लॉब है जो VSSetConstantBuffer कॉल के साथ भेजा जाएगा? (मतलब डेटा को बाइंड करने और अपडेट करने का क्रम मायने नहीं रखता, मैं इसे पाइपलाइन में या किसी तरह सीपीयू में अपडेट कर सकता हूं)
  • बफर को सेट या बाइंड करते समय, क्या मुझे PerOrame बफर को अपडेट करने के लिए PerFrame बफर और स्लॉट 1 को अपडेट करने के लिए स्लॉट 0 को रेफर करने की आवश्यकता है? क्या मेरे कोड में इस कॉल से किसी तरह का भ्रम हो सकता है जिससे सभी बफ़र्स ओवरराइट हो सकते हैं?
  • डी 3 डी 11 कैसे जानता है कि मैं किस बफर को अपडेट या मैप करना चाहता हूं? क्या यह पता है कि ID3D11Buffer COM का उपयोग किया गया है?

संपादित करें -

ऊपर दिए गए उदाहरण में निरंतर बफर रजिस्टर टैग को बदला। के बजाय (b #) का उपयोग करना (c #) किसी कारण से बफ़र्स को सही तरीके से अपडेट करने से प्रभावित कर रहा था। निश्चित नहीं है कि मैंने मूल सिंटैक्स कहाँ से लिया है या यदि यह बिल्कुल मान्य है, लेकिन यह मेरी मुख्य समस्या है।

जवाबों:


18

ID3D11Buffer स्मृति का एक वास्तविक हिस्सा है जो आपके डेटा को रखता है, चाहे वह एक शीर्ष बफर, निरंतर बफर या जो भी हो।

लगातार बफ़र उसी तरह काम करते हैं जैसे कि वर्टेक्स बफ़र और अन्य प्रकार के बफ़र्स। अर्थात्, जब तक यह वास्तव में फ्रेम प्रदान नहीं करता है, तब तक GPU में डेटा तक पहुंच नहीं होती है, इसलिए बफर को तब तक वैध रहना पड़ता है जब तक कि GPU इसके साथ नहीं होता है। आपको प्रत्येक निरंतर बफर को डबल-बफर करना चाहिए, ताकि आपके पास अगले फ्रेम के अपडेट के लिए एक कॉपी हो और वर्तमान फ्रेम को रेंडर करते समय पढ़ने के लिए जीपीयू के लिए एक कॉपी हो। यह इस बात से संबंधित है कि आप कण प्रणाली या इस तरह के लिए गतिशील शीर्ष बफ़र कैसे करेंगे।

register(cb0), register(cb1)VSSetConstantBuffers में स्लॉट के साथ HLSL अनुरूप में सेटिंग्स। जब आप प्रति-फ्रेम स्थिरांक VSSetConstantBuffers(0, 1, &pBuffer)को अपडेट करते हैं तो आप CB0 को सेट करने के लिए करते हैं और जब आप प्रति-ऑब्जेक्ट को अद्यतन करते हैं तो आप VSSetConstantBuffers(1, 1, &pBuffer)CB1 को सेट करने के लिए करते हैं । प्रत्येक कॉल केवल शुरू / गिनती मापदंडों द्वारा संदर्भित बफ़र्स को अपडेट करता है, और दूसरों को स्पर्श नहीं करता है।

आपको इसे अपडेट करने के लिए बफर अपडेट करने की आवश्यकता नहीं है। वास्तव में, जब आप इसे अपडेट करते हैं, तो इसे बाध्य नहीं किया जाना चाहिए , या यह ड्राइवर को आंतरिक रूप से अतिरिक्त मेमोरी कॉपी बनाने के लिए मजबूर कर सकता है ( अपडेट के लिए MSDN पृष्ठ देखें , विशेष रूप से एक पृष्ठ के बारे में विवाद पर टिप्पणी)।

मुझे यकीन नहीं है कि आपका क्या मतलब है "डी 3 डी 11 कैसे जानता है कि मैं किस बफर को अपडेट या मैप करना चाहता हूं?" यह उस व्यक्ति को अपडेट या मैप करता है जिसका पॉइंटर आपने पास किया है।


3

अद्यतन करने के बाद लगातार बफ़र्स को फिर से बाँधने की आवश्यकता के विषय में बहुत भ्रम की स्थिति प्रतीत होती है। जैसा कि मैं खुद इस बारे में सीख रहा हूं मैंने इस पर विपरीत विचारों के साथ बहुत सारे विषयों और चर्चाओं को देखा है। अर्थात् सर्वश्रेष्ठ उत्तर यहाँ, की सिफारिश बुला XXSetConstantBuffersआप के माध्यम से अद्यतन करने के बाद UpdateSubresourceया Map/Unmap

इसके अलावा, कुछ डी 3 डी एमएसडीएन नमूने और दस्तावेज़ीकरण इस पैटर्न का उपयोग करते हुए प्रतीत होते हैं, प्रति फ्रेम या बाध्यकारी XXSetConstantBuffersवस्तु के आधार पर (कॉलिंग ), भले ही वे केवल एक मौजूदा बफर को अपडेट करते हैं, और एक विशिष्ट स्लॉट को पूरी तरह से अलग बफर के साथ नहीं बदलते हैं ।

मुझे लगता है कि सबसे बुरी गलत धारणा यह है कि XXSetConstantBuffersवास्तव में "डेटा जिसे आप पहले जीपीयू में अपडेट करते हैं या अपडेट के बारे में सूचित करते हैं, ताकि यह नया मान ले - जो पूरी तरह से गलत लगता है।

वास्तव में, उपयोग करते समय UpdateSubresourceया Map/Unmap, प्रलेखन में कहा गया है कि आंतरिक रूप से कई प्रतियां जीपीयू द्वारा बनाई जा सकती हैं यदि इसे अभी भी पुराने डेटा की आवश्यकता है, लेकिन यह एपीआई के उपयोगकर्ता के लिए चिंता का विषय नहीं है जब यह पहले से ही बाध्य बफर को अपडेट करने की बात आती है। इसलिए स्पष्ट रूप से अनबाउंड होने की आवश्यकता सतही लगती है।

मेरे प्रयोग के दौरान, मैं इस नतीजे पर पहुंचा हूं कि बफ़र्स को XXSetConstantBuffersअपडेट करने के बाद उन्हें फिर से बाँधना अनावश्यक है , जब तक कि वे पहले से ही बाध्य न हों! जब तक आप एक ही बफ़र्स (शेड्स, ईवन पाइप लाइन चरणों के बीच साझा) का उपयोग करते हैं जो एक बार बंधे होते हैं (उदाहरण के लिए स्टार्ट-अप चरण में), तो आपको उन्हें फिर से बाँधने की ज़रूरत नहीं है - बस उन्हें अपडेट करें।

मेरे प्रयोगों की प्रकृति को बेहतर दिखाने के लिए कुछ कोड:

// 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);
    }
}

यहाँ इंटरनेट (gamedev फ़ोरम) के कुछ विषय दिए गए हैं, जो इस दृष्टिकोण को अपनाने और अनुशंसा करने के लिए प्रतीत होते हैं: http://www.gamedev.net/topic/649410-set-constant-buffers-every-frame/?view=findpost/p=5105032 ( http://www.gamedev.net/topic/647203-updating-constant-buffers/#entry50900000000

समझाने के लिए, यह वास्तव में लगता है कि जब तक आप बफर को पूरी तरह से नहीं बदलते हैं, तब तक बंधन की आवश्यकता नहीं है लेकिन जब तक आप बफ़र्स और उनके लेआउट को शेडर्स (अनुशंसित अभ्यास) के बीच साझा करते हैं, इन मामलों में किया जाना चाहिए:

  • शुरुआत में - प्रारंभिक बंधन - उदाहरण के लिए बफर बनाने के बाद।
  • यदि आपको एक या अधिक चरणों के विशिष्ट स्लॉट के लिए बाध्य एक से अधिक बफर का उपयोग करने की आवश्यकता है / बनाया गया है।
  • डिवाइस को साफ़ करने की स्थिति के बाद कॉन्टेक्स्ट (जब बफ़र्स / विंडो का आकार बदल रहा हो)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.