RTT for 1.26

BlitzMax Forums/BlitzMax Programming/RTT for 1.26

I have edited Indiepath's RTT code to work at Bmax 1.26.
There are a couple of issues :
- OGL driver doesn't work with MIPMAPPED images (which has always been the case).
- Resulting image is smaller using the supplied examples. I can workaround this but not sure why it is happening. I asked before and it was due to a Viewport flag but that doesn't help now (plus Viewports have changed since 1.24).
So, if Tim can give the OK for me to post the source as Public Domain and license free I will do.

you have my permission.

Thanks Tim
OK. At the moment it's not a module 'cos it's easier to work on.
The OGL Mipmap problem is avoided by not going into that bit of code.
The 'wrong scale' problem can be avoided by setting the render image to the graphicswidth/height.
Let me know if it works, any improvments and whether anybody can see what I am doing wrong to create a smaller output than input.
Finally, I have taken out the setmono from the original source.

Rem
bbdoc: Render 2 Texture Module
'End Rem
Module Indiepath.Render2Texture

ModuleInfo "Version: 1.0"
ModuleInfo "Author: Tim Fisher"
ModuleInfo "License: Indiepath License for non warranted software"
ModuleInfo "Summary: This software is free to use, you may distribute within a pre-compiled executable file only."
ModuleInfo "Contact: If you have a license question then ask, don't assume! If you don't have a copy of the license then get one."
ModuleInfo "Modserver:Indiepath"
EndRem
'Rem
'SetGraphicsDriver GLMax2DDriver()
Graphics 800,600,0

tRender.Initialise()

AutoMidHandle(1)

Local logo:TImage = LoadImage("bmax160_2.png",MIPMAPPEDIMAGE|FILTEREDIMAGE)
Local myImage:TImage = tRender.Create(ImageWidth(logo),ImageHeight(logo),MIPMAPPED|FILTEREDIMAGE)

'Local myImage:TImage = tRender.Create(GraphicsWidth(),GraphicsHeight(),MIPMAPPED|FILTEREDIMAGE)
Local ii:Float = 0

SetBlend(ALPHABLEND)
	tRender.TextureRender_Begin(myImage,False)
	tRender.Cls	
	Repeat
		SetRotation(ii)
		DrawImage logo , 320 , 240
		ii:+5
	Until ii>360
	tRender.TextureRender_End()
	SetImageHandle(logo,0,0)
	tRender.BackBufferRender_Begin()
	tRender.Cls
	DrawImage myimage , 0,0
	DrawImage logo,300,0
	tRender.BackBufferRender_End()
	Flip
WaitKey()
'End Rem

Const DDERR_INVALIDSURFACETYPE 	= $88760250
Const DDERR_INVALIDPARAMS		= $80070057
Const DDERR_INVALIDOBJECT 		= $88760082
Const DDERR_NOTFOUND 			= $887600ff
Const DDERR_SURFACELOST 		= $887601c2

Global tRenderERROR:String

Function tError:String(err:Int)

	Select err
		Case DDERR_INVALIDSURFACETYPE
			Return "DDERR_INVALIDSURFACETYPE"
			
		Case DDERR_INVALIDPARAMS
			Return "DDERR_INVALIDPARAMS"
			
		Case DDERR_INVALIDOBJECT 
			Return "DDERR_INVALIDOBJECT"
			
		Case DDERR_NOTFOUND
			Return "DDERR_NOTFOUND"
			
		Case DDERR_SURFACELOST
			Return "DDERR_SURFACELOST"
			
	End Select
End Function

Type tRender
	
	?Win32
	Global DXFrame:TD3D7ImageFrame
	Global backbuffer:IDirectDrawSurface7

	?
	Global GLFrame:TGLImageFrame
	Global DX:Int 
	Global Image:TImage = CreateImage(1,1)
	Global Width:Int
	Global Height:Int
	Global o_r,o_g,o_b
Rem
bbdoc: Initialise the Module
about: 
The module must be initialised before use. This ensures usage of the correct render pipeline. If the render device is changed then you must 
re initialise the module.
End Rem		
	Function Initialise()
	?Win32
		If _max2dDriver.ToString() = "DirectX7"
			DX = True
			D3D7GraphicsDriver().Direct3DDevice7().GetRenderTarget Varptr backbuffer

		'	ViewPort = New D3DVIEWPORT7
		Else
	?
			DX = False
		'	GlEnable(GL_TEXTURE_2D)
	?Win32
		EndIf
	?
		DebugLog "tRender : Initialise OK"
		Return True
	End Function
	
'#################################################################################

Rem
bbdoc: Create Image with Render Characteristics
returns: TImage Handle to Object
about: 
 <table>
		<tr><td><b>Width:Int</td><td>Width in Pixels of Image, try to follow the normal rules of textures</td></tr>
		<tr><td><b>Height:Int</td><td>Height in Pixels of Image</td></tr>
		<tr><td><b>Flags:Int</td><td>Normal Image Flags</td></tr>
	</table>
End Rem	
	Function Create:TImage(Width:Int,Height:Int,Flags:Int=FILTEREDIMAGE)

		Local t:TImage=New TImage
		t.width=width
		t.height=height
		t.flags=flags
		t.mask_r= 0
		t.mask_g= 0
		t.mask_b= 0
		t.pixmaps=New TPixmap[1]
		t.frames=New TImageFrame[1]
		t.seqs=New Int[1]
		t.pixmaps[0]= t.Lock(0,True,False)
		t.seqs[0]=GraphicsSeq
		
	'	MaskPixmap( t.pixmaps[0],mask_r,mask_g,mask_b )
	?Win32	
		If DX
			t.frames[0] = CreateFrame(TD3D7Max2DDriver(_max2dDriver),t.Width,t.Height,t.flags)
		Else
	?
			t.frames[0] = TGLImageFrame.CreateFromPixmap:TGLImageFrame( t.pixmaps[0],t.flags )
	?Win32
		EndIf
	?
		
		DebugLog "tRender : Create OK"
		
		
		Return t

	End Function

'#################################################################################
?Win32			
	Function CreateFrame:TD3D7ImageFrame( driver:TD3D7Max2DDriver,width,height,flags )				
		Function Pow2Size( n )
			Local t=1
			While t<n
				t:*2
			Wend
			Return t
		End Function

		Local	swidth=Pow2Size(width)
		Local	sheight=Pow2Size(height)
		Local	desc:DDSURFACEDESC2=New DDSURFACEDESC2
		Local	res
						
		desc.dwSize=SizeOf(desc)
		desc.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT
		desc.dwWidth=swidth
		desc.dwHeight=sheight	
		desc.ddsCaps=DDSCAPS_TEXTURE|DDSCAPS_3DDEVICE|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM  ' **************************************************
		desc.ddsCaps2=DDSCAPS2_HINTDYNAMIC'|DDSCAPS2_TEXTUREMANAGE
		desc.ddpf_dwSize=SizeOf(DDPIXELFORMAT)
		desc.ddpf_dwFlags=DDPF_RGB|DDPF_ALPHAPIXELS
		desc.ddpf_BitCount=32
		desc.ddpf_BitMask_0=$ff0000
		desc.ddpf_BitMask_1=$00ff00
		desc.ddpf_BitMask_2=$0000ff
		desc.ddpf_BitMask_3=$ff000000

		
		Local surf:IDirectDrawSurface7
		If flags & MIPMAPPEDIMAGE desc.ddsCaps:|DDSCAPS_MIPMAP|DDSCAPS_COMPLEX
		res=D3D7GraphicsDriver().DirectDraw7().CreateSurface( desc,Varptr surf,Null )
		If res<>DD_OK 
			tRenderERROR = tError(res)
			DebugLog "tRender : CreateFrame ERROR : "+tRenderERROR
			Return Null
		EndIf
		'RuntimeError "Create DX7 surface Failed"	
							
		Local frame:TD3D7ImageFrame=New TD3D7ImageFrame
		frame.driver=driver
		frame.surface=surf
		frame.sinfo=New DDSURFACEDESC2
		frame.sinfo.dwSize=SizeOf(frame.sinfo)
		frame.xyzuv=New Float[24]
		frame.width=width
		frame.height=height
		frame.flags=flags
		frame.SetUV 0.0,0.0,Float(width)/swidth,Float(height)/sheight
	
			
		'frame.BuildMipMaps()
		DebugLog "tRender : CreateFrame OK"
		Return frame
	End Function
?
	
'#################################################################################
Rem
bbdoc: Set Screen Viewport
about: 
 <table>
	<tr><td><b>X:Int</td><td>Set the X Position of the Viewport</td></tr>
	<tr><td><b>Y:Int</td><td>Set the Y Position of the Viewport</td></tr>
	<tr><td><b>Width:Int</td><td>Set the Viewport Width in Pixels</td></tr>
	<tr><td><b>Height:Int</td><td>Set the Viewport Height in Pixels</td></tr>
	<tr><td><b>FlipY:Byte</td><td>Flip the Viewport on the Y Axis, OpenGL only. Automatically done by the Texture Renderer.</td></tr>
 </table>
End Rem	
	Function ViewportSet(X:Int=0,Y:Int=0,Width:Int,Height:Int,FlipY:Byte=False)
	?Win32
		If DX
			Local viewport:D3DVIEWPORT7=New D3DVIEWPORT7
			viewport.dwX=x
			viewport.dwY=y
			viewport.dwWidth=width
			viewport.dwHeight=height
			D3D7GraphicsDriver().Direct3DDevice7().SetViewport(viewport)
		Else
	?
			If FlipY
				glViewport(X, Y, Width, Height)
				glMatrixMode(GL_PROJECTION)
				glPushMatrix()
				glLoadIdentity()
				gluOrtho2D(X, GraphicsWidth(), GraphicsHeight(),Y)
				glScalef(1, -1, 1)
				glTranslatef(0, -GraphicsHeight(), 0)
				glMatrixMode(GL_MODELVIEW)
			Else
				glViewport(X, Y, Width, Height)
				glMatrixMode(GL_PROJECTION)
				glLoadIdentity()
				glOrtho(X, GraphicsWidth(), GraphicsHeight(), Y, -1, 1)
				glMatrixMode(GL_MODELVIEW)
				glLoadIdentity()
			EndIf
	?Win32
		EndIf
	?
		DebugLog "tRender : ViewportSet OK"
		Return True
	EndFunction
	
'#################################################################################
Rem
bbdoc: Begin the Texture Render Process
about: 
 <table>
	<tr><td><b>Image:TImage</td><td>The Image to Render to, as created with "Create" command.</td></tr>
	<tr><td><b>Viewport:Byte</td><td>True (default) to Automatically resize the viewport to the image size</td></tr>
 </table>
End Rem
	Function TextureRender_Begin(Image1:TImage,Viewport:Byte=True)
	SetScale Float(GraphicsWidth())/Float(ImageWidth(image1)),Float(GraphicsHeight())/Float(ImageHeight(image1))


		Image = Image1
		If Viewport Then 
			Width = image1.width
			Height = image1.height
		Else
			Width = GraphicsWidth()
			Height = GraphicsHeight()
		EndIf
		If DX
			DebugLog "w : " + width + " h : " + height
			ViewportSet(0,0,Width,Height)
		Else
			ViewportSet(0,0,Width,Height,True)
		EndIf
		
	?Win32	
		If DX
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image1.frame(0))
			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget( DXFrame.Surface,0)
			D3D7GraphicsDriver().Direct3DDevice7().BeginScene()
		Else
	?
			GLFrame:TGLImageFrame = TGLImageFrame(Image1.frame(0))
		'	ViewportSet(0,0,Width,Height,True)
	?Win32
		EndIf		
	?
	'	debuglog "tRender : TextureRender_Begin OK"
		Return True
	End Function
	
'#################################################################################
Rem
bbdoc: Clear the Current Viewport with color
about: 
 <table>
	<tr><td><b>col:Int</td><td>The Color to clear the Viewport with includes Alpha AARRGGBB format.</td></tr>
 </table>
End Rem
	Function Cls(col:Int=$FF000000)
	?Win32	
		If dx
			D3D7GraphicsDriver().Direct3DDevice7().Clear 1,Null,D3DCLEAR_TARGET,col,0,0
		Else
	?
				Local Red# 	= (col Shr 16) & $FF
				Local Green# 	= (col Shr 8) & $FF
				Local Blue# 	= col & $FF
				Local Alpha# 	= (col Shr 24) & $FF
			'	DebugLog Alpha
				glClearColor red/255.0,green/255.0,blue/255.0,alpha/255.0
				glClear GL_COLOR_BUFFER_BIT
	?Win32	
		EndIf
	?
	End Function
	
'#################################################################################
'?win32	
	Function SetMipMap(Image:TImage,Level:Int)
		If DX
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image.frame(0))
			
			'DXFrame.BuildMipMaps()
			
			Local	src:IDirectDrawSurface7
			Local	dest:IDirectDrawSurface7
			Local	caps2:DDSCAPS2
			caps2=New DDSCAPS2
			caps2.dwCaps=DDSCAPS_TEXTURE|DDSCAPS_MIPMAP'|DDSCAPS_3DDEVICE
			caps2.dwCaps2= DDSCAPS2_MIPMAPSUBLEVEL
		
			src = DXFrame.Surface
					
			Local res = src.GetAttachedSurface(caps2,Varptr dest)
			
			If res<>DD_OK 
				tRenderERROR = tError(res)
				Return False
			EndIf
			
			DXFrame.Surface =  dest
			dest.Release_
			
			DebugLog "MipMap Selected OK"
			Return True
		Else
			Return False
		EndIf	
	End Function
'?	
'#################################################################################
Rem
bbdoc: End the Texture Render Process
about: 
This Must be called when you have finished rendering to the Texture
End Rem	
	Function TextureRender_End()
	SetScale 1.0,1.0

	?Win32
		If dx
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()
		Else
	?
			glBindTexture GL_TEXTURE_2D,GLFrame.name
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, Width, Height, 0)
			glBindTexture GL_TEXTURE_2D,0
	?Win32
		EndIf
	?
		ViewportSet(0,0,GraphicsWidth(),GraphicsHeight())
		DrawImageRect Image,-2000,-2000,1,1
	'	debuglog "tRender : TextureRender_End OK"
		Return True
	End Function
	
'#################################################################################
Rem
bbdoc: Begin the Normal BackBuffer Rendering Process
End Rem
	Function BackBufferRender_Begin()
	?Win32
		If DX Then 
			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget(backbuffer,0)
			
			
		Else
	?		
		'	If GLFrame <> Null Then glBindTexture GL_TEXTURE_2D,GLFrame.name
	?Win32
		EndIf
	?
		ViewportSet(0,0,GraphicsWidth(),GraphicsHeight())
		DebugLog "tRender : BackBufferRender_Begin OK"
		Return True
	End Function
	
'#################################################################################
Rem
bbdoc: End the Normal BackBuffer Rendering process
about: 
This Must be called when you have finished rendering to the BackBuffer and Before the Flip Command.
End Rem	
	Function BackBufferRender_End()
	?Win32
		If DX
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()
		Else
	?
			glBindTexture GL_TEXTURE_2D,0
	?Win32
		EndIf
	?
		DebugLog "tRender : BackBufferRender_End OK"
		Return True
	End Function
	
'#################################################################################
		

End Type

If the problems can be solved I'll comment the code, add nicer variable names and make it a module again.

<edit> For those interested I fixed the scale issue. My theory was the 'camera' used in Bmax is offset a little (zoomed out?) so I was rendering something smaller then smaller again. A simple setscale fixed it.... I think.

Nice, thank you. I'll have a play with this later.

Got some steps further?

May be some commands like glOrtho (with respective values) helps to get the right scale in openGL-mode ?!


bye
MB

I must be missing something but it works OK for OGL. The DX driver works with the setscale although, for small images, the result is a bit blurred.

For OGL it's not working as expected on my machine.

My game uses a resolution of 800x600 and I'm drawing some interface-graphics (a bottom image displaying some other images/informations) - this is about 800x250 pixel but it's displayed with about 1000*400 pixels.

So if using OpenGL as driver its a setscale of about 0.78, 340.0/600 to fit it... but this depends on the dimensions of the images. That's why I mentioned glOrtho - there are some values which are also not 1:1 related to resolution or image-dimensions.
Next to the "wrong scale" aspect the images look very blurry here.

With DX no problems occour after all.


bye
MB

As an aside, how have viewports changed since V1.24? Do they work on all PCs now?

For OGL it's not working as expected on my machine.
. If anybody has the answer then please post as I never touched the OGL method in the module.

Viewports always have worked with systems fullfilling DX8+ specs.

DX7 has no scissor test (DX9+ and OpenGL have it), you need to do it through clip planes and 2D office cards (totally outdated intel/sis/s3 with Pre GMA900 technology so unable to run BM Max2D anyway) just support 2 of them (one for near clip, one for far, but none left for the side. all newer support 4+)

Try if you see all 4 Rects (Topleft,TopRight,BottomLeft,BottomRight) and all values from 50 to 750.

For doing this - exchange your example with:
SetBlend(ALPHABLEND)
	tRender.TextureRender_Begin(myImage,False)
	tRender.Cls	
	For Local i:Int = 0 To 16 'drawing 16 marks horizontally - from 0 to 800px
	  DrawLine(i*50,0,i*50,600)
	  DrawText("x="+(i*50),i*50, 50)
	Next
	SetColor 255,0,0
		DrawRect(0,0,11,11) 'top left
		DrawRect(790,0,11,11) 'top right
		DrawRect(790,590,11,11) 'bottom right
		DrawRect(0,590,11,11) 'bottom left
	SetColor 200,200,200	
		DrawRect(1,1,10,10) 'top left
		DrawRect(789,1,10,10) 'top right
		DrawRect(789,589,10,10) 'bottom right
		DrawRect(1,589,10,10) 'bottom left
	SetColor 255,255,255
	tRender.TextureRender_End()
	SetImageHandle(logo,0,0)
	tRender.BackBufferRender_Begin()
	tRender.Cls
	DrawImage myimage , 0,0
	DrawImage logo,300,0
	tRender.BackBufferRender_End()
	Flip
WaitKey()


and replace:
Graphics 800,600,0


with:
SetGraphicsDriver GLMax2DDriver()
Graphics 800,600,0



bye
MB

Yep I get the same problem. If anybody has the answer then please post as I never touched the OGL method in the module.

Thanks University-lessons to give me some time on my laptop ;D ... what else is useful in "algorithms and programming".


think I got something which is usable...

Add function (or use the given one in the frame-create-function):

	Function NextPowSize(n) 
			Local t = 1
			While t<n
				t:*2
			Wend
			Return t
		End Function


and change TextureRender_End to:
	Function TextureRender_End()
	SetScale 1.0, 1.0

	?Win32
		If dx
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()
		Else
	?
			glBindTexture GL_TEXTURE_2D, GLFrame.name
'			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, Width, Height, 0)
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, NextPowSize(width), NextPowSize(Height), 0) 
			glBindTexture GL_TEXTURE_2D, 0
	?Win32
		EndIf
	?
		ViewportSet(0,0,GraphicsWidth(),GraphicsHeight())
		DrawImageRect Image,-2000,-2000,1,1


	'	debuglog "tRender : TextureRender_End OK"
		Return True
	End Function


As you see I changed the width/height thing... Because Textures are surely saved with power of 2 and some sprites (eg when fullscreen-resolutions) aren't saved this way - the stretching occours.

If now the function nextpowsize returns the correct dimensions - for 800x600 its 1024x1024, all stretching is gone... and it looks smooth again ;D...


Hope it works for you too.

ah and... may be for OGL you can comment out:
		DrawImageRect Image,-2000,-2000,1,1

in the same function - for me commenting it out didnt change a thing in OGL.


bye
MB

Ok ... instead of this huge change you may also change

Just change the given width/height-variables to a new value:
	Function Create:TImage(Width:Int,Height:Int,Flags:Int=FILTEREDIMAGE)
		Local t:TImage=New TImage
		t.width=NextPowSize(width)
		t.height=NextPowSize(height)
...


would work too I think.


bye
MB

Sorry for the elementary question, but: what does this Render To Texture do? What can it achieve that standard BlitzMax commands can't?

with rtt you can manipulate an image the easy way...


imagine you have a part of your screen filled with a background image... and onto this image you draw other images (sprites). So you will have to draw them each time for each flip and so on...

Also if you only change the drawn-on-top-images every second this will save you the time for a whole second (60 flips or how your fps is set). When the images change you just rebuild the rtt-image and you will be able to draw this for a whole second.

short: ability to draw images on another image and use this "composition" for the next draw-loops until this image has to be changed again.
(huds may be a nice usage for this piece of code).


bye
MB

You can also use it to blur/bloom images and create image feedback effects.
It can also be used to produce unique sprites from seperate images ("composite" sprites).

Is this a stable solution for OpenGL and DirectX?

It's completely stable until somebody reports a problem with it.

Ok...
Now it seems to work when using a trender.create-command with values corresponding to app-resolution (eg. 800x600).

But I don't want to waste memory so I want it the way I'm able to create 500x200 too (which boosts to 512x256).
All tRenders I create are dependend on the overall applications resolution ... so up to now I'm only able to create tRender-images with 800x600 (my app is using this res) else it get's blurry.

So if I create a tRender-image with 800x400 and put it on 0,0 (with resolution of 800x600) all things drawn within this image are halfed-heigh - image at 200 is visually at 100 with half of height ... and so on.

Why do the functions above scale my objects?

At the moment the function only can be used as "composition layers" not as "compositable spritelayer".


bye
MB

I put the scale commands in due to the scaling problem in my original post. Tim suggested this was due to the viewport parm used but I got the same with both.
I checked on GameDev as my own attempt at RTT had the same issue but with DX. The response was

I'd guess you are transforming the data twice. If the output to the texture looks right, then make sure you move it to the screen as already transformed data. Otherwise it's like using a camera to take a picture of a picture that's 5 feet away.

Same might be happening with OGL.

will this work in the 1.28 update? i'd like to know before i upgrade.

Cheers
Charlie

It works for me but I have changed the code to fix the scaling issue and removed OGL. The code above works as well but is messy. For OGL I would use Diablo's HighGfx code.
Here is the DX code I use now :
' Original author Tim Fisher
Graphics 800,600,0
tRender.Initialise()
Local logo:TImage = LoadImage("max.png",MIPMAPPEDIMAGE|FILTEREDIMAGE)
Local myImage:TImage = tRender.Create(ImageWidth(logo) , ImageHeight(logo) , MIPMAPPED | FILTEREDIMAGE)
tRender.TextureRender(myImage,True)
Cls	
DrawImage logo,0,0

SetBlend(ALPHABLEND)

While Not KeyHit(KEY_ESCAPE)
	tRender.BackBufferRender() 
	Cls
	SetAlpha 1.0
	DrawImage myimage , MouseX(),MouseY()
	DrawImage logo,300,200
	Flip
	tRender.TextureRender(myImage,True)
'	Cls	
'	DrawImage logo,0,0
	SetAlpha Rnd(0.3,1.0)
	SetColor Rand(255) , Rand(255) , Rand(255)
	Local cubx:Int = Rand(0 , 200)
	Local cuby:Int=Rand(0,200)
	DrawRect cubx,cuby,20,20
	SetColor 255,255,255
	DrawText "Hello World",10,10

	Delay 250
Wend

Const DDERR_INVALIDSURFACETYPE 	= $88760250
Const DDERR_INVALIDPARAMS		= $80070057
Const DDERR_INVALIDOBJECT 		= $88760082
Const DDERR_NOTFOUND 			= $887600ff
Const DDERR_SURFACELOST 		= $887601c2

Global tRenderERROR:String

Function tError:String(err:Int)

	Select err
		Case DDERR_INVALIDSURFACETYPE
			Return "DDERR_INVALIDSURFACETYPE"
			
		Case DDERR_INVALIDPARAMS
			Return "DDERR_INVALIDPARAMS"
			
		Case DDERR_INVALIDOBJECT 
			Return "DDERR_INVALIDOBJECT"
			
		Case DDERR_NOTFOUND
			Return "DDERR_NOTFOUND"
			
		Case DDERR_SURFACELOST
			Return "DDERR_SURFACELOST"
			
	End Select
End Function

Type tRender
	
	Global DXFrame:TD3D7ImageFrame
	Global backbuffer:IDirectDrawSurface7
	Global Image:TImage = CreateImage(1,1)
	Global Width:Int
	Global Height:Int
	Global o_r , o_g , o_b

Rem
bbdoc: Initialise the Module
about: 
The module must be initialised before use. This ensures usage of the correct render pipeline. If the render device is changed then you must 
re initialise the module.
End Rem		
	Function Initialise()
			D3D7GraphicsDriver().Direct3DDevice7().GetRenderTarget Varptr backbuffer
		DebugLog "tRender : Initialise OK"
		Return True
	End Function
	
'#################################################################################

Rem
bbdoc: Create Image with Render Characteristics
returns: TImage Handle to Object
about: 
 <table>
		<tr><td><b>Width:Int</td><td>Width in Pixels of Image, try to follow the normal rules of textures</td></tr>
		<tr><td><b>Height:Int</td><td>Height in Pixels of Image</td></tr>
		<tr><td><b>Flags:Int</td><td>Normal Image Flags</td></tr>
	</table>
End Rem	
	Function Create:TImage(Width:Int,Height:Int,Flags:Int=FILTEREDIMAGE)

		Local t:TImage=New TImage
		t.width=width
		t.height=height
		t.flags=flags
		t.mask_r= 0
		t.mask_g= 0
		t.mask_b= 0
		t.pixmaps=New TPixmap[1]
		t.frames=New TImageFrame[1]
		t.seqs=New Int[1]
		t.pixmaps[0]= t.Lock(0,True,False)
		t.seqs[0]=GraphicsSeq
		
			t.frames[0] = CreateFrame(TD3D7Max2DDriver(_max2dDriver),t.Width,t.Height,t.flags)
			
		DebugLog "tRender : Create OK"
		
		
		Return t

	End Function

'#################################################################################
		
	Function CreateFrame:TD3D7ImageFrame( driver:TD3D7Max2DDriver,width,height,flags )				
		Function Pow2Size( n )
			Local t=1
			While t<n
				t:*2
			Wend
			Return t
		End Function

		Local	swidth=Pow2Size(width)
		Local	sheight=Pow2Size(height)
		Local	desc:DDSURFACEDESC2=New DDSURFACEDESC2
		Local	res
						
		desc.dwSize=SizeOf(desc)
		desc.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT
		desc.dwWidth=swidth
		desc.dwHeight=sheight	
		desc.ddsCaps=DDSCAPS_TEXTURE|DDSCAPS_3DDEVICE|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM  ' **************************************************
		desc.ddsCaps2=DDSCAPS2_HINTDYNAMIC'|DDSCAPS2_TEXTUREMANAGE
		desc.ddpf_dwSize=SizeOf(DDPIXELFORMAT)
		desc.ddpf_dwFlags=DDPF_RGB|DDPF_ALPHAPIXELS
		desc.ddpf_BitCount=32
		desc.ddpf_BitMask_0=$ff0000
		desc.ddpf_BitMask_1=$00ff00
		desc.ddpf_BitMask_2=$0000ff
		desc.ddpf_BitMask_3=$ff000000

		
		Local surf:IDirectDrawSurface7
		If flags & MIPMAPPEDIMAGE desc.ddsCaps:|DDSCAPS_MIPMAP|DDSCAPS_COMPLEX
		res=D3D7GraphicsDriver().DirectDraw7().CreateSurface( desc,Varptr surf,Null )
		If res<>DD_OK 
			tRenderERROR = tError(res)
			DebugLog "tRender : CreateFrame ERROR : "+tRenderERROR
			Return Null
		EndIf
		'RuntimeError "Create DX7 surface Failed"	
							
		Local frame:TD3D7ImageFrame=New TD3D7ImageFrame
		frame.driver=driver
		frame.surface=surf
		frame.sinfo=New DDSURFACEDESC2
		frame.sinfo.dwSize=SizeOf(frame.sinfo)
		frame.xyzuv=New Float[24]
		frame.width=width
		frame.height=height
		frame.flags=flags
		frame.SetUV 0.0,0.0,Float(width)/swidth,Float(height)/sheight
	
			
		'frame.BuildMipMaps()
		DebugLog "tRender : CreateFrame OK"
		Return frame
	End Function

	
Rem
bbdoc: Begin the Texture Render Process
about: 
 <table>
	<tr><td><b>Image:TImage</td><td>The Image to Render to, as created with "Create" command.</td></tr>
	<tr><td><b>Viewport:Byte</td><td>True (default) to Automatically resize the viewport to the image size</td></tr>
 </table>
End Rem
	Function TextureRender(Image1:TImage,Viewport:Byte=True)
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()

		If Viewport Then 
			Width = image1.width
			Height = image1.height
			
		Else
			Width = GraphicsWidth()
			Height = GraphicsHeight()
		EndIf
		SetViewport(0 , 0 , width , height)
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image1.frame(0))
			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget( DXFrame.Surface,0)
			D3D7GraphicsDriver().Direct3DDevice7().BeginScene()
	'	debuglog "tRender : TextureRender_Begin OK"
		Return True
	End Function
	
'?win32	
	Function SetMipMap(Image:TImage,Level:Int)
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image.frame(0))
			
			'DXFrame.BuildMipMaps()
			
			Local	src:IDirectDrawSurface7
			Local	dest:IDirectDrawSurface7
			Local	caps2:DDSCAPS2
			caps2=New DDSCAPS2
			caps2.dwCaps=DDSCAPS_TEXTURE|DDSCAPS_MIPMAP'|DDSCAPS_3DDEVICE
			caps2.dwCaps2= DDSCAPS2_MIPMAPSUBLEVEL
		
			src = DXFrame.Surface
					
			Local res = src.GetAttachedSurface(caps2,Varptr dest)
			
			If res<>DD_OK 
				tRenderERROR = tError(res)
				Return False
			EndIf
			
			DXFrame.Surface =  dest
			dest.Release_
			
			DebugLog "MipMap Selected OK"
			Return True
	End Function
'#################################################################################
Rem
bbdoc: Begin the Normal BackBuffer Rendering Process
End Rem
	Function BackBufferRender()
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()

			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget(backbuffer,0)
		SetViewport(0,0,GraphicsWidth(),GraphicsHeight())
		DebugLog "tRender : BackBufferRender_Begin OK"
		Return True
	End Function
	
End Type


<edit> Just realise I have removed all the module info which listed Tim 'Indiepath' as the code originator so thanks to Tim.
In addition, the code now uses the setviewport command. There have been reports of this not working on some graphics cards.

Using the above code, I can't save myImage to a file. Anyone knows how I can achieve this? When using
SavePixmapPNG(myImage.Lock(0,1,0),"tex.png") 

I got an empty PNG. The size is correct, it's just empty.

Thanks

PS: I got the same issues with Klepto's FBO code.

It's probably because the image is outside Bmax control really so doesn't have a lock method. I could be wrong though.
<Edit> You might have to resort to drawing, grabbing and saving.

Does this do what you're after?
' Original author Tim Fisher
Graphics 800,600,0
tRender.Initialise()
Local logo:TImage = LoadImage("max.png",MIPMAPPEDIMAGE|FILTEREDIMAGE)
Local myImage:TImage = tRender.Create(ImageWidth(logo)*4 , ImageHeight(logo)*4 , MIPMAPPED | FILTEREDIMAGE)
tRender.TextureRender(myImage,True)
Cls	
DrawImageRect logo,0,0,ImageWidth(logo),ImageHeight(logo)

SetBlend(ALPHABLEND)
Local t1:Int,t2:Int
While Not KeyHit(KEY_ESCAPE)
	tRender.BackBufferRender() 
	Cls
	SetAlpha 1.0
	DrawImage myimage , MouseX(),MouseY()
	DrawImage logo,300,200
	DrawText (t2-t1),0,0
	Flip
	t1:Int=MilliSecs()
	tRender.TextureRender(myImage,True)
'	Cls	
'	DrawImage logo,0,0
	SetAlpha Rnd(0.3,1.0)
	SetColor Rand(255) , Rand(255) , Rand(255)
	Local cubx:Int = Rand(0 , 200)
	Local cuby:Int=Rand(0,200)
	DrawRect cubx,cuby,20,20
	SetColor 255,255,255
	DrawText "Hello World",10,10
	DrawText ImageWidth(myimage)+ " " + ImageHeight(myimage),10,30
	t2:Int=MilliSecs()
Wend
tRender.BackBufferRender() 
Cls
DrawImage myimage,0,0
Local mypix:TPixmap=CreatePixmap(ImageWidth(logo),ImageHeight(logo),PF_RGBA8888)
ClearPixels(mypix)
mypix=GrabPixmap(0,0,ImageWidth(logo),ImageHeight(logo))
SavePixmapPNG(mypix,"output.png")

Const DDERR_INVALIDSURFACETYPE 	= $88760250
Const DDERR_INVALIDPARAMS		= $80070057
Const DDERR_INVALIDOBJECT 		= $88760082
Const DDERR_NOTFOUND 			= $887600ff
Const DDERR_SURFACELOST 		= $887601c2

Global tRenderERROR:String

Function tError:String(err:Int)

	Select err
		Case DDERR_INVALIDSURFACETYPE
			Return "DDERR_INVALIDSURFACETYPE"
			
		Case DDERR_INVALIDPARAMS
			Return "DDERR_INVALIDPARAMS"
			
		Case DDERR_INVALIDOBJECT 
			Return "DDERR_INVALIDOBJECT"
			
		Case DDERR_NOTFOUND
			Return "DDERR_NOTFOUND"
			
		Case DDERR_SURFACELOST
			Return "DDERR_SURFACELOST"
			
	End Select
End Function

Type tRender
	
	Global DXFrame:TD3D7ImageFrame
	Global backbuffer:IDirectDrawSurface7
	Global Image:TImage = CreateImage(1,1)
	Global Width:Int
	Global Height:Int
	Global o_r , o_g , o_b

Rem
bbdoc: Initialise the Module
about: 
The module must be initialised before use. This ensures usage of the correct render pipeline. If the render device is changed then you must 
re initialise the module.
End Rem		
	Function Initialise()
			D3D7GraphicsDriver().Direct3DDevice7().GetRenderTarget Varptr backbuffer
		DebugLog "tRender : Initialise OK"
		Return True
	End Function
	
'#################################################################################

Rem
bbdoc: Create Image with Render Characteristics
returns: TImage Handle to Object
about: 
 <table>
		<tr><td><b>Width:Int</td><td>Width in Pixels of Image, try to follow the normal rules of textures</td></tr>
		<tr><td><b>Height:Int</td><td>Height in Pixels of Image</td></tr>
		<tr><td><b>Flags:Int</td><td>Normal Image Flags</td></tr>
	</table>
End Rem	
	Function Create:TImage(Width:Int,Height:Int,Flags:Int=FILTEREDIMAGE)

		Local t:TImage=New TImage
		t.width=width
		t.height=height
		t.flags=flags
		t.mask_r= 0
		t.mask_g= 0
		t.mask_b= 0
		t.pixmaps=New TPixmap[1]
		t.frames=New TImageFrame[1]
		t.seqs=New Int[1]
		t.pixmaps[0]= t.Lock(0,True,False)
		t.seqs[0]=GraphicsSeq
		
			t.frames[0] = CreateFrame(TD3D7Max2DDriver(_max2dDriver),t.Width,t.Height,t.flags)
			
		DebugLog "tRender : Create OK"
		
		
		Return t

	End Function

'#################################################################################
		
	Function CreateFrame:TD3D7ImageFrame( driver:TD3D7Max2DDriver,width,height,flags )				
		Function Pow2Size( n )
			Local t=1
			While t<n
				t:*2
			Wend
			Return t
		End Function

		Local	swidth=Pow2Size(width)
		Local	sheight=Pow2Size(height)
		Local	desc:DDSURFACEDESC2=New DDSURFACEDESC2
		Local	res
						
		desc.dwSize=SizeOf(desc)
		desc.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT
		desc.dwWidth=swidth
		desc.dwHeight=sheight	
		desc.ddsCaps=DDSCAPS_TEXTURE|DDSCAPS_3DDEVICE|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM  ' **************************************************
		desc.ddsCaps2=DDSCAPS2_HINTDYNAMIC'|DDSCAPS2_TEXTUREMANAGE
		desc.ddpf_dwSize=SizeOf(DDPIXELFORMAT)
		desc.ddpf_dwFlags=DDPF_RGB|DDPF_ALPHAPIXELS
		desc.ddpf_BitCount=32
		desc.ddpf_BitMask_0=$ff0000
		desc.ddpf_BitMask_1=$00ff00
		desc.ddpf_BitMask_2=$0000ff
		desc.ddpf_BitMask_3=$ff000000

		
		Local surf:IDirectDrawSurface7
		If flags & MIPMAPPEDIMAGE desc.ddsCaps:|DDSCAPS_MIPMAP|DDSCAPS_COMPLEX
		res=D3D7GraphicsDriver().DirectDraw7().CreateSurface( desc,Varptr surf,Null )
		If res<>DD_OK 
			tRenderERROR = tError(res)
			DebugLog "tRender : CreateFrame ERROR : "+tRenderERROR
			Return Null
		EndIf
		'RuntimeError "Create DX7 surface Failed"	
							
		Local frame:TD3D7ImageFrame=New TD3D7ImageFrame
		frame.driver=driver
		frame.surface=surf
		frame.sinfo=New DDSURFACEDESC2
		frame.sinfo.dwSize=SizeOf(frame.sinfo)
		frame.xyzuv=New Float[24]
		frame.width=width
		frame.height=height
		frame.flags=flags
		frame.SetUV 0.0,0.0,Float(width)/swidth,Float(height)/sheight
	
			
		'frame.BuildMipMaps()
		DebugLog "tRender : CreateFrame OK"
		Return frame
	End Function

	
Rem
bbdoc: Begin the Texture Render Process
about: 
 <table>
	<tr><td><b>Image:TImage</td><td>The Image to Render to, as created with "Create" command.</td></tr>
	<tr><td><b>Viewport:Byte</td><td>True (default) to Automatically resize the viewport to the image size</td></tr>
 </table>
End Rem
	Function TextureRender(Image1:TImage,Viewport:Byte=True)
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()

		If Viewport Then 
			Width = image1.width
			Height = image1.height
			
		Else
			Width = GraphicsWidth()
			Height = GraphicsHeight()
		EndIf
		SetViewport(0 , 0 , width , height)
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image1.frame(0))
			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget( DXFrame.Surface,0)
			D3D7GraphicsDriver().Direct3DDevice7().BeginScene()
	'	debuglog "tRender : TextureRender_Begin OK"
		Return True
	End Function
	
'?win32	
	Function SetMipMap(Image:TImage,Level:Int)
			Local DXFrame:TD3D7ImageFrame = TD3D7ImageFrame (image.frame(0))
			
			'DXFrame.BuildMipMaps()
			
			Local	src:IDirectDrawSurface7
			Local	dest:IDirectDrawSurface7
			Local	caps2:DDSCAPS2
			caps2=New DDSCAPS2
			caps2.dwCaps=DDSCAPS_TEXTURE|DDSCAPS_MIPMAP'|DDSCAPS_3DDEVICE
			caps2.dwCaps2= DDSCAPS2_MIPMAPSUBLEVEL
		
			src = DXFrame.Surface
					
			Local res = src.GetAttachedSurface(caps2,Varptr dest)
			
			If res<>DD_OK 
				tRenderERROR = tError(res)
				Return False
			EndIf
			
			DXFrame.Surface =  dest
			dest.Release_
			
			DebugLog "MipMap Selected OK"
			Return True
	End Function
'#################################################################################
Rem
bbdoc: Begin the Normal BackBuffer Rendering Process
End Rem
	Function BackBufferRender()
			D3D7GraphicsDriver().Direct3DDevice7().EndScene()

			D3D7GraphicsDriver().Direct3DDevice7().SetRenderTarget(backbuffer,0)
		SetViewport(0,0,GraphicsWidth(),GraphicsHeight())
		DebugLog "tRender : BackBufferRender_Begin OK"
		Return True
	End Function
	
End Type




Yes, grabpixmap works. But grabbing the backbuffer with alpha produces bad results (or I have to cheat and draw/grab multiple times), so I thought about using RTT.

The problem is a pixmap is never created not the structure to create one. It's possible to do though... I'd expect.