#include "fbgfx.bi"

#include once "MyTDT\OpenGL.bas"

#define _TrashLock( _P... ) _P
#define _WrapSync( _P... ) 'will hang because of other mutexes

#define __DebugThrash( _P... ) _P
#define __DebugThrashLock( _P... )
#define __DebugThrashDraw( _P... ) '_P
#define __DebugThrashState( _P... )
#define __DebugThrashTexture( _P... ) '_P
#define __DebugThrashPerFrame( _P... ) '_P

#include once "gl\gl.bi"
#include once "gl\glext.bi"

extern "windows-ms"
    
  type u32 as ulong  : type s32 as long
  type f32 as single : type f64 as double
  
  type f32x2 : as f32 X, Y        : end type
  type f32x3 : as f32 X, Y, Z     : end type
  type f32x4 : as f32 X, Y, Z, W  : end type

  type f64x2 : as f64 X, Y        : end type
  type f64x3 : as f64 X, Y, Z     : end type
  type f64x4 : as f64 X, Y, Z, W  : end type

  type u32x2 : as u32 X, Y        : end type
  type u32x3 : as u32 X, Y, Z     : end type
  type u32x4 : as u32 X, Y, Z, W  : end type
  
  type RVX 'RTVLX
    as f32 fX,fY,fZ
    as f32 fRHW
    as u32 ucolor , uSpecular
    as f32 fU,fV 
  end type
  
  enum thPixFmt
    thPIXEL_FORMAT_NONE     = 0
    thPIXEL_FORMAT_UNKNOWN  = 1
    thPIXEL_FORMAT_P8       = 2 'used
    thPIXEL_FORMAT_R5G5B5   = 3
    thPIXEL_FORMAT_R5G6B5   = 4 'used
    thPIXEL_FORMAT_R8G8B8   = 5 
    thPIXEL_FORMAT_A8R8G8B8 = 6
    thPIXEL_FORMAT_R4G4B4   = 7 'used
    thPIXEL_FORMAT_A4L4     = 8 'used
  end enum
  
  type thDriverInfo
    Drivername as zstring ptr
  end type
  type thLockInfo    
    as any ptr pdata 
    as u32 Stride
    as s32 format
    as u32 width
    as u32 Height
    as RECT Rect
  end type
  
  type thTexture as any
  
  #macro _WroteCmd(_size)
    g_pWriteCmd += _size
    if g_pWriteCmd >= g_pCmdBufferLimit then
      g_pWriteCmd = g_pCmdBuffer      
      _WrapSync( g_bWrap=1 : mutexlock(g_WrapMutex) ) ': printf("+") 
      InterlockedExchangeAdd( @g_iBuffered , _size )
    else      
      InterlockedExchangeAdd( @g_iBuffered , _size )
    end if
    if g_iBuffered > (cuint(g_pCmdBufferLimit)-cuint(g_pCmdBuffer)) then
      puts("Cross Thread Buffer overflow!"): system      
    end if    
  #endmacro  
  #macro _glCmdInt( _cmd , _lock , _data , _size , _parms... ) 
    scope      
      'printf(">")
      using ThrashGL
      _WrapSync( if g_bWrap then g_bWrap=0 : mutexlock(g_WrapMutex):mutexunlock(g_WrapMutex) )
      'if g_TID <> GetCurrentThreadID() then puts("Thrash from wrong thread!")
      #if _lock
        _TrashLock( mutexlock(ThrashGL.g_WriteMutex) )
      #endif
      with *cptr( _cmd##Parms ptr , g_pWriteCmd ) 
        .pfCmd = cast(any ptr,@_cmd) : _parms 
      end with
      #if len(#_data)        
        memcpy( g_pWriteCmd+sizeof(_cmd##Parms) , _data , _size )
      #endif
      _WroteCmd( _size+sizeof(_cmd##Parms) )
      #if _lock
        _TrashLock( mutexunlock(ThrashGL.g_WriteMutex) )
      #endif
      'printf("<")
    end scope
  #endmacro
  #define glCmd( _cmd , _parms... ) _glCmdInt( _cmd , 1 , , 0 , _parms )
  #define glCmdUnlocked( _cmd , _parms... ) _glCmdInt( _cmd , 0 , , 0 , _parms )      
    
  namespace ThrashGL
    static shared as byte g_bInit = 0 , g_bWrap
    'static shared as ulong g_TID
    static shared as long g_iPage = 0 , g_iBuffered
    static shared as any ptr g_pSyncMutex , g_WriteMutex , g_pTexMutex , g_WrapMutex
    static shared as any ptr g_pCmdBuffer , g_pCmdBufferLimit , g_pReadCmd , g_pWriteCmd
    
    const cMaxTextures = 255
    type Texture
      pOrgTex as thTexture ptr
      as any ptr pTexData
      as ulong lTex
      as short wWid,wHei
      as byte  bFmt      
    end type
    static shared as Texture g_tTex(cMaxTextures) = any
    static shared as long g_iTexCount=0
    
    #define _parms( _n ) end type : type _n##Parms extends mtHeader
    type mtHeader 
      pfCmd as function stdcall ( as any ptr ) as long
      _parms(mtScreenres)     : as long lWid,lHei
      _parms(mtGenTexture)    : as long lTex
      _parms(mtTexImage)      : as Texture ptr pTex : as u32 ptr pPix,pPal
      _parms(mtBindTexture)   : as Texture ptr pTex
      _parms(mtClearTextures) : as long lTexCnt
      _parms(mtFlip     )     : as any ptr pSyncMutex
      _parms(mtClearWindow)   : as byte bFullClear
      _parms(mtWritePixels)   : as any ptr pSyncMutex,pData : as long lX,lY,lWid,lHei,lBpp,lPitch
      _parms(mtDrawLine )     : as RVX a,b
      _parms(mtDrawTri  )     : as RVX a,b,c
      _parms(mtDrawQuad )     : as RVX a,b,c,d
    end type    
    
    #define _X( _fX ) ((_fX)*w)
    #define _Y( _fY ) ((_fY)*w)
    #define _Z( _fZ ) ((((_fZ)*2)-1)*w)
    #define _Color( _Var ) uCor = lCor(_Var.uColor) : glColor4ubv( cast(any ptr,@uCor) ) 
    #define _Vtx( _Var ) w = 1/_Var.frhw : glTexCoord2f( _Var.fU , _Var.fV ): glVertex4f( _X(_Var.fX) , _Y(_Var.fY) , _Z(_Var.fZ) , w )
    
    private function lCor( lCorIn as u32 ) as u32
      return (lCorIn and &hFF00FF00) or ((lCorIn and &h0000FF) shl 16) or ((lCorIn and &hFF0000) shr 16)    
    end function
    private function GetTexFmt( bFormat as long , byref lIntFmt as long , byref lFmt as long , byref lPacking as long ) as long
      lIntFmt=0 : lFmt=0 : lPacking=0
      select case bFormat
      case thPIXEL_FORMAT_P8        'used
        lIntFmt = GL_RGB : lFmt = GL_RGBA : lPacking = GL_UNSIGNED_BYTE
      case -1 'thPIXEL_FORMAT_R5G5B5   
      case thPIXEL_FORMAT_R5G6B5    'used
        lIntFmt = GL_RGB : lFmt = GL_RGB : lPacking = GL_UNSIGNED_SHORT_5_6_5
      case -1 'thPIXEL_FORMAT_R8G8B8
        lIntFmt = GL_RGB : lFmt = GL_RGB : lPacking = GL_UNSIGNED_BYTE
      case -1 'thPIXEL_FORMAT_A8R8G8B8
        lIntFmt = GL_RGBA : lFmt = GL_RGBA : lPacking = GL_UNSIGNED_BYTE
      case thPIXEL_FORMAT_R4G4B4    'used
        lIntFmt = GL_RGBA4 : lFmt = GL_RGBA : lPacking = GL_UNSIGNED_SHORT_4_4_4_4
      case thPIXEL_FORMAT_A4L4      'used
        lIntFmt = GL_LUMINANCE4_ALPHA4 : lFmt = GL_LUMINANCE_ALPHA : lPacking = GL_UNSIGNED_BYTE
      case else
        puts("Unsupported texture format")        
      end select
      return bFormat
    end function
    
    private function mtScreenres( pParms as mtScreenresParms ptr ) as long 
      with *pParms
        screenres .lWid,.lHei,32,,fb.GFX_OPENGL
                
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        '// Standard OpenGL: 0 is at the bottom, ScreenHeight is at the top
        glOrtho(0.0f, .lWid, 0.0f, .lHei, -1.0f, 1.0f)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        '// flip the rendering upside down and shift it into view
        glScalef(1.0f, -1.0f, 1.0f)
        glTranslatef(0.0f, -.lHei, 0.0f)
        
        glEnable( GL_TEXTURE )
        glEnable( GL_BLEND )
        'glEnable( GL_DEPTH_TEST )
        
        'glEnable(GL_CULL_FACE)
        'glFrontFace(GL_CW) 'inverted because of the negative scale
        
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
                 
        'glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
        'glPolygonMode( GL_BACK, GL_LINE )
        
        'screenres 640,480,16,3 : g_iPage=0 : screenset g_iPage,g_iPage xor 1
        screencontrol fb.SET_WINDOW_POS,680,0
      end with
      return sizeof(*pParms)
    end function
    private function mtGenTexture( pParms as mtGenTextureParms ptr ) as long
      mutexlock( ThrashGL.g_pTexMutex )
      var lTexIDX = pParms->lTex      
      with g_tTex(lTexIDX)
        glGenTextures(1, @.lTex)
        glBindTexture(GL_TEXTURE_2D, .lTex)
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP)
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP)
        dim as long lInt=any , lFmt=any , lPack=any : GetTexFmt( .bFmt,lInt,lFmt,lPack )
        glTexImage2D(GL_TEXTURE_2D,0,lInt,.wWid,.wHei,0,lFmt,lPack,0)        
        if glGetError() then printf(!"Failed to create texture of type %i\n",.bFmt)':getchar()        
      end with
      mutexunlock( g_pTexMutex )
      return sizeof(*pParms)
    end function
    private function mtTexImage( pParms as mtTexImageParms ptr ) as long      
      mutexlock( g_pTexMutex )            
      with *(pParms->pTex)
        glBindTexture(GL_TEXTURE_2D, .lTex)                
        dim as any ptr pData = pParms->pPix
        dim as any ptr pTemp = 0
        dim as long lInt=any , lFmt=any , lPack=any
        select case GetTexFmt( .bFmt,lInt,lFmt,lPack )
        case thPIXEL_FORMAT_P8       = 2 'used
          var lPixels = clng(.wWid)*.wHei , pPal = pParms->pPal
          if pData then            
            if .pTexData = 0 then .pTexData = malloc(lPixels*5)
            memcpy( .pTexData , pData , lPixels )            
          end if
          pData = .pTexData+lPixels
          dim as const ubyte ptr pIn = .pTexData 
          dim as ubyte ptr pOut = pData
          for iN as long = 0 to lPixels-1
            *pOut = pPal[*pIn] : pIn += 1 : pOut += 1
          next
        case thPIXEL_FORMAT_R5G6B5   = 4 'used        
        case thPIXEL_FORMAT_R4G4B4   = 7 'used
        case thPIXEL_FORMAT_A4L4     = 8 'used
          var lPixels = clng(.wWid)*.wHei
          pTemp = malloc(lPixels)
          dim as ubyte ptr pIn = pData , pOut = pTemp
          for iN as long = 0 to (lPixels\2)-1
            pOut[0] = *pIn shl 4   
            pOut[1] = *pIn and &hF0
            pIn += 1 : pOut += 2
          next
        end select
        glTexImage2D(GL_TEXTURE_2D,0,lInt,.wWid,.wHei,0,lFmt,lPack,pData)                
        if glGetError() then puts("Failed to setup texture")        
        if pTemp then free(pTemp)
      end with      
      mutexunlock( g_pTexMutex )
      mutexunlock( ThrashGL.g_pSyncMutex )
      return sizeof(*pParms)
    end function
    private function mtBindTexture( pParms as mtBindTextureParms ptr ) as long
      with *pParms        
        var lTex = iif(.pTex,.pTex->lTex,0)        
        glBindTexture(GL_TEXTURE_2D, lTex)        
      end with
      return sizeof(*pParms)
    end function
    private function mtClearTextures( pParms as mtClearTexturesParms ptr ) as long      
      mutexlock( g_pTexMutex )
      with *pParms
        for N as long = 0 to .lTexCnt-1
          with g_tTex( N )
            glDeleteTextures( 1 , @.lTex ) : .lTex = 0
            if .pTexData then free(.pTexData):.pTexData=0
            .pOrgTex = 0 : .wWid=0 : .wHei=0 : .bFmt = 0
          end with
        next
      end with
      g_iTexCount = 0
      mutexunlock( g_pTexMutex )
      mutexunlock( g_pSyncMutex )
      return sizeof(*pParms)
    end function
    private function mtFlip( pParms as mtFlipParms ptr ) as long
      with *pParms
        flip
        mutexunlock( .pSyncMutex )
      end with
      return sizeof(*pParms)
    end function
    private function mtClearWindow( pParms as mtClearWindowParms ptr ) as long
      glClear( iif(pParms->bFullClear,GL_COLOR_BUFFER_BIT,0) or GL_DEPTH_BUFFER_BIT )
      return sizeof(*pParms)
    end function
    private function mtWritePixels( pParms as mtWritePixelsParms ptr ) as long
      with *pParms        
        var pImage = iif(.pData,.pData,cast(any ptr,pParms+1))
        glRasterPos2i( .lX, .lY ) : glPixelZoom(1, -1)
        glDrawPixels( .lWid , .lHei , GL_RGB , GL_UNSIGNED_SHORT_5_6_5 , pImage )
        if .pSyncMutex then mutexunlock( .pSyncMutex )
        return sizeof(*pParms)+iif(.pData,0,.lHei*.lPitch)        
      end with
    end function
    private function mtDrawLine( pParms as mtDrawLineParms ptr ) as long
      dim uCor as ulong = any , w as single = any            
      with *pParms        
        glBegin( GL_LINES )
          _Color( .a ) : _Vtx( .a )
          _Color( .b ) : _Vtx( .b )
        glEnd()
      end with
      return sizeof(*pParms)
    end function
    private function mtDrawTri( pParms as mtDrawTriParms ptr ) as long
      dim uCor as ulong = any , w as single = any      
      with *pParms        
        glBegin( GL_TRIANGLES )
          _Color( .a ) : _Vtx( .a )
          _Color( .b ) : _Vtx( .b )
          _Color( .c ) : _Vtx( .c )
        glEnd()
      end with
      return sizeof(*pParms)
    end function
    private function mtDrawQuad( pParms as mtDrawQuadParms ptr ) as long
      dim uCor as ulong = any , w as single = any      
      with *pParms        
        glBegin( GL_QUADS )
          _Color( .a ) : _Vtx( .a )
          _Color( .b ) : _Vtx( .b )
          _Color( .c ) : _Vtx( .c )
          _Color( .d ) : _Vtx( .d )
        glEnd()
      end with
      return sizeof(*pParms)
    end function
    
    private sub ThrashThread( dummy as any ptr )
      
      'StartExceptions()
      
      dim fnCmd as function stdcall ( pParms as any ptr ) as long
      SetThreadPriority( GetCurrentThread() , THREAD_PRIORITY_TIME_CRITICAL )
      do
        while g_iBuffered = 0
          sleep 1,1
          'static as zstring*8 pzTmp = "|/-\"
          'static as double dStart : if dStart=0 then dStart=timer
          'static as long iPos : iPos = clng((timer-dStart)*8) and 3
          'printf(!"%c\8",pzTmp[iPos]) : sleep 1,1
        wend
        fnCmd = *cptr(any ptr ptr,g_pReadCmd)        
        var iSz = fnCmd( g_pReadCmd )
        g_pReadCmd += iSz : InterlockedExchangeAdd( @g_iBuffered , -iSz )
        if g_pReadCmd >= g_pCmdBufferLimit then 
          'printf("-")
          _WrapSync(mutexunlock(g_WrapMutex)) 
          g_pReadCmd = g_pCmdBuffer
        end if
      loop
    end sub
    
  end namespace  
    
  static shared pfTHRASH_about as function() as thDriverInfo ptr
  function THRASH_about() as thDriverInfo ptr
    __DebugThrash( puts(__FUNCTION__) )    
    return pfTHRASH_about()
  end function  
  static shared pfTHRASH_is as function() as u32
  function THRASH_is() as u32
    __DebugThrash( puts(__FUNCTION__) )
    return pfTHRASH_is()
  end function
  static shared pfTHRASH_init as function() as u32
  function THRASH_init() as u32
    __DebugThrash( puts(__FUNCTION__) )
    
    var uResu = pfTHRASH_init()
    
    using ThrashGL
    
    if g_pSyncMutex=0 then
      'g_TID = GetCurrentThreadID()
      g_pSyncMutex = mutexcreate()
      g_pTexMutex = mutexcreate()
      _TrashLock( g_WriteMutex = mutexcreate() )
      _WrapSync( g_WrapMutex  = mutexcreate() )
      g_pCmdBuffer = malloc(1024*1024)
      g_pCmdBufferLimit = g_pCmdBuffer+256*1024
    end if
    
    threadcreate( @ThrashThread , 0 )
    g_pReadCmd   = g_pCmdBuffer : g_pWriteCmd  = g_pCmdBuffer      
    
    mutexlock( g_pTexMutex )
    dim as Texture tBlank
    for N as long = 0 to cMaxTextures
      g_tTex(N) = tBlank
    next N
    g_iTexCount = 0
    mutexunlock( g_pTexMutex )
    
    return uResu
  end function
  static shared pfTHRASH_setvideomode as function(mode as const u32, pending as const u32, depth as const u32) as u32
  function THRASH_setvideomode(mode as const u32, pending as const u32, depth as const u32) as u32
    var resu = pfTHRASH_setvideomode(mode,pending,depth)
    __DebugThrash( printf(!"THRASH_setvideomode(mode=%i pending=%i depth=%i)=%i\n",mode,pending,depth,resu) )
    
    if ThrashGL.g_bInit = 0 then
      ThrashGL.g_bInit = 1 : glCmd( mtScreenres , .lWid=640 : .lHei=480 )
    end if
    
    return resu
  end function    
  static shared pfTHRASH_lockwindow as function() as thLockInfo ptr
  function THRASH_lockwindow() as thLockInfo ptr 
    __DebugThrashLock( puts(__FUNCTION__) )    
    'screenlock
    return pfTHRASH_lockwindow()
  end function
  static shared pfTHRASH_unlockwindow as function(pInfo as thLockInfo ptr) as u32
  function THRASH_unlockwindow(pInfo as thLockInfo ptr) as u32 
    
    mutexlock( ThrashGL.g_pSyncMutex )
    
    _TrashLock( mutexlock(ThrashGL.g_WriteMutex) )
    #define _sz pInfo->Height*pInfo->Stride
    
    #if 0 'offload
      _glCmdInt( mtWritePixels , 0 , pInfo->pData , _sz , _
        : .lX=0:.lY=0 : .pData=0 : .pSyncMutex=0 _
        : .lWid=pInfo->width : .lHei=pInfo->height _
        : .lBpp=16 : .lPitch=pInfo->Stride _
      )
    #else 'sync
      _glCmdInt( mtWritePixels , 0 , , 0 , _
        : .lX=0:.lY=0 : .pData=pInfo->pData : .pSyncMutex=0 _
        : .lWid=pInfo->width : .lHei=pInfo->height _
        : .lBpp=16 : .lPitch=pInfo->Stride _
      )
    #endif
    _TrashLock( mutexunlock(ThrashGL.g_WriteMutex) )
        
    glCmd( mtFlip , .pSyncMutex = ThrashGL.g_pSyncMutex )
    mutexlock( ThrashGL.g_pSyncMutex ) : mutexunlock( ThrashGL.g_pSyncMutex )
        
    __DebugThrashLock( puts(__FUNCTION__) )
            
    #if 0
      with *pInfo      
        var pScr = screenptr, pIn = .pData
        for Y as long = 0 to .Height-1
          memcpy( pScr , pIn , .width*2 )
          pIn += .Stride : pScr += .width*2
        next Y
        screenunlock
      end with
    #endif
    var uResu = pfTHRASH_unlockwindow(pInfo)    
    'mutexlock( ThrashGL.g_pSyncMutex ) : mutexunlock( ThrashGL.g_pSyncMutex )
    return uResu
  end function  
  static shared pfTHRASH_sync as function(dummy as const u32) as u32
  function THRASH_sync(dummy as const u32) as u32
    __DebugThrash( puts(__FUNCTION__) )
    return pfTHRASH_sync(dummy)
  end function 
  static shared pfTHRASH_clip as function(x as const u32,y as const u32,xx as const u32,yy as const u32) as u32
  function THRASH_clip(x as const u32,y as const u32,xx as const u32,yy as const u32) as u32
    __DebugThrash( puts(__FUNCTION__) )
    return pfTHRASH_clip(x,y,xx,yy)
  end function  
  static shared pfTHRASH_treset as function() as u32
  function THRASH_treset() as u32
    __DebugThrash( puts(__FUNCTION__) )
    mutexlock( ThrashGL.g_pSyncMutex )
    glCmd( mtClearTextures , .lTexCnt = ThrashGL.g_iTexCount )
    ThrashGL.g_iTexCount = 0
    mutexlock( ThrashGL.g_pSyncMutex ): mutexunlock( ThrashGL.g_pSyncMutex )
    return pfTHRASH_treset()
  end function  
  static shared pfTHRASH_readrect as function(x as const u32,y as const u32,wid as const u32,hei as const u32,pixels as u32 ptr) as u32
  function THRASH_readrect(x as const u32,y as const u32,wid as const u32,hei as const u32,pixels as u32 ptr) as u32
    __DebugThrash( puts(__FUNCTION__) )
    return pfTHRASH_readrect(x,y,wid,hei,pixels)
  end function
  static shared pfTHRASH_restore as function() as u32
  function THRASH_restore() as u32
    __DebugThrash( puts(__FUNCTION__) )
    return pfTHRASH_restore()
  end function
  
  static shared pfTHRASH_window as function(idx as const u32) as u32
  function THRASH_window(idx as const u32) as u32
    var resu = pfTHRASH_window(idx)
    __DebugThrashLock( printf(!"THRASH_window(idx=%i)=%i\n",idx,resu) )
    'screenset idx : g_iPage=idx
    return resu
  end function  
  static shared pfTHRASH_clearwindow as function() as u32
  function THRASH_clearwindow() as u32
    __DebugThrashPerFrame( puts(__FUNCTION__) )
    
    glCmd( mtClearWindow , .bFullClear=true )
    
    return pfTHRASH_clearwindow()
  end function
  static shared pfTHRASH_flushwindow as function() as u32
  function THRASH_flushwindow() as u32
    __DebugThrashPerFrame( puts(__FUNCTION__) )
    'glFlush()
    return pfTHRASH_flushwindow()
  end function
  static shared pfTHRASH_pageflip as sub()
  sub THRASH_pageflip()
    __DebugThrashPerFrame( puts(__FUNCTION__) )
    'g_iPage xor= 1 : screenset g_iPage,g_iPage xor 1
        
    mutexlock( ThrashGL.g_pSyncMutex )
    glCmd( mtFlip , .pSyncMutex = ThrashGL.g_pSyncMutex )
    
    pfTHRASH_pageflip()
    
    mutexlock( ThrashGL.g_pSyncMutex ) : mutexunlock( ThrashGL.g_pSyncMutex )
  end sub  
  
  static shared pfTHRASH_talloc as function(wid as const u32,hei as const u32,fmt as const u32,pal as const WINBOOL,state as const u32) as thTexture ptr
  function THRASH_talloc(wid as const u32,hei as const u32,fmt as const u32,pal as const WINBOOL,state as const u32) as thTexture ptr
    __DebugThrashTexture( puts(__FUNCTION__) )          
    
    var pTex = pfTHRASH_talloc(wid,hei,fmt,pal,state)
    
    mutexlock( ThrashGL.g_pTexMutex )
      with ThrashGL.g_tTex(ThrashGL.g_iTexCount)
        .pOrgTex = pTex : .pTexData = 0 : .wWid = wid : .wHei = hei : .bFmt = fmt
      end with
      glCmd( mtGenTexture , .lTex = g_iTexCount )
      function = cptr(thTexture ptr , @ThrashGL.g_tTex(ThrashGL.g_iTexCount) )
      ThrashGL.g_iTexCount += 1
    mutexunlock( ThrashGL.g_pTexMutex )
    
    select case fmt
    case 2,4,7,8
    case else
      printf(!"new fmt: %i \n",fmt)
      getchar()
    end select
    
  end function  
  static shared pfTHRASH_tupdate as function(tex as thTexture ptr, pixels as const u32 ptr, pal as const u32 ptr) as thTexture ptr
  function THRASH_tupdate(tex as thTexture ptr, pixels as const u32 ptr, pal as const u32 ptr) as thTexture ptr
    __DebugThrashTexture( puts(__FUNCTION__) )
    'if pal=0 then puts("update without palette :D")    
    
    while ThrashGL.g_iBuffered > ((cuint(ThrashGL.g_pCmdBufferLimit)-cuint(ThrashGL.g_pCmdBuffer))\2)
      sleep 1,1
    wend
    
    dim as any ptr pTex = 0        
    if pixels then
      mutexlock( ThrashGL.g_pSyncMutex )
      mutexlock( ThrashGL.g_pTexMutex )
        if tex then
          glCmd( mtTexImage , .pTex = cast(any ptr,tex) : .pPix = cast(any ptr,pixels) : .pPal = cast(any ptr,pal) )
          pTex = cptr( ThrashGL.Texture ptr , tex )->pOrgTex
        end if    
      mutexunlock( ThrashGL.g_pTexMutex )    
      mutexlock( ThrashGL.g_pSyncMutex ) : mutexunlock( ThrashGL.g_pSyncMutex )
    elseif tex then
      pTex = cptr( ThrashGL.Texture ptr , tex )->pOrgTex      
    end if
    return pfTHRASH_tupdate(pTex,pixels,pal)
  end function
  
  static shared pfTHRASH_setstate as function(state as const u32, value as any ptr) as u32
  function THRASH_setstate(state as const u32, value as any ptr) as u32
    __DebugThrashState( puts(__FUNCTION__) )
    return pfTHRASH_setstate(state,value)
  end function  
  static shared pfTHRASH_settexture as function(tex as thTexture ptr) as u32
  function THRASH_settexture(tex as thTexture ptr) as u32
    __DebugThrashState( puts(__FUNCTION__) )
    glCmd( mtBindTexture , .pTex = cast(any ptr,tex) )
    var pTex = iif( tex , cptr( ThrashGL.Texture ptr , tex )->pOrgTex , 0 )     
    return pfTHRASH_settexture(pTex)    
  end function  
  static shared pfTHRASH_drawline as sub(a as RVX ptr, b as RVX ptr)
  sub THRASH_drawline(a as RVX ptr, b as RVX ptr)
    __DebugThrashDraw( puts(__FUNCTION__) )    
    
    glCmd( mtDrawLine , .a = *a : .b = *b )
    
    'line (a->fX,a->fY)-(b->fX,b->fY),lcor(a->uColor)    
    pfTHRASH_drawline(a,b)
  end sub
  static shared pfTHRASH_drawtri as sub(a as RVX ptr, b as RVX ptr, c as RVX ptr)
  sub THRASH_drawtri(a as RVX ptr, b as RVX ptr, c as RVX ptr)
    __DebugThrashDraw( puts(__FUNCTION__) )
    'with *a
      '__DebugThrash( printf(!"drawtri: %f,%f,%f %f %4X %f,%f\n",.fX,.fY,.fZ,.fRHW,.uColor,.fU,.fV) )
      'getchar()
    'end with
    
    glCmd( mtDrawTri , .a=*a : .b=*b : .c=*c )
    
    'line (a->fX,a->fY)-(b->fX,b->fY),lcor(a->uColor)
    'line -(c->fX,c->fY),lcor(b->uColor)
    'line -(a->fX,a->fY),lcor(c->uColor)
    pfTHRASH_drawtri(a,b,c)
  end sub
  static shared pfTHRASH_drawquad as sub(a as RVX ptr, b as RVX ptr, c as RVX ptr, d as RVX ptr)
  sub THRASH_drawquad(a as RVX ptr, b as RVX ptr, c as RVX ptr, d as RVX ptr)
    __DebugThrashDraw( puts(__FUNCTION__) )
    
    glCmd( mtDrawQuad , .a=*a : .b=*b : .c=*c : .d=*d )
        
    'line (a->fX,a->fY)-(b->fX,b->fY),lcor(a->uColor)
    'line -(c->fX,c->fY),lcor(b->uColor)
    'line -(d->fX,d->fY),lcor(c->uColor)
    'line -(a->fX,a->fY),lcor(d->uColor)
    pfTHRASH_drawquad(a,b,c,d)
  end sub
  static shared pfTHRASH_drawtrimesh as sub(count as const u32,verts as RVX ptr, idx as u32 ptr)
  sub THRASH_drawtrimesh(count as const u32,verts as RVX ptr, idx as u32 ptr)
    __DebugThrashDraw( puts(__FUNCTION__) )    
    
    _TrashLock( mutexlock(ThrashGL.g_WriteMutex) )
    'if ThrashGL.g_TID <> GetCurrentThreadID() then puts("Thrash from wrong thread!")
    for I as long = 0 to count*3-1 step 3
      glCmdUnlocked( mtDrawTri , .a=verts[idx[I+0]] : .b=verts[idx[I+1]] : .c=verts[idx[I+2]] )
    next
    _TrashLock( mutexunlock(ThrashGL.g_WriteMutex) )
    
    #if 0
      for I as long = 0 to count*3-1 step 3
        line  ( verts[idx[I  ]].fX , verts[idx[I  ]].fY )-( verts[idx[I+1]].fX , verts[idx[I+1]].fY ), lcor(verts[idx[I]].uColor)
        line -( verts[idx[I+2]].fX , verts[idx[I+2]].fY ), lcor(verts[idx[I+1]].uColor)
        line -( verts[idx[I  ]].fX , verts[idx[I  ]].fY ), lcor(verts[idx[I+2]].uColor)
      next I
    #endif
    
    'printf( !"drawtrimesh: count: %i\n" , count) 
    'with verts[0]
    '  __DebugThrash( printf(!"trimesh[0]: %f,%f,%f %f %4X %f,%f\n",.fX,.fY,.fZ,.fRHW,.uColor,.fU,.fV) )
    'end with
    'with verts[1]
    '  __DebugThrash( printf(!"trimesh[1]: %f,%f,%f %f %4X %f,%f\n",.fX,.fY,.fZ,.fRHW,.uColor,.fU,.fV) )
    'end with
    'with verts[2]
    '  __DebugThrash( printf(!"trimesh[1]: %f,%f,%f %f %4X %f,%f\n",.fX,.fY,.fZ,.fRHW,.uColor,.fU,.fV) )
    'end with
    'getchar()
    
    pfTHRASH_drawtrimesh(count,verts,idx)
  end sub
  
end extern