/********************************************************************** 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 #endif #include "GRendererXbox360.h" #include "GRendererCommonImpl.h" #include // for memset() #if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW) #undef new #endif #include #include #include #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 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 VertexDecls[VD_Count]; GPtr VertexShaders[VS_Count]; // Allocated pixel shaders GPtr PixelShaders[PS_Count]; GPtr 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 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 RTStackType; GRenderTargetXbox360Impl* pCurRenderTarget; bool CurRenderTargetSet; RTStackType RenderTargetStack; GArrayLH > 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 > 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 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 *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 *pshaderPtr, const char* pshaderText) { if (!*pshaderPtr) { GPtr 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 *pshaderPtr, const char* pshaderText) { if (!*pshaderPtr) { GPtr pmsg; GPtr 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<VertexFormats= (1<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 > 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 psrcinRef = (GTextureXbox360Impl*)psrcin; GPtr 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) && (ibitmapDrawPrimitiveUP(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(1, pim->MipMapCount); else { levelsNeeded = 1; UInt mipw = w, miph = h; while (mipw > 1 || miph > 1) { mipw >>= 1; miph >>= 1; levelsNeeded++; } } GPtr 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; levelGetSurfaceLevel(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 rect = rects[i].src; GPoint 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, ¶ms); params.Base += TileSize; TileSize += XGSurfaceSize(TargetWidth, TargetHeight, D3DFMT_D24S8, D3DMULTISAMPLE_NONE); pRenderer->pDevice->CreateDepthStencilSurface(TargetWidth, TargetHeight, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, 0, &pStencilSurface, ¶ms); 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; }