Files
GTASource/rage/scaleform/Src/GRenderer/GRendererXbox360Impl.cpp

3620 lines
118 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
/**********************************************************************
Filename : GRendererD3D9Xbox360.cpp
Content : Sample GRenderer implementation for XBox 360
Created : February 8, 2006
Authors :
Copyright : (c) 2001-2005 Scaleform Corp. All Rights Reserved.
Notes :
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GImage.h"
#include "GAtomic.h"
#include "GArray.h"
#include "GHash.h"
#include "GString.h"
#include "GMsgFormat.h"
#if defined(GFC_OS_WIN32)
#include <windows.h>
#endif
#include "GRendererXbox360.h"
#include "GRendererCommonImpl.h"
#include <string.h> // for memset()
#if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW)
#undef new
#endif
#include <d3d9.h>
#include <d3dx9.h>
#include <xgraphics.h>
#if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW)
#define new GFC_DEFINE_NEW
#endif
// ***** Classes implemented
class GRendererXbox360Impl;
class GTextureXbox360Impl;
// #define GRENDERER_USE_PS20
#ifdef GRENDERER_USE_PS20
#include "ShadersD3D9/asm20.cpp"
#else
#include "ShadersD3D9/hlsl.cpp"
#endif
#include "D3D9/D3D9Shaders.cpp"
using namespace D3D9;
// ***** GTextureXbox360Impl implementation
// GTextureXbox360Impl declaration
class GTextureXbox360Impl : public GTextureD3D9
{
public:
// Renderer
GRendererXbox360Impl* pRenderer;
SInt TexWidth, TexHeight;
// D3D9 Texture pointer
IDirect3DTexture9* pD3DTexture;
D3DFORMAT TextureFormat;
GTextureXbox360Impl(GRendererXbox360Impl *prenderer);
~GTextureXbox360Impl();
virtual void ReleaseTexture();
// Obtains the renderer that create TextureInfo
virtual GRenderer* GetRenderer() const;
virtual bool IsDataValid() const;
// Texture loading logic.
virtual bool InitTexture(IDirect3DTexture9 *ptex, bool managed);
virtual bool InitTexture(GImageBase* pim, UInt usage);
virtual bool InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage);
virtual void Update(int level, int n, const UpdateRect *rects, const GImageBase *pim);
virtual int Map(int level, int n, MapRect* maps, int flags);
virtual bool Unmap(int level, int n, MapRect* maps, int flags);
// Remove texture from renderer, notifies of renderer destruction
void RemoveFromRenderer();
virtual IDirect3DTexture9* GetNativeTexture() const { return pD3DTexture; }
virtual int IsYUVTexture() { return 0; }
virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps);
};
class GTextureXbox360ImplYUV : public GTextureXbox360Impl
{
public:
bool IsAlpha;
IDirect3DTexture9* pD3DTextureUVA[3];
GTextureXbox360ImplYUV(GRendererXbox360Impl *prenderer) : GTextureXbox360Impl(prenderer)
{ pD3DTextureUVA[0] = pD3DTextureUVA[1] = pD3DTextureUVA[2] = 0; }
~GTextureXbox360ImplYUV();
virtual bool InitTexture(IDirect3DTexture9 *, bool) { return 0; }
virtual bool InitTexture(GImageBase*, UInt = Usage_Wrap) { return 0; }
virtual bool InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage);
virtual void Update(int, int, const UpdateRect *, const GImageBase *) { }
virtual int Map(int level, int n, MapRect* maps, int flags);
virtual bool Unmap(int level, int n, MapRect* maps, int flags);
virtual void ReleaseTexture();
virtual int IsYUVTexture() { return IsAlpha ? 2 : 1; }
virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps);
};
class GRenderTargetXbox360Impl : public GRenderTargetD3D9
{
public:
UInt TargetWidth, TargetHeight;
UInt BaseTile, TileSize;
GRendererXbox360Impl* pRenderer;
GPtr<GTextureXbox360Impl> pTexture; // if this is non-null, then pRenderTexture is NULL
IDirect3DSurface9* pRenderSurface; // if this is non-null, then pTexture is NULL
IDirect3DSurface9* pStencilSurface;
GRenderTargetXbox360Impl(GRendererXbox360Impl *pRend);
~GRenderTargetXbox360Impl();
virtual GRenderer* GetRenderer() const;
void RemoveFromRenderer();
void ReleaseResources();
virtual bool InitRenderTarget(D3D9RenderTargetParams RTParams);
virtual bool InitRenderTarget(GTexture *ptarget, UInt base);
virtual bool InitRenderTarget(GTexture *ptarget, GTexture* pdepth = 0, GTexture* pstencil = 0)
{
return InitRenderTarget(ptarget, (UInt)0);
}
};
// ***** Vertex Declarations and Shaders
// Our vertex coords consist of two signed 16-bit integers, for (x,y) position only.
static D3DVERTEXELEMENT9 StripVertexDecl[] =
{
{0, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
D3DDECL_END()
};
static D3DVERTEXELEMENT9 GlyphVertexDecl[] =
{
{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
{0, 24, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
D3DDECL_END()
};
// Gouraud vertices used with Edge AA
static D3DVERTEXELEMENT9 VertexDeclXY16iC32[] =
{
{0, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{0, 4, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
D3DDECL_END()
};
static D3DVERTEXELEMENT9 VertexDeclXY16iCF32[] =
{
{0, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{0, 4, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
{0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1 },
D3DDECL_END()
};
// Vertex shader declarations we can use
enum VDeclType
{
VD_None,
VD_Strip,
VD_Glyph,
VD_XY16iC32,
VD_XY16iCF32,
VD_Count
};
// Vertex declaration lookup table, must correspond to VDeclType +1
static const D3DVERTEXELEMENT9 *VertexDeclTypeTable[VD_Count - 1] =
{
StripVertexDecl,
GlyphVertexDecl,
VertexDeclXY16iC32,
VertexDeclXY16iCF32
};
enum VShaderType
{
VS_None,
VS_Strip,
VS_Glyph,
VS_XY16iC32,
VS_XY16iCF32,
VS_XY16iCF32_T2,
VS_Count
};
// Vertex shader text lookup table, must correspond to VShaderType +1
static const char *VertexShaderTextTable[VS_Count - 1] =
{
pStripVShaderText,
pGlyphVShaderText,
pStripVShaderXY16iC32Text,
pStripVShaderXY16iCF32Text,
pStripVShaderXY16iCF32_T2Text
};
// Vertex buffer structure used for glyphs.
struct GGlyphVertex
{
float x,y,z,w;
float u,v;
GColor color;
void SetVertex2D(float xx, float yy, float uu, float vv, GColor c)
{
x = xx; y = yy; u = uu; v = vv;
color = c;
}
};
// Pixel shaders
enum PixelShaderType
{
PS_None = 0,
PS_SolidColor,
PS_CxformTexture,
PS_CxformTextureMultiply,
PS_TextTextureAlpha,
PS_TextTextureColor,
PS_TextTextureColorMultiply,
PS_TextTextureYUV,
PS_TextTextureYUVMultiply,
PS_TextTextureYUVA,
PS_TextTextureYUVAMultiply,
PS_CxformGauraud,
PS_CxformGauraudNoAddAlpha,
PS_CxformGauraudTexture,
PS_Cxform2Texture,
// Multiplies - must come in same order as other gourauds
PS_CxformGauraudMultiply,
PS_CxformGauraudMultiplyNoAddAlpha,
PS_CxformGauraudMultiplyTexture,
PS_CxformMultiply2Texture,
PS_CmatrixTexture,
PS_CmatrixTextureMultiply,
PS_Count
};
struct PixelShaderDesc
{
// Shader, used if not mull.
const char* pShader;
};
PixelShaderDesc PixelShaderInitTable[PS_Count] =
{
// Non-AA Shaders.
{ pSource_PS_SolidColor},
{ pSource_PS_CxformTexture},
{ pSource_PS_CxformTextureMultiply},
{ pSource_PS_TextTextureAlpha},
{ pSource_PS_TextTextureColor},
{ pSource_PS_TextTextureColorMultiply},
{ pSource_PS_TextTextureYUV},
{ pSource_PS_TextTextureYUVMultiply},
{ pSource_PS_TextTextureYUVA},
{ pSource_PS_TextTextureYUVAMultiply},
// AA Shaders.
{ pSource_PS_CxformGauraud},
{ pSource_PS_CxformGauraudNoAddAlpha},
// Texture stage fixed-function fall-backs only (their implementation is incorrect).
{ pSource_PS_CxformGauraudTexture},
{ pSource_PS_Cxform2Texture},
{ pSource_PS_CxformGauraudMultiply},
{ pSource_PS_CxformGauraudMultiplyNoAddAlpha},
{ pSource_PS_CxformGauraudMultiplyTexture},
{ pSource_PS_CxformMultiply2Texture},
{ pSource_PS_CmatrixTexture},
{ pSource_PS_CmatrixTextureMultiply}
};
class GRendererXbox360Impl : public GRendererXbox360
{
public:
// Some renderer state.
bool ModeSet;
SInt RenderMode;
// Shaders and declarations that are currently set.
VDeclType VDeclIndex;
VShaderType VShaderIndex;
// Current pixel shader index
int PShaderIndex;
// Video Mode Configuration Flags (VMConfigFlags)
UInt32 VMCFlags;
// Created vertex declarations and shaders.
GPtr<IDirect3DVertexDeclaration9> VertexDecls[VD_Count];
GPtr<IDirect3DVertexShader9> VertexShaders[VS_Count];
// Allocated pixel shaders
GPtr<IDirect3DPixelShader9> PixelShaders[PS_Count];
GPtr<IDirect3DPixelShader9> StaticFilterShaders[FS2_Count];
// Direct3DDevice
IDirect3DDevice9* pDevice;
// This flag indicates whether we've checked for stencil after BeginDisplay or not.
bool StencilChecked;
// This flag is stencil is available, after check.
bool StencilAvailable;
DWORD StencilCounter;
bool StencilEnabled;
// Output size.
Float DisplayWidth;
Float DisplayHeight;
Matrix UserMatrix;
Matrix ViewportMatrix;
GViewport ViewRect;
Matrix CurrentMatrix;
Cxform CurrentCxform;
#ifndef GFC_NO_3D
bool UVPMatricesChanged;
GMatrix3D ViewMatrix;
GMatrix3D ProjMatrix;
GMatrix3D UVPMatrix;
const GMatrix3D *pWorldMatrix;
#endif
// Link list of all allocated textures
GRendererNode Textures;
GRendererNode RenderTargets;
mutable GLock TexturesLock;
// Statistics
Stats RenderStats;
// Video memory
GMemoryStat TextureVMem;
GMemoryStat BufferVMem;
// Counts
GCounterStat TextureUploadCnt;
GCounterStat TextureUpdateCnt;
GCounterStat DPLineCnt;
GCounterStat DPTriangleCnt;
GCounterStat LineCnt;
GCounterStat TriangleCnt;
GCounterStat MaskCnt;
GCounterStat FilterCnt;
// Vertex data pointer
const void* pVertexData;
const void* pIndexData;
VertexFormat VertexFmt;
_D3DFORMAT IndexFmt;
// Current sample mode
BitmapSampleMode SampleMode;
typedef GArrayConstPolicy<0, 4, true> NeverShrinkPolicy;
typedef GArrayLH<BlendType, GStat_Default_Mem, NeverShrinkPolicy> BlendModeStackType;
// Current blend mode
BlendType BlendMode;
BlendModeStackType BlendModeStack;
// render target
struct RTState
{
GRenderTargetXbox360Impl* pRT;
GMatrix2D ViewMatrix;
GMatrix3D ViewMatrix3D;
GMatrix3D PerspMatrix3D;
const GMatrix3D* pWorldMatrix3D;
GViewport ViewRect;
SInt RenderMode;
SInt StencilCounter;
bool StencilEnabled;
RTState() { pRT = 0; pWorldMatrix3D = 0; }
RTState(GRenderTargetXbox360Impl* prt, const GMatrix2D& vm,
const GMatrix3D& vm3d, const GMatrix3D &vp3d, const GMatrix3D* pw3d, const GViewport& vp, SInt rm, SInt sc, bool se)
: pRT(prt), ViewMatrix(vm), ViewMatrix3D(vm3d), PerspMatrix3D(vp3d), pWorldMatrix3D(pw3d), ViewRect(vp),
RenderMode(rm), StencilCounter(sc), StencilEnabled(se) { }
};
typedef GArrayLH<RTState, GStat_Default_Mem, NeverShrinkPolicy> RTStackType;
GRenderTargetXbox360Impl* pCurRenderTarget;
bool CurRenderTargetSet;
RTStackType RenderTargetStack;
GArrayLH<GPtr<GRenderTargetXbox360Impl> > TempRenderTargets;
// Presentation parameters specified to configure the mode.
D3DPRESENT_PARAMETERS PresentParams;
HWND hWnd;
// Buffer used to pass along glyph vertices
enum { GlyphVertexBufferSize = 6 * 48 };
GGlyphVertex GlyphVertexBuffer[GlyphVertexBufferSize];
// Linked list used for buffer cache testing, otherwise holds no data.
CacheNode CacheList;
// resources to retain during tiling frames
GArray<GPtr<GTexture> > FrameResources;
// all D3D call should be done on the main thread, but textures can be released from
// different threads so we collect system resources and release them on the main thread
GArray<IDirect3DResource9*> ResourceReleasingQueue;
GLock ResourceReleasingQueueLock;
// this method is called from GTextureXXX destructor to put a system resource into releasing queue
void AddResourceForReleasing(IDirect3DResource9* ptexture)
{
GASSERT(ptexture);
if (!ptexture) return;
GLock::Locker guard(&ResourceReleasingQueueLock);
ResourceReleasingQueue.PushBack(ptexture);
}
// this method is called from GetTexture, EndDisplay and destructor to actually release
// collected system resources
void ReleaseQueuedResources()
{
GLock::Locker guard(&ResourceReleasingQueueLock);
for (UInt i = 0; i < ResourceReleasingQueue.GetSize(); ++i)
ResourceReleasingQueue[i]->Release();
ResourceReleasingQueue.Clear();
}
GRendererXbox360Impl()
{
ModeSet = 0;
VMCFlags = 0;
StencilAvailable = 0;
StencilChecked = 0;
StencilCounter = 0;
VDeclIndex = VD_None;
VShaderIndex = VS_None;
PShaderIndex = PS_None;
SampleMode = Sample_Linear;
IndexFmt = D3DFMT_UNKNOWN;
VertexFmt = Vertex_None;
pDevice = 0;
hWnd = 0;
pVertexData = 0;
pIndexData = 0;
// Glyph buffer z, w components are always 0 and 1.
UInt i;
for(i = 0; i < GlyphVertexBufferSize; i++)
{
GlyphVertexBuffer[i].z = 0.0f;
GlyphVertexBuffer[i].w = 1.0f;
}
RenderMode = 0;
pCurRenderTarget = 0;
CurRenderTargetSet = 0;
#ifndef GFC_NO_3D
pWorldMatrix = 0;
S3DDisplay = StereoCenter;
#endif
}
~GRendererXbox360Impl()
{
Clear();
}
void Clear()
{
// Remove/notify all textures
{
GLock::Locker guard(&TexturesLock);
while (Textures.pFirst != &Textures)
((GTextureXbox360Impl*)Textures.pFirst)->RemoveFromRenderer();
while (RenderTargets.pFirst != &RenderTargets)
((GRenderTargetXbox360Impl*)RenderTargets.pFirst)->RemoveFromRenderer();
}
CacheList.ReleaseList();
if (ModeSet)
ResetVideoMode();
ReleaseQueuedResources();
}
void ReleaseResources()
{
Clear();
}
// Helpers used to create vertex declarations and shaders.
bool CreateVertexDeclaration(GPtr<IDirect3DVertexDeclaration9> *pdeclPtr, const D3DVERTEXELEMENT9 *pvertexElements)
{
if (!*pdeclPtr)
{
if (pDevice->CreateVertexDeclaration(pvertexElements, &pdeclPtr->GetRawRef()) != S_OK)
{
GFC_DEBUG_WARNING(1, "GRendererXbox360 - failed to create vertex declaration");
return 0;
}
}
return 1;
}
bool CreateVertexShader(GPtr<IDirect3DVertexShader9> *pshaderPtr, const char* pshaderText)
{
if (!*pshaderPtr)
{
GPtr<ID3DXBuffer> pshader;
HRESULT hr;
#ifdef GRENDERER_VSHADER_PROFILE
hr = D3DXCompileShader(pshaderText, (UInt)strlen(pshaderText),
NULL, NULL, "main", GRENDERER_VSHADER_PROFILE, D3DXSHADER_DEBUG, &pshader.GetRawRef(), NULL, NULL);
#else
hr = D3DXAssembleShader(pshaderText, (UInt)strlen(pshaderText),
NULL, NULL, Xbox360SHADER_DEBUG, &pshader.GetRawRef(), NULL);
#endif
if (FAILED(hr))
return 0;
if (!pshader ||
((hr = pDevice->CreateVertexShader( (DWORD*)pshader->GetBufferPointer(),
&pshaderPtr->GetRawRef())) != S_OK) )
{
GFC_DEBUG_WARNING1(1, "GRendererXbox360 - Can't create D3D9 vshader; error code = %d", hr);
return 0;
}
}
return 1;
}
bool CreatePixelShader(GPtr<IDirect3DPixelShader9> *pshaderPtr, const char* pshaderText)
{
if (!*pshaderPtr)
{
GPtr<ID3DXBuffer> pmsg;
GPtr<ID3DXBuffer> pshader;
HRESULT hr;
#ifdef GRENDERER_PSHADER_PROFILE
hr = D3DXCompileShader(pshaderText, (UInt)strlen(pshaderText),
NULL, NULL, "main", GRENDERER_PSHADER_PROFILE, D3DXSHADER_DEBUG, &pshader.GetRawRef(), &pmsg.GetRawRef(), NULL);
#else
hr = D3DXAssembleShader(pshaderText, (UInt)strlen(pshaderText),
NULL, NULL, Xbox360SHADER_DEBUG, &pshader.GetRawRef(), &pmsg.GetRawRef());
#endif
if (FAILED(hr))
{
GFC_DEBUG_WARNING1(1, "GRendererXbox360 - PixelShader errors:\n %s ", pmsg->GetBufferPointer() );
return 0;
}
if (!pshader ||
((hr = pDevice->CreatePixelShader( (DWORD*)pshader->GetBufferPointer(),
&pshaderPtr->GetRawRef())) != S_OK) )
{
GFC_DEBUG_WARNING1(1, "GRendererXbox360 - Can't create D3D9 pshader; error code = %d", hr);
return 0;
}
}
return 1;
}
// Initialize the vshader we use for SWF mesh rendering.
bool InitShaders()
{
bool success = 1;
UInt i;
for (i = 1; i < VD_Count; i++)
if (!CreateVertexDeclaration(&VertexDecls[i], VertexDeclTypeTable[i-1]))
{
success = 0;
break;
}
for (i = 1; i < VS_Count; i++)
if (!CreateVertexShader(&VertexShaders[i], VertexShaderTextTable[i-1]))
{
success = 0;
break;
}
for (i = 1; i < PS_Count; i++)
{
if (PixelShaderInitTable[i-1].pShader)
{
if (!CreatePixelShader(&PixelShaders[i], PixelShaderInitTable[i-1].pShader))
{
success = 0;
break;
}
}
}
for (int i = 0; i < FS2_FCMatrix; i++)
if (FShaderSources2[i])
{
if (!CreatePixelShader(&StaticFilterShaders[i], FShaderSources2[i]))
{
success = 0;
break;
}
}
if (!success)
{
ReleaseShaders();
return 0;
}
return 1;
}
void ReleaseShaders()
{
if (pDevice)
{
pDevice->SetVertexShader(0);
pDevice->SetPixelShader(0);
pDevice->SetVertexDeclaration(0);
}
UInt i;
for (i=0; i< VD_Count; i++)
if (VertexDecls[i])
VertexDecls[i] = 0;
for (i=0; i< VS_Count; i++)
if (VertexShaders[i])
VertexShaders[i] = 0;
for (i=0; i< PS_Count; i++)
if (PixelShaders[i])
PixelShaders[i] = 0;
for (i=0; i< FS2_Count; i++)
StaticFilterShaders[i] = 0;
}
// Sets a shader to specified type.
void SetVertexDecl(VDeclType vd)
{
if (VDeclIndex != vd)
{
VDeclIndex = vd;
pDevice->SetVertexDeclaration(VertexDecls[vd]);
}
}
void SetVertexShader(VShaderType vt)
{
if (VShaderIndex != vt)
{
VShaderIndex = vt;
pDevice->SetVertexShader(VertexShaders[vt]);
}
}
// Set shader to a device based on a constant
void SetPixelShader(PixelShaderType shaderType)
{
if (shaderType != PShaderIndex)
{
pDevice->SetPixelShader(PixelShaders[shaderType]);
PShaderIndex = shaderType;
}
}
// Utility. Mutates *width, *height and *data to create the
// next mip level.
static void MakeNextMiplevel(int* width, int* height, UByte* data)
{
GASSERT(width);
GASSERT(height);
GASSERT(data);
GASSERT_ON_RENDERER_MIPMAP_GEN;
int NewW = *width >> 1;
int NewH = *height >> 1;
if (NewW < 1) NewW = 1;
if (NewH < 1) NewH = 1;
if (NewW * 2 != *width || NewH * 2 != *height)
{
// Image can't be shrunk Along (at least) one
// of its dimensions, so don't bother
// resampling. Technically we should, but
// it's pretty useless at this point. Just
// change the image dimensions and leave the
// existing pixels.
}
else
{
// Resample. Simple average 2x2 --> 1, in-place.
for (int j = 0; j < NewH; j++) {
UByte* out = ((UByte*) data) + j * NewW;
UByte* in = ((UByte*) data) + (j << 1) * *width;
for (int i = 0; i < NewW; i++) {
int a;
a = (*(in + 0) + *(in + 1) + *(in + 0 + *width) + *(in + 1 + *width));
*(out) = UByte(a >> 2);
out++;
in += 2;
}
}
}
// Munge parameters to reflect the shrunken image.
*width = NewW;
*height = NewH;
}
void ApplySampleMode(int stageIndex, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps)
{
GUNUSED(useMipmaps);
// Set sampling
_D3DTEXTUREFILTERTYPE filter =
(SampleMode == Sample_Point) ? D3DTEXF_POINT : D3DTEXF_LINEAR;
if (WrapMode == Wrap_Clamp)
{
pDevice->SetSamplerState(stageIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
pDevice->SetSamplerState(stageIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
else
{
GASSERT(WrapMode == Wrap_Repeat);
pDevice->SetSamplerState(stageIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
pDevice->SetSamplerState(stageIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
}
pDevice->SetSamplerState(stageIndex, D3DSAMP_MINFILTER, filter);
pDevice->SetSamplerState(stageIndex, D3DSAMP_MAGFILTER, filter);
}
// Fill helper function:
// Applies fill texture by setting it to the specified stage, initializing samplers and vertex constants
void ApplyFillTexture(const FillTexture &fill, UInt stageIndex, UInt matrixVertexConstIndex)
{
GASSERT (fill.pTexture != 0);
if (fill.pTexture == 0) return; // avoid crash in release build
GTextureXbox360Impl* ptexture = ((GTextureXbox360Impl*)fill.pTexture);
ptexture->Bind(stageIndex, fill.WrapMode, fill.SampleMode, 0);
// Set up the bitmap matrix for texgen.
const Matrix& m = fill.TextureMatrix;
Float p[4] = { 0, 0, 0, 0 };
p[0] = m.M_[0][0];
p[1] = m.M_[0][1];
p[3] = m.M_[0][2];
pDevice->SetVertexShaderConstantF(matrixVertexConstIndex, p, 1);
p[0] = m.M_[1][0];
p[1] = m.M_[1][1];
p[3] = m.M_[1][2];
pDevice->SetVertexShaderConstantF(matrixVertexConstIndex + 1, p, 1);
}
void ApplyPShaderCxform(const Cxform &cxform, UInt cxformPShaderConstIndex) const
{
const float mult = 1.0f / 255.0f;
float cxformData[4 * 2] =
{
cxform.M_[0][0], cxform.M_[1][0],
cxform.M_[2][0], cxform.M_[3][0],
// Cxform color is already pre-multiplied
cxform.M_[0][1] * mult, cxform.M_[1][1] * mult,
cxform.M_[2][1] * mult, cxform.M_[3][1] * mult
};
pDevice->SetPixelShaderConstantF(cxformPShaderConstIndex, cxformData, 2);
}
class GFxFillStyle
{
public:
enum FillMode
{
FM_None,
FM_Color,
FM_Bitmap,
FM_Gouraud
};
FillMode Mode;
GouraudFillType GouraudType;
GColor Color;
Cxform BitmapColorTransform;
FillTexture Fill;
FillTexture Fill2;
GFxFillStyle()
{
Mode = FM_None;
GouraudType = GFill_Color;
Fill.pTexture = 0;
Fill2.pTexture = 0;
}
// Push our style into Direct3D
void Apply(GRendererXbox360Impl *prenderer) const
{
IDirect3DDevice9* pdevice = prenderer->pDevice;
GASSERT(Mode != FM_None);
// Complex
prenderer->ApplyBlendMode(prenderer->BlendMode);
pdevice->SetRenderState(D3DRS_ALPHABLENDENABLE,
((Color.GetAlpha()== 0xFF) &&
(prenderer->BlendMode <= Blend_Normal) &&
(Mode != FM_Gouraud)) ? FALSE : TRUE);
// Non-Edge AA Rendering.
if (Mode == FM_Color)
{
// Solid color fills.
prenderer->SetPixelShader(PS_SolidColor);
prenderer->ApplyColor(Color);
}
else if (Mode == FM_Bitmap)
{
// Gradient and texture fills.
GASSERT(Fill.pTexture != NULL);
prenderer->ApplyColor(Color);
prenderer->ApplySampleMode(Fill.SampleMode);
if (Fill.pTexture == NULL)
{
// Just in case, for release build.
prenderer->SetPixelShader(PS_None);
}
else
{
pdevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
if ((prenderer->BlendMode == Blend_Multiply) ||
(prenderer->BlendMode == Blend_Darken) )
prenderer->SetPixelShader(PS_CxformTextureMultiply);
else
prenderer->SetPixelShader(PS_CxformTexture);
prenderer->ApplyPShaderCxform(BitmapColorTransform, 2);
// Set texture to stage 0; matrix to constants 4 and 5
prenderer->ApplyFillTexture(Fill, 0, 4);
}
}
// Edge AA - relies on Gouraud shading and texture mixing
else if (Mode == FM_Gouraud)
{
PixelShaderType shader = PS_None;
// No texture: generate color-shaded triangles.
if (Fill.pTexture == NULL)
{
pdevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
if (prenderer->VertexFmt == Vertex_XY16iC32)
{
// Cxform Alpha Add can not be non-zero is this state because
// cxform blend equations can not work correctly.
// If we hit this assert, it means Vertex_XY16iCF32 should have been used.
//GASSERT (BitmapColorTransform.M_[3][1] < 1.0f);
shader = PS_CxformGauraudNoAddAlpha;
}
else
{
GASSERT(prenderer->VertexFmt != Vertex_XY16i);
shader = PS_CxformGauraud;
}
prenderer->ApplyPShaderCxform(BitmapColorTransform, 2);
}
// We have a textured or multi-textured gouraud case.
else
{
pdevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
prenderer->ApplyPShaderCxform(BitmapColorTransform, 2);
// Set texture to stage 0; matrix to constants 4 and 5
prenderer->ApplyFillTexture(Fill, 0, 4);
if ((GouraudType == GFill_1TextureColor) ||
(GouraudType == GFill_1Texture))
{
shader = PS_CxformGauraudTexture;
}
else
{
shader = PS_Cxform2Texture;
// Set second texture to stage 1; matrix to constants 5 and 6
prenderer->ApplyFillTexture(Fill2, 1, 6);
}
}
if ((prenderer->BlendMode == Blend_Multiply) ||
(prenderer->BlendMode == Blend_Darken) )
{
shader = (PixelShaderType)(shader + (PS_CxformGauraudMultiply - PS_CxformGauraud));
// For indexing to work, these should hold:
GCOMPILER_ASSERT( (PS_Cxform2Texture - PS_CxformGauraud) ==
(PS_CxformMultiply2Texture - PS_CxformGauraudMultiply));
GCOMPILER_ASSERT( (PS_CxformGauraudMultiply - PS_CxformGauraud) ==
(PS_Cxform2Texture - PS_CxformGauraud + 1) );
}
prenderer->SetPixelShader(shader);
}
}
void Disable()
{
Mode = FM_None;
Fill.pTexture = 0;
}
void SetColor(GColor color)
{
Mode = FM_Color; Color = color;
}
void SetCxform(const Cxform& colorTransform)
{
BitmapColorTransform = colorTransform;
}
void SetBitmap(const FillTexture* pft, const Cxform& colorTransform)
{
Mode = FM_Bitmap;
Fill = *pft;
Color = GColor(0xFFFFFFFF);
SetCxform(colorTransform);
}
// Sets the interpolated color/texture fill style used for shapes with EdgeAA.
// The specified textures are applied to vertices {0, 1, 2} of each triangle based
// on factors of Complex vertex. Any or all subsequent pointers can be NULL, in which case
// texture is not applied and vertex colors used instead.
void SetGouraudFill(GouraudFillType gfill,
const FillTexture *ptexture0,
const FillTexture *ptexture1,
const FillTexture *ptexture2, const Cxform& colorTransform)
{
// Texture2 is not yet used.
if (ptexture0 || ptexture1 || ptexture2)
{
const FillTexture *p = ptexture0;
if (!p) p = ptexture1;
SetBitmap(p, colorTransform);
// Used in 2 texture mode
if (ptexture1)
Fill2 = *ptexture1;
}
else
{
SetCxform(colorTransform);
Fill.pTexture = 0;
}
Mode = GFxFillStyle::FM_Gouraud;
GouraudType = gfill;
}
bool IsValid() const
{
return Mode != FM_None;
}
}; // class GFxFillStyle
// Style state.
enum StyleIndex
{
FILL_STYLE = 0,
LINE_STYLE,
STYLE_COUNT
};
GFxFillStyle CurrentStyles[STYLE_COUNT];
// Given an image, returns a Pointer to a GTexture struct
// that can later be passed to FillStyleX_bitmap(), to set a
// bitmap fill style.
GTextureD3D9* CreateTexture()
{
ReleaseQueuedResources();
GLock::Locker guard(&TexturesLock);
return new GTextureXbox360Impl(this);
}
GTextureD3D9* CreateTextureYUV()
{
ReleaseQueuedResources();
GLock::Locker guard(&TexturesLock);
return new GTextureXbox360ImplYUV(this);
}
GTextureD3D9* CreateDepthStencilBuffer()
{
return NULL;
}
// Helper function to query renderer capabilities.
bool GetRenderCaps(RenderCaps *pcaps)
{
if (!ModeSet)
{
GFC_DEBUG_WARNING(1, "GRendererXBox360::GetRenderCaps fails - video mode not set");
pcaps->CapBits |= Cap_NoTexOverwrite;
return 0;
}
pcaps->CapBits = Cap_Index16 | Cap_Index32 |
Cap_FillGouraud |Cap_FillGouraudTex |
Cap_CxformAdd | Cap_NestedMasks |
Cap_TexNonPower2 | Cap_TexNonPower2Wrap | Cap_TexNonPower2Mip |
Cap_RenderTargetPrePass | Cap_RenderTargetNonPow2 |
Cap_Filter_ColorMatrix | Cap_Filter_Blurs;
pcaps->BlendModes = (1<<Blend_None) | (1<<Blend_Normal) |
(1<<Blend_Multiply) | (1<<Blend_Lighten) | (1<<Blend_Darken) |
(1<<Blend_Add) | (1<<Blend_Subtract);
pcaps->VertexFormats= (1<<Vertex_None) | (1<<Vertex_XY16i) |
(1<<Vertex_XY16iC32) | (1<<Vertex_XY16iCF32);
if (VMCFlags & VMConfig_SupportTiling)
pcaps->CapBits |= Cap_NoTexOverwrite;
D3DCAPS9 caps9;
pDevice->GetDeviceCaps(&caps9);
pcaps->MaxTextureSize = G_Min(caps9.MaxTextureWidth, caps9.MaxTextureWidth);
return 1;
}
void EndFrame()
{
FrameResources.Clear();
GRenderer::EndFrame();
}
void PushRenderTarget(const GRectF& frameRect, GRenderTarget* prt)
{
GASSERT(RenderTargetStack.GetSize() == 0 ||
(CurRenderTargetSet == 0 && RenderTargetStack.GetSize() == 1));
if (!pCurRenderTarget)
{
GFC_DEBUG_WARNING(1, "GRendererXbox360: SetDisplayRenderTarget not called, render targets unavailable");
return;
}
// push onto RT stack
RenderTargetStack.PushBack(RTState(pCurRenderTarget, ViewportMatrix,
#ifndef GFC_NO_3D
ViewMatrix, ProjMatrix, pWorldMatrix,
#else
GMatrix3D(), GMatrix3D(), 0,
#endif
ViewRect, RenderMode, StencilCounter, StencilEnabled));
pCurRenderTarget = (GRenderTargetXbox360Impl*)prt;
// set ViewportMatrix
float dx = frameRect.Width();
float dy = frameRect.Height();
if (dx < 1) { dx = 1; }
if (dy < 1) { dy = 1; }
// Adjust by -0.5 pixel to match DirectX pixel coverage rules.
Float xhalfPixelAdjust = (pCurRenderTarget->TargetWidth > 0) ? (1.0f / (Float) pCurRenderTarget->TargetWidth) : 0.0f;
Float yhalfPixelAdjust = (pCurRenderTarget->TargetHeight> 0) ? (1.0f / (Float) pCurRenderTarget->TargetHeight) : 0.0f;
ViewportMatrix.SetIdentity();
ViewportMatrix.M_[0][0] = 2.0f / dx;
ViewportMatrix.M_[1][1] = -2.0f / dy;
ViewportMatrix.M_[0][2] = -1.0f - ViewportMatrix.M_[0][0] * (frameRect.Left) - xhalfPixelAdjust;
ViewportMatrix.M_[1][2] = 1.0f - ViewportMatrix.M_[1][1] * (frameRect.Top) + yhalfPixelAdjust;
#ifndef GFC_NO_3D
// set 3D view
GMatrix3D matView, matPersp;
MakeViewAndPersp3D(frameRect, matView, matPersp, DEFAULT_FLASH_FOV);
SetView3D(matView);
SetPerspective3D(matPersp);
SetWorld3D(0);
#endif
if (pCurRenderTarget->pTexture)
ViewRect = GViewport(pCurRenderTarget->pTexture->TexWidth, pCurRenderTarget->pTexture->TexHeight,
0,0,pCurRenderTarget->TargetWidth, pCurRenderTarget->TargetHeight, GViewport::View_RenderTextureAlpha);
else // Must be external render target, assuming viewport will be set by BeginDisplay.
ViewRect = GViewport(64,64,0,0,64,64);
RenderMode = GViewport::View_AlphaComposite;
StencilEnabled = 0;
CurRenderTargetSet = 0;
}
void CheckRenderTarget()
{
if (CurRenderTargetSet || !pCurRenderTarget)
return;
// apply render target
// Set texture as render target
if (!FAILED(pDevice->SetRenderTarget(0, pCurRenderTarget->pRenderSurface)))
{
// Set stencil; this will disable it if not available.
pDevice->SetDepthStencilSurface(pCurRenderTarget->pStencilSurface);
}
pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
D3DVIEWPORT9 vp;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
if (pCurRenderTarget->pTexture)
{
vp.X = 0;
vp.Y = 0;
vp.Width = pCurRenderTarget->pTexture->TexWidth;
vp.Height = pCurRenderTarget->pTexture->TexHeight;
pDevice->SetViewport(&vp);
UInt32 Flags = D3DCLEAR_TARGET;
if (pCurRenderTarget->pStencilSurface)
Flags |= D3DCLEAR_STENCIL;
pDevice->Clear( 0, NULL, Flags, D3DCOLOR_ARGB(0,0,0,0), 1.0f, 0 );
}
vp.X = ViewRect.Left;
vp.Y = ViewRect.Top;
vp.Width = ViewRect.Width;
vp.Height = ViewRect.Height;
pDevice->SetViewport(&vp);
ApplyBlendMode(BlendMode);
if (StencilEnabled)
{
pDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
EndSubmitMask();
}
else
DisableMask();
CurRenderTargetSet = 1;
}
void PopRenderTarget()
{
if (!pCurRenderTarget)
{
GFC_DEBUG_WARNING(1, "GRendererXbox360: SetDisplayRenderTarget not called, render targets unavailable");
return;
}
if (CurRenderTargetSet)
pDevice->Resolve(D3DRESOLVE_RENDERTARGET0, NULL, pCurRenderTarget->pTexture->pD3DTexture, NULL, 0, 0, NULL, 1.f, 0, NULL);
RTState rts = RenderTargetStack.Back();
RenderTargetStack.PopBack();
pCurRenderTarget = rts.pRT;
ViewportMatrix = rts.ViewMatrix;
ViewRect = rts.ViewRect;
RenderMode = rts.RenderMode;
StencilEnabled = rts.StencilEnabled;
StencilCounter = rts.StencilCounter;
#ifndef GFC_NO_3D
// restore 3D view matrix
SetView3D(rts.ViewMatrix3D);
SetPerspective3D(rts.PerspMatrix3D);
SetWorld3D(rts.pWorldMatrix3D);
#endif
if (!FAILED(pDevice->SetRenderTarget(0, pCurRenderTarget->pRenderSurface)))
{
// Set stencil; this will disable it if not available.
pDevice->SetDepthStencilSurface(pCurRenderTarget->pStencilSurface);
}
CurRenderTargetSet = 0;
}
GTextureXbox360Impl* PushTempRenderTarget(const GRectF& frameRect, UInt inw, UInt inh, bool needStencil = 0)
{
GUNUSED(needStencil);
GRenderTargetXbox360Impl* pRT = 0;
UInt w = 1, h = 1;
for (UInt i = 0; i < TempRenderTargets.GetSize(); i++)
if (TempRenderTargets[i]->pTexture->GetRefCount() <= 1 &&
TempRenderTargets[i]->pTexture->TexWidth >= SInt(inw) &&
TempRenderTargets[i]->pTexture->TexHeight >= SInt(inh))
{
pRT = TempRenderTargets[i];
goto done;
}
while (w < inw) w <<= 1;
while (h < inh) h <<= 1;
for (UInt i = 0; i < TempRenderTargets.GetSize(); i++)
if (TempRenderTargets[i]->pTexture->GetRefCount() <= 1)
{
pDevice->SetTexture(0, NULL);
pDevice->SetTexture(1, NULL);
pRT = TempRenderTargets[i];
pRT->pTexture->InitDynamicTexture(w, h, GImage::Image_ARGB_8888, 0, GTexture::Usage_RenderTarget);
// should use handlers instead
pRT->InitRenderTarget(pRT->pTexture);
goto done;
}
{
GTexture* prtt = CreateTexture();
prtt->InitDynamicTexture(w, h, GImage::Image_ARGB_8888, 0, GTexture::Usage_RenderTarget);
pRT = (GRenderTargetXbox360Impl *)CreateRenderTarget();
pRT->InitRenderTarget(prtt);
TempRenderTargets.PushBack(*pRT);
prtt->Release();
}
done:
pRT->TargetWidth = inw;
pRT->TargetHeight = inh;
PushRenderTarget(frameRect, pRT);
return pRT->pTexture;
}
void ReleaseTempRenderTargets(UInt keepArea)
{
GArray<GPtr<GRenderTargetXbox360Impl> > NewTempRenderTargets;
for (UInt i = 0; i < TempRenderTargets.GetSize(); i++)
if (TempRenderTargets[i]->pTexture->GetRefCount() > 1 ||
UInt(TempRenderTargets[i]->pTexture->TexWidth * TempRenderTargets[i]->pTexture->TexHeight) <= keepArea)
{
NewTempRenderTargets.PushBack(TempRenderTargets[i]);
keepArea -= TempRenderTargets[i]->pTexture->TexWidth * TempRenderTargets[i]->pTexture->TexHeight;
}
TempRenderTargets.Clear();
TempRenderTargets.Append(NewTempRenderTargets.GetDataPtr(), NewTempRenderTargets.GetSize());
}
GRenderTargetXbox360Impl* CreateRenderTarget()
{
return new GRenderTargetXbox360Impl(this);
}
void SetDisplayRenderTarget(GRenderTarget *renderTarget, bool bSetState)
{
GASSERT(RenderTargetStack.GetSize() == 0);
pCurRenderTarget = (GRenderTargetXbox360Impl*)renderTarget;
if (bSetState)
{
pDevice->SetRenderTarget(0, pCurRenderTarget->pRenderSurface);
pDevice->SetDepthStencilSurface(pCurRenderTarget->pStencilSurface);
}
CurRenderTargetSet = 1;
}
UInt CheckFilterSupport(const BlurFilterParams& params)
{
UInt flags = FilterSupport_Ok;
// blurs/shadows may be scaled up and require multiple passes, but were sorted into prepass and main pass when set.
if (params.Passes > 1 || (params.Mode & (Filter_Blur|Filter_Shadow)))
flags |= FilterSupport_Multipass;
return flags;
}
static inline void SetUniform(Float* uniforms, FragShaderType2 shader, FragShader2Uniform var, const Float *val, UInt size)
{
memcpy(uniforms + 4*FShaderUniforms[shader][var], val, size*sizeof(Float));
}
static inline void SetUniform(Float* uniforms, FragShaderType2 shader, FragShader2Uniform var, Float x, Float y=0, Float z=0, Float w=1)
{
uniforms[4*FShaderUniforms[shader][var] + 0] = x;
uniforms[4*FShaderUniforms[shader][var] + 1] = y;
uniforms[4*FShaderUniforms[shader][var] + 2] = z;
uniforms[4*FShaderUniforms[shader][var] + 3] = w;
}
void DrawBlurRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& indestrect, const BlurFilterParams& params, bool islast)
{
GUNUSED(islast);
GPtr<GTextureXbox360Impl> psrcinRef = (GTextureXbox360Impl*)psrcin;
GPtr<GTextureXbox360Impl> psrc = (GTextureXbox360Impl*)psrcin;
GRectF srcrect, destrect(-1,-1,1,1);
UInt n = params.Passes;
BlurFilterParams pass[3];
FragShaderType2 passis[3];
pass[0] = params;
pass[1] = params;
pass[2] = params;
bool mul = (BlendMode == Blend_Multiply || BlendMode == Blend_Darken);
passis[0] = passis[1] = FS2_FBox2Blur;
passis[2] = mul ? FS2_FBox2BlurMul : FS2_FBox2Blur;
if (params.Mode & Filter_Shadow)
{
if (params.Mode & Filter_HideObject)
{
passis[2] = FS2_FBox2Shadowonly;
if (params.Mode & Filter_Highlight)
passis[2] = (FragShaderType2) (passis[2] + FS2_shadows_Highlight);
}
else
{
if (params.Mode & Filter_Inner)
passis[2] = FS2_FBox2InnerShadow;
else
passis[2] = FS2_FBox2Shadow;
if (params.Mode & Filter_Knockout)
passis[2] = (FragShaderType2) (passis[2] + FS2_shadows_Knockout);
if (params.Mode & Filter_Highlight)
passis[2] = (FragShaderType2) (passis[2] + FS2_shadows_Highlight);
}
if (mul)
passis[2] = (FragShaderType2) (passis[2] + FS2_shadows_Mul);
}
if (params.BlurX * params.BlurY > 32)
{
n *= 2;
pass[0].BlurY = 1;
pass[1].BlurX = 1;
pass[2].BlurX = 1;
passis[0] = passis[1] = FS2_FBox1Blur;
if (passis[2] == FS2_FBox2Blur)
passis[2] = FS2_FBox1Blur;
else if (passis[2] == FS2_FBox2BlurMul)
passis[2] = FS2_FBox1BlurMul;
}
for (UInt i = 0; i < 3; i++)
if (!StaticFilterShaders[passis[i]])
return;
UInt bufWidth = (UInt)ceilf(insrcrect.Width());
UInt bufHeight = (UInt)ceilf(insrcrect.Height());
UInt last = n-1;
SetVertexDecl(VD_Glyph);
SetVertexShader(VS_Glyph);
PShaderIndex = (PixelShaderType)~0;
for (UInt i = 0; i < n; i++)
{
UInt passi = (i == last) ? 2 : (i&1);
const BlurFilterParams& pparams = pass[passi];
pDevice->SetPixelShader(StaticFilterShaders[passis[passi]]);
GTextureXbox360Impl* pnextsrc;
if (i != n - 1)
{
pnextsrc = PushTempRenderTarget(GRectF(-1,-1,1,1), bufWidth, bufHeight);
ApplyMatrix(GMatrix2D::Identity);
destrect = GRectF(-1,-1,1,1);
}
else
{
pnextsrc = 0;
ApplyMatrix(CurrentMatrix);
destrect = indestrect;
}
srcrect = GRectF(insrcrect.Left * 1.0f/psrc->TexWidth, insrcrect.Top * 1.0f/psrc->TexHeight,
insrcrect.Right * 1.0f/psrc->TexWidth, insrcrect.Bottom * 1.0f/psrc->TexHeight);
CheckRenderTarget();
if (i < last)
{
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
pDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
else
{
ApplyBlendMode(BlendMode, true, true);
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
}
const float mult = 1.0f / 255.0f;
Float uniforms[32];
if (FShaderUniforms[passis[passi]][FSU_cxadd] >= 0)
{
if (i == n - 1)
{
float cxformData[4 * 2] =
{
params.cxform.M_[0][0] * params.cxform.M_[3][0],
params.cxform.M_[1][0] * params.cxform.M_[3][0],
params.cxform.M_[2][0] * params.cxform.M_[3][0],
params.cxform.M_[3][0],
params.cxform.M_[0][1] * mult * params.cxform.M_[3][0],
params.cxform.M_[1][1] * mult * params.cxform.M_[3][0],
params.cxform.M_[2][1] * mult * params.cxform.M_[3][0],
params.cxform.M_[3][1] * mult
};
SetUniform(uniforms, passis[passi], FSU_cxmul, cxformData, 4);
SetUniform(uniforms, passis[passi], FSU_cxadd, cxformData+4, 4);
}
else
{
const float add[] = {0,0,0,0};
const float mul[] = {1,1,1,1};
SetUniform(uniforms, passis[passi], FSU_cxmul, mul, 4);
SetUniform(uniforms, passis[passi], FSU_cxadd, add, 4);
}
}
Float SizeX = UInt(pparams.BlurX-1) * 0.5f;
Float SizeY = UInt(pparams.BlurY-1) * 0.5f;
if (passis[passi] == FS2_FBox1Blur || passis[passi] == FS2_FBox1BlurMul)
{
const float fsize[] = {pparams.BlurX > 1 ? SizeX : SizeY, 0, 0, 1.0f/((SizeX*2+1)*(SizeY*2+1))};
SetUniform(uniforms, passis[passi], FSU_fsize, fsize, 4);
SetUniform(uniforms, passis[passi], FSU_texscale, pparams.BlurX > 1 ? 1.0f/psrc->TexWidth : 0, pparams.BlurY > 1 ? 1.0f/psrc->TexHeight : 0);
}
else
{
const float fsize[] = {SizeX, SizeY, 0, 1.0f/((SizeX*2+1)*(SizeY*2+1))};
SetUniform(uniforms, passis[passi], FSU_fsize, fsize, 4);
SetUniform(uniforms, passis[passi], FSU_texscale, 1.0f/psrc->TexWidth, 1.0f/psrc->TexHeight);
}
if (FShaderUniforms[passis[passi]][FSU_offset] >= 0)
{
SetUniform(uniforms, passis[passi], FSU_offset, -params.Offset.x, -params.Offset.y);
SetUniform(uniforms, passis[passi], FSU_srctexscale, psrc->TexWidth/Float(psrcinRef->TexWidth), psrc->TexHeight/Float(psrcinRef->TexHeight));
}
if (FShaderUniforms[passis[passi]][FSU_scolor] >= 0)
{
SetUniform(uniforms, passis[passi], FSU_scolor,
params.Color.GetRed() * mult, params.Color.GetGreen() * mult, params.Color.GetBlue() * mult, params.Color.GetAlpha() * mult);
if (FShaderUniforms[passis[passi]][FSU_scolor2] >= 0)
SetUniform(uniforms, passis[passi], FSU_scolor2,
params.Color2.GetRed() * mult, params.Color2.GetGreen() * mult, params.Color2.GetBlue() * mult, params.Color2.GetAlpha() * mult);
}
pDevice->SetPixelShaderConstantF(0, uniforms, 8);
psrc->Bind(FShaderUniforms[passis[passi]][FSU_tex], Wrap_Clamp, Sample_Linear, false);
if (FShaderUniforms[passis[passi]][FSU_srctex] >= 0)
((GTextureXbox360Impl*)psrcin)->Bind(FShaderUniforms[passis[passi]][FSU_srctex], Wrap_Clamp, Sample_Linear, false);
GColor w;
GGlyphVertex strip[4] =
{
{ destrect.Left, destrect.Top, 0,1, srcrect.Left, srcrect.Top, w},
{ destrect.Right, destrect.Top, 0,1, srcrect.Right, srcrect.Top, w},
{ destrect.Left, destrect.Bottom, 0,1, srcrect.Left, srcrect.Bottom, w},
{ destrect.Right, destrect.Bottom,0,1, srcrect.Right, srcrect.Bottom, w}
};
pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, strip, sizeof(GGlyphVertex));
if (i != n - 1)
{
PopRenderTarget();
psrc = pnextsrc;
}
}
pDevice->SetTexture(0, NULL);
pDevice->SetTexture(1, NULL);
ApplyBlendMode(BlendMode);
FilterCnt.AddCount(n);
RenderStats.Filters += n;
}
void DrawColorMatrixRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& destrect, const Float *matrix, bool islast)
{
GUNUSED(islast);
CheckRenderTarget();
GTextureXbox360Impl* psrc = (GTextureXbox360Impl*) psrcin;
if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken)
SetPixelShader(PS_CmatrixTextureMultiply);
else
SetPixelShader(PS_CmatrixTexture);
SetVertexDecl(VD_Glyph);
SetVertexShader(VS_Glyph);
PShaderIndex = (PixelShaderType)~0;
GRectF srcrect = GRectF(insrcrect.Left * 1.0f/psrc->TexWidth, insrcrect.Top * 1.0f/psrc->TexHeight,
insrcrect.Right * 1.0f/psrc->TexWidth, insrcrect.Bottom * 1.0f/psrc->TexHeight);
ApplyMatrix(CurrentMatrix);
psrc->Bind(0, Wrap_Clamp, Sample_Linear, 0);
pDevice->SetPixelShaderConstantF(0, matrix, 4);
pDevice->SetPixelShaderConstantF(4, matrix+16, 1);
ApplyBlendMode(BlendMode, false, true);
GColor w(0xffffffff);
GGlyphVertex strip[4] =
{
{ destrect.Left, destrect.Top, 0,1, srcrect.Left, srcrect.Top, w},
{ destrect.Right, destrect.Top, 0,1, srcrect.Right, srcrect.Top, w},
{ destrect.Left, destrect.Bottom, 0,1, srcrect.Left, srcrect.Bottom, w},
{ destrect.Right, destrect.Bottom,0,1, srcrect.Right, srcrect.Bottom, w}
};
pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, strip, sizeof(GGlyphVertex));
pDevice->SetTexture(0, NULL);
pDevice->SetTexture(1, NULL);
ApplyBlendMode(BlendMode);
}
// Set up to render a full frame from a movie and fills the
// background. Sets up necessary transforms, to scale the
// movie to fit within the given dimensions. Call
// EndDisplay() when you're done.
//
// The Rectangle (ViewportX0, ViewportY0, ViewportX0 +
// ViewportWidth, ViewportY0 + ViewportHeight) defines the
// window coordinates taken up by the movie.
//
// The Rectangle (x0, y0, x1, y1) defines the pixel
// coordinates of the movie that correspond to the viewport
// bounds.
void BeginDisplay(
GColor backgroundColor, const GViewport &vpin,
Float x0, Float x1, Float y0, Float y1)
{
if (!ModeSet)
{
GFC_DEBUG_WARNING(1, "GRendererD3D9::BeginDisplay failed - video mode not set");
return;
}
CheckRenderTarget();
RenderMode = (vpin.Flags & GViewport::View_AlphaComposite);
DisplayWidth = fabsf(x1 - x0);
DisplayHeight = fabsf(y1 - y0);
// Matrix to map from SWF Movie (TWIPs) coords to
// viewport coordinates.
float dx = x1 - x0;
float dy = y1 - y0;
if (dx < 1) { dx = 1; }
if (dy < 1) { dy = 1; }
float clipX0 = x0;
float clipX1 = x1;
float clipY0 = y0;
float clipY1 = y1;
// In some cases destination source coordinates can be outside D3D viewport.
// D3D will not allow that; however, it is useful to support.
// So if this happens, clamp the viewport and re-calculate source values.
GViewport viewport(vpin);
int viewportX0_save = viewport.Left;
int viewportY0_save = viewport.Top;
int viewportW_save = viewport.Width;
int viewportH_save = viewport.Height;
if (viewport.Left < 0)
{
clipX0 = x0 + ((-viewport.Left) * dx) / (float) viewport.Width;
if ((-viewport.Left) >= viewport.Width)
viewport.Width = 0;
else
viewport.Width += viewport.Left;
viewport.Left = 0;
}
if (viewportX0_save + viewportW_save > (int) vpin.BufferWidth)
{
clipX1 = x0 + ((vpin.BufferWidth - viewportX0_save) * dx) / (float) viewportW_save;
viewport.Width = vpin.BufferWidth - viewport.Left;
}
if (viewport.Top < 0)
{
clipY0 = y0 + ((-viewport.Top) * dy) / (float) viewport.Height;
if ((-viewport.Top) >= viewport.Height)
viewport.Height = 0;
else
viewport.Height += viewport.Top;
viewport.Top = 0;
}
if (viewportY0_save + viewportH_save > (int) vpin.BufferHeight)
{
clipY1 = y0 + ((vpin.BufferHeight - viewportY0_save) * dy) / (float) viewportH_save;
viewport.Height = vpin.BufferHeight - viewport.Top;
}
dx = clipX1 - clipX0;
dy = clipY1 - clipY0;
// Viewport.
D3DVIEWPORT9 vp;
vp.MinZ = 0.0f;
vp.MaxZ = 0.0f;
if (viewport.Flags & GViewport::View_UseScissorRect)
{
int scissorX0_save = viewport.ScissorLeft;
int scissorY0_save = viewport.ScissorTop;
if (viewport.ScissorLeft < viewport.Left) viewport.ScissorLeft = viewport.Left;
if (viewport.ScissorTop < viewport.Top) viewport.ScissorTop = viewport.Top;
viewport.ScissorWidth -= (viewport.ScissorLeft - scissorX0_save);
viewport.ScissorHeight -= (viewport.ScissorTop - scissorY0_save);
if (viewport.ScissorLeft + viewport.ScissorWidth > viewport.Left + viewport.Width)
viewport.ScissorWidth = viewport.Left + viewport.Width - viewport.ScissorLeft;
if (viewport.ScissorTop + viewport.ScissorHeight > viewport.Top + viewport.Height)
viewport.ScissorHeight = viewport.Top + viewport.Height - viewport.ScissorTop;
vp.X = viewport.ScissorLeft;
vp.Y = viewport.ScissorTop;
vp.Width = viewport.ScissorWidth;
vp.Height = viewport.ScissorHeight;
clipX0 = clipX0 + dx / viewport.Width * (viewport.ScissorLeft - viewport.Left);
clipY0 = clipY0 + dy / viewport.Height * (viewport.ScissorTop - viewport.Top);
dx = dx / viewport.Width * viewport.ScissorWidth;
dy = dy / viewport.Height * viewport.ScissorHeight;
}
else
{
vp.X = viewport.Left;
vp.Y = viewport.Top;
vp.Width = viewport.Width;
vp.Height = viewport.Height;
}
pDevice->SetViewport(&vp);
ViewRect = viewport;
ViewportMatrix.SetIdentity();
ViewportMatrix.M_[0][0] = 2.0f / dx;
ViewportMatrix.M_[1][1] = -2.0f / dy;
// Adjust by -0.5 pixel to match DirectX pixel coverage rules.
Float xhalfPixelAdjust = (vp.Width > 0) ? (1.0f / (Float) vp.Width) : 0.0f;
Float yhalfPixelAdjust = (vp.Height> 0) ? (1.0f / (Float) vp.Height) : 0.0f;
// MA: it does not seem necessary to subtract viewport.
// Need to work through details of viewport cutting, there still seems to
// be a 1-pixel issue at edges. Could that be due to edge fill rules ?
ViewportMatrix.M_[0][2] = -1.0f - ViewportMatrix.M_[0][0] * (clipX0) - xhalfPixelAdjust;
ViewportMatrix.M_[1][2] = 1.0f - ViewportMatrix.M_[1][1] * (clipY0) + yhalfPixelAdjust;
ViewportMatrix *= UserMatrix;
// Blending render states.
BlendModeStack.Clear();
BlendModeStack.Reserve(16);
BlendMode = Blend_None;
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
ApplyBlendMode(BlendMode);
// Not necessary of not alpha testing:
//pDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
//pDevice->SetRenderState(D3DRS_ALPHAREF, 0x00);
pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); // Important!
union
{
float fbias;
DWORD d;
} bias;
bias.fbias = -0.5f;
// Must reset both stages since ApplySampleMode modifies both.
pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
SampleMode = Sample_Linear;
pDevice->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, bias.d );
// No ZWRITE by default
pDevice->SetRenderState(D3DRS_ZWRITEENABLE, 0);
pDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
pDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
pDevice->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE,FALSE);
// Turn of back-face culling.
pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
pDevice->SetTexture(0, 0);
pDevice->SetTexture(1, 0); // No texture
// Start the scene
if (!(VMCFlags&VMConfig_NoSceneCalls))
pDevice->BeginScene();
// Vertex format.
// Set None so that shader is forced to be set and then set to default: strip.
VDeclIndex = VD_None;
VShaderIndex = VS_None;
PShaderIndex = PS_None;
SetVertexDecl(VD_Strip);
SetVertexShader(VS_Strip);
// Start with solid shader.
SetPixelShader(PS_SolidColor);
#ifndef GFC_NO_3D
pWorldMatrix = 0;
#endif
// Init test for depth-stencil presence.
StencilChecked = 0;
StencilAvailable= 0;
StencilCounter = 0;
StencilEnabled = 0;
// Clear the background, if background color has alpha > 0.
if (backgroundColor.GetAlpha() > 0)
{
// @@ for testing
/*
static SInt testColor = 0;
pDevice->Clear(
0,
NULL,
D3DCLEAR_TARGET | (StencilAvailable ? (D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL) : 0),
testColor += 5,
0.0f,
0);
*/
// Draw a big quad.
ApplyColor(backgroundColor);
SetMatrix(Matrix::Identity);
ApplyMatrix(CurrentMatrix);
// The and x/y matrix are scaled by 20 twips already, so it
// should ok to just convert these values into integers.
struct Vertex
{
SInt16 x, y;
};
Vertex strip[4] =
{
{ (SInt16)x0, (SInt16)y0 },
{ (SInt16)x1, (SInt16)y0 },
{ (SInt16)x0, (SInt16)y1 },
{ (SInt16)x1, (SInt16)y1 }
};
pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, strip, sizeof(Vertex));
}
}
// Clean up after rendering a frame. Client program is still
// responsible for calling Present or glSwapBuffers()
void EndDisplay()
{
// End Scene
if (pDevice)
{
SetPixelShader(PS_None);
SetVertexShader(VS_None);
pDevice->SetTexture(0,0);
if (!(VMCFlags&VMConfig_NoSceneCalls))
pDevice->EndScene();
}
ReleaseQueuedResources();
}
// Set the current transform for mesh & line-strip rendering.
void SetMatrix(const Matrix& m)
{
CurrentMatrix = m;
}
void SetUserMatrix(const Matrix& m)
{
UserMatrix = m;
#ifndef GFC_NO_3D
UVPMatricesChanged = 1;
#endif
}
// Set the current color transform for mesh & line-strip rendering.
void SetCxform(const Cxform& cx)
{
CurrentCxform = cx;
}
// Structure describing color combines applied for a given blend mode.
struct BlendModeDesc
{
D3DBLENDOP BlendOp;
D3DBLEND SrcArg, DestArg;
};
struct BlendModeDescAlpha
{
D3DBLENDOP BlendOp;
D3DBLEND SrcArg, DestArg;
D3DBLEND SrcAlphaArg, DestAlphaArg;
};
void ApplyBlendMode(BlendType mode, bool forceAc = 0, bool sourceAc = 0)
{
static BlendModeDesc modes[15] =
{
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // None
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Normal
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Layer
{ D3DBLENDOP_ADD, D3DBLEND_DESTCOLOR, D3DBLEND_ZERO }, // Multiply
// (For multiply, should src be pre-multiplied by its inverse alpha?)
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Screen *??
{ D3DBLENDOP_MAX, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Lighten
{ D3DBLENDOP_MIN, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Darken
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Difference *??
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Add
{ D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Subtract
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Invert *??
{ D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE }, // Alpha *??
{ D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE }, // Erase *?? What color do we erase to?
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // Overlay *??
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA }, // HardLight *??
};
static BlendModeDescAlpha acmodes[15] =
{
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // None
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Normal
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Layer
{ D3DBLENDOP_ADD, D3DBLEND_DESTCOLOR, D3DBLEND_ZERO, D3DBLEND_DESTCOLOR, D3DBLEND_ZERO }, // Multiply
// (For multiply, should src be pre-multiplied by its inverse alpha?)
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Screen *??
{ D3DBLENDOP_MAX, D3DBLEND_SRCALPHA, D3DBLEND_ONE, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Lighten
{ D3DBLENDOP_MIN, D3DBLEND_SRCALPHA, D3DBLEND_ONE, D3DBLEND_SRCALPHA, D3DBLEND_ONE }, // Darken
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Difference *??
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_ONE }, // Add
{ D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_ONE }, // Subtract
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Invert *??
{ D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_ONE }, // Alpha *??
{ D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_ONE }, // Erase *??
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Overlay *??
{ D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, // Hardlight *??
};
// For debug build
GASSERT(((UInt) mode) < 15);
// For release
if (((UInt) mode) >= 15)
mode = Blend_None;
if (!pDevice)
return;
if ((RenderMode & GViewport::View_AlphaComposite) || forceAc)
{
pDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_BLENDOP, acmodes[mode].BlendOp);
pDevice->SetRenderState(D3DRS_BLENDOPALPHA, acmodes[mode].BlendOp);
if (sourceAc && acmodes[mode].SrcArg == D3DBLEND_SRCALPHA)
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
else
pDevice->SetRenderState(D3DRS_SRCBLEND, acmodes[mode].SrcArg);
pDevice->SetRenderState(D3DRS_DESTBLEND, acmodes[mode].DestArg);
pDevice->SetRenderState(D3DRS_SRCBLENDALPHA, acmodes[mode].SrcAlphaArg);
pDevice->SetRenderState(D3DRS_DESTBLENDALPHA, acmodes[mode].DestAlphaArg);
}
else
{
pDevice->SetRenderState(D3DRS_BLENDOP, modes[mode].BlendOp);
pDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
if (sourceAc && modes[mode].SrcArg == D3DBLEND_SRCALPHA)
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
else
pDevice->SetRenderState(D3DRS_SRCBLEND, modes[mode].SrcArg);
pDevice->SetRenderState(D3DRS_DESTBLEND, modes[mode].DestArg);
}
}
void ApplySampleMode(BitmapSampleMode mode)
{
if (SampleMode != mode)
{
SampleMode = mode;
_D3DTEXTUREFILTERTYPE filter =
(mode == Sample_Point) ? D3DTEXF_POINT : D3DTEXF_LINEAR;
pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, filter);
pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
pDevice->SetSamplerState(1, D3DSAMP_MINFILTER, filter);
pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, filter);
}
}
// Pushes a Blend mode onto renderer.
virtual void PushBlendMode(BlendType mode)
{
// Blend modes need to be applied cumulatively, ideally through an extra RT texture.
// If the nested clip has a "Multiply" effect, and its parent has an "Add", the result
// of Multiply should be written to a buffer, and then used in Add.
// For now we only simulate it on one level -> we apply the last effect on top of stack.
// (Note that the current "top" element is BlendMode, it is not actually stored on stack).
// Although incorrect, this will at least ensure that parent's specified effect
// will be applied to children.
BlendModeStack.PushBack(BlendMode);
if ((mode > Blend_Layer) && (BlendMode != mode))
{
BlendMode = mode;
ApplyBlendMode(BlendMode);
}
}
// Pops a blend mode, restoring it to the previous one.
virtual void PopBlendMode()
{
if (BlendModeStack.GetSize() != 0)
{
// Find the next top interesting mode.
BlendType newBlendMode = Blend_None;
for (SInt i = (SInt)BlendModeStack.GetSize()-1; i>=0; i--)
if (BlendModeStack[i] > Blend_Layer)
{
newBlendMode = BlendModeStack[i];
break;
}
BlendModeStack.PopBack();
if (newBlendMode != BlendMode)
{
BlendMode = newBlendMode;
ApplyBlendMode(BlendMode);
}
return;
}
// Stack was empty..
GFC_DEBUG_WARNING(1, "GRendererD3D9::PopBlendMode - blend mode stack is empty");
}
#ifndef GFC_NO_3D
virtual void SetPerspective3D(const GMatrix3D &projMatIn)
{
ProjMatrix = projMatIn;
UVPMatricesChanged = 1;
}
virtual void SetView3D(const GMatrix3D &viewMatIn)
{
ViewMatrix = viewMatIn;
UVPMatricesChanged = 1;
}
virtual void SetWorld3D(const GMatrix3D *pWorldMatIn)
{
pWorldMatrix = pWorldMatIn;
}
virtual void SetStereoDisplay(StereoDisplay sDisplay, bool setstate)
{
S3DDisplay = sDisplay;
UVPMatricesChanged = 1;
GASSERT(!setstate);
}
void UpdateStereoProjection()
{
Float eyeZ = -ViewMatrix.M_[3][2];
if (S3DDisplay == StereoLeft)
{
GMatrix3D left;
GetStereoProjectionMatrix(&left, NULL, ProjMatrix, eyeZ);
ProjMatrix = left;
}
else
if (S3DDisplay == StereoRight)
{
GMatrix3D right;
GetStereoProjectionMatrix(NULL, &right, ProjMatrix, eyeZ);
ProjMatrix = right;
}
}
#endif
// multiply current GRenderer::Matrix with d3d GRenderer::Matrix
void ApplyMatrix(const Matrix& matIn)
{
#ifndef GFC_NO_3D
if (pWorldMatrix)
{
if (UVPMatricesChanged)
{
UpdateStereoProjection();
UVPMatricesChanged = 0;
UVPMatrix = UserMatrix;
UVPMatrix.Append(ViewMatrix);
UVPMatrix.Append(ProjMatrix);
if (RenderTargetStack.GetSize())
Adjust3DMatrixForRT(UVPMatrix, ProjMatrix, RenderTargetStack[0].ViewMatrix, ViewportMatrix);
}
GMatrix3D Final = GMatrix3D(matIn);
Final.Append(*pWorldMatrix);
Final.Append(UVPMatrix);
Final.Transpose();
pDevice->SetVertexShaderConstantF(0, Final, 4);
}
else
#endif
{
Matrix m(ViewportMatrix);
m.Prepend(matIn);
float row0[4];
float row1[4];
float row2[4];
float row3[4];
row0[0] = m.M_[0][0];
row0[1] = m.M_[0][1];
row0[2] = 0;
row0[3] = m.M_[0][2];
row1[0] = m.M_[1][0];
row1[1] = m.M_[1][1];
row1[2] = 0;
row1[3] = m.M_[1][2];
row2[0] = 0;
row2[1] = 0;
row2[2] = 1.0f;
row2[3] = 0;
row3[0] = 0;
row3[1] = 0;
row3[2] = 0;
row3[3] = 1.0f;
pDevice->SetVertexShaderConstantF(0, row0, 1);
pDevice->SetVertexShaderConstantF(1, row1, 1);
pDevice->SetVertexShaderConstantF(2, row2, 1);
pDevice->SetVertexShaderConstantF(3, row3, 1);
}
}
// Set the given color.
void ApplyColor(GColor c)
{
const float mult = 1.0f / 255.0f;
const float alpha = c.GetAlpha() * mult;
// Set color.
float rgba[4] =
{
c.GetRed() * mult,
c.GetGreen() * mult,
c.GetBlue() * mult, alpha
};
if ((BlendMode == Blend_Multiply) ||
(BlendMode == Blend_Darken))
{
rgba[0] = gflerp(1.0f, rgba[0], alpha);
rgba[1] = gflerp(1.0f, rgba[1], alpha);
rgba[2] = gflerp(1.0f, rgba[2], alpha);
}
pDevice->SetPixelShaderConstantF(0, rgba, 1);
}
// Don't fill on the {0 == left, 1 == right} side of a path.
void FillStyleDisable()
{
CurrentStyles[FILL_STYLE].Disable();
}
// Don't draw a line on this path.
void LineStyleDisable()
{
CurrentStyles[LINE_STYLE].Disable();
}
// Set fill style for the left interior of the shape. If
// enable is false, turn off fill for the left interior.
void FillStyleColor(GColor color)
{
CurrentStyles[FILL_STYLE].SetColor(CurrentCxform.Transform(color));
}
// Set the line style of the shape. If enable is false, turn
// off lines for following curve segments.
void LineStyleColor(GColor color)
{
CurrentStyles[LINE_STYLE].SetColor(CurrentCxform.Transform(color));
}
void FillStyleBitmap(const FillTexture *pfill)
{
CurrentStyles[FILL_STYLE].SetBitmap(pfill, CurrentCxform);
}
// Sets the interpolated color/texture fill style used for shapes with EdgeAA.
void FillStyleGouraud(GouraudFillType gfill,
const FillTexture *ptexture0,
const FillTexture *ptexture1,
const FillTexture *ptexture2)
{
CurrentStyles[FILL_STYLE].SetGouraudFill(gfill, ptexture0, ptexture1, ptexture2, CurrentCxform);
}
void SetVertexData(const void* pvertices, int numVertices, VertexFormat vf, CacheProvider *pcache)
{
pVertexData = pvertices;
VertexFmt = vf;
// Note: this could populate a static/dynamic buffer instead (if not null).
// Test cache buffer management support.
CacheList.VerifyCachedData( this, pcache, CacheNode::Buffer_Vertex, (pvertices!=0),
numVertices, (((pvertices!=0)&&numVertices) ? *((SInt16*)pvertices) : 0) );
}
void SetIndexData(const void* pindices, int numIndices, IndexFormat idxf, CacheProvider *pcache)
{
pIndexData = pindices;
switch(idxf)
{
case Index_None: IndexFmt = D3DFMT_UNKNOWN; break;
case Index_16: IndexFmt = D3DFMT_INDEX16; break;
case Index_32: IndexFmt = D3DFMT_INDEX32; break;
}
// Note: this could populate a dynamic buffer instead (if not null).
// Test cache buffer management support.
CacheList.VerifyCachedData( this, pcache, CacheNode::Buffer_Index, (pindices!=0),
numIndices, (((pindices!=0)&&numIndices) ? *((SInt16*)pindices) : 0) );
}
void ReleaseCachedData(CachedData *pdata, CachedDataType type)
{
// Releases cached data that was allocated from the cache providers.
CacheList.ReleaseCachedData(pdata, type);
}
void DrawIndexedTriList(int baseVertexIndex, int minVertexIndex, int numVertices,
int startIndex, int triangleCount)
{
if (!ModeSet)
return;
if (!pVertexData || !pIndexData || (IndexFmt == D3DFMT_UNKNOWN)) // Must have vertex data.
{
GFC_DEBUG_WARNING(!pVertexData, "GRendererXBox360::DrawIndexedTriList failed, vertex data not specified");
GFC_DEBUG_WARNING(!pIndexData, "GRendererXBox360::DrawIndexedTriList failed, index data not specified");
GFC_DEBUG_WARNING((IndexFmt == D3DFMT_UNKNOWN), "GRendererXBox360::DrawIndexedTriList failed, index buffer format not specified");
return;
}
CheckRenderTarget();
bool isGouraud = (CurrentStyles[FILL_STYLE].Mode == GFxFillStyle::FM_Gouraud);
int vertexSize= 2 * sizeof(SInt16);
// Ensure we have the right vertex size.
// MA: Should loosen some rules to allow for other decl/shader combinations?
if (isGouraud)
{
if (VertexFmt == Vertex_XY16iC32)
{
SetVertexDecl(VD_XY16iC32);
SetVertexShader(VS_XY16iC32);
vertexSize = sizeof(VertexXY16iC32);
}
else if (VertexFmt == Vertex_XY16iCF32)
{
SetVertexDecl(VD_XY16iCF32);
if (CurrentStyles[FILL_STYLE].GouraudType != GFill_2Texture)
SetVertexShader(VS_XY16iCF32);
else
SetVertexShader(VS_XY16iCF32_T2);
vertexSize = sizeof(VertexXY16iCF32);
}
}
else
{
SetVertexDecl(VD_Strip);
SetVertexShader(VS_Strip);
GASSERT(VertexFmt == Vertex_XY16i);
vertexSize= 2 * sizeof(SInt16);
}
// Set up current style.
CurrentStyles[FILL_STYLE].Apply(this);
ApplyMatrix(CurrentMatrix);
// TODO - should use a VB instead, and use DrawPrimitive().
// or at least use a Dynamic buffer for now...
const int bytesInIndex = ((IndexFmt == D3DFMT_INDEX16) ? sizeof(UInt16) : sizeof(UInt32));
const UByte* pindices = (UByte*)pIndexData + startIndex * bytesInIndex;
// Draw the mesh.
// XBox360 refuses to render more than 65535 indexes.
const int maxTriangles = 65535/3;
RenderStats.Triangles += triangleCount;
TriangleCnt.AddCount(triangleCount);
while (triangleCount > maxTriangles)
{
pDevice->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST, minVertexIndex, numVertices, maxTriangles,
pindices, IndexFmt,
((UByte*)pVertexData) + baseVertexIndex * vertexSize,
vertexSize );
triangleCount -= maxTriangles;
pindices += bytesInIndex * maxTriangles * 3;
RenderStats.Primitives++;
DPTriangleCnt.AddCount(1);
}
if (triangleCount > 0)
{
pDevice->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST, minVertexIndex, numVertices, triangleCount,
pindices, IndexFmt,
((UByte*)pVertexData) + baseVertexIndex * vertexSize,
vertexSize );
RenderStats.Primitives++;
DPTriangleCnt.AddCount(1);
}
}
// Draw the line strip formed by the sequence of points.
void DrawLineStrip(int baseVertexIndex, int lineCount)
{
if (!ModeSet)
return;
if (!pVertexData) // Must have vertex data.
{
GFC_DEBUG_WARNING(!pVertexData, "GRendererXBox360::DrawLineStrip failed, vertex data not specified");
return;
}
CheckRenderTarget();
// MA: Should loosen some rules to allow for other decl/shader combinations?
GASSERT(VertexFmt == Vertex_XY16i);
SetVertexDecl(VD_Strip);
SetVertexShader(VS_Strip);
// Set up current style.
CurrentStyles[LINE_STYLE].Apply(this);
ApplyMatrix(CurrentMatrix);
pDevice->DrawPrimitiveUP(
D3DPT_LINESTRIP, lineCount,
((UByte*)pVertexData) + baseVertexIndex * 2 * sizeof(SInt16), sizeof(SInt16)*2 );
RenderStats.Lines += lineCount;
RenderStats.Primitives++;
LineCnt.AddCount(lineCount);
DPLineCnt.AddCount(1);
}
// Draw a set of rectangles textured with the given bitmap, with the given color.
// Apply given transform; ignore any currently set transforms.
//
// Intended for textured glyph rendering.
void DrawBitmaps(BitmapDesc* pbitmapList, int listSize, int startIndex, int count,
const GTexture* pti, const Matrix& m,
CacheProvider *pcache)
{
if (!ModeSet || !pbitmapList || !pti)
return;
GTextureXbox360Impl* ptexture = (GTextureXbox360Impl*)pti;
// Texture must be valid for rendering
if (!ptexture->pD3DTexture && !ptexture->CallRecreate())
{
GFC_DEBUG_WARNING(1, "GRendererXBox360::DrawBitmaps failed, empty texture specified/could not recreate texture");
return;
}
CheckRenderTarget();
// Test cache buffer management support.
// Optimized implementation could use this spot to set vertex buffer and/or initialize offset in it.
// Note that since bitmap lists are usually short, it would be a good idea to combine many of them
// into one buffer, instead of creating individual buffers for each pbitmapList data instance.
CacheList.VerifyCachedData( this, pcache, CacheNode::Buffer_BitmapList, (pbitmapList!=0),
listSize, (((pbitmapList!=0)&&listSize) ? ((SInt)pbitmapList->Coords.Left) : 0) );
ApplyPShaderCxform(CurrentCxform, 2);
ApplyBlendMode(BlendMode);
ApplySampleMode(Sample_Linear);
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
// Set texture.
bool IsAlpha = 0;
if (ptexture->IsYUVTexture() == 2)
{
if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken)
SetPixelShader(PS_TextTextureYUVAMultiply);
else
SetPixelShader(PS_TextTextureYUVA);
}
else if (ptexture->IsYUVTexture() == 1)
{
if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken)
SetPixelShader(PS_TextTextureYUVMultiply);
else
SetPixelShader(PS_TextTextureYUV);
}
else
{
IsAlpha = (ptexture->TextureFormat == D3DFMT_A8 || ptexture->TextureFormat == D3DFMT_LIN_A8);
if (IsAlpha)
SetPixelShader(PS_TextTextureAlpha);
else if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken)
SetPixelShader(PS_TextTextureColorMultiply);
else
SetPixelShader(PS_TextTextureColor);
}
ptexture->Bind(0, Wrap_Clamp, Sample_Linear, 0 == ptexture->IsYUVTexture());
// Switch to non-texgen shader.
SetVertexDecl(VD_Glyph);
SetVertexShader(VS_Glyph);
ApplyMatrix(m);
SInt ibitmap = 0, ivertex = 0;
GCOMPILER_ASSERT((GlyphVertexBufferSize%6) == 0);
while (ibitmap < count)
{
for(ivertex = 0; (ivertex < GlyphVertexBufferSize) && (ibitmap<count); ibitmap++, ivertex+= 6)
{
BitmapDesc & bd = pbitmapList[ibitmap + startIndex];
GGlyphVertex* pv = GlyphVertexBuffer + ivertex;
// Triangle 1.
pv[0].SetVertex2D(bd.Coords.Left, bd.Coords.Top, bd.TextureCoords.Left, bd.TextureCoords.Top, bd.Color);
pv[1].SetVertex2D(bd.Coords.Right, bd.Coords.Top, bd.TextureCoords.Right, bd.TextureCoords.Top, bd.Color);
pv[2].SetVertex2D(bd.Coords.Left, bd.Coords.Bottom, bd.TextureCoords.Left, bd.TextureCoords.Bottom, bd.Color);
// Triangle 2.
pv[3].SetVertex2D(bd.Coords.Left, bd.Coords.Bottom, bd.TextureCoords.Left, bd.TextureCoords.Bottom, bd.Color);
pv[4].SetVertex2D(bd.Coords.Right, bd.Coords.Top, bd.TextureCoords.Right, bd.TextureCoords.Top, bd.Color);
pv[5].SetVertex2D(bd.Coords.Right, bd.Coords.Bottom,bd.TextureCoords.Right, bd.TextureCoords.Bottom, bd.Color);
}
// Draw the generated triangles.
if (ivertex)
{
pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, ivertex / 3, GlyphVertexBuffer, sizeof(GGlyphVertex));
RenderStats.Primitives++;
DPTriangleCnt.AddCount(1);
}
}
RenderStats.Triangles += count * 2;
TriangleCnt.AddCount(count * 2);
for (int i = 0; i < 4; i++)
pDevice->SetTexture(i, 0);
}
void BeginSubmitMask(SubmitMaskMode maskMode)
{
if (!ModeSet)
return;
CheckRenderTarget();
if (!StencilAvailable)
{
if (!StencilChecked)
{
// Test for depth-stencil presence.
IDirect3DSurface9 *pdepthStencilSurface = 0;
pDevice->GetDepthStencilSurface(&pdepthStencilSurface);
if (pdepthStencilSurface)
{
D3DSURFACE_DESC sd;
pdepthStencilSurface->GetDesc(&sd);
switch(sd.Format)
{ // Xbox360 formats
case D3DFMT_D24S8:
case D3DFMT_LIN_D24S8:
case D3DFMT_D24FS8:
case D3DFMT_LIN_D24FS8:
StencilAvailable = 1;
break;
}
pdepthStencilSurface->Release();
pdepthStencilSurface = 0;
}
else
StencilAvailable = 0;
StencilChecked = 1;
}
if (!StencilAvailable)
{
#ifdef GFC_BUILD_DEBUG
static bool StencilWarned = 0;
if (!StencilWarned)
{
GFC_DEBUG_WARNING(1, "GRendererD3D9::BeginSubmitMask used, but stencil is not available");
StencilWarned = 1;
}
#endif
return;
}
}
pDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0);
pDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
pDevice->SetRenderState(D3DRS_STENCILMASK, 0xFF);
pDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
pDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
switch(maskMode)
{
case Mask_Clear:
pDevice->Clear(0, 0, D3DCLEAR_STENCIL, 0, 0.0f, 0);
pDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
pDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
pDevice->SetRenderState(D3DRS_STENCILREF, 1);
StencilCounter = 1;
break;
case Mask_Increment:
pDevice->SetRenderState(D3DRS_STENCILREF, StencilCounter);
pDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
pDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR);
StencilCounter++;
break;
case Mask_Decrement:
pDevice->SetRenderState(D3DRS_STENCILREF, StencilCounter);
pDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
pDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR);
StencilCounter--;
break;
}
RenderStats.Masks++;
MaskCnt.AddCount(1);
}
void EndSubmitMask()
{
if (!ModeSet)
return;
if (!StencilAvailable)
return;
StencilEnabled = 1;
// Enable frame-buffer writes.
pDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF);
// We draw only where the (stencil == StencilCounter), i.e. where the latest mask was drawn.
// However, we don't change the stencil buffer.
pDevice->SetRenderState(D3DRS_STENCILREF, StencilCounter);
pDevice->SetRenderState(D3DRS_STENCILMASK, 0xFF);
pDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
pDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
}
void DisableMask()
{
if (!ModeSet)
return;
if (!StencilAvailable)
return;
StencilCounter = 0;
StencilEnabled = 0;
pDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
}
virtual void GetRenderStats(Stats *pstats, bool resetStats)
{
if (pstats)
memcpy(pstats, &RenderStats, sizeof(Stats));
if (resetStats)
RenderStats.Clear();
}
// StatBag statistics.
virtual void GetStats(GStatBag* pbag, bool reset)
{
if (pbag)
{
pbag->Add(GStatRender_Texture_VMem, &TextureVMem);
pbag->Add(GStatRender_Buffer_VMem, &BufferVMem);
pbag->Add(GStatRender_TextureUpload_Cnt, &TextureUploadCnt);
pbag->Add(GStatRender_TextureUpdate_Cnt, &TextureUpdateCnt);
pbag->Add(GStatRender_DP_Line_Cnt, &DPLineCnt);
pbag->Add(GStatRender_DP_Triangle_Cnt, &DPTriangleCnt);
pbag->Add(GStatRender_Line_Cnt, &LineCnt);
pbag->Add(GStatRender_Triangle_Cnt, &TriangleCnt);
pbag->Add(GStatRender_Mask_Cnt, &MaskCnt);
pbag->Add(GStatRender_Filter_Cnt, &FilterCnt);
}
if (reset)
{
TextureUploadCnt.Reset();
TextureUpdateCnt.Reset();
DPLineCnt.Reset();
DPTriangleCnt.Reset();
LineCnt.Reset();
TriangleCnt.Reset();
MaskCnt.Reset();
FilterCnt.Reset();
}
}
// GRendererD3D9 interface implementation
virtual bool SetDependentVideoMode(
LPDIRECT3DDEVICE9 pd3dDevice,
D3DPRESENT_PARAMETERS* ppresentParams,
UInt32 vmConfigFlags,
HWND hwnd)
{
if (!pd3dDevice || !ppresentParams)
return 0;
// TODO: Need to check device caps ?
pDevice = pd3dDevice;
pDevice->AddRef();
if (!InitShaders())
{
ReleaseShaders();
pDevice->Release();
pDevice = 0;
return 0;
}
VMCFlags = vmConfigFlags;
memcpy(&PresentParams, ppresentParams, sizeof(D3DPRESENT_PARAMETERS));
hWnd = hwnd;
ModeSet = 1;
return 1;
}
// Returns back to original mode (cleanup)
virtual bool ResetVideoMode()
{
if (!ModeSet)
return 1;
pDevice->SetTexture(0, 0);
pDevice->SetTexture(1, 0);
// Not that we no longer generate a texture DataLost event on 360;
// since textures can survive the reset.
ReleaseShaders();
pDevice->Release();
pDevice = 0;
hWnd = 0;
ModeSet = 0;
return 1;
}
virtual DisplayStatus CheckDisplayStatus() const
{
return ModeSet ? DisplayStatus_Ok : DisplayStatus_NoModeSet;
}
// Direct3D9 Access
// Return various Dirext3D related information
virtual LPDIRECT3D9 GetDirect3D() const
{
LPDIRECT3D9 pd3d = 0;
if (pDevice)
pDevice->GetDirect3D(&pd3d);
return pd3d;
}
virtual LPDIRECT3DDEVICE9 GetDirect3DDevice() const
{
return pDevice;
}
virtual bool GetDirect3DPresentParameters(D3DPRESENT_PARAMETERS* ppresentParams) const
{
if (!ModeSet)
return 0;
memcpy(ppresentParams, &PresentParams, sizeof(D3DPRESENT_PARAMETERS));
return 1;
}
}; // class GRendererXbox360Impl
// ***** GTextureD3D9 implementation
GTextureXbox360Impl::GTextureXbox360Impl(GRendererXbox360Impl *prenderer)
: GTextureD3D9(&prenderer->Textures)
{
pRenderer = prenderer;
pD3DTexture = 0;
}
GTextureXbox360Impl::~GTextureXbox360Impl()
{
if (pD3DTexture)
{
if (pRenderer && pRenderer->pDevice && pD3DTexture->IsSet(pRenderer->pDevice))
{
pRenderer->pDevice->SetTexture(0,0);
pRenderer->pDevice->SetTexture(1,0);
}
GASSERT(pRenderer);
if (pRenderer)
// because all D3D operation should be done from the main thread
// we add this resource to renderer deletion queue
pRenderer->AddResourceForReleasing(pD3DTexture);
else
// it should never happen
pD3DTexture->Release();
}
if (!pRenderer)
return;
GLock::Locker guard(&pRenderer->TexturesLock);
if (pFirst)
RemoveNode();
}
// Obtains the renderer that create TextureInfo
GRenderer* GTextureXbox360Impl::GetRenderer() const
{ return pRenderer; }
bool GTextureXbox360Impl::IsDataValid() const
{ return (pD3DTexture != 0); }
void GTextureXbox360Impl::ReleaseTexture()
{
if (pD3DTexture)
{
pD3DTexture->Release();
pD3DTexture = 0;
}
}
// Remove texture from renderer, notifies renderer destruction
void GTextureXbox360Impl::RemoveFromRenderer()
{
pRenderer = 0;
if (AddRef_NotZero())
{
if (pD3DTexture)
{
pD3DTexture->Release();
pD3DTexture = 0;
}
CallHandlers(ChangeHandler::Event_RendererReleased);
if (pNext) // We may have been released by user
RemoveNode();
Release();
}
else
{
if (pNext) // We may have been released by user
RemoveNode();
}
}
// Creates a D3D texture of the specified dest dimensions, from a
// resampled version of the given src image. Does a bilinear
// resampling to create the dest image.
// Source can be 4,3, or 1 bytes/pixel. Destination is either 1 or 4 bytes/pixel.
void SoftwareResample(
UByte* pDst, int dstWidth, int dstHeight, int dstPitch,
UByte* pSrc, int srcWidth, int srcHeight, int srcPitch,
int bytesPerPixel )
{
switch(bytesPerPixel)
{
case 4:
GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch,
pSrc, srcWidth, srcHeight, srcPitch,
GRenderer::ResizeRgbaToRgba);
break;
case 3:
GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch,
pSrc, srcWidth, srcHeight, srcPitch,
GRenderer::ResizeRgbToRgba);
break;
case 1:
GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch,
pSrc, srcWidth, srcHeight, srcPitch,
GRenderer::ResizeGray);
break;
}
}
bool GTextureXbox360Impl::InitTexture(IDirect3DTexture9 *ptex, bool managed)
{
GUNUSED(managed);
if (!pRenderer || !pRenderer->pDevice)
return 0;
if (pD3DTexture)
{
pD3DTexture->Release();
pD3DTexture = 0;
}
if (ptex)
{
pD3DTexture = ptex;
ptex->AddRef();
}
CallHandlers(ChangeHandler::Event_DataChange);
return 1;
}
// NOTE: This function destroys pim's data in the process of making mipmaps.
bool GTextureXbox360Impl::InitTexture(GImageBase* pim, UInt usage)
{
GUNUSED(usage);
if (!pRenderer || !pRenderer->pDevice)
return 0;
// Delete old data
if (pD3DTexture)
{
pD3DTexture->Release();
pD3DTexture = 0;
}
if (!pim || !pim->pData)
{
// Kill texture
CallHandlers(ChangeHandler::Event_DataChange);
return 1;
}
// Determine format
UInt bytesPerPixel = 0;
switch(pim->Format)
{
case GImage::Image_ARGB_8888:
bytesPerPixel = 4;
TextureFormat = D3DFMT_A8R8G8B8;
break;
case GImage::Image_RGB_888:
bytesPerPixel = 3;
TextureFormat = D3DFMT_DXT1;
//TextureFormat = D3DFMT_A8R8G8B8;
break;
case GImage::Image_A_8:
bytesPerPixel = 1;
TextureFormat = D3DFMT_A8;
break;
case GImage::Image_DXT1:
TextureFormat = D3DFMT_LIN_DXT1;
bytesPerPixel = 1;
break;
case GImage::Image_DXT3:
TextureFormat = D3DFMT_LIN_DXT3;
bytesPerPixel = 1;
break;
case GImage::Image_DXT5:
TextureFormat = D3DFMT_LIN_DXT5;
bytesPerPixel = 1;
break;
default:
// Unsupported format
GASSERT(0);
return 0;
}
// Don't use DXT1 for thin textures (it must be a multiple of 4 for DirectX)
/*
if (((TextureFormat== D3DFMT_DXT1) || (TextureFormat== D3DFMT_DXT3)) &&
((Width<4) || (Height<4)))
TextureFormat = D3DFMT_A8R8G8B8;
*/
UInt w = pim->Width;
UInt h = pim->Height;
UInt levelsNeeded;
if (pim->IsDataCompressed() || pim->MipMapCount > 1)
levelsNeeded = G_Max<UInt>(1, pim->MipMapCount);
else
{
levelsNeeded = 1;
UInt mipw = w, miph = h;
while (mipw > 1 || miph > 1)
{
mipw >>= 1;
miph >>= 1;
levelsNeeded++;
}
}
GPtr<GImage> presampleImage;
// Set the actual data.
if (!pim->IsDataCompressed() && (w != pim->Width || h != pim->Height ||
(pim->Format == GImage::Image_RGB_888) ||
(pim->Format == GImage::Image_ARGB_8888)) )
{
// Resample the image to new size
if (presampleImage = *new GImage(
((pim->Format == GImage::Image_RGB_888) ? GImage::Image_ARGB_8888 : pim->Format), w, h))
{
GASSERT_ON_RENDERER_RESAMPLING;
if (w != pim->Width || h != pim->Height)
{
// Resample will store an extra Alpha byte for RGB_888 -> RGBA_8888
SoftwareResample(presampleImage->pData, w, h, presampleImage->Pitch,
pim->pData, pim->Width, pim->Height, pim->Pitch, bytesPerPixel);
}
else if (pim->Format == GImage::Image_RGB_888)
{
// Need to insert a dummy alpha byte in the image data, for Xbox360LoadSurfaceFromMemory.
for (UInt y = 0; y < h; y++)
{
UByte* scanline = pim->GetScanline(y);
UByte* pdest = presampleImage->GetScanline(y);
for (UInt x = 0; x < w; x++)
{
pdest[x * 4 + 0] = scanline[x * 3 + 0]; // red
pdest[x * 4 + 1] = scanline[x * 3 + 1]; // green
pdest[x * 4 + 2] = scanline[x * 3 + 2]; // blue
pdest[x * 4 + 3] = 255; // alpha
}
}
}
else if (pim->Format == GImage::Image_ARGB_8888)
{
XMemCpy(presampleImage->GetScanline(0), pim->GetScanline(0),
pim->GetPitch() * pim->Height);
}
// HACK: disable mipmaps, need to do something with them! (AB)
if (pim->MipMapCount > 1)
{
GFC_DEBUG_WARNING(1, "GRendererD3D9 - Existing mipmap levels have been skipped due to resampling");
levelsNeeded = 1;
}
// Swap byte order for Big-Endian XBox 360
if (presampleImage->Format == GImage::Image_ARGB_8888)
{
for (UInt y = 0; y < h; y++)
{
UInt32* pdest = (UInt32*)presampleImage->GetScanline(y);
for (UInt x = 0; x < w; x++)
pdest[x] = _byteswap_ulong(pdest[x]);
}
// The new image has 4 bytes/pixel.
bytesPerPixel = 4;
}
}
}
// Create the texture.
// For now, only generate mipmaps for alpha textures.
// MA: For some reason we need to specify levelsNeeded-1, otherwise
// surface accesses may crash (when running with Gamebryo).
// So, 256x256 texture seems to have levelCount of 8 (not 9).
if (pim->MipMapCount <= 1 && bytesPerPixel != 1)
levelsNeeded = 1;
else
levelsNeeded = G_Max(1u, levelsNeeded - 1);
//levelsNeeded = GTL::gmax(1u, (bytesPerPixel == 1) ? UInt(levelsNeeded - 1) : 1u);
HRESULT result = pRenderer->pDevice->CreateTexture(
w, h,
levelsNeeded,
0, // XBox: D3DUSAGE_BORDERSOURCE_TEXTURE
TextureFormat,
D3DPOOL_DEFAULT,
&pD3DTexture, NULL);
if (result != S_OK)
{
GFC_DEBUG_ERROR(1, "GTextureD3D9 - Can't create texture");
return 0;
}
if (pim->IsDataCompressed() || pim->MipMapCount > 1)
{
IDirect3DSurface9* psurface = NULL;
UInt level;
GImageBase* psourceImage= presampleImage ? presampleImage.GetPtr() : pim;
RECT sourceRect = { 0,0, w,h};
D3DFORMAT sourceSurfaceFormat = TextureFormat;
// Determine source data format correctly (it may be different from texture).
if (TextureFormat == D3DFMT_A8R8G8B8)
sourceSurfaceFormat = D3DFMT_LIN_A8B8G8R8;
else if (TextureFormat == D3DFMT_A8)
sourceSurfaceFormat = D3DFMT_LIN_A8;
for(level = 0; level < levelsNeeded; level++)
{
// Load all levels...
if (pD3DTexture->GetSurfaceLevel(level, &psurface) != S_OK)
{
pD3DTexture->Release();
pD3DTexture = 0;
return 0;
}
GASSERT(psurface);
UInt mipW, mipH;
const UByte* pmipData = psourceImage->GetMipMapLevelData(level, &mipW, &mipH);
sourceRect.right = mipW;
sourceRect.bottom = mipH;
result = D3DXLoadSurfaceFromMemory(
psurface, NULL, NULL,
pmipData,
sourceSurfaceFormat,
(pim->IsDataCompressed()) ?
GImageBase::GetMipMapLevelSize(psourceImage->Format, mipW, 1) :
GImageBase::GetPitch(psourceImage->Format, mipW),
NULL,
&sourceRect,
FALSE, 0, 0, // no packed mipmap for now
D3DX_DEFAULT, 0);
psurface->Release();
psurface = 0;
if (result != S_OK)
{
GFC_DEBUG_ERROR1(1, "GTextureD3D9 - Can't load surface from memory, result = %d", result);
pD3DTexture->Release();
pD3DTexture = 0;
return 0;
}
}
}
else
{
// Will need a buffer for destructive mipmap generation
if ((bytesPerPixel == 1) && (levelsNeeded >1) && !presampleImage)
{
GASSERT_ON_RENDERER_MIPMAP_GEN;
// A bit hacky, needs to be more general
presampleImage = *GImage::CreateImage(pim->Format, pim->Width, pim->Height);
GASSERT(pim->Pitch == presampleImage->Pitch);
XMemCpy(presampleImage->pData, pim->pData, pim->Height * pim->Pitch);
}
IDirect3DSurface9* psurface = NULL;
UInt level;
GImageBase* psourceImage= presampleImage ? presampleImage.GetPtr() : pim;
RECT sourceRect = { 0,0, w,h};
for(level = 0; level<levelsNeeded; level++)
{
// Load all levels...
if (pD3DTexture->GetSurfaceLevel(level, &psurface) != S_OK)
{
pD3DTexture->Release();
pD3DTexture = 0;
return 0;
}
GASSERT(psurface);
result = D3DXLoadSurfaceFromMemory(
psurface, NULL, NULL,
psourceImage->pData,
(bytesPerPixel == 1) ? D3DFMT_LIN_A8 : D3DFMT_LIN_A8B8G8R8, // NOTE: format order conversion from GL
(bytesPerPixel == 1) ? sourceRect.right : psourceImage->Pitch,
NULL,
&sourceRect,
FALSE, 0, 0, // no packed mipmap for now
D3DX_DEFAULT, 0);
psurface->Release();
psurface = 0;
if (result != S_OK)
{
GFC_DEBUG_ERROR1(1, "GTextureD3D9 - Can't load surface from memory, result = %d", result);
pD3DTexture->Release();
pD3DTexture = 0;
return 0;
}
// For Alpha-only images, we might need to generate the next mipmap level.
if (level< (levelsNeeded-1))
{
GCOMPILER_ASSERT(sizeof(sourceRect.right) == sizeof(int));
GRendererXbox360Impl::MakeNextMiplevel((int*) &sourceRect.right, (int*) &sourceRect.bottom,
psourceImage->pData);
}
}
}
CallHandlers(ChangeHandler::Event_DataChange);
return 1;
}
bool GTextureXbox360Impl::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage)
{
if (!pRenderer || !pRenderer->pDevice)
return 0;
if (pD3DTexture)
pD3DTexture->Release();
TexWidth = width;
TexHeight = height;
if (usage & Usage_Update)
{
if (format == GImage::Image_ARGB_8888)
TextureFormat = D3DFMT_LIN_A8R8G8B8;
else if (format == GImage::Image_RGB_888)
TextureFormat = D3DFMT_LIN_A8R8G8B8;
else if (format == GImage::Image_A_8)
TextureFormat = D3DFMT_LIN_A8;
else
{ // Unsupported format
GASSERT(0);
return 0;
}
}
else
{
if (format == GImage::Image_ARGB_8888)
TextureFormat = D3DFMT_A8R8G8B8;
else if (format == GImage::Image_RGB_888)
TextureFormat = D3DFMT_A8R8G8B8;
else if (format == GImage::Image_A_8)
TextureFormat = D3DFMT_A8;
else
{ // Unsupported format
GASSERT(0);
return 0;
}
}
HRESULT result = pRenderer->pDevice->CreateTexture(width, height, mipmaps+1, 0, TextureFormat,
(usage & Usage_Map) ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT, &pD3DTexture, NULL);
if (result != S_OK)
return 0;
return 1;
}
void GTextureXbox360Impl::Update(int level, int n, const UpdateRect *rects, const GImageBase *pim)
{
UInt bytesPerPixel = 0, convert = 0;
if (pim->Format == GImage::Image_ARGB_8888)
bytesPerPixel = 4;
else if (pim->Format == GImage::Image_RGB_888)
{
convert = 1;
bytesPerPixel = 4;
}
else if (pim->Format == GImage::Image_A_8)
bytesPerPixel = 1;
else
GASSERT(0);
if (!pD3DTexture && !CallRecreate())
{
GFC_DEBUG_WARNING(1, "GTextureXBox360::Update failed, could not recreate texture");
return;
}
// Ensure that our texture isn't set on the device during Update.
pRenderer->pDevice->SetTexture(0, 0);
pRenderer->pDevice->SetTexture(1, 0);
for (int i = 0; i < n; i++)
{
GRect<int> rect = rects[i].src;
GPoint<int> dest = rects[i].dest;
D3DLOCKED_RECT lr;
RECT destr;
UInt32 lockFlags = (pRenderer->VMCFlags & GRendererXbox360::VMConfig_SupportTiling) ? D3DLOCK_NOOVERWRITE : 0;
destr.left = dest.x;
destr.bottom = dest.y + rect.Height() - 1;
destr.right = dest.x + rect.Width() - 1;
destr.top = dest.y;
if (FAILED( pD3DTexture->LockRect(level, &lr, &destr, lockFlags) ))
return;
if (convert && pim->Format == GImage::Image_RGB_888)
{
UByte *pdest = (UByte*)lr.pBits;
//pdest += dest.y * lr.Pitch + dest.x * bytesPerPixel;
for (int j = 0; j < rect.Height(); j++)
for (int k = 0; k < rect.Width(); k++)
{
pdest[j * lr.Pitch + k * bytesPerPixel +0] = pim->pData[(j + rect.Top) * pim->Pitch + (k + rect.Left) * 3 +0];
pdest[j * lr.Pitch + k * bytesPerPixel +1] = pim->pData[(j + rect.Top) * pim->Pitch + (k + rect.Left) * 3 +1];
pdest[j * lr.Pitch + k * bytesPerPixel +2] = pim->pData[(j + rect.Top) * pim->Pitch + (k + rect.Left) * 3 +2];
pdest[j * lr.Pitch + k * bytesPerPixel +3] = 255;
}
}
else
{
UByte *pdest = (UByte*)lr.pBits;
for (int j = 0; j < rect.Height(); j++)
memcpy(pdest + j * lr.Pitch,
pim->GetScanline(j + rect.Top) + bytesPerPixel * rect.Left,
rect.Width() * bytesPerPixel);
}
pD3DTexture->UnlockRect(level);
}
}
int GTextureXbox360Impl::Map(int level, int n, MapRect* maps, int flags)
{
GUNUSED(flags);
GASSERT(n > 0 && maps);
D3DLOCKED_RECT rect;
pD3DTexture->LockRect(level, &rect, NULL, 0);
maps[0].pData = (UByte*)rect.pBits;
maps[0].pitch = rect.Pitch;
maps[0].width = TexWidth;
maps[0].height = TexHeight;
return 1;
}
bool GTextureXbox360Impl::Unmap(int level, int n, MapRect* maps, int flags)
{
GUNUSED3(n,maps,flags);
pD3DTexture->UnlockRect(level);
return 1;
}
void GTextureXbox360Impl::Bind(int stageIndex, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps)
{
if (!pD3DTexture)
CallRecreate();
pRenderer->pDevice->SetTexture(stageIndex, pD3DTexture);
pRenderer->ApplySampleMode(stageIndex, WrapMode, SampleMode, useMipmaps);
if (pRenderer->VMCFlags & GRendererXbox360::VMConfig_SupportTiling)
pRenderer->FrameResources.PushBack(this);
}
GTextureXbox360ImplYUV::~GTextureXbox360ImplYUV()
{
for (int i = 0; i < 3; i++)
if (pD3DTextureUVA[i])
{
GASSERT(pRenderer);
if (pRenderer)
pRenderer->AddResourceForReleasing(pD3DTextureUVA[i]);
else
// it should never happen
pD3DTextureUVA[i]->Release();
}
}
void GTextureXbox360ImplYUV::ReleaseTexture()
{
GTextureXbox360Impl::ReleaseTexture();
for (int i = 0; i < 3; i++)
if (pD3DTextureUVA[i])
{
pD3DTextureUVA[i]->Release();
pD3DTextureUVA[i] = 0;
}
}
bool GTextureXbox360ImplYUV::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage)
{
if (!pRenderer || !pRenderer->pDevice)
return 0;
GUNUSED(usage);
ReleaseTexture();
TexWidth = width;
TexHeight = height;
HRESULT result = pRenderer->pDevice->CreateTexture(width, height, mipmaps+1, 0,
D3DFMT_LIN_A8, D3DPOOL_DEFAULT, &pD3DTexture, NULL);
result |= pRenderer->pDevice->CreateTexture(width>>1, height>>1, mipmaps+1, 0,
D3DFMT_LIN_A8, D3DPOOL_DEFAULT, &pD3DTextureUVA[0], NULL);
result |= pRenderer->pDevice->CreateTexture(width>>1, height>>1, mipmaps+1, 0,
D3DFMT_LIN_A8, D3DPOOL_DEFAULT, &pD3DTextureUVA[1], NULL);
if (format == GImage::Image_ARGB_8888)
{
IsAlpha = 1;
result |= pRenderer->pDevice->CreateTexture(width, height, mipmaps+1, 0,
D3DFMT_LIN_A8, D3DPOOL_DEFAULT, &pD3DTextureUVA[2], NULL);
}
else
IsAlpha = 0;
if (result != S_OK)
{
GFC_DEBUG_ERROR(1, "GTextureXbox360 - Can't create YUV texture");
ReleaseTexture();
return 0;
}
return 1;
}
int GTextureXbox360ImplYUV::Map(int level, int n, MapRect* maps, int flags)
{
GUNUSED(flags);
GASSERT(n >= (IsAlpha ? 4 : 3) && maps);
for (int i = 0; i < 4; i++)
pRenderer->pDevice->SetTexture(i, 0);
int h = TexHeight;
int w = TexWidth;
for (int i = 0; i < level; i++)
{
h >>= 1;
if (h < 1)
h = 1;
w >>= 1;
if (w < 1)
w = 1;
}
n = IsAlpha ? 4 : 3;
D3DLOCKED_RECT rect;
pD3DTexture->LockRect(level, &rect, NULL, 0);
maps[0].pData = (UByte*)rect.pBits;
maps[0].pitch = rect.Pitch;
maps[0].width = w;
maps[0].height = h;
for (int i = 1; i < n; i++)
{
pD3DTextureUVA[i-1]->LockRect(level, &rect, NULL, 0);
maps[i].pData = (UByte*)rect.pBits;
maps[i].pitch = rect.Pitch;
maps[i].width = (i == 3) ? w : (w >> 1);
maps[i].height = (i == 3) ? h : (h >> 1);
}
return n;
}
bool GTextureXbox360ImplYUV::Unmap(int level, int n, MapRect* maps, int flags)
{
GUNUSED3(n,maps,flags);
pD3DTexture->UnlockRect(level);
for (int i = 0; i < 3; i++)
if (pD3DTextureUVA[i])
pD3DTextureUVA[i]->UnlockRect(level);
return 1;
}
void GTextureXbox360ImplYUV::Bind(int stageIndex, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps)
{
if (!pD3DTexture)
CallRecreate();
GASSERT(stageIndex == 0);
pRenderer->pDevice->SetTexture(0, pD3DTexture);
pRenderer->pDevice->SetTexture(1, pD3DTextureUVA[0]);
pRenderer->pDevice->SetTexture(2, pD3DTextureUVA[1]);
pRenderer->ApplySampleMode(0, WrapMode, SampleMode, useMipmaps);
pRenderer->ApplySampleMode(1, WrapMode, SampleMode, useMipmaps);
pRenderer->ApplySampleMode(2, WrapMode, SampleMode, useMipmaps);
if (pD3DTextureUVA[2])
{
pRenderer->pDevice->SetTexture(3, pD3DTextureUVA[2]);
pRenderer->ApplySampleMode(3, WrapMode, SampleMode, useMipmaps);
}
if (pRenderer->VMCFlags & GRendererXbox360::VMConfig_SupportTiling)
pRenderer->FrameResources.PushBack(this);
}
bool GRenderTargetXbox360Impl::InitRenderTarget(GTexture *pTarget, UInt Base)
{
ReleaseResources();
pTexture = (GTextureXbox360Impl*)pTarget;
TargetWidth = pTexture->TexWidth;
TargetHeight = pTexture->TexHeight;
TileSize = XGSurfaceSize(TargetWidth, TargetHeight, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE);
BaseTile = Base;
D3DSURFACE_PARAMETERS params;
params.Base = BaseTile;
params.ColorExpBias = 0;
params.HierarchicalZBase = 0xffffffff;
params.HiZFunc = D3DHIZFUNC_DEFAULT;
pRenderer->pDevice->CreateRenderTarget(TargetWidth, TargetHeight, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, 0,
&pRenderSurface, &params);
params.Base += TileSize;
TileSize += XGSurfaceSize(TargetWidth, TargetHeight, D3DFMT_D24S8, D3DMULTISAMPLE_NONE);
pRenderer->pDevice->CreateDepthStencilSurface(TargetWidth, TargetHeight, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, 0,
&pStencilSurface, &params);
return true;
}
//
// Store the render texture and depth buffer that is passed in
//
GRenderTargetXbox360Impl::GRenderTargetXbox360Impl(GRendererXbox360Impl *pRend) : GRenderTargetD3D9(&pRend->RenderTargets)
{
pRenderer = pRend; BaseTile = TileSize = 0;
pRenderSurface = 0;
pStencilSurface = 0;
}
GRenderTargetXbox360Impl::~GRenderTargetXbox360Impl()
{
if (pRenderSurface)
{
GASSERT(pRenderer);
if (pRenderer)
{
pRenderer->AddResourceForReleasing(pRenderSurface);
pRenderer->AddResourceForReleasing(pStencilSurface);
}
}
if (!pRenderer)
return;
GLock::Locker gruad(&pRenderer->TexturesLock);
if (pFirst)
RemoveNode();
}
void GRenderTargetXbox360Impl::ReleaseResources()
{
if (pRenderSurface)
{
pRenderSurface->Release();
pRenderSurface = 0;
}
if (pStencilSurface)
{
pStencilSurface->Release();
pStencilSurface = 0;
}
}
GRenderer* GRenderTargetXbox360Impl::GetRenderer() const
{
return pRenderer;
}
void GRenderTargetXbox360Impl::RemoveFromRenderer()
{
pRenderer = 0;
if (AddRef_NotZero())
{
ReleaseResources();
CallHandlers(ChangeHandler::Event_RendererReleased);
if (pNext) // We may have been released by user.
RemoveNode();
Release();
}
else
{
if (pNext) // We may have been released by user.
RemoveNode();
}
}
bool GRenderTargetXbox360Impl::InitRenderTarget(D3D9RenderTargetParams RTParams)
{
ReleaseResources();
BaseTile = TileSize = 0;
pRenderSurface = RTParams.pRenderSurface;
pStencilSurface = RTParams.pStencilSurface;
pTexture = NULL;
GASSERT(pRenderSurface && pStencilSurface);
D3DSURFACE_DESC desc;
pRenderSurface->GetDesc(&desc);
TargetWidth = desc.Width;
TargetHeight = desc.Height;
return true;
}
// Factory.
GRendererXbox360* GRendererXbox360::CreateRenderer()
{
return new GRendererXbox360Impl;
}