FWIW, I had implemented this a few years ago with my (long defunct) Linux-D3D project. I've attached what I had.
It's written in C++, but converting it to C shouldn't be too difficult. It hasn't had any real testing, so I can't say how good of an implementation it is (I only implemented it to satisfy a simple demo app), but perhaps it may be a good starting point.
/*************************************************************************** * Copyright (C) 2008 by Chris Robinson * * chris.k...@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "config.h" #include <d3d9.h> #include "d3dxgl.h" struct D3DXGLRenderToSurface : public ID3DXRenderToSurface { virtual HRESULT WINAPI QueryInterface(REFIID riid, LPVOID *object) { TRACE("(%p)->(%s, %p)\n", this, iid_to_str(riid), object); *object = NULL; if(IsEqualIID(riid, IID_IUnknown)) *object = this; else if(IsEqualIID(riid, IID_ID3DXRenderToSurface)) *object = this; if(*object) { ((IUnknown*)*object)->AddRef(); return D3D_OK; } return E_NOINTERFACE; } virtual ULONG WINAPI AddRef() { ULONG ref = ++refCount; TRACE("%p new ref: %u\n", this, ref); return ref; } virtual ULONG WINAPI Release() { ULONG ref = --refCount; TRACE("%p new ref: %u\n", this, ref); if (ref == 0) delete this; return ref; } virtual HRESULT WINAPI GetDevice(LPDIRECT3DDEVICE9 *device) { TRACE("(%p)->(%p)\n", this, device); if(!device) { WARN("NULL device storage specified\n"); return D3DERR_INVALIDCALL; } *device = Parent; (*device)->AddRef(); return D3D_OK; } virtual HRESULT WINAPI GetDesc(D3DXRTS_DESC *desc) { TRACE("(%p)->(%p)\n", this, desc); if(!desc) { WARN("NULL description storage specified\n"); return D3DERR_INVALIDCALL; } desc->Width = Width; desc->Height = Height; desc->Format = Format; desc->DepthStencil = (DepthStencilFormat!=D3DFMT_UNKNOWN); desc->DepthStencilFormat = DepthStencilFormat; return D3D_OK; } virtual HRESULT WINAPI BeginScene(LPDIRECT3DSURFACE9 surface, CONST D3DVIEWPORT9 *viewport) { TRACE("(%p)->(%p, %p)\n", this, surface, viewport); D3DSURFACE_DESC desc; bool gotRT = false, gotDS = false; HRESULT hr; if(InScene) { WARN("Already in scene\n"); return D3DERR_INVALIDCALL; } if(FAILED(surface->GetDesc(&desc)) || FAILED(Parent->GetViewport(&SavedVP))) return E_FAIL; if(!(desc.Usage&D3DUSAGE_RENDERTARGET)) { if(desc.Pool != D3DPOOL_SYSTEMMEM) { ERR("Can't copy to non-systemmem texture\n"); return D3DERR_INVALIDCALL; } /* Can't render directly to the user surface, so we need to use the * internal render target and copy to the user-supplied surface in * EndScene */ UserSurface = surface; UserSurface->AddRef(); } hr = Parent->GetRenderTarget(0, &SavedRT); if(SUCCEEDED(hr)) { gotRT = true; hr = Parent->GetDepthStencilSurface(&SavedDS); } if(SUCCEEDED(hr)) { gotDS = true; hr = Parent->SetRenderTarget(0, UserSurface ? RenderTarget : surface); } if(SUCCEEDED(hr)) hr = Parent->SetDepthStencilSurface(DepthStencil); if(SUCCEEDED(hr) && viewport) hr = Parent->SetViewport(viewport); if(SUCCEEDED(hr)) hr = Parent->BeginScene(); if(FAILED(hr)) { if(UserSurface) UserSurface->Release(); UserSurface = NULL; if(gotDS) { Parent->SetDepthStencilSurface(SavedDS); if(SavedDS) SavedDS->Release(); SavedDS = NULL; } if(gotRT) { Parent->SetRenderTarget(0, SavedRT); if(SavedRT) SavedRT->Release(); SavedRT = NULL; } Parent->SetViewport(&SavedVP); } if(SUCCEEDED(hr)) InScene = true; return hr; } virtual HRESULT WINAPI EndScene(DWORD mipfilter) { TRACE("(%p)->(%#x)\n", this, mipfilter); if(!InScene) { WARN("Not in scene\n"); return D3DERR_INVALIDCALL; } Parent->EndScene(); InScene = false; if(UserSurface) { Parent->GetRenderTargetData(RenderTarget, UserSurface); UserSurface->Release(); UserSurface = NULL; } Parent->SetDepthStencilSurface(SavedDS); if(SavedDS) SavedDS->Release(); SavedDS = NULL; Parent->SetRenderTarget(0, SavedRT); if(SavedRT) SavedRT->Release(); SavedRT = NULL; Parent->SetViewport(&SavedVP); return D3D_OK; } virtual HRESULT WINAPI OnLostDevice() { return OnResetDevice(); } virtual HRESULT WINAPI OnResetDevice() { TRACE("(%p)->()\n", this); if(RenderTarget) RenderTarget->Release(); RenderTarget = NULL; if(DepthStencil) DepthStencil->Release(); DepthStencil = NULL; return Initialize(); } D3DXGLRenderToSurface(IDirect3DDevice9 *parent, UINT width, UINT height, D3DFORMAT format, D3DFORMAT depthStencilFormat) : refCount(0), Parent(parent), Width(width), Height(height), Format(format), DepthStencilFormat(depthStencilFormat), RenderTarget(NULL), DepthStencil(NULL), InScene(false), SavedRT(NULL), SavedDS(NULL), UserSurface(NULL) { TRACE("(%p)->()\n", this); Parent->AddRef(); } virtual ~D3DXGLRenderToSurface() { TRACE("(%p)->()\n", this); if(RenderTarget) RenderTarget->Release(); if(DepthStencil) DepthStencil->Release(); Parent->Release(); } HRESULT Initialize() { TRACE("(%p)->()\n", this); HRESULT hr; hr = Parent->CreateRenderTarget(Width, Height, Format, D3DMULTISAMPLE_NONE, 0, FALSE, &RenderTarget, NULL); if(SUCCEEDED(hr) && DepthStencilFormat != D3DFMT_UNKNOWN) hr = Parent->CreateDepthStencilSurface(Width, Height, DepthStencilFormat, D3DMULTISAMPLE_NONE, 0, TRUE, &DepthStencil, NULL); return hr; } private: ULONG refCount; IDirect3DDevice9 *Parent; UINT Width; UINT Height; D3DFORMAT Format; D3DFORMAT DepthStencilFormat; IDirect3DSurface9 *RenderTarget; IDirect3DSurface9 *DepthStencil; bool InScene; IDirect3DSurface9 *SavedRT; IDirect3DSurface9 *SavedDS; D3DVIEWPORT9 SavedVP; IDirect3DSurface9 *UserSurface; static const char *FUNC_PRE; }; const char *D3DXGLRenderToSurface::FUNC_PRE = "D3DXGLRenderToSurface::"; extern "C" { DECLSPEC_EXPORT HRESULT WINAPI D3DXCreateRenderToSurface(LPDIRECT3DDEVICE9 device, UINT width, UINT height, D3DFORMAT format, BOOL enableDepthStencil, D3DFORMAT depthStencilFormat, LPD3DXRENDERTOSURFACE *iface) { TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, width, height, format, enableDepthStencil, depthStencilFormat, iface); if(!enableDepthStencil) depthStencilFormat = D3DFMT_UNKNOWN; D3DXGLRenderToSurface *rts = new D3DXGLRenderToSurface(device, width, height, format, depthStencilFormat); rts->AddRef(); HRESULT hr = rts->Initialize(); if(FAILED(hr)) { rts->Release(); rts = NULL; } *iface = rts; return hr; } }// extern "C"