سؤال

أحتاج إلى معرفة كيفية إعادة البيانات من مواد وأسطح D3D إلى ذاكرة النظام.ما هي أسرع طريقة للقيام بمثل هذه الأشياء وكيف؟

وأيضًا إذا كنت بحاجة إلى قسم فرعي واحد فقط، فكيف يمكن للمرء قراءة هذا الجزء فقط دون الحاجة إلى إعادة قراءة كل شيء مرة أخرى إلى ذاكرة النظام؟

باختصار أنا أبحث عن وصف موجز لكيفية نسخ ما يلي إلى ذاكرة النظام:

  1. أ نَسِيج
  2. أ مجموعة فرعية من أ نَسِيج
  3. أ سطح
  4. أ مجموعة فرعية من أ سطح
  5. أ نسيج D3DUSAGE_RENDERTARGET
  6. أ مجموعة فرعية من أ نسيج D3DUSAGE_RENDERTARGET

هذا هو Direct3D 9، ولكن الإجابات حول الإصدارات الأحدث من D3D ستكون موضع تقدير أيضًا.

هل كانت مفيدة؟

المحلول

الجزء الأكثر مشاركة هو القراءة من بعض الأسطح الموجودة في ذاكرة الفيديو ("التجمع الافتراضي").غالبًا ما يتم تقديم هذه الأهداف.

دعونا نحصل على الأجزاء السهلة أولاً:

  1. القراءة من مادة ما هي نفس القراءة من سطح ذو مستوى 0 لذلك النسيج.انظر أدناه.
  2. الشيء نفسه بالنسبة لمجموعة فرعية من الملمس.
  3. القراءة من سطح موجود في تجمع الذاكرة غير الافتراضي ("النظام" أو "المُدار") هي مجرد قفله وقراءة البايتات.
  4. الشيء نفسه بالنسبة لمجموعة فرعية من السطح.فقط قم بقفل الجزء ذي الصلة وقراءته.

لقد تركنا الآن الأسطح الموجودة في ذاكرة الفيديو ("التجمع الافتراضي").قد يكون هذا أي سطح/نسيج تم وضع علامة عليه كهدف عرض، أو أي سطح/نسيج عادي قمت بإنشائه في التجمع الافتراضي، أو المخزن المؤقت نفسه.الجزء المعقد هنا هو أنه لا يمكنك قفله.

الإجابة المختصرة هي: GetRenderTargetData الطريقة على جهاز D3D

إجابة أطول (مخطط تقريبي للكود الذي سيكون أدناه):

  1. غ = احصل على السطح المستهدف للعرض (يمكن أن يكون هذا سطحًا للنسيج، أو مخزنًا مؤقتًا، وما إلى ذلك)
  2. لو غ متعدد العينات (GetDesc، تحقق من D3DSURFACE_DESC.MultiSampleType)، ثم:أ) قم بإنشاء سطح هدف عرض آخر بنفس الحجم ونفس التنسيق ولكن بدون أخذ عينات متعددة؛ب) تمتد من غ في هذا السطح الجديد.ج) غ = هذا السطح الجديد (أيالمضي قدما على هذا السطح الجديد).
  3. عن = إنشاء سطح عادي خارج الشاشة (CreateOffscreenPlainSurface، تجمع D3DPOOL_SYSTEMMEM)
  4. الجهاز->GetRenderTargetData( غ, عن )
  5. الآن عن يحتوي على بيانات هدف التقديم.LockRect () وقراءة البيانات وUnlockRect () عليها.
  6. تنظيف

يتبع ذلك إجابة أطول (لصق من قاعدة التعليمات البرمجية التي أعمل عليها).هذا سوف لن تجميع خارج الصندوق، لأنه يستخدم بعض الفئات والوظائف ووحدات الماكرو والأدوات المساعدة من بقية قاعدة التعليمات البرمجية؛ولكن يجب أن تبدأ.لقد حذفت أيضًا معظم عمليات التحقق من الأخطاء (على سبيل المثال.ما إذا كان العرض/الارتفاع المحدد خارج الحدود).لقد حذفت أيضًا الجزء الذي يقرأ وحدات البكسل الفعلية وربما يحولها إلى تنسيق وجهة مناسب (وهذا أمر سهل للغاية، ولكن يمكن أن يطول، اعتمادًا على عدد تحويلات التنسيق التي تريد دعمها).

bool GfxDeviceD3D9::ReadbackImage( /* params */ )
{
    HRESULT hr;
    IDirect3DDevice9* dev = GetD3DDevice();
    SurfacePointer renderTarget;
    hr = dev->GetRenderTarget( 0, &renderTarget );
    if( !renderTarget || FAILED(hr) )
        return false;

    D3DSURFACE_DESC rtDesc;
    renderTarget->GetDesc( &rtDesc );

    SurfacePointer resolvedSurface;
    if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
    {
        hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
        if( FAILED(hr) )
            return false;
        hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
        if( FAILED(hr) )
            return false;
        renderTarget = resolvedSurface;
    }

    SurfacePointer offscreenSurface;
    hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
    if( FAILED(hr) )
        return false;

    hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
    bool ok = SUCCEEDED(hr);
    if( ok )
    {
        // Here we have data in offscreenSurface.
        D3DLOCKED_RECT lr;
        RECT rect;
        rect.left = 0;
        rect.right = rtDesc.Width;
        rect.top = 0;
        rect.bottom = rtDesc.Height;
        // Lock the surface to read pixels
        hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
        if( SUCCEEDED(hr) )
        {
            // Pointer to data is lt.pBits, each row is
            // lr.Pitch bytes apart (often it is the same as width*bpp, but
            // can be larger if driver uses padding)

            // Read the data here!
            offscreenSurface->UnlockRect();
        }
        else
        {
            ok = false;
        }
    }

    return ok;
}

SurfacePointer يوجد في الكود أعلاه مؤشر ذكي لكائن COM (يقوم بإصدار كائن عند التعيين أو المدمر).يبسط معالجة الأخطاء كثيرًا.هذا مشابه جدًا لـ _comptr_t الأشياء في Visual C++.

يقرأ الكود أعلاه السطح بالكامل.إذا كنت تريد قراءة جزء منه بكفاءة، فأعتقد أن أسرع طريقة هي تقريبًا:

  1. إنشاء سطح حمام سباحة افتراضي بالحجم المطلوب.
  2. StretchRect من جزء من السطح الأصلي إلى السطح الأصغر.
  3. المضي قدما كالمعتاد مع الأصغر.

في الواقع، هذا مشابه تمامًا لما يفعله الكود أعلاه للتعامل مع الأسطح متعددة العينات.إذا كنت ترغب في الحصول على جزء فقط من سطح متعدد العينات، فيمكنك إجراء حل متعدد العينات والحصول على جزء منه في StretchRect واحد، على ما أعتقد.

يحرر:تمت إزالة جزء من التعليمات البرمجية التي تقوم بالقراءة الفعلية لوحدات البكسل وتحويلات التنسيق.لم يكن مرتبطًا بشكل مباشر بالسؤال، وكان الرمز طويلًا.

يحرر:تم التحديث لمطابقة السؤال الذي تم تحريره.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top