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"


Reply via email to