davecamp / Render2Texture

A render to texture solution for the BlitzMax language
2 stars 2 forks source link

Restructuring TRenderImage #13

Open GWRon opened 7 years ago

GWRon commented 7 years ago

In reference to some posts in issue #12: This is a copy of my initial proposal

Maybe a `TRenderImage.GetVirtualImage:TImage()" could return the TRenderImage we use __now___. So you have something like this:

local rt:TRenderTarget = CreateRenderTarget(100,100)
SetRenderTarget(rt)
DrawRect(0,0,10,10)
SetRenderTarget(null)

local localImage:TImage = rt.GetImage()
'returns TGPUImage which extends TImage and is the _old_ TRenderImage)
local gpuImage:TImage = rt.GetVirtualImage()

If you create SetRenderTarget() to accept objects (SetRenderTarget(target:object) rather than SetRenderTarget(target:TRenderTarget)) you could even allow existing TImages as rendertarget .

Should SetRenderTarget(null) return something? Returning an TImage by default is something "cpu/gpu hungry" - only if one passes a TImage to SetRenderTarget() this might be a desired default behaviour. Maybe one should consider returning the current TRenderTarget instance ... which might contain a valid image:TImage reference but always contains a valid gpuImage:TGPUImage reference - except it gets deleted (dunno if that was a bug that it was available after the SetRenderTarget(null)-call??). Of course a "null" rendertarget returns null, and therefor no image/gpuimage.

Might be better to not return the RenderTarget at all and let the user decide what to do - even if this is not that "auto-magic" as I would like to see it. So hmm, returning the current set TRenderTarget sounds good for me - but maybe not the best for others.

So for me this would be something "nice" (at least in the current situation):

local rt:TRenderTarget = CreateRenderTarget(100,100)
SetRenderTarget(rt)
DrawRect(0,0,10,10)
SetRenderTarget(null)

'dynamic process - passing an image!
local mySprite:TImage = LoadImage("background.png")
local rt_copy:TRenderTarget = SetRenderTarget( mySprite )
DrawRect(0,0,mySprite.width*0.5,mySprite.height*0.5)
local rt2:TRenderTarget = SetRenderTarget(null)
'mySprite contains the actual image now - on De-Referencing via 
'SetRenderTarget(null) the actual content was fetched from the GPU 
'to the texture of the image "mySprite" - which is referenced
'via "rt.image" too 

local localImage:TImage = rt.GetImage() 'the same as "mySprite" now
'returns TGPUImage which extends TImage and is the _old_ TRenderImage)
local gpuImage:TImage = rt.GetVirtualImage() 'the reference to the texture on the GPU
local gpuImage2:TGPUImage = rt.GetVirtualImage() 'the same reference as "gpuImage

Alternatively to the "use passed image as canvas" is to copy the image on passing it as render target. This depends on how people intent to use that thing. If you want to draw additional stuff on an image this is different to "create a composition of sprites". As image.copy() isn't that hard to write I assume the approach of using the given image as "canvas" is the better solution. What you pass is what will be modified.

Maybe the rt.GetImage() should always fetch the current state on the gpu ... so if you call it "in between" you stall the pipeline but get the current state (maybe of interest for some "recursive effects").

Your thoughts on this?

GWRon commented 7 years ago

Let's make it structured:


Type TRenderTarget
  Field gpuImage:TGPUImage 'current name: TRenderImage
  Field image:TImage
  Global currentRenderTarget:TRenderTarget

  Function Create:TRenderTarget(target:object)
    'backup current one (a global holding the current render target)
    local oldTarget:TRenderTarget = currentRenderTarget

    'reset
    if not target
      currentRenderTarget = null
      return oldTarget
    'same target as the current? Okay...
    elseif target = currentTarget  
      return target
    endif  

    'create a new target
    if TRenderTarget(target)
      'assign the new target as the current one
      currentRenderTarget = TRenderTarget(target)
    elseif TGPUImage(target)
      'Create a TRenderTarget
      'Set the TGPUImage(target) as gpuImage (further commands draw on this image)
    elseif TImage(target)
      'Create a TRenderTarget
      'Create a gpuImage with the dimensions of the image
      'Draw the image one time at "0,0"
      '  Maybe it is needed to copy the content of the image to the gpuImage
      '  in a non-concurrent-way (you actually "render on itself"
      'alternatively:
      'set gpuImage-texture to reflect the image-texture (if already uploaded to gpu)

    else
       Throw "Unsupported RenderTarget-type"
    endif

    return oldRenderTarget
  End Function

  Method GetImage:TImage()
    'assign current gpuImage.GetPixmap() to "image"
    'so it is the same reference each time!
   [...]

    return image
  End Method

  'or GetGPUImage() - depends on how you name it
  Method GetVirtualImage:TRenderImage()
    return gpuImage
  End Method
End Type

Function SetRenderTarget:TRenderTarget(target:object)
  TRenderTarget.Create(target)
End Function

The needed casts of the param can get neglected as this wont be a cpu-bound thing. You could even extend it to make it work with TPixmap.

davecamp commented 7 years ago

wrong post. lol.