valido puede habilitar encontrar dispositivo disponible cómo configurar como activar acelerador aceleracion directx textures gpgpu geometry-surface

directx - puede - habilitar aceleracion de direct3d



Hacer lectura de texturas y superficies de Direct3D (1)

Necesito descubrir cómo recuperar los datos de las texturas y superficies D3D en la memoria del sistema. ¿Cuál es la forma más rápida de hacer tales cosas y cómo?

Además, si solo necesito una subrect, ¿cómo se puede leer solo esa parte sin tener que volver a leer toda la información en la memoria del sistema?

En resumen, estoy buscando descripciones concisas de cómo copiar lo siguiente a la memoria del sistema :

  1. una textura
  2. un subconjunto de una textura
  3. una superficie
  4. un subconjunto de una superficie
  5. una textura D3DUSAGE_RENDERTARGET
  6. un subconjunto de una textura D3DUSAGE_RENDERTARGET

Esto es Direct3D 9, pero las respuestas sobre las versiones más recientes de D3D también serían apreciadas.


La parte más complicada es leer desde alguna superficie que está en la memoria de video ("grupo predeterminado"). Esto a menudo es objetivo de renderizado.

Primero consigamos las partes fáciles:

  1. leer desde una textura es lo mismo que leer desde la superficie de 0 niveles de esa textura. Vea abajo.
  2. lo mismo para el subconjunto de una textura.
  3. leer desde una superficie que está en un grupo de memoria no predeterminado ("sistema" o "administrado") es simplemente bloquearlo y leer bytes.
  4. lo mismo para un subconjunto de superficie. Simplemente bloquee la parte relevante y léala.

Así que ahora hemos dejado superficies que están en la memoria de video ("grupo predeterminado"). Esta sería cualquier superficie / textura marcada como objetivo de renderizado, o cualquier superficie / textura regular que hayas creado en el grupo predeterminado, o el backbuffer mismo. La parte compleja aquí es que no puedes bloquearla.

La respuesta corta es: Método GetRenderTargetData en el dispositivo D3D.

Respuesta más larga (un bosquejo aproximado del código que se mostrará a continuación):

  1. rt = obtienes la superficie del objetivo (puede ser la superficie de la textura, o el backbuffer, etc.)
  2. si rt tiene varias muestras (GetDesc, compruebe D3DSURFACE_DESC.MultiSampleType), entonces: a) cree otra superficie objetivo de renderizado del mismo tamaño, el mismo formato pero sin multimuestreo; b) StretchRect de rt en esta nueva superficie; c) rt = esta nueva superficie (es decir, proceda en esta nueva superficie).
  3. off = crear superficie plana fuera de pantalla (CreateOffscreenPlainSurface, D3DPOOL_SYSTEMMEM pool)
  4. dispositivo-> GetRenderTargetData ( rt , apagado )
  5. now off contiene datos de destino de renderizado. LockRect (), datos leídos, UnlockRect () en él.
  6. limpiar

Incluso una respuesta más larga (pegar desde la base de código en la que estoy trabajando) sigue. Esto no se compilará de inmediato, ya que utiliza algunas clases, funciones, macros y utilidades del resto de la base de código; pero debería ayudarte a empezar. También omité la mayoría de las comprobaciones de errores (por ejemplo, si el ancho / alto dado está fuera de límites). También omití la parte que lee los píxeles reales y posiblemente los convierta en un formato de destino adecuado (eso es bastante fácil, pero puede ser largo, dependiendo del número de conversiones de formato que quiera admitir).

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 en el código anterior es un puntero inteligente a un objeto COM (libera objeto en la asignación o destructor). Simplifica el manejo de errores mucho. Esto es muy similar a _comptr_t cosas en Visual C ++.

El código anterior lee toda la superficie. Si quiere leer solo una parte de manera eficiente, entonces creo que la manera más rápida es aproximada:

  1. crear una superficie de grupo predeterminada que tenga el tamaño necesario.
  2. StretchRect de parte de la superficie original a la más pequeña.
  3. proceder como siempre con el más pequeño.

De hecho, esto es bastante similar a lo que hace el código anterior para manejar superficies con múltiples muestras. Si desea obtener solo una parte de una superficie con múltiples muestras, puede hacer una resolución de varias muestras y obtener parte de ella en un StretchRect, creo.

Editar : pieza eliminada de código que hace una lectura real de los píxeles y las conversiones de formato. No estaba directamente relacionado con la pregunta, y el código era largo.

Editar : actualizado para que coincida con la pregunta editada.