/********************************************************************** Filename : GRendererPS3Impl.cpp Content : PS3 libgcm Sample renderer implementation Created : 1 February 2007 Authors : Andrew Reisse Copyright : (c) 2007 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 "GRendererPS3.h" #include "GRendererCommonImpl.h" #include // for memset() #include #include #include // ***** Classes implemented class GRendererPS3Impl; class GTexturePS3Impl; // Define this macro if mask should be implemented through // z-buffer instead of stencil. Will overwrite z-buffer contents. // #define GFC_ZBUFFER_MASKING // Pixel shaders enum PixelShaderType { PS_None = 0, PS_Solid, PS_TextTexture, PS_TextTextureColor, PS_TextTextureColorMultiply, PS_TextTextureYUV, PS_TextTextureYUVMultiply, PS_TextTextureYUVA, PS_TextTextureYUVAMultiply, PS_CxformTexture, PS_CxformTextureMultiply, PS_CxformGouraud, PS_CxformGouraudNoAddAlpha, PS_CxformGouraudTexture, PS_Cxform2Texture, // Multiplies - must come in same order as other gourauds PS_CxformGouraudMultiply, PS_CxformGouraudMultiplyNoAddAlpha, PS_CxformGouraudMultiplyTexture, PS_CxformMultiply2Texture, PS_CmatrixTexture, PS_BlurFilter, PS_ShadowFilter, PS_ShadowKoFilter, PS_ShadowOnlyFilter, PS_Blur1Filter, PS_Shadow1Filter, PS_ShadowKo1Filter, PS_ShadowOnly1Filter, PS_Count }; #include "PS3/PS3Shaders.h" struct CgShader { CGprogram prog; GAllocatorPS3::Handle memobj; UInt32 offset; CGparameter cxmul, cxadd; CGparameter tex[6]; UInt texi[6]; CGparameter uniforms[FSU_Count]; }; struct CgVProgram { CGprogram prog; void* uc; CGparameter mvp; CGparameter texgenx[2], texgeny[2]; CGparameter pos, tc, color, factor; UInt posi, tci, colori, factori; }; /* Compiled shaders */ extern int _binary_p_cxform_fpo_start; extern int _binary_p_cxformmul_fpo_start; extern int _binary_p_cxg2t_fpo_start; extern int _binary_p_cxg2tm_fpo_start; extern int _binary_p_cxg_fpo_start; extern int _binary_p_cxgm_fpo_start; extern int _binary_p_cxgmna_fpo_start; extern int _binary_p_cxgna_fpo_start; extern int _binary_p_cxgt_fpo_start; extern int _binary_p_cxgtm_fpo_start; extern int _binary_p_solid_fpo_start; extern int _binary_p_text_fpo_start; extern int _binary_p_texta_fpo_start; extern int _binary_p_textmul_fpo_start; extern int _binary_v_1tex_vpo_start; extern int _binary_v_2tex_vpo_start; extern int _binary_v_text_vpo_start; #ifndef GFC_NO_YUV_TEXTURES extern int _binary_p_cxformyuv_fpo_start; extern int _binary_p_cxformyuvmul_fpo_start; extern int _binary_p_cxformyuva_fpo_start; extern int _binary_p_cxformyuvamul_fpo_start; #endif // ***** GTexturePS3Impl implementation // GTexturePS3Impl declaration class GTexturePS3Impl : public GTexturePS3 { public: // Renderer GRendererPS3Impl* pRenderer; // texture data Handle TexMem; CellGcmTexture Tex; GTexturePS3Impl(GRendererPS3Impl *prenderer); ~GTexturePS3Impl(); // Obtains the renderer that create TextureInfo virtual GRenderer* GetRenderer() const; virtual bool IsDataValid() const; // Creates a texture based on parameters virtual bool InitTexture(CellGcmTexture *pTex, bool unused = 0); virtual bool InitTexture(GImageBase* pim, UInt usage = Usage_Wrap); 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); virtual CellGcmTexture* GetNativeTexture() const { return const_cast(&Tex); } // Releases texture and clears vals virtual void ReleaseTexture(); // Remove texture from renderer, notifies of renderer destruction void RemoveFromRenderer(); virtual int IsYUVTexture() { return 0; } virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps); }; #ifndef GFC_NO_YUV_TEXTURES class GTexturePS3ImplYUV : public GTexturePS3Impl { CellGcmTexture TexUVA[3]; Handle TexMemUVA[3]; public: GTexturePS3ImplYUV (GRendererPS3Impl *prenderer); ~GTexturePS3ImplYUV(); virtual bool InitTexture(CellGcmTexture *pTex) { return 0; } virtual bool InitTexture(GImageBase* pim, UInt) { return 0; } 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); virtual void ReleaseTexture(); virtual int IsYUVTexture() { return TexMemUVA[2] ? 2 : 1; } virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps); }; #endif class GRenderTargetPS3Impl : public GRenderTargetPS3 { public: GPtr pTexture, pStencilTexture; GRendererPS3Impl* pRenderer; CellGcmSurface Surf; UInt ActualHeight; bool Tiled; UInt TargetWidth, TargetHeight; bool IsTemp; GRenderTargetPS3Impl(GRendererPS3Impl* prenderer); ~GRenderTargetPS3Impl(); virtual GRenderer* GetRenderer() const; void RemoveFromRenderer(); virtual bool InitRenderTarget(GTexture* ptarget, GTexture* pdepth = 0, GTexture* pstencil = 0); virtual bool InitRenderTarget(PS3RenderTargetParams params); void SetRenderTarget(GRenderer::StereoDisplay disp); }; // Vertex buffer structure used for glyphs. struct GGlyphVertex { float x,y; 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; } }; class GBufferNode : public GRendererNode, public GNewOverrideBase { public: GAllocatorPS3::Handle BufferH; void* pData; UInt32 offset; GRenderer::CachedData* pcache; GBufferNode() : GRendererNode() { BufferH = 0; pcache = 0; pData = 0; } GBufferNode(GBufferNode *p, GRenderer::CachedData *d) : GRendererNode(p) { pData = 0; BufferH = 0; pcache = d; } }; // ***** Renderer Implementation #define M(x,r,c) x[(r)*4+(c)] static void MakeOrtho(Float *O, Float left, Float right, Float top, Float bottom, Float near, Float far) { for (int i = 0; i < 16; i++) O[i] = 0; M(O, 0,0) = 2.0f / (right - left); M(O, 1,1) = 2.0f / (top - bottom); M(O, 2,2) = -2.0f / (far - near); M(O, 3,3) = 1; M(O, 0,3) = -(right + left) / (right - left); M(O, 1,3) = -(top + bottom) / (top - bottom); M(O, 2,3) = -(far + near) / (far - near); } #undef M class GRendererPS3Impl : public GRendererPS3 { public: // Some renderer state. GAllocatorPS3* Allocator; CellGcmContextData* GcmCtx; SInt32 MaxCmdSize; bool ModeSet; SInt RenderMode; GMatrix3D BaseMatrix, CurMatrix; // Output size. Float DisplayWidth; Float DisplayHeight; GViewport ViewRect; GRenderer::Matrix UserMatrix; GRenderer::Matrix CurrentMatrix; GRenderer::Cxform CurrentCxform; #ifndef GFC_NO_3D bool UVPMatricesChanged; GMatrix3D ViewMatrix, 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 GBufferNode BufferObjects; const void* pVertexData; const void* pIndexData; UInt32 IndexArray; SInt VertexArray; VertexFormat VertexFmt; SInt IndexFmt; // vertex array Handle VA_memobj; UByte* VA_base; SInt VA_location; UInt32 VA_offset, VA_size; UInt32 VA_start, VA_end, VA_lastv, VA_count, VA_shift; UInt VA_label; // Stencil counter - for increment/decrement compares. SInt StencilCounter; bool StencilEnabled; // 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 { GRenderTargetPS3Impl* pRT; GMatrix3D ViewMatrix; GMatrix3D ViewMatrix3D; GMatrix3D PerspMatrix3D; const GMatrix3D* pWorldMatrix3D; GViewport ViewRect; SInt RenderMode; SInt StencilCounter; bool StencilEnabled; RTState() { pRT = 0; pWorldMatrix3D = 0; } RTState(GRenderTargetPS3Impl* prt, const GMatrix3D& 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; GRenderTargetPS3Impl* pCurRenderTarget; RTStackType RenderTargetStack; GArrayLH > TempRenderTargets; GArrayLH > TempStencilBuffers; // Linked list used for buffer cache testing, otherwise holds no data. CacheNode CacheList; int CurrentShader; int CurVProgram; CgShader CgFShaders[PS_Count]; CgVProgram CgVShaders[3]; CgShader FilterShaders[FS2_Count]; UInt32 VMCFlags; // **** GRendererPS3Impl() { ModeSet = 1; SampleMode = Sample_Linear; RenderMode = 0; pVertexData = 0; pIndexData = 0; VertexFmt = Vertex_None; IndexFmt = 0; StencilCounter = 0; memset(CgFShaders, 0, sizeof(CgFShaders)); memset(FilterShaders, 0, sizeof(FilterShaders)); VA_size = 0; VA_memobj = 0; Allocator = 0; #ifndef GFC_NO_3D pWorldMatrix = 0; S3DDisplay = StereoCenter; #endif } ~GRendererPS3Impl() { Clear(); } GAllocatorPS3* GetAllocator() { return Allocator; } void Clear() { // Remove/notify all textures { GLock::Locker guard(&TexturesLock); while (Textures.pFirst != &Textures) ((GTexturePS3Impl*)Textures.pFirst)->RemoveFromRenderer(); while (RenderTargets.pFirst != &RenderTargets) ((GRenderTargetPS3Impl*)RenderTargets.pFirst)->RemoveFromRenderer(); } CacheList.ReleaseList(); for (GRendererNode *p = BufferObjects.pFirst; p; ) { GBufferNode *pn = (GBufferNode*)p; if (pn->BufferH) Allocator->Free(pn->BufferH); if (pn->pcache) pn->pcache->ReleaseDataByRenderer(); p = p->pNext; if (p == BufferObjects.pLast) p->pNext = 0; if (pn != &BufferObjects) delete pn; } BufferObjects.pFirst = NULL; Allocator->Free(VA_memobj); VA_memobj = 0; for (int i = 0; i < PS_Count; i++) if (CgFShaders[i].memobj) { Allocator->Free(CgFShaders[i].memobj); CgFShaders[i].memobj = 0; } for (int i = 0; i < FS2_Count; i++) if (FilterShaders[i].memobj) { Allocator->Free(FilterShaders[i].memobj); FilterShaders[i].memobj = 0; } TempStencilBuffers.Clear(); TempRenderTargets.Clear(); } void ReleaseResources() { Clear(); }; // Utility. Mutates *width, *height and *data to create the // next mip level. static void MakeNextMiplevel(UInt* width, UInt* height, UByte* data, int bpp) { GASSERT(width); GASSERT(height); GASSERT(data); UInt newW = *width >> 1; UInt newH = *height >> 1; if (newW < 1) newW = 1; if (newH < 1) newH = 1; if (newW * 2 != *width || newH * 2 != *height) { // width or height was already 1, one dimensional resample if (bpp == 1) { UByte* out = ((UByte*) data); UByte* in = ((UByte*) data); for (UInt i = 0; i < newW * newH; i++) { int a = (*(in + 0) + *(in + 1)); *(out) = UByte(a >> 1); out++; in += 2; } } else if (bpp == 3) { UByte* out = ((UByte*) data); UByte* in = ((UByte*) data); for (UInt i = 0; i < newW * newH; i++) { int r,g,b; r = ( *(in + 0) + *(in + 3)); g = ( *(in + 1) + *(in + 4)); b = ( *(in + 2) + *(in + 5)); out[0] = UByte(r >> 1); out[1] = UByte(g >> 1); out[2] = UByte(b >> 1); out += 3; in += 6; } } else { UByte* out = ((UByte*) data); UByte* in = ((UByte*) data); for (UInt i = 0; i < newW * newH; i++) { int r,g,b,a; r = ( *(in + 0) + *(in + 4)); g = ( *(in + 1) + *(in + 5)); b = ( *(in + 2) + *(in + 6)); a = ( *(in + 3) + *(in + 7)); out[0] = UByte(r >> 1); out[1] = UByte(g >> 1); out[2] = UByte(b >> 1); out[3] = UByte(a >> 1); out += 4; in += 8; } } } else if (bpp == 1) { // Resample. Simple average 2x2 --> 1, in-place. for (UInt j = 0; j < newH; j++) { UByte* out = ((UByte*) data) + j * newW; UByte* in = ((UByte*) data) + (j << 1) * *width; for (UInt i = 0; i < newW; i++) { int a = (*(in + 0) + *(in + 1) + *(in + 0 + *width) + *(in + 1 + *width)); *(out) = UByte(a >> 2); out++; in += 2; } } } else if (bpp == 3) { // Resample. Simple average 2x2 --> 1, in-place. for (UInt j = 0; j < newH; j++) { UByte* out = ((UByte*) data) + j * (3 * newW); UByte* in = ((UByte*) data) + (j << 1) * (3 * *width); for (UInt i = 0; i < newW; i++) { int r,g,b; r = ( *(in + 0) + *(in + 3) + *(in + 0 + (*width * 3)) + *(in + 3 + (*width * 3)) ); g = ( *(in + 1) + *(in + 4) + *(in + 1 + (*width * 3)) + *(in + 4 + (*width * 3)) ); b = ( *(in + 2) + *(in + 5) + *(in + 2 + (*width * 3)) + *(in + 5 + (*width * 3)) ); out[0] = UByte(r >> 2); out[1] = UByte(g >> 2); out[2] = UByte(b >> 2); out += 3; in += 6; } } } else { // Resample. Simple average 2x2 --> 1, in-place. for (UInt j = 0; j < newH; j++) { UByte* out = ((UByte*) data) + j * (4 * newW); UByte* in = ((UByte*) data) + (j << 1) * (4 * *width); for (UInt i = 0; i < newW; i++) { int r,g,b,a; r = ( *(in + 0) + *(in + 4) + *(in + 0 + (*width * 4)) + *(in + 4 + (*width * 4)) ); g = ( *(in + 1) + *(in + 5) + *(in + 1 + (*width * 4)) + *(in + 5 + (*width * 4)) ); b = ( *(in + 2) + *(in + 6) + *(in + 2 + (*width * 4)) + *(in + 6 + (*width * 4)) ); a = ( *(in + 3) + *(in + 7) + *(in + 3 + (*width * 4)) + *(in + 7 + (*width * 4)) ); out[0] = UByte(r >> 2); out[1] = UByte(g >> 2); out[2] = UByte(b >> 2); out[3] = UByte(a >> 2); out += 4; in += 8; } } } // Set parameters to reflect the new size *width = newW; *height = newH; } // Fill helper function: // Applies fill texture by setting it to the specified stage, initializing samplers and vertex constants void ApplyFillTexture(const FillTexture &fill, int stageIndex0) { GASSERT (fill.pTexture != 0); if (fill.pTexture == 0) return; // avoid crash in release build GTexturePS3Impl* ptexture = ((GTexturePS3Impl*)fill.pTexture); ptexture->Bind(stageIndex0, fill.WrapMode, fill.SampleMode, 0); SetTexgenState (stageIndex0, fill); } void ApplyPShaderCxform(const Cxform &cxform) const { Float scm[4] = { cxform.M_[0][0], cxform.M_[1][0], cxform.M_[2][0], cxform.M_[3][0] }; Float sca[4] = { cxform.M_[0][1] / 255.0f, cxform.M_[1][1] / 255.0f, cxform.M_[2][1] / 255.0f, cxform.M_[3][1] / 255.0f }; cellGcmSetFragmentProgramParameter (GcmCtx, CgFShaders[CurrentShader].prog, CgFShaders[CurrentShader].cxmul, scm, CgFShaders[CurrentShader].offset); cellGcmSetFragmentProgramParameter (GcmCtx, CgFShaders[CurrentShader].prog, CgFShaders[CurrentShader].cxadd, sca, CgFShaders[CurrentShader].offset); } void SetTexgenState (SInt stageIndex, const FillTexture& fill) { const GRenderer::Matrix& m = fill.TextureMatrix; Float p[4] = { 0, 0, 0, 0 }; GASSERT(stageIndex < 2); p[0] = m.M_[0][0]; p[1] = m.M_[0][1]; p[3] = m.M_[0][2]; cellGcmSetVertexProgramParameter (GcmCtx, CgVShaders[CurVProgram].texgenx[stageIndex], p); p[0] = m.M_[1][0]; p[1] = m.M_[1][1]; p[3] = m.M_[1][2]; cellGcmSetVertexProgramParameter (GcmCtx, CgVShaders[CurVProgram].texgeny[stageIndex], p); } void SetPixelShader (PixelShaderType ps) { CurrentShader = ps; } void TransferShaders() { cellGcmSetUpdateFragmentProgramParameter(GcmCtx, CgFShaders[CurrentShader].offset); cellGcmSetFragmentProgram(GcmCtx, CgFShaders[CurrentShader].prog, CgFShaders[CurrentShader].offset); cellGcmSetVertexProgram(GcmCtx, CgVShaders[CurVProgram].prog, CgVShaders[CurVProgram].uc); } bool SetVertexProgram (VertexFormat vf, SInt numtex) { switch (vf) { case Vertex_XY16iCF32: case Vertex_XY16iC32: CurVProgram = (numtex == 2) ? 1 : 0; return 1; default: CurVProgram = numtex ? numtex-1 : 2; return 1; } } class GFxFillStyle { public: enum FillMode { FM_None, FM_Color, FM_Bitmap, FM_Gouraud }; FillMode Mode; GouraudFillType GouraudType; GColor Color; FillTexture Fill, Fill2; GRenderer::Cxform BitmapColorTransform; bool HasNonzeroAdditiveCxform; GFxFillStyle() { Mode = FM_None; HasNonzeroAdditiveCxform = false; } // Push our style into OpenGL. void Apply(GRendererPS3Impl *prenderer) const { GASSERT(Mode != FM_None); if (Mode == FM_Color) { prenderer->SetVertexProgram(prenderer->VertexFmt, 0); prenderer->SetPixelShader(PS_Solid); prenderer->ApplyColor(Color); if ((Color.GetAlpha() == 0xFF) && (prenderer->BlendMode <= Blend_Normal)) cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_FALSE); else cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_TRUE); } else if (Mode == FM_Bitmap) { if (Fill.pTexture == NULL) { prenderer->SetVertexProgram(prenderer->VertexFmt, 0); prenderer->SetPixelShader(PS_Solid); prenderer->ApplyColor(Color); if ((Color.GetAlpha() == 0xFF) && (prenderer->BlendMode <= Blend_Normal)) cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_FALSE); else cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_TRUE); } else { prenderer->SetVertexProgram(prenderer->VertexFmt, 1); cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_TRUE); PixelShaderType shader = PS_CxformTexture; //if (((GTexturePS3Impl*)Fill.pTexture)->IsYUVTexture()) // shader = PS_CxformTextureYUV; if (prenderer->BlendMode == Blend_Multiply || prenderer->BlendMode == Blend_Darken) shader = (PixelShaderType)(shader + (PS_CxformTextureMultiply - PS_CxformTexture)); prenderer->SetPixelShader(shader); prenderer->ApplyFillTexture(Fill, 0); prenderer->ApplyPShaderCxform(BitmapColorTransform); } } else if (Mode == FM_Gouraud) { PixelShaderType shader = PS_None; prenderer->SetVertexProgram(prenderer->VertexFmt, GouraudType == GFill_2Texture || GouraudType == GFill_2TextureColor ? 2 : 1); // No texture: generate color-shaded triangles. if (Fill.pTexture == NULL) { cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_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_CxformGouraudNoAddAlpha; } else { shader = PS_CxformGouraud; } } // We have a textured or multi-textured gouraud case. else { cellGcmSetBlendEnable(prenderer->GcmCtx, CELL_GCM_TRUE); if ((GouraudType == GFill_1TextureColor) || (GouraudType == GFill_1Texture)) shader = PS_CxformGouraudTexture; else shader = PS_Cxform2Texture; } if ((prenderer->BlendMode == Blend_Multiply) || (prenderer->BlendMode == Blend_Darken) ) { shader = (PixelShaderType)(shader + (PS_CxformGouraudMultiply - PS_CxformGouraud)); // For indexing to work, these should hold: GCOMPILER_ASSERT( (PS_Cxform2Texture - PS_CxformGouraud) == (PS_CxformMultiply2Texture - PS_CxformGouraudMultiply)); GCOMPILER_ASSERT( (PS_CxformGouraudMultiply - PS_CxformGouraud) == (PS_Cxform2Texture - PS_CxformGouraud + 1) ); } prenderer->SetPixelShader(shader); prenderer->ApplyPShaderCxform(BitmapColorTransform); if (Fill.pTexture) { prenderer->ApplyFillTexture(Fill, 0); prenderer->SetTexgenState (0, Fill); if (Fill2.pTexture && (GouraudType == GFill_2TextureColor) || (GouraudType == GFill_2Texture)) { prenderer->ApplyFillTexture(Fill2, 1); prenderer->SetTexgenState (1, Fill2); } } } } void Disable() { Mode = FM_None; Fill.pTexture = 0; } void SetColor(GColor color) { Mode = FM_Color; Color = color; } void SetCxform(const Cxform& colorTransform) { BitmapColorTransform = colorTransform; if ( BitmapColorTransform.M_[0][1] > 1.0f || BitmapColorTransform.M_[1][1] > 1.0f || BitmapColorTransform.M_[2][1] > 1.0f || BitmapColorTransform.M_[3][1] > 1.0f ) HasNonzeroAdditiveCxform = true; else HasNonzeroAdditiveCxform = false; } 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; } }; // 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. GTexturePS3* CreateTexture() { GLock::Locker gruad(&TexturesLock); return new GTexturePS3Impl(this); } GTexturePS3* CreateTextureYUV() { GLock::Locker gruad(&TexturesLock); return new GTexturePS3ImplYUV(this); } GTexturePS3* CreateDepthStencilBuffer() { GLock::Locker gruad(&TexturesLock); return new GTexturePS3Impl(this); } GRenderTargetPS3Impl* CreateRenderTarget() { return new GRenderTargetPS3Impl(this); } // Helper function to query renderer capabilities. bool GetRenderCaps(RenderCaps *pcaps) { if (!ModeSet) { GFC_DEBUG_WARNING(1, "GRendererPS3::GetRenderCaps fails - video mode not set"); return 0; } pcaps->CapBits = Cap_Index16 | Cap_Index32 | Cap_NestedMasks; pcaps->BlendModes = (1<VertexFormats= (1<CapBits |= Cap_CxformAdd | Cap_FillGouraud | Cap_FillGouraudTex | Cap_TexNonPower2 | Cap_TexNonPower2Wrap | Cap_TexNonPower2Mip; pcaps->CapBits |= Cap_RenderTargets | Cap_RenderTargetNonPow2; pcaps->CapBits |= Cap_Filter_Blurs | Cap_Filter_ColorMatrix; if (VMCFlags & VMConfig_FastTexUpdate) pcaps->CapBits |= Cap_NoTexOverwrite; pcaps->MaxTextureSize = 4096; return 1; } void SetDisplayRenderTarget(GRenderTarget* prt, bool setstate = 1) { GASSERT(RenderTargetStack.GetSize() == 0); pCurRenderTarget = (GRenderTargetPS3Impl*)prt; #ifndef GFC_NO_3D if (setstate) pCurRenderTarget->SetRenderTarget(S3DDisplay); #else if (setstate) pCurRenderTarget->SetRenderTarget(GRenderer::StereoCenter); #endif } void PushRenderTarget(const GRectF& FrameRect, GRenderTarget* prt) { RenderTargetStack.PushBack(RTState(pCurRenderTarget,BaseMatrix, #ifndef GFC_NO_3D ViewMatrix, ProjMatrix, pWorldMatrix, #else GMatrix3D(), GMatrix3D(), 0, #endif ViewRect,RenderMode,StencilCounter,StencilEnabled)); pCurRenderTarget = (GRenderTargetPS3Impl*)prt; float dx = FrameRect.Width(); float dy = FrameRect.Height(); if (dx < 1) { dx = 1; } if (dy < 1) { dy = 1; } MakeOrtho(BaseMatrix, FrameRect.Left, FrameRect.Right, FrameRect.Top, FrameRect.Bottom, -1, 1); ViewRect = GViewport(pCurRenderTarget->Surf.width, pCurRenderTarget->Surf.height, 0,0,pCurRenderTarget->TargetWidth, pCurRenderTarget->TargetHeight); RenderMode = GViewport::View_RenderTextureAlpha; #ifndef GFC_NO_3D // set 3D view GMatrix3D matView, matPersp; MakeViewAndPersp3D(FrameRect, matView, matPersp, DEFAULT_FLASH_FOV); SetView3D(matView); SetPerspective3D(matPersp); SetWorld3D(0); #endif pCurRenderTarget->SetRenderTarget(S3DDisplay); float scale[4], offset[4]; scale[0] = ViewRect.Width * 0.5f; scale[1] = -ViewRect.Height * 0.5f; offset[0] = ViewRect.Left + ViewRect.Width * 0.5f; offset[1] = ViewRect.Top + ViewRect.Height * 0.5f; scale[2] = 0.5; offset[2] = 0.5; scale[3] = offset[3] = 0; cellGcmSetViewport(GcmCtx, ViewRect.Left, ViewRect.Top, ViewRect.Width, ViewRect.Height, 0, 1, scale, offset); if (ViewRect.Flags & GViewport::View_UseScissorRect) cellGcmSetScissor(GcmCtx, ViewRect.ScissorLeft, ViewRect.BufferHeight-ViewRect.ScissorTop-ViewRect.ScissorHeight, ViewRect.ScissorWidth, ViewRect.ScissorHeight); else cellGcmSetScissor(GcmCtx, 0,0, 4096,4096); if (pCurRenderTarget->pTexture) { cellGcmSetClearColor(GcmCtx, 0); cellGcmSetClearDepthStencil(GcmCtx, 0); UInt32 clears = CELL_GCM_CLEAR_R|CELL_GCM_CLEAR_G|CELL_GCM_CLEAR_B|CELL_GCM_CLEAR_A; if (pCurRenderTarget->pStencilTexture) clears |= CELL_GCM_CLEAR_S|CELL_GCM_CLEAR_Z; cellGcmSetClearSurface(GcmCtx, clears); } StencilEnabled = 0; cellGcmSetStencilTestEnable(GcmCtx, CELL_GCM_FALSE); ApplyBlendMode(BlendMode); } void PopRenderTarget() { if (pCurRenderTarget->IsTemp) pCurRenderTarget->pStencilTexture = 0; RTState rts = RenderTargetStack.Back(); RenderTargetStack.PopBack(); pCurRenderTarget = rts.pRT; BaseMatrix = 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 pCurRenderTarget->SetRenderTarget(S3DDisplay); float scale[4], offset[4]; scale[0] = ViewRect.Width * 0.5f; scale[1] = ViewRect.Height * 0.5f; offset[0] = ViewRect.Left + ViewRect.Width * 0.5f; offset[1] = ViewRect.Top + ViewRect.Height * 0.5f; scale[2] = 0.5; offset[2] = 0.5; scale[3] = offset[3] = 0; if (!(ViewRect.Flags & GViewport::View_IsRenderTexture)) scale[1] = -scale[1]; cellGcmSetViewport(GcmCtx, ViewRect.Left, ViewRect.Top, ViewRect.Width, ViewRect.Height, 0, 1, scale, offset); if (ViewRect.Flags & GViewport::View_UseScissorRect) cellGcmSetScissor(GcmCtx, ViewRect.ScissorLeft, ViewRect.BufferHeight-ViewRect.ScissorTop-ViewRect.ScissorHeight, ViewRect.ScissorWidth, ViewRect.ScissorHeight); else cellGcmSetScissor(GcmCtx, 0,0, 4096,4096); cellGcmSetStencilTestEnable(GcmCtx, StencilEnabled ? CELL_GCM_TRUE : CELL_GCM_FALSE); if (StencilEnabled) { cellGcmSetStencilFunc(GcmCtx, CELL_GCM_EQUAL, StencilCounter, 0xFF); cellGcmSetStencilOp(GcmCtx, CELL_GCM_KEEP, CELL_GCM_KEEP, CELL_GCM_KEEP); } ApplyBlendMode(BlendMode); } GTexturePS3Impl* PushTempRenderTarget(const GRectF& FrameRect, UInt inw, UInt inh, bool wantStencil = 0) { GRenderTargetPS3Impl* pRT = 0; SInt w = 1, h = 1; bool needsinit = false; for (UInt i = 0; i < TempRenderTargets.GetSize(); i++) if (TempRenderTargets[i]->pTexture->GetRefCount() <= 1 && TempRenderTargets[i]->pTexture->Tex.width >= SInt(inw) && TempRenderTargets[i]->pTexture->Tex.height >= SInt(inh)) { pRT = TempRenderTargets[i]; goto dostencil; } while (w < SInt(inw)) w <<= 1; while (h < SInt(inh)) h <<= 1; for (UInt i = 0; i < TempRenderTargets.GetSize(); i++) if (TempRenderTargets[i]->pTexture->GetRefCount() <= 1) { pRT = TempRenderTargets[i]; needsinit = true; goto dostencil; } { GTexture* prtt = CreateTexture(); pRT = (GRenderTargetPS3Impl *)CreateRenderTarget(); pRT->IsTemp = true; pRT->pTexture = (GTexturePS3Impl*) prtt; // will be properly initialized later TempRenderTargets.PushBack(*pRT); prtt->Release(); needsinit = true; } dostencil: GTexturePS3Impl* pDepthStencil = 0; if (w < pRT->pTexture->Tex.width) w = pRT->pTexture->Tex.width; if (h < pRT->pTexture->Tex.height) h = pRT->pTexture->Tex.height; GASSERT(!pRT->pStencilTexture); if (wantStencil && (pRT->pStencilTexture.GetPtr() == 0 || pRT->pStencilTexture->GetRefCount() > 1)) { for (UInt i = 0; i < TempStencilBuffers.GetSize(); i++) { if (TempStencilBuffers[i]->GetRefCount() <= 1 && TempStencilBuffers[i]->Tex.width >= SInt(inw) && TempStencilBuffers[i]->Tex.height >= SInt(inh)) { pDepthStencil = TempStencilBuffers[i]; break; } } if (!pDepthStencil) { for (UInt i = 0; i < TempStencilBuffers.GetSize(); i++) if (TempStencilBuffers[i]->GetRefCount() <= 1) { pDepthStencil = TempStencilBuffers[i]; pDepthStencil->InitDynamicTexture(w, h, GImage::Image_DepthStencil, 0, GTexture::Usage_RenderTarget); break; } if (!pDepthStencil) { pDepthStencil = (GTexturePS3Impl*) CreateDepthStencilBuffer(); pDepthStencil->InitDynamicTexture(w, h, GImage::Image_DepthStencil, 0, GTexture::Usage_RenderTarget); TempStencilBuffers.PushBack(*pDepthStencil); } } needsinit = 1; } if (needsinit) { if (w != pRT->pTexture->Tex.width || h != pRT->pTexture->Tex.height) pRT->pTexture->InitDynamicTexture(w, h, GImage::Image_ARGB_8888, 0, GTexture::Usage_RenderTarget); // should use handlers instead pRT->InitRenderTarget(pRT->pTexture, pDepthStencil, pDepthStencil); } pRT->TargetWidth = inw; pRT->TargetHeight = inh; PushRenderTarget(FrameRect, pRT); return pRT->pTexture; } void ReleaseTempRenderTargets(UInt keepArea) { GArray > NewTempRenderTargets; GArray > NewTempStencilBuffers; UInt stencilArea = keepArea; for (UInt i = 0; i < TempRenderTargets.GetSize(); i++) if (TempRenderTargets[i]->pTexture->GetRefCount() > 1 || UInt(TempRenderTargets[i]->pTexture->Tex.width * TempRenderTargets[i]->pTexture->Tex.height) <= keepArea) { NewTempRenderTargets.PushBack(TempRenderTargets[i]); keepArea -= TempRenderTargets[i]->pTexture->Tex.width * TempRenderTargets[i]->pTexture->Tex.height; } TempRenderTargets.Clear(); TempRenderTargets.Append(NewTempRenderTargets.GetDataPtr(), NewTempRenderTargets.GetSize()); for (UInt i = 0; i < TempStencilBuffers.GetSize(); i++) if (TempStencilBuffers[i]->GetRefCount() > 1 || UInt(TempStencilBuffers[i]->Tex.width * TempStencilBuffers[i]->Tex.height) <= stencilArea) { NewTempStencilBuffers.PushBack(TempStencilBuffers[i]); stencilArea -= TempStencilBuffers[i]->Tex.width * TempStencilBuffers[i]->Tex.height; } TempStencilBuffers.Clear(); TempStencilBuffers.Append(NewTempStencilBuffers.GetDataPtr(), NewTempStencilBuffers.GetSize()); } void DrawColorMatrixRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& destrect, const Float *matrix, bool islast) { GUNUSED(islast); GTexturePS3Impl* psrc = (GTexturePS3Impl*) psrcin; CgShader* pShader = &FilterShaders[(BlendMode == Blend_Multiply || BlendMode == Blend_Darken) ? FS2_FCMatrixMul : FS2_FCMatrix]; GRectF srcrect = GRectF(insrcrect.Left * 1.0f/psrc->Tex.width, insrcrect.Top * 1.0f/psrc->Tex.height, insrcrect.Right * 1.0f/psrc->Tex.width, insrcrect.Bottom * 1.0f/psrc->Tex.height); SetVertexProgram(Vertex_None, 0); ApplyMatrix(CurrentMatrix); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, CurMatrix); cellGcmSetFragmentProgramParameter (GcmCtx, pShader->prog, pShader->uniforms[FSU_cxmul], matrix, pShader->offset); cellGcmSetFragmentProgramParameter (GcmCtx, pShader->prog, pShader->uniforms[FSU_cxadd], matrix+16, pShader->offset); ApplyBlendMode(BlendMode, true, true); psrc->Bind(-1, Wrap_Clamp, Sample_Linear, 0); cellGcmSetUpdateFragmentProgramParameter(GcmCtx, pShader->offset); cellGcmSetFragmentProgram(GcmCtx, pShader->prog, pShader->offset); cellGcmSetVertexProgram(GcmCtx, CgVShaders[CurVProgram].prog, CgVShaders[CurVProgram].uc); Float vertices[] = {destrect.Left, destrect.Top, srcrect.Left, srcrect.Top, destrect.Right, destrect.Top, srcrect.Right, srcrect.Top, destrect.Right, destrect.Bottom, srcrect.Right, srcrect.Bottom, destrect.Left, destrect.Bottom, srcrect.Left, srcrect.Bottom}; cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 16, 2, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].tci, 8, 16, 2, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetInvalidateVertexCache(GcmCtx); cellGcmSetDrawInlineArray(GcmCtx, CELL_GCM_PRIMITIVE_QUADS, 16, vertices); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].tci, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); ApplyBlendMode(BlendMode); } UInt CheckFilterSupport(const BlurFilterParams& params) { UInt flags = FilterSupport_Ok; if (params.Passes > 1 || (params.BlurX * params.BlurY > 32)) flags |= FilterSupport_Multipass; return flags; } inline void SetUniform(FragShaderType2 shader, FragShader2Uniform var, const Float *val, UInt size) { GUNUSED(size); cellGcmSetFragmentProgramParameter (GcmCtx, FilterShaders[shader].prog, FilterShaders[shader].uniforms[var], val, FilterShaders[shader].offset); } inline void SetUniform(FragShaderType2 shader, FragShader2Uniform var, Float x, Float y=0, Float z=0, Float w=1) { const Float val[] = {x,y,z,w}; cellGcmSetFragmentProgramParameter (GcmCtx, FilterShaders[shader].prog, FilterShaders[shader].uniforms[var], val, FilterShaders[shader].offset); } void DrawBlurRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& indestrect, const BlurFilterParams& params, bool islast) { GUNUSED(islast); GPtr psrcinRef = (GTexturePS3Impl*)psrcin; GPtr psrc = (GTexturePS3Impl*)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; } UInt bufWidth = (UInt)ceilf(insrcrect.Width()); UInt bufHeight = (UInt)ceilf(insrcrect.Height()); UInt last = n-1; SetVertexProgram(Vertex_None, 0); for (UInt i = 0; i < n; i++) { UInt passi = (i == n-1) ? 2 : (i&1); const CgShader *pShader = &FilterShaders[passis[passi]]; const BlurFilterParams& pparams = pass[passi]; GTexturePS3Impl* 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->Tex.width, insrcrect.Top * 1.0f/psrc->Tex.height, insrcrect.Right * 1.0f/psrc->Tex.width, insrcrect.Bottom * 1.0f/psrc->Tex.height); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, CurMatrix); if (i < last) { cellGcmSetBlendFunc(GcmCtx, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA); cellGcmSetBlendEquation(GcmCtx, CELL_GCM_FUNC_ADD, CELL_GCM_FUNC_ADD); cellGcmSetBlendEnable(GcmCtx, CELL_GCM_TRUE); } else ApplyBlendMode(BlendMode, true, true); const float mult = 1.0f / 255.0f; if (pShader->uniforms[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(passis[passi], FSU_cxmul, cxformData, 4); SetUniform(passis[passi], FSU_cxadd, cxformData+4, 4); } else { const float add[] = {0,0,0,0}; const float mul[] = {1,1,1,1}; SetUniform(passis[passi], FSU_cxmul, mul, 4); SetUniform(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(passis[passi], FSU_fsize, fsize, 4); SetUniform(passis[passi], FSU_texscale, pparams.BlurX > 1 ? 1.0f/psrc->Tex.width : 0, pparams.BlurY > 1 ? 1.0f/psrc->Tex.height : 0); } else { const float fsize[] = {SizeX, SizeY, 0, 1.0f/((SizeX*2+1)*(SizeY*2+1))}; SetUniform(passis[passi], FSU_fsize, fsize, 4); SetUniform(passis[passi], FSU_texscale, 1.0f/psrc->Tex.width, 1.0f/psrc->Tex.height); } if (pShader->uniforms[FSU_offset]) { SetUniform(passis[passi], FSU_offset, -params.Offset.x, -params.Offset.y); if (pShader->uniforms[FSU_srctexscale]) SetUniform(passis[passi], FSU_srctexscale, psrc->Tex.width/Float(psrcinRef->Tex.width), psrc->Tex.height/Float(psrcinRef->Tex.height)); } if (pShader->uniforms[FSU_scolor]) { SetUniform(passis[passi], FSU_scolor, params.Color.GetRed() * mult, params.Color.GetGreen() * mult, params.Color.GetBlue() * mult, params.Color.GetAlpha() * mult); if (pShader->uniforms[FSU_scolor2]) SetUniform(passis[passi], FSU_scolor2, params.Color2.GetRed() * mult, params.Color2.GetGreen() * mult, params.Color2.GetBlue() * mult, params.Color2.GetAlpha() * mult); } psrc->Bind(-pShader->texi[0]-1, Wrap_Clamp, Sample_Linear, 0); if (pShader->tex[1]) ((GTexturePS3Impl*)psrcin)->Bind(-pShader->texi[1]-1, Wrap_Clamp, Sample_Linear, 0); cellGcmSetUpdateFragmentProgramParameter(GcmCtx, FilterShaders[passis[passi]].offset); cellGcmSetFragmentProgram(GcmCtx, FilterShaders[passis[passi]].prog, FilterShaders[passis[passi]].offset); cellGcmSetVertexProgram(GcmCtx, CgVShaders[CurVProgram].prog, CgVShaders[CurVProgram].uc); Float vertices[] = {destrect.Left, destrect.Top, srcrect.Left, srcrect.Top, destrect.Right, destrect.Top, srcrect.Right, srcrect.Top, destrect.Right, destrect.Bottom, srcrect.Right, srcrect.Bottom, destrect.Left, destrect.Bottom, srcrect.Left, srcrect.Bottom}; cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 16, 2, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].tci, 8, 16, 2, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetInvalidateVertexCache(GcmCtx); cellGcmSetDrawInlineArray(GcmCtx, CELL_GCM_PRIMITIVE_QUADS, 16, vertices); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].tci, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); if (i != n - 1) { PopRenderTarget(); psrc = pnextsrc; } } ApplyBlendMode(BlendMode); FilterCnt.AddCount(n); RenderStats.Filters += n; } // 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 &viewport, Float x0, Float x1, Float y0, Float y1) { GFC_DEBUG_ERROR(!pCurRenderTarget, "No render target set"); VA_base = (UByte*) Allocator->Map(VA_memobj); VA_location = Allocator->GetLocation(VA_memobj); VA_offset = Allocator->GetOffset(VA_memobj); RenderMode = (viewport.Flags & GViewport::View_AlphaComposite); DisplayWidth = fabsf(x1 - x0); DisplayHeight = fabsf(y1 - y0); float scale[4], offset[4]; scale[0] = viewport.Width * 0.5f; scale[1] = viewport.Height * 0.5f; offset[0] = viewport.Left + viewport.Width * 0.5f; offset[1] = viewport.Top + viewport.Height * 0.5f; scale[2] = 0.5; offset[2] = 0.5; scale[3] = offset[3] = 0; if (!(viewport.Flags & GViewport::View_IsRenderTexture)) scale[1] = -scale[1]; cellGcmSetViewport(GcmCtx, viewport.Left, viewport.Top, viewport.Width, viewport.Height, 0, 1, scale, offset); ViewRect = viewport; if (viewport.Flags & GViewport::View_UseScissorRect) cellGcmSetScissor(GcmCtx, viewport.ScissorLeft, viewport.BufferHeight-viewport.ScissorTop-viewport.ScissorHeight, viewport.ScissorWidth, viewport.ScissorHeight); else cellGcmSetScissor(GcmCtx, 0,0, 4096,4096); MakeOrtho(BaseMatrix, x0, x1, y0, y1, -1, 1); memcpy(CurMatrix, BaseMatrix, sizeof(Float) * 16); #ifndef GFC_NO_3D pWorldMatrix = 0; #endif cellGcmSetCullFaceEnable(GcmCtx, CELL_GCM_FALSE); cellGcmSetZMinMaxControl(GcmCtx, CELL_GCM_TRUE, CELL_GCM_FALSE, CELL_GCM_FALSE); cellGcmSetDepthTestEnable(GcmCtx, CELL_GCM_FALSE); cellGcmSetStencilTestEnable(GcmCtx, CELL_GCM_FALSE); cellGcmSetAlphaTestEnable(GcmCtx, CELL_GCM_FALSE); cellGcmSetBlendEquation(GcmCtx, CELL_GCM_FUNC_ADD, CELL_GCM_FUNC_ADD); cellGcmSetBlendFunc(GcmCtx, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA); for (int i = 0; i < 16; i++) { cellGcmSetVertexDataArray(GcmCtx, i, 0,0,0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } // Sample Mode SampleMode = Sample_Linear; BlendMode = Blend_Normal; // Stencil counter starts out at 0. StencilCounter = 0; StencilEnabled = 0; // Clear the background, if background color has alpha > 0. if (backgroundColor.GetAlpha() > 0) { // Draw a big quad. if (backgroundColor.GetAlpha() == 0xFF) cellGcmSetBlendEnable(GcmCtx, CELL_GCM_FALSE); else cellGcmSetBlendEnable(GcmCtx, CELL_GCM_TRUE); SetVertexProgram(Vertex_None, 0); CurrentShader = PS_Solid; ApplyColor(backgroundColor); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, BaseMatrix); TransferShaders(); Float bgVertBuffer[8]; bgVertBuffer[0] = x0; bgVertBuffer[1] = y0; bgVertBuffer[2] = x1; bgVertBuffer[3] = y0; bgVertBuffer[4] = x1; bgVertBuffer[5] = y1; bgVertBuffer[6] = x0; bgVertBuffer[7] = y1; cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(Float)*2, 2, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetInvalidateVertexCache(GcmCtx); cellGcmSetDrawInlineArray(GcmCtx, CELL_GCM_PRIMITIVE_QUADS, 8, bgVertBuffer); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } cellGcmSetBlendEnable(GcmCtx, CELL_GCM_TRUE); } // Clean up after rendering a frame. Client program is still // responsible for calling glSwapBuffers() or whatever. void EndDisplay() { Allocator->Unmap(VA_memobj, VA_base); VA_base = 0; } // Set the current transform for mesh & line-strip rendering. void SetMatrix(const GRenderer::Matrix& m) { CurrentMatrix = m; } void SetUserMatrix(const GRenderer::Matrix& m) { UserMatrix = m; #ifndef GFC_NO_3D UVPMatricesChanged = 1; #endif } // Set the current color transform for mesh & line-strip rendering. void SetCxform(const GRenderer::Cxform& cx) { CurrentCxform = cx; } struct BlendModeDesc { UInt32 op, src, dest; }; struct BlendModeDescAlpha { UInt32 op, srcc, srca, destc, desta; }; void ApplyBlendMode(BlendType mode, bool forceAc = 0, bool sourceAc = 0) { static BlendModeDesc modes[15] = { { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // None { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Normal { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Layer { CELL_GCM_FUNC_ADD, CELL_GCM_DST_COLOR, CELL_GCM_ZERO }, // Multiply // (For multiply, should src be pre-multiplied by its inverse alpha?) { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Screen *?? { CELL_GCM_MAX, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE }, // Lighten { CELL_GCM_MIN, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE }, // Darken { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Difference *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE }, // Add { CELL_GCM_FUNC_REVERSE_SUBTRACT, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE }, // Subtract { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Invert *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Alpha *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Erase *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Overlay *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // HardLight *?? }; // Blending into alpha textures with premultiplied colors static BlendModeDescAlpha acmodes[30] = { { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // None { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Normal { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Layer { CELL_GCM_FUNC_ADD, CELL_GCM_DST_COLOR, CELL_GCM_DST_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ZERO }, // Multiply { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Screen *?? { CELL_GCM_MAX, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE }, // Lighten *?? { CELL_GCM_MIN, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE }, // Darken *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Difference { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ZERO, CELL_GCM_ONE, CELL_GCM_ONE }, // Add { CELL_GCM_FUNC_REVERSE_SUBTRACT, CELL_GCM_SRC_ALPHA, CELL_GCM_ZERO, CELL_GCM_ONE, CELL_GCM_ONE }, // Subtract { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Invert *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Alpha *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Erase *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // Overlay *?? { CELL_GCM_FUNC_ADD, CELL_GCM_SRC_ALPHA, CELL_GCM_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA, CELL_GCM_ONE_MINUS_SRC_ALPHA }, // HardLight *?? }; // For debug build GASSERT(((UInt) mode) < 15); // For release if (((UInt) mode) >= 15) mode = Blend_None; if ((RenderMode & GViewport::View_AlphaComposite) || forceAc) { if (sourceAc && acmodes[mode].srcc == CELL_GCM_SRC_ALPHA) cellGcmSetBlendFunc(GcmCtx, CELL_GCM_ONE, acmodes[mode].destc, acmodes[mode].srca, acmodes[mode].desta); else cellGcmSetBlendFunc(GcmCtx, acmodes[mode].srcc, acmodes[mode].destc, acmodes[mode].srca, acmodes[mode].desta); cellGcmSetBlendEquation(GcmCtx, acmodes[mode].op, acmodes[mode].op); } else { if (sourceAc && modes[mode].src == CELL_GCM_SRC_ALPHA) cellGcmSetBlendFunc(GcmCtx, CELL_GCM_ONE, modes[mode].dest, modes[mode].src, modes[mode].dest); else cellGcmSetBlendFunc(GcmCtx, modes[mode].src, modes[mode].dest, modes[mode].src, modes[mode].dest); cellGcmSetBlendEquation(GcmCtx, modes[mode].op, modes[mode].op); } } // 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, "GRendererGL::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 SetStereoDisplay(StereoDisplay sDisplay, bool setstate) { S3DDisplay = sDisplay; UVPMatricesChanged = 1; if (setstate) pCurRenderTarget->SetRenderTarget(S3DDisplay); } virtual void SetWorld3D(const GMatrix3D *pWorldMatIn) { pWorldMatrix = pWorldMatIn; } 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 void ApplyMatrix(const GRenderer::Matrix& m1) { #ifndef GFC_NO_3D if (pWorldMatrix) { if (UVPMatricesChanged) { UpdateStereoProjection(); UVPMatricesChanged = 0; UVPMatrix = UserMatrix; UVPMatrix.Append(ViewMatrix); UVPMatrix.Append(ProjMatrix); if (RenderTargetStack.GetSize()) { GMatrix2D vm1(BaseMatrix.M_[0][0], BaseMatrix.M_[0][1], BaseMatrix.M_[1][0], BaseMatrix.M_[1][1], BaseMatrix.M_[0][3], BaseMatrix.M_[1][3]); GMatrix2D vm2(RenderTargetStack[0].ViewMatrix.M_[0][0], RenderTargetStack[0].ViewMatrix.M_[0][1], RenderTargetStack[0].ViewMatrix.M_[1][0], RenderTargetStack[0].ViewMatrix.M_[1][1], RenderTargetStack[0].ViewMatrix.M_[0][3], RenderTargetStack[0].ViewMatrix.M_[1][3]); Adjust3DMatrixForRT(UVPMatrix, ProjMatrix, vm2, vm1); } } CurMatrix = GMatrix3D(m1); CurMatrix.Append(*pWorldMatrix); CurMatrix.Append(UVPMatrix); CurMatrix.Transpose(); } else #endif { GRenderer::Matrix m(UserMatrix); m.Prepend(m1); GMatrix3D m3d; Float* mat = m3d; mat[0] = m.M_[0][0]; mat[4] = m.M_[1][0]; mat[1] = m.M_[0][1]; mat[5] = m.M_[1][1]; mat[10] = 1; mat[3] = m.M_[0][2]; mat[7] = m.M_[1][2]; mat[15] = 1; CurMatrix.MultiplyMatrix(BaseMatrix, m3d); } } // Set the given color. void ApplyColor(const GColor c) { float args[4]; if ((BlendMode == Blend_Multiply) || (BlendMode == Blend_Darken)) { const float mult = 1.0f / 255.0f; const float alpha = c.GetAlpha() * mult; args[3] = gflerp(1.0f, c.GetRed() * mult, alpha); args[2] = gflerp(1.0f, c.GetGreen() * mult, alpha); args[1] = gflerp(1.0f, c.GetBlue() * mult, alpha); args[0] = alpha; cellGcmSetVertexData4f(GcmCtx, CgVShaders[CurVProgram].colori, args); } else { args[3] = c.GetRed() * (1.0f / 255.0f); args[2] = c.GetGreen() * (1.0f / 255.0f); args[1] = c.GetBlue() * (1.0f / 255.0f); args[0] = c.GetAlpha() * (1.0f / 255.0f); cellGcmSetVertexData4f(GcmCtx, CgVShaders[CurVProgram].colori, args); } } // 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; VertexArray = 0; UInt32 size; switch (vf) { case Vertex_XY16i: size = 2 * sizeof(short); break; case Vertex_XY32f: size = 2 * sizeof(float); break; case Vertex_XY16iC32: size = 2 * sizeof(short) + 4; break; case Vertex_XY16iCF32: size = 2 * sizeof(short) + 8; break; case -1: size = sizeof(GGlyphVertex); break; default: return; } if (VA_size < size * numVertices || numVertices == 0) return; cellGcmSetWriteBackEndLabel(GcmCtx, VA_label, VA_lastv); cellGcmFlush(GcmCtx); // Two areas of the vertex buffer are available for writing: // after any previous writes (> VA_end) // up to the last rendered vertices (< pos) UInt32 lv = *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)); UInt32 pos = lv & ((1 << VA_shift) - 1); UInt32 lastv = VA_lastv & ((1 << VA_shift) - 1); UInt32 count = lv >> VA_shift; if (VA_end > VA_size) pos = 0; if (VA_end + numVertices * size <= VA_size && ((pos <= lastv && count == VA_count) || pos >= VA_end + numVertices * size)) { VA_start = VA_end; } else if (numVertices * size <= pos && pos <= lastv && count == VA_count) { VA_start = 0; VA_count = (VA_count + 1) & ((1 << (32-VA_shift)) - 1); } else { while( *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)) != VA_lastv) sys_timer_usleep(10); VA_start = 0; VA_count = 0; *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)) = 0; } if (pvertices) memcpy(VA_base + VA_start, pvertices, numVertices * size); VertexArray = 1; VA_end = VA_start + numVertices * size; VA_lastv = VA_end | ((VA_count << VA_shift) & ~((1 << VA_shift)-1)); // 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; IndexArray = 0; switch(idxf) { case Index_None: IndexFmt = 0; break; default: IndexFmt = idxf; break; } // 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); if (pdata->GetRendererData()) { GBufferNode *pn = (GBufferNode*)pdata->GetRendererData(); Allocator->Free(pn->BufferH); pn->RemoveNode(); delete pn; pdata->SetRendererData(0); } } void DrawIndexedTriList(int baseVertexIndex, int minVertexIndex, int numVertices, int startIndex, int triangleCount) { GUNUSED2(minVertexIndex, numVertices); if (!pVertexData || !pIndexData || !IndexFmt) // Must have vertex data. { GFC_DEBUG_WARNING(!pVertexData, "GRendererPS3::DrawIndexedTriList failed, vertex data not specified"); GFC_DEBUG_WARNING(!pIndexData, "GRendererPS3::DrawIndexedTriList failed, index data not specified"); GFC_DEBUG_WARNING(!IndexFmt, "GRendererPS3::DrawIndexedTriList failed, index buffer format not specified"); return; } // Set up current style. CurrentStyles[FILL_STYLE].Apply(this); ApplyMatrix(CurrentMatrix); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, CurMatrix); TransferShaders(); const void* pindices = (UByte*)(IndexArray ? 0 : pIndexData) + startIndex * ((IndexFmt == Index_16) ? sizeof(UInt16) : sizeof(UInt32)); UInt32 vertexBase = 0; SInt vertexLoc = VA_location; SInt vertexIndex = baseVertexIndex; if (VertexArray) { vertexBase = VA_offset + VA_start; } else { vertexBase = VA_offset; vertexIndex = 0; } // Gouraud colors if (VertexFmt == Vertex_XY16iC32) { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(VertexXY16iC32), 2, CELL_GCM_VERTEX_S32K, vertexLoc, vertexBase + vertexIndex * sizeof(VertexXY16iC32)); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].colori, 0, sizeof(VertexXY16iC32), 4, CELL_GCM_VERTEX_UB, vertexLoc, vertexBase + vertexIndex * sizeof(VertexXY16iC32) + 2 * sizeof(UInt16)); } else if (VertexFmt == Vertex_XY16iCF32) { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(VertexXY16iCF32), 2, CELL_GCM_VERTEX_S32K, vertexLoc, vertexBase + vertexIndex * sizeof(VertexXY16iCF32)); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].colori, 0, sizeof(VertexXY16iCF32), 4, CELL_GCM_VERTEX_UB, vertexLoc, vertexBase + vertexIndex * sizeof(VertexXY16iCF32) + 2 * sizeof(UInt16)); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].factori, 0, sizeof(VertexXY16iCF32), 4, CELL_GCM_VERTEX_UB, vertexLoc, vertexBase + vertexIndex * sizeof(VertexXY16iCF32) + 2 * sizeof(UInt16) + 4); } else { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(SInt16) * 2, 2, CELL_GCM_VERTEX_S32K, vertexLoc, vertexBase + vertexIndex * sizeof(SInt16) * 2); } cellGcmSetInvalidateVertexCache(GcmCtx); if (CurrentStyles[FILL_STYLE].Fill.pTexture) cellGcmSetInvalidateTextureCache(GcmCtx, CELL_GCM_INVALIDATE_TEXTURE); if (!VertexArray) { UInt vsize; if (VertexFmt == Vertex_XY16iC32) vsize = sizeof(VertexXY16iC32); else if (VertexFmt == Vertex_XY16iCF32) vsize = sizeof(VertexXY16iCF32); else vsize = 4; int trianglesPerCall = VA_size / (3 * vsize); for (int j = 0; j < triangleCount; j += trianglesPerCall) { cellGcmSetWriteBackEndLabel(GcmCtx, VA_label, VA_lastv); cellGcmFlush(GcmCtx); while( *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)) != VA_lastv) sys_timer_usleep(10); VA_lastv = 1 + VA_size | (VA_count << VA_shift); VA_count++; int triangles; if (j + trianglesPerCall >= triangleCount) triangles = triangleCount - j; else triangles = trianglesPerCall; if (IndexFmt == Index_16) for (int i = 0; i < 3 * triangles; i++) memcpy(VA_base + i * vsize, ((UByte*)pVertexData) + vsize * (baseVertexIndex + ((UInt16*)pindices)[i+j*3]), vsize); else for (int i = 0; i < 3 * triangles; i++) memcpy(VA_base + i * vsize, ((UByte*)pVertexData) + vsize * (baseVertexIndex + ((UInt32*)pindices)[i+j*3]), vsize); *((volatile UInt32 *)cellGcmGetLabelAddress(VA_label)) = 0; cellGcmSetInvalidateVertexCache(GcmCtx); cellGcmSetDrawArrays(GcmCtx, CELL_GCM_PRIMITIVE_TRIANGLES, 0, 3*triangles); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); } VA_start = 0; VA_end = VA_size + 1; } else if (IndexFmt == Index_16) { if (triangleCount * 6 > MaxCmdSize) { UInt16 *pindex = (UInt16*) pindices; int maxtris = MaxCmdSize / 6; for (int i = 0; i < triangleCount; i += maxtris) { int count = maxtris; if (i + count >= triangleCount) count = triangleCount - i; cellGcmSetDrawInlineIndexArray16(GcmCtx, CELL_GCM_PRIMITIVE_TRIANGLES, 0, 3*count, pindex); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); pindex += 3 * count; } } else { cellGcmSetDrawInlineIndexArray16(GcmCtx, CELL_GCM_PRIMITIVE_TRIANGLES, 0, 3*triangleCount, (UInt16*)pindices); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); } } else if (triangleCount * 12 > MaxCmdSize) { UInt32 *pindex = (UInt32*) pindices; int maxtris = MaxCmdSize / 12; for (int i = 0; i < triangleCount; i += maxtris) { int count = maxtris; if (i + count >= triangleCount) count = triangleCount - i; cellGcmSetDrawInlineIndexArray32(GcmCtx, CELL_GCM_PRIMITIVE_TRIANGLES, 0, 3*count, pindex); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); pindex += 3 * count; } } else { cellGcmSetDrawInlineIndexArray32(GcmCtx, CELL_GCM_PRIMITIVE_TRIANGLES, 0, 3*triangleCount, (UInt32*)pindices); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); } RenderStats.Triangles += triangleCount; TriangleCnt.AddCount(triangleCount); if (VertexFmt == Vertex_XY16iC32) { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].colori, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } else if (VertexFmt == Vertex_XY16iCF32) { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].colori, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].factori, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } else { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } } // Draw the line strip formed by the sequence of Points. void DrawLineStrip(int baseVertexIndex, int lineCount) { if (!pVertexData) // Must have vertex data. { GFC_DEBUG_WARNING(!pVertexData, "GRendererPS3::DrawLineStrip failed, vertex data not specified"); return; } // Set up current style. CurrentStyles[LINE_STYLE].Apply(this); ApplyMatrix(CurrentMatrix); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, CurMatrix); TransferShaders(); UInt32 vertexBase = 0; SInt vertexLoc = VA_location; cellGcmSetInvalidateVertexCache(GcmCtx); if (CurrentStyles[LINE_STYLE].Fill.pTexture) cellGcmSetInvalidateTextureCache(GcmCtx, CELL_GCM_INVALIDATE_TEXTURE); if (VertexArray) { vertexBase = VA_offset + VA_start; cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(SInt16) * 2, 2, CELL_GCM_VERTEX_S32K, vertexLoc, vertexBase + baseVertexIndex * sizeof(SInt16) * 2); cellGcmSetDrawArrays(GcmCtx, CELL_GCM_PRIMITIVE_LINE_STRIP, 0, lineCount + 1); } else { cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(SInt16) * 2, 2, CELL_GCM_VERTEX_S32K, CELL_GCM_LOCATION_LOCAL, 0); cellGcmSetDrawInlineArray(GcmCtx, CELL_GCM_PRIMITIVE_LINE_STRIP, (lineCount + 1), ((UByte*)pVertexData) + baseVertexIndex * sizeof(SInt16) * 2); } RenderStats.Lines += lineCount; RenderStats.Primitives++; LineCnt.AddCount(lineCount); DPLineCnt.AddCount(1); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, 0, 0, CELL_GCM_VERTEX_F, CELL_GCM_LOCATION_LOCAL, 0); } // 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 (!pbitmapList || !pti) return; // 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) ); cellGcmSetBlendEnable(GcmCtx, CELL_GCM_TRUE); GTexturePS3Impl *ptex = (GTexturePS3Impl*)pti; if (ptex->IsYUVTexture() == 1) { if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureYUVMultiply); else SetPixelShader(PS_TextTextureYUV); } else if (ptex->IsYUVTexture() == 2) { if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureYUVAMultiply); else SetPixelShader(PS_TextTextureYUVA); } else if ((ptex->Tex.format & 0x9F) == CELL_GCM_TEXTURE_B8) SetPixelShader(PS_TextTexture); else if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureColorMultiply); else SetPixelShader(PS_TextTextureColor); ApplyPShaderCxform(CurrentCxform); SetVertexProgram(Vertex_None, 0); ptex->Bind(0, Wrap_Clamp, Sample_Linear, !ptex->IsYUVTexture()); // Custom matrix per call. ApplyMatrix(m); cellGcmSetVertexProgramParameter(GcmCtx, CgVShaders[CurVProgram].mvp, CurMatrix); TransferShaders(); SInt ibitmap = 0, ivertex = 0; SetVertexData(0, count * 6, (VertexFormat)-1, 0); if (!VertexArray) { UInt vsize = sizeof(GGlyphVertex); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].posi, 0, sizeof(GGlyphVertex), 2, CELL_GCM_VERTEX_F, VA_location, VA_offset); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].tci, 0, sizeof(GGlyphVertex), 2, CELL_GCM_VERTEX_F, VA_location, VA_offset + sizeof(Float)*2); cellGcmSetVertexDataArray(GcmCtx, CgVShaders[CurVProgram].colori, 0, sizeof(GGlyphVertex), 4, CELL_GCM_VERTEX_UB, VA_location, VA_offset + sizeof(Float)*4); cellGcmSetInvalidateTextureCache(GcmCtx, CELL_GCM_INVALIDATE_TEXTURE); int primsPerCall = VA_size / (6 * vsize); for (int j = 0; j < count; j += primsPerCall) { cellGcmSetWriteBackEndLabel(GcmCtx, VA_label, VA_lastv); cellGcmFlush(GcmCtx); while( *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)) != VA_lastv) sys_timer_usleep(10); VA_start = 0; VA_lastv = 1 + VA_size | (VA_count << VA_shift); VA_count++; int prims; if (j + primsPerCall >= count) prims = count - j; else prims = primsPerCall; for(ivertex = 0, ibitmap = 0; ibitmapAdd(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(); } } void InitFragShader1 (PixelShaderType ps, const void *progin) { CgFShaders[ps].prog = (CGprogram)progin; cellGcmCgInitProgram(CgFShaders[ps].prog); CgFShaders[ps].cxmul = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "cmul"); CgFShaders[ps].cxadd = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "cadd"); CgFShaders[ps].tex[0] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex"); CgFShaders[ps].tex[1] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex1"); if (CgFShaders[ps].tex[0]) { CgFShaders[ps].texi[0] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[0]) - CG_TEXUNIT0); } else { CgFShaders[ps].tex[0] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex_y"); if (CgFShaders[ps].tex[0]) { CgFShaders[ps].tex[2] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex_u"); CgFShaders[ps].tex[3] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex_v"); CgFShaders[ps].tex[4] = cellGcmCgGetNamedParameter (CgFShaders[ps].prog, "tex_a"); CgFShaders[ps].texi[0] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[0]) - CG_TEXUNIT0); CgFShaders[ps].texi[2] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[2]) - CG_TEXUNIT0); CgFShaders[ps].texi[3] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[3]) - CG_TEXUNIT0); if (CgFShaders[ps].tex[4]) CgFShaders[ps].texi[4] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[4]) - CG_TEXUNIT0); } } if (CgFShaders[ps].tex[1]) CgFShaders[ps].texi[1] = (CGresource)(cellGcmCgGetParameterResource(CgFShaders[ps].prog, CgFShaders[ps].tex[1]) - CG_TEXUNIT0); void *uc, *io; size_t ucsize; cellGcmCgGetUCode(CgFShaders[ps].prog, &uc, &ucsize); CgFShaders[ps].memobj = Allocator->Allocate(CELL_GCM_LOCATION_LOCAL, ucsize, 128); CgFShaders[ps].offset = Allocator->GetOffset(CgFShaders[ps].memobj); io = Allocator->Map(CgFShaders[ps].memobj); memcpy(io, uc, ucsize); Allocator->Unmap(CgFShaders[ps].memobj, io); } void InitFragShader (FragShaderType2 ps, const void *progin) { FilterShaders[ps].prog = (CGprogram)progin; cellGcmCgInitProgram(FilterShaders[ps].prog); FilterShaders[ps].uniforms[FSU_cxmul] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "cxmul"); FilterShaders[ps].uniforms[FSU_cxadd] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "cxadd"); FilterShaders[ps].tex[0] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "tex"); if (FilterShaders[ps].tex[0]) { FilterShaders[ps].texi[0] = (CGresource)(cellGcmCgGetParameterResource(FilterShaders[ps].prog, FilterShaders[ps].tex[0]) - CG_TEXUNIT0); FilterShaders[ps].uniforms[FSU_texscale] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "texscale"); FilterShaders[ps].uniforms[FSU_fsize] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "fsize"); FilterShaders[ps].uniforms[FSU_offset] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "offset"); FilterShaders[ps].uniforms[FSU_scolor] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "scolor"); FilterShaders[ps].uniforms[FSU_scolor2] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "scolor2"); FilterShaders[ps].uniforms[FSU_srctexscale] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "srctexscale"); FilterShaders[ps].tex[1] = cellGcmCgGetNamedParameter (FilterShaders[ps].prog, "srctex"); if (FilterShaders[ps].tex[1]) FilterShaders[ps].texi[1] = (CGresource)(cellGcmCgGetParameterResource(FilterShaders[ps].prog, FilterShaders[ps].tex[1]) - CG_TEXUNIT0); } void *uc, *io; size_t ucsize; cellGcmCgGetUCode(FilterShaders[ps].prog, &uc, &ucsize); FilterShaders[ps].memobj = Allocator->Allocate(CELL_GCM_LOCATION_LOCAL, ucsize, 128); FilterShaders[ps].offset = Allocator->GetOffset(FilterShaders[ps].memobj); io = Allocator->Map(FilterShaders[ps].memobj); memcpy(io, uc, ucsize); Allocator->Unmap(FilterShaders[ps].memobj, io); } void InitVertexShader1 (SInt vs, const void *progin) { CgVShaders[vs].prog = (CGprogram) progin; cellGcmCgInitProgram(CgVShaders[vs].prog); CgVShaders[vs].texgenx[0] = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "texgenx0"); CgVShaders[vs].texgeny[0] = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "texgeny0"); CgVShaders[vs].texgenx[1] = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "texgenx1"); CgVShaders[vs].texgeny[1] = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "texgeny1"); CgVShaders[vs].factor = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "ifactor"); CgVShaders[vs].color = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "icolor"); CgVShaders[vs].pos = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "ipos"); CgVShaders[vs].tc = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "itc0"); CgVShaders[vs].mvp = cellGcmCgGetNamedParameter (CgVShaders[vs].prog, "mvp"); size_t ucsize; cellGcmCgGetUCode(CgVShaders[vs].prog, &CgVShaders[vs].uc, &ucsize); CgVShaders[vs].posi = (CGresource)(cellGcmCgGetParameterResource(CgVShaders[vs].prog, CgVShaders[vs].pos) - CG_ATTR0); if (CgVShaders[vs].tc) CgVShaders[vs].tci = (CGresource)(cellGcmCgGetParameterResource(CgVShaders[vs].prog, CgVShaders[vs].tc) - CG_ATTR0); if (CgVShaders[vs].color) CgVShaders[vs].colori = (CGresource)(cellGcmCgGetParameterResource(CgVShaders[vs].prog, CgVShaders[vs].color) - CG_ATTR0); if (CgVShaders[vs].factor) CgVShaders[vs].factori = (CGresource)(cellGcmCgGetParameterResource(CgVShaders[vs].prog, CgVShaders[vs].factor) - CG_ATTR0); } void InitVertexShader (VertexShaderType2 vs, const void *progin) { } virtual bool SetDependentVideoMode(const DependentVideoModeParams& params) { if (Allocator && Allocator != params.Alloc) Clear(); Allocator = params.Alloc; GcmCtx = params.Ctx; MaxCmdSize = params.MaxCmdSize; VMCFlags = params.vmConfigFlags; CurVProgram = 0; CurrentShader = 0; if (params.VertexArraySize < 6 * 32) { GFC_DEBUG_WARNING(1, "Vertex array too small"); return false; } if (VA_size != params.VertexArraySize || VA_memobj == 0) { VA_size = params.VertexArraySize; VA_memobj = Allocator->Allocate(CELL_GCM_LOCATION_MAIN, VA_size, 64); } VA_base = 0; VA_location = Allocator->GetLocation(VA_memobj); VA_start = 0; VA_end = 0; VA_label = params.BaseLabel; VA_lastv = 0; VA_count = 0; for (int i = 31; i; i--) if ((1U << i) > VA_size) VA_shift = i; *((volatile UInt32 *) cellGcmGetLabelAddress(VA_label)) = 0; ModeSet = 1; if (CgFShaders[PS_TextTexture].memobj) return 1; InitVertexShader1(0, &_binary_v_1tex_vpo_start); InitVertexShader1(1, &_binary_v_2tex_vpo_start); InitVertexShader1(2, &_binary_v_text_vpo_start); InitFragShader1 (PS_Solid, &_binary_p_solid_fpo_start); InitFragShader1 (PS_TextTexture, &_binary_p_texta_fpo_start); InitFragShader1 (PS_TextTextureColor, &_binary_p_text_fpo_start); InitFragShader1 (PS_TextTextureColorMultiply, &_binary_p_textmul_fpo_start); InitFragShader1 (PS_CxformTexture, &_binary_p_cxform_fpo_start); InitFragShader1 (PS_CxformTextureMultiply, &_binary_p_cxformmul_fpo_start); InitFragShader1 (PS_CxformGouraud, &_binary_p_cxg_fpo_start); InitFragShader1 (PS_CxformGouraudMultiply, &_binary_p_cxgm_fpo_start); InitFragShader1 (PS_CxformGouraudNoAddAlpha, &_binary_p_cxgna_fpo_start); InitFragShader1 (PS_CxformGouraudMultiplyNoAddAlpha, &_binary_p_cxgmna_fpo_start); InitFragShader1 (PS_CxformGouraudTexture, &_binary_p_cxgt_fpo_start); InitFragShader1 (PS_CxformGouraudMultiplyTexture, &_binary_p_cxgtm_fpo_start); InitFragShader1 (PS_Cxform2Texture, &_binary_p_cxg2t_fpo_start); InitFragShader1 (PS_CxformMultiply2Texture, &_binary_p_cxg2tm_fpo_start); #ifndef GFC_NO_YUV_TEXTURES InitFragShader1 (PS_TextTextureYUV, &_binary_p_cxformyuv_fpo_start); InitFragShader1 (PS_TextTextureYUVMultiply, &_binary_p_cxformyuvmul_fpo_start); InitFragShader1 (PS_TextTextureYUVA, &_binary_p_cxformyuva_fpo_start); InitFragShader1 (PS_TextTextureYUVAMultiply, &_binary_p_cxformyuvamul_fpo_start); #endif PS3InitShaders(this); return 1; } // Returns back to original mode (cleanup) virtual bool ResetVideoMode() { ModeSet = 0; return 1; } virtual DisplayStatus CheckDisplayStatus() const { return ModeSet ? DisplayStatus_Ok : DisplayStatus_NoModeSet; } virtual void RemoveAllTextures() { // Remove/notify all GLock::Locker guard(&TexturesLock); while (Textures.pFirst != &Textures) ((GTexturePS3Impl*)Textures.pFirst)->RemoveFromRenderer(); } }; // end class GRendererPS3Impl // ***** GTexturePS3 implementation GTexturePS3Impl::GTexturePS3Impl(GRendererPS3Impl *prenderer) : GTexturePS3(&prenderer->Textures) { pRenderer = prenderer; TexMem = 0; Tex.width = Tex.height = Tex.pitch = 0; } GTexturePS3Impl::~GTexturePS3Impl() { ReleaseTexture(); if (!pRenderer) return; GLock::Locker gruad(&pRenderer->TexturesLock); if (pFirst) RemoveNode(); } // Obtains the renderer that create TextureInfo GRenderer* GTexturePS3Impl::GetRenderer() const { return pRenderer; } bool GTexturePS3Impl::IsDataValid() const { return (TexMem != 0); } // Remove texture from renderer, notifies renderer destruction void GTexturePS3Impl::RemoveFromRenderer() { if (AddRef_NotZero()) { ReleaseTexture(); pRenderer = 0; CallHandlers(ChangeHandler::Event_RendererReleased); if (pNext) // We may have been released by user RemoveNode(); Release(); } else { pRenderer = 0; if (pNext) // We may have been released by user RemoveNode(); } } // Releases texture and clears values void GTexturePS3Impl::ReleaseTexture() { if (TexMem) pRenderer->Allocator->Free(TexMem); TexMem = 0; Tex.width = Tex.height = Tex.pitch = 0; } // Forward declarations for sampling/mipmaps void HardwareResample(int bytesPerPixel, int srcWidth, int srcHeight, UByte* psrcData, int dstWidth, int dstHeight); UByte* SoftwareResample(int bytesPerPixel, int srcWidth, int srcHeight, int srcPitch, UByte* psrcData, int dstWidth, int dstHeight); void GenerateMipmaps(unsigned internalFormat, unsigned inputFormat, int bytesPerPixel, GImageBase* pim); bool GTexturePS3Impl::InitTexture(CellGcmTexture *pTex, bool unused) { GUNUSED(unused); ReleaseTexture(); Tex = *pTex; CallHandlers(ChangeHandler::Event_DataChange); return 1; } static UByte* LoadTextureSzBlock(UByte *pdest, const UByte *psrc, UInt w, UInt h, SInt bpp, SInt pitch, UInt x, UInt y, UInt b) { if (b == 1) { switch (bpp) { case 4: pdest[0] = psrc[y*pitch+x*4+3]; pdest[1] = psrc[y*pitch+x*4+0]; pdest[2] = psrc[y*pitch+x*4+1]; pdest[3] = psrc[y*pitch+x*4+2]; return pdest + 4; case 3: pdest[0] = 255; pdest[1] = psrc[y*pitch+x*3+0]; pdest[2] = psrc[y*pitch+x*3+1]; pdest[3] = psrc[y*pitch+x*3+2]; return pdest + 4; case 1: pdest[0] = psrc[y*pitch+x]; return pdest + 1; default: return 0; } } else { pdest = LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, x, y, b-1); pdest = LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, x + (1<<(b-2)), y, b-1); pdest = LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, x, y + (1<<(b-2)), b-1); return LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, x + (1<<(b-2)), y + (1<<(b-2)), b-1); } } static inline UByte* LoadTextureSz(UByte *pdest, const UByte *psrc, UInt w, UInt h, SInt bpp, SInt pitch) { UInt lw = 0; while (w != (1U << lw)) lw++; UInt lh = 0; while (h != (1U << lh)) lh++; if (w == h) return LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, 0,0,lw+1); else if (w > h) { for (UInt i = 0; i < (1U << (lw-lh)); i++) pdest = LoadTextureSzBlock(pdest, psrc, w,h,bpp,pitch, i<pitch == 0) return LoadTextureSz(pdest,psrc,w,h,bpp,pitch); switch (bpp) { case 1: for (UInt j = 0; j < h; j++, psrc += pitch, pdest += ptex->pitch) memcpy(pdest, psrc, w*bpp); return pdest; case 4: for (UInt j = 0; j < h; j++, psrc += pitch, pdest += ptex->pitch) for (UInt i = 0; i < w; i++) { pdest[i*4+0] = psrc[i*4+3]; pdest[i*4+1] = psrc[i*4+0]; pdest[i*4+2] = psrc[i*4+1]; pdest[i*4+3] = psrc[i*4+2]; } return pdest; case 3: for (UInt j = 0; j < h; j++, psrc += pitch, pdest += ptex->pitch) for (UInt i = 0; i < w; i++) { pdest[i*4+0] = 255; pdest[i*4+1] = psrc[i*3+0]; pdest[i*4+2] = psrc[i*3+1]; pdest[i*4+3] = psrc[i*3+2]; } return pdest; } GASSERT(0); return pdest; } bool GTexturePS3Impl::InitTexture(GImageBase* pim, UInt usage) { // Delete old data ReleaseTexture(); if (!pim) { // Kill texture CallHandlers(ChangeHandler::Event_DataChange); return 1; } // Determine format UInt bytesPerPixel=0; GImage::ImageFormat destFmt = pim->Format; if (pim->Format == GImage::Image_ARGB_8888) { bytesPerPixel = 4; Tex.format = CELL_GCM_TEXTURE_A8R8G8B8 | CELL_GCM_TEXTURE_NR; } else if (pim->Format == GImage::Image_RGB_888) { bytesPerPixel = 3; destFmt = GImage::Image_ARGB_8888; Tex.format = CELL_GCM_TEXTURE_D8R8G8B8 | CELL_GCM_TEXTURE_NR; } else if (pim->Format == GImage::Image_A_8) { bytesPerPixel = 1; Tex.format = CELL_GCM_TEXTURE_B8 | CELL_GCM_TEXTURE_NR; } else if (pim->Format == GImage::Image_DXT1) { bytesPerPixel = 1; Tex.format = CELL_GCM_TEXTURE_COMPRESSED_DXT1 | CELL_GCM_TEXTURE_NR; } else if (pim->Format == GImage::Image_DXT3) { bytesPerPixel = 1; Tex.format = CELL_GCM_TEXTURE_COMPRESSED_DXT23 | CELL_GCM_TEXTURE_NR; } else if (pim->Format == GImage::Image_DXT5) { bytesPerPixel = 1; Tex.format = CELL_GCM_TEXTURE_COMPRESSED_DXT45 | CELL_GCM_TEXTURE_NR; } else { // Unsupported format GASSERT(0); } UInt w = pim->Width; UInt h = pim->Height; UInt ww = 1; while (ww < w) { ww <<= 1; } UInt hh = 1; while (hh < h) { hh <<= 1; } if (ww != w || hh != h) { Tex.format |= CELL_GCM_TEXTURE_LN; switch (pim->Format) { case GImage::Image_DXT1: Tex.pitch = ((w+3)/4) * 8; break; case GImage::Image_DXT3: case GImage::Image_DXT5: Tex.pitch = ((w+3)/4) * 16; break; case GImage::Image_RGB_888: Tex.pitch = w*4; break; default: Tex.pitch = pim->Pitch; } } else { Tex.format |= CELL_GCM_TEXTURE_SZ; Tex.pitch = 0; } // Create the texture. UPInt tsize = Tex.pitch ? (Tex.pitch * h) : GImage::GetMipMapLevelSize(destFmt, w, h); Tex.mipmap = 1; Tex.width = w; Tex.height = h; Tex.depth = 1; Tex.cubemap = CELL_GCM_FALSE; Tex.dimension = CELL_GCM_TEXTURE_DIMENSION_2; if (pim->Format == GImage::Image_A_8) Tex.remap = CELL_GCM_REMAP_MODE (CELL_GCM_TEXTURE_REMAP_ORDER_XYXY, CELL_GCM_TEXTURE_REMAP_FROM_R, CELL_GCM_TEXTURE_REMAP_FROM_A, CELL_GCM_TEXTURE_REMAP_FROM_G, CELL_GCM_TEXTURE_REMAP_FROM_B, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_ONE, CELL_GCM_TEXTURE_REMAP_ONE, CELL_GCM_TEXTURE_REMAP_ONE); else Tex.remap = CELL_GCM_REMAP_MODE (CELL_GCM_TEXTURE_REMAP_ORDER_XYXY, CELL_GCM_TEXTURE_REMAP_FROM_A, CELL_GCM_TEXTURE_REMAP_FROM_R, CELL_GCM_TEXTURE_REMAP_FROM_G, CELL_GCM_TEXTURE_REMAP_FROM_B, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP); if (pim->Format == GImage::Image_A_8) { UInt wtmp = w, htmp = h; do { wtmp = wtmp >> 1; htmp = htmp >> 1; if (wtmp < 1) wtmp = 1; if (htmp < 1) htmp = 1; if (Tex.pitch == 0) tsize += GImage::GetMipMapLevelSize(destFmt, wtmp, htmp); else tsize += Tex.pitch * htmp; Tex.mipmap++; } while (wtmp > 1 || htmp > 1); } else if (pim->MipMapCount > 1) { UInt wtmp = w, htmp = h; if (Tex.pitch == 0) for (UInt i = 0; i < pim->MipMapCount; i++) { wtmp = wtmp >> 1; htmp = htmp >> 1; if (wtmp < 1) wtmp = 1; if (htmp < 1) htmp = 1; tsize += GImage::GetMipMapLevelSize(destFmt, wtmp, htmp); } else if (pim->IsDataCompressed()) { UInt blockSize = (pim->Format == GImage::Image_DXT1 ? 8 : 16); for (UInt i = 0; i < pim->MipMapCount; i++) { htmp = htmp >> 1; if (htmp < 1) htmp = 1; tsize += ((htmp+3)/4) * blockSize * Tex.pitch; } } else for (UInt i = 0; i < pim->MipMapCount; i++) { htmp = htmp >> 1; if (htmp < 1) htmp = 1; tsize += htmp * Tex.pitch; } Tex.mipmap = pim->MipMapCount; } TexMem = pRenderer->Allocator->Allocate(CELL_GCM_LOCATION_LOCAL, tsize, 128); void *pTexData = pRenderer->Allocator->Map(TexMem); UByte *pdest = (UByte*)pTexData; Tex.offset = pRenderer->Allocator->GetOffset(TexMem); Tex.location = pRenderer->Allocator->GetLocation(TexMem); if (pim->MipMapCount <= 1 && !pim->IsDataCompressed() && Tex.mipmap > 1) { UByte *psrc = (UByte*) GALLOC(pim->Pitch * h, GStat_Default_Mem); memcpy(psrc, pim->pData, pim->Pitch * h); pdest = LoadTexture(pdest, &Tex, psrc, w, h, bytesPerPixel, pim->Pitch); if (Tex.mipmap > 1) do { GRendererPS3Impl::MakeNextMiplevel(&w, &h, psrc, bytesPerPixel); pdest = LoadTexture(pdest, &Tex, psrc, w, h, bytesPerPixel, w*bytesPerPixel); } while (w > 1 || h > 1); GFREE(psrc); } else { // Use original image directly. UInt level = 0; do { UInt mipW, mipH, mipPitch; const UByte* pdata = pim->GetMipMapLevelData(level, &mipW, &mipH, &mipPitch); if (pdata == 0) //???? { GFC_DEBUG_WARNING(1, "GRendererPS3: can't find mipmap level in texture"); break; } if (pim->IsDataCompressed()) { if (Tex.pitch) { UInt bw = (mipW+3)/4; UInt bh = (mipH+3)/4; UInt blockSize = (pim->Format == GImage::Image_DXT1 ? 8 : 16); for (UInt j = 0; j < bh; j++, pdata += bw*blockSize, pdest += Tex.pitch) memcpy(pdest, pdata, bw*blockSize); } else { memcpy(pdest, pdata, GImage::GetMipMapLevelSize(pim->Format, mipW, mipH)); pdest += GImage::GetMipMapLevelSize(pim->Format, mipW, mipH); } } else { pdest = LoadTexture(pdest, &Tex, pdata, mipW, mipH, bytesPerPixel, mipPitch); } level++; } while(level < pim->MipMapCount && level < Tex.mipmap); } GASSERT(pdest <= (UByte*)pTexData+tsize); pRenderer->Allocator->Unmap(TexMem, pTexData); CallHandlers(ChangeHandler::Event_DataChange); return 1; } bool GTexturePS3Impl::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage) { ReleaseTexture(); // Determine format UInt bytesPerPixel=0; int destFmt = format; if (format == GImage::Image_ARGB_8888) { bytesPerPixel = 4; Tex.format = CELL_GCM_TEXTURE_A8R8G8B8 | CELL_GCM_TEXTURE_NR | CELL_GCM_TEXTURE_LN; } else if (format == GImage::Image_RGB_888) { bytesPerPixel = 4; destFmt = GImage::Image_ARGB_8888; Tex.format = CELL_GCM_TEXTURE_D8R8G8B8 | CELL_GCM_TEXTURE_NR | CELL_GCM_TEXTURE_LN; } else if (format == GImage::Image_A_8) { bytesPerPixel = 1; Tex.format = CELL_GCM_TEXTURE_B8 | CELL_GCM_TEXTURE_NR | CELL_GCM_TEXTURE_LN; } else if (format == GImage::Image_DepthStencil) { bytesPerPixel = 4; Tex.format = CELL_GCM_TEXTURE_DEPTH24_D8; } else { // Unsupported format GASSERT(0); } // Create the texture. Tex.mipmap = 1 + mipmaps; Tex.pitch = width * bytesPerPixel; Tex.width = width; Tex.height = height; Tex.depth = 1; Tex.cubemap = CELL_GCM_FALSE; Tex.dimension = CELL_GCM_TEXTURE_DIMENSION_2; if (format == GImage::Image_A_8) Tex.remap = CELL_GCM_REMAP_MODE (CELL_GCM_TEXTURE_REMAP_ORDER_XYXY, CELL_GCM_TEXTURE_REMAP_FROM_R, CELL_GCM_TEXTURE_REMAP_FROM_A, CELL_GCM_TEXTURE_REMAP_FROM_G, CELL_GCM_TEXTURE_REMAP_FROM_B, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_ONE, CELL_GCM_TEXTURE_REMAP_ONE, CELL_GCM_TEXTURE_REMAP_ONE); else Tex.remap = CELL_GCM_REMAP_MODE (CELL_GCM_TEXTURE_REMAP_ORDER_XYXY, CELL_GCM_TEXTURE_REMAP_FROM_A, CELL_GCM_TEXTURE_REMAP_FROM_R, CELL_GCM_TEXTURE_REMAP_FROM_G, CELL_GCM_TEXTURE_REMAP_FROM_B, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP); UPInt tsize = Tex.pitch * height; UInt wtmp = width, htmp = height; for (int i = 0; i < mipmaps; i++) { wtmp = wtmp >> 1; htmp = htmp >> 1; if (wtmp < 1) wtmp = 1; if (htmp < 1) htmp = 1; tsize += Tex.pitch * htmp * bytesPerPixel; } TexMem = pRenderer->Allocator->Allocate(CELL_GCM_LOCATION_LOCAL, tsize, 128); Tex.offset = pRenderer->Allocator->GetOffset(TexMem); Tex.location = pRenderer->Allocator->GetLocation(TexMem); return 1; } void GTexturePS3Impl::Update(int level, int n, const UpdateRect *rects, const GImageBase *pim) { if ((pRenderer->VMCFlags & GRendererPS3::VMConfig_FastTexUpdate) == 0) { // wait on rendering for old texture contents volatile UInt32* label = ((volatile UInt32 *) cellGcmGetLabelAddress(pRenderer->VA_label)); cellGcmSetWriteTextureLabel(pRenderer->GcmCtx, pRenderer->VA_label, pRenderer->VA_lastv); cellGcmFlush(pRenderer->GcmCtx); while( *label != pRenderer->VA_lastv) { sys_timer_usleep(10); } pRenderer->VA_start = 0; pRenderer->VA_lastv = (1 + pRenderer->VA_size) | (pRenderer->VA_count << pRenderer->VA_shift); pRenderer->VA_count++; } UInt bpp = 0; if (pim->Format == GImage::Image_ARGB_8888) bpp = 4; else if (pim->Format == GImage::Image_RGB_888) bpp = 3; else if (pim->Format == GImage::Image_A_8) bpp = 1; else GASSERT(0); UByte *pTexData = (UByte*)pRenderer->Allocator->Map(TexMem); int h = Tex.height; for (int i = 0; i < level; i++) { pTexData += Tex.pitch * h; h >>= 1; if (h < 1) h = 1; } if (bpp == 3) { GASSERT(0); } else for (int i = 0; i < n; i++) { for (int j = 0; j < rects[i].src.Height(); j++) memcpy(pTexData + Tex.pitch * (rects[i].dest.y + j) + bpp * rects[i].dest.x, pim->pData + pim->Pitch * (j + rects[i].src.Top) + pim->GetBytesPerPixel() * rects[i].src.Left, rects[i].src.Width() * bpp); } pRenderer->Allocator->Unmap(TexMem, pTexData); cellGcmSetInvalidateTextureCache(pRenderer->GcmCtx, CELL_GCM_INVALIDATE_TEXTURE); CallHandlers(ChangeHandler::Event_DataChange); return; } int GTexturePS3Impl::Map(int level, int n, MapRect* maps, int flags) { GASSERT(level <= Tex.mipmap && n > 0 && maps); UInt bpp = 0; if ((Tex.format & 0x9F) == CELL_GCM_TEXTURE_B8) bpp = 1; else bpp = 4; UByte *pTexData = (UByte*)pRenderer->Allocator->Map(TexMem); int h = Tex.height; int w = Tex.width; for (int i = 0; i < level; i++) { pTexData += Tex.pitch * h; h >>= 1; if (h < 1) h = 1; w >>= 1; if (w < 1) w = 1; } maps[0].width = w; maps[0].height = h; maps[0].pitch = w * bpp; maps[0].pData = pTexData; return 1; } bool GTexturePS3Impl::Unmap(int level, int n, MapRect* maps, int flags) { GASSERT(level <= Tex.mipmap && n > 0 && maps); UByte *pTexData = maps[0].pData; int h = Tex.height; for (int i = 0; i < level; i++) { pTexData -= Tex.pitch * h; h >>= 1; if (h < 1) h = 1; } pRenderer->Allocator->Unmap(TexMem, pTexData); return 1; } // GTexturePS3Impl implementation // Creates an OpenGL texture of the specified dst dimensions, from a // resampled version of the given src image. Does a bilinear // resampling to create the dst image. UByte* SoftwareResample( int format, int srcWidth, int srcHeight, int srcPitch, UByte* psrcData, int dstWidth, int dstHeight) { UByte* rescaled = (UByte*)GALLOC(dstWidth * dstHeight * 4, GStat_Default_Mem); switch(format) { case GImage::Image_RGB_888: GRenderer::ResizeImage(&rescaled[0], dstWidth, dstHeight, dstWidth * 3, psrcData, srcWidth, srcHeight, srcPitch, GRenderer::ResizeRgbToRgb); break; case GImage::Image_ARGB_8888: GRenderer::ResizeImage(&rescaled[0], dstWidth, dstHeight, dstWidth * 4, psrcData, srcWidth, srcHeight, srcPitch, GRenderer::ResizeRgbaToRgba); break; case GImage::Image_A_8: GRenderer::ResizeImage(&rescaled[0], dstWidth, dstHeight, dstWidth, psrcData, srcWidth, srcHeight, srcPitch, GRenderer::ResizeGray); break; } return rescaled; } void GTexturePS3Impl::Bind(int stageIndex0, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps) { int stageIndex = stageIndex0 < 0 ? (-stageIndex0-1) : pRenderer->CgFShaders[pRenderer->CurrentShader].texi[stageIndex0]; cellGcmSetTexture(pRenderer->GcmCtx, stageIndex, &Tex); if (WrapMode == GRenderer::Wrap_Clamp) cellGcmSetTextureAddress(pRenderer->GcmCtx, stageIndex, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL, CELL_GCM_TEXTURE_ZFUNC_NEVER, 0); else cellGcmSetTextureAddress(pRenderer->GcmCtx, stageIndex, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL, CELL_GCM_TEXTURE_ZFUNC_NEVER, 0); cellGcmSetTextureControl(pRenderer->GcmCtx, stageIndex, CELL_GCM_TRUE, 0, 12 << 8, CELL_GCM_TEXTURE_MAX_ANISO_1); // Lod bias is 1:4:8 signed fixed point; -0.5 = 0x1f80 cellGcmSetTextureFilter(pRenderer->GcmCtx, stageIndex, useMipmaps ? 0x1f80 : 0, (SampleMode == GRenderer::Sample_Point) ? CELL_GCM_TEXTURE_NEAREST : (useMipmaps ? CELL_GCM_TEXTURE_LINEAR_LINEAR : CELL_GCM_TEXTURE_LINEAR), (SampleMode == GRenderer::Sample_Point) ? CELL_GCM_TEXTURE_NEAREST : CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_CONVOLUTION_QUINCUNX); } #ifndef GFC_NO_YUV_TEXTURES // ***** GTexturePS3 implementation GTexturePS3ImplYUV::GTexturePS3ImplYUV(GRendererPS3Impl *prenderer) : GTexturePS3Impl(prenderer) { TexMemUVA[0] = TexMemUVA[1] = TexMemUVA[2] = 0; } GTexturePS3ImplYUV::~GTexturePS3ImplYUV() { ReleaseTexture(); } bool GTexturePS3ImplYUV::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage) { ReleaseTexture(); GASSERT(mipmaps == 0); GUNUSED(usage); Tex.format = CELL_GCM_TEXTURE_B8 | CELL_GCM_TEXTURE_NR | CELL_GCM_TEXTURE_LN; Tex.mipmap = 1; Tex.pitch = width; Tex.width = width; Tex.height = height; Tex.depth = 1; Tex.cubemap = CELL_GCM_FALSE; Tex.dimension = CELL_GCM_TEXTURE_DIMENSION_2; Tex.remap = CELL_GCM_REMAP_MODE(CELL_GCM_TEXTURE_REMAP_ORDER_XYXY, CELL_GCM_TEXTURE_REMAP_FROM_A, CELL_GCM_TEXTURE_REMAP_FROM_R, CELL_GCM_TEXTURE_REMAP_FROM_G, CELL_GCM_TEXTURE_REMAP_FROM_B, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP, CELL_GCM_TEXTURE_REMAP_REMAP); TexMem = pRenderer->Allocator->Allocate(CELL_GCM_LOCATION_MAIN, Tex.pitch * Tex.height, 128); Tex.offset = pRenderer->Allocator->GetOffset(TexMem); Tex.location = pRenderer->Allocator->GetLocation(TexMem); for (int i = 0; i < (format == GImage::Image_ARGB_8888 ? 3 : 2); i++) { TexUVA[i] = Tex; if (i != 2) { TexUVA[i].width = Tex.width >> 1; TexUVA[i].height = Tex.height >> 1; TexUVA[i].pitch = Tex.width >> 1; } TexMemUVA[i] = pRenderer->Allocator->Allocate(CELL_GCM_LOCATION_MAIN, TexUVA[i].pitch * TexUVA[i].height, 128); TexUVA[i].offset = pRenderer->Allocator->GetOffset(TexMemUVA[i]); TexUVA[i].location = pRenderer->Allocator->GetLocation(TexMemUVA[i]); } return 1; } void GTexturePS3ImplYUV::Bind(int stageIndex0, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps) { GASSERT(stageIndex0 == 0); int stageIndex[4]; stageIndex[0] = pRenderer->CgFShaders[pRenderer->CurrentShader].texi[0]; stageIndex[1] = pRenderer->CgFShaders[pRenderer->CurrentShader].texi[2]; stageIndex[2] = pRenderer->CgFShaders[pRenderer->CurrentShader].texi[3]; cellGcmSetTexture(pRenderer->GcmCtx, stageIndex[0], &Tex); cellGcmSetTexture(pRenderer->GcmCtx, stageIndex[1], TexUVA); cellGcmSetTexture(pRenderer->GcmCtx, stageIndex[2], TexUVA+1); if (TexMemUVA[2]) { stageIndex[3] = pRenderer->CgFShaders[pRenderer->CurrentShader].texi[4]; cellGcmSetTexture(pRenderer->GcmCtx, stageIndex[3], TexUVA+2); } for (int i = 0; i < (TexMemUVA[2] ? 4 : 3); i++) { if (WrapMode == GRenderer::Wrap_Clamp) cellGcmSetTextureAddress(pRenderer->GcmCtx, stageIndex[i], CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL, CELL_GCM_TEXTURE_ZFUNC_NEVER, 0); else cellGcmSetTextureAddress(pRenderer->GcmCtx, stageIndex[i], CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL, CELL_GCM_TEXTURE_ZFUNC_NEVER, 0); cellGcmSetTextureControl(pRenderer->GcmCtx, stageIndex[i], CELL_GCM_TRUE, 0, 12 << 8, CELL_GCM_TEXTURE_MAX_ANISO_1); // Lod bias is 1:4:8 signed fixed point; -0.5 = 0x1f80 cellGcmSetTextureFilter(pRenderer->GcmCtx, stageIndex[i], useMipmaps ? 0x1f80 : 0, (SampleMode == GRenderer::Sample_Point) ? CELL_GCM_TEXTURE_NEAREST : (useMipmaps ? CELL_GCM_TEXTURE_LINEAR_LINEAR : CELL_GCM_TEXTURE_LINEAR), (SampleMode == GRenderer::Sample_Point) ? CELL_GCM_TEXTURE_NEAREST : CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_CONVOLUTION_QUINCUNX); } } void GTexturePS3ImplYUV::ReleaseTexture() { if (TexMem) pRenderer->Allocator->Free(TexMem); TexMem = 0; for (int i = 0; i < 3; i++) { if (TexMemUVA[i]) pRenderer->Allocator->Free(TexMemUVA[i]); TexMemUVA[i] = 0; } } int GTexturePS3ImplYUV::Map(int level, int n, MapRect* maps, int flags) { GASSERT(level == 0 && n >= 3 && maps); maps[0].width = Tex.width; maps[0].height = Tex.height; maps[0].pitch = Tex.width; maps[0].pData = (UByte*)pRenderer->Allocator->Map(TexMem); maps[1].width = Tex.width >> 1; maps[1].height = Tex.height >> 1; maps[1].pitch = Tex.width >> 1; maps[1].pData = (UByte*)pRenderer->Allocator->Map(TexMemUVA[0]); maps[2].width = Tex.width >> 1; maps[2].height = Tex.height >> 1; maps[2].pitch = Tex.width >> 1; maps[2].pData = (UByte*)pRenderer->Allocator->Map(TexMemUVA[1]); if (n >= 4 && TexMemUVA[2]) { maps[3].width = Tex.width; maps[3].height = Tex.height; maps[3].pitch = Tex.width; maps[3].pData = (UByte*)pRenderer->Allocator->Map(TexMemUVA[2]); return 4; } else return 3; } bool GTexturePS3ImplYUV::Unmap(int level, int n, MapRect* maps, int flags) { GASSERT(level == 0 && n >= 3 && maps); pRenderer->Allocator->Unmap(TexMem, maps[0].pData); pRenderer->Allocator->Unmap(TexMemUVA[0], maps[1].pData); pRenderer->Allocator->Unmap(TexMemUVA[1], maps[2].pData); if (TexMemUVA[2] && n >= 4) pRenderer->Allocator->Unmap(TexMemUVA[2], maps[3].pData); return 1; } #endif GRenderTargetPS3Impl::~GRenderTargetPS3Impl() { if (!pRenderer) return; GLock::Locker gruad(&pRenderer->TexturesLock); if (pFirst) RemoveNode(); } GRenderTargetPS3Impl::GRenderTargetPS3Impl(GRendererPS3Impl* prenderer) : GRenderTargetPS3(&prenderer->RenderTargets), pRenderer(prenderer) { IsTemp = 0; ActualHeight = 0; Tiled = 0; } GRenderer* GRenderTargetPS3Impl::GetRenderer() const { return pRenderer; } void GRenderTargetPS3Impl::RemoveFromRenderer() { pRenderer = 0; if (AddRef_NotZero()) { pTexture = 0; pStencilTexture = 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(); } } bool GRenderTargetPS3Impl::InitRenderTarget(GTexture *ptarget, GTexture* pdepth, GTexture* pstencil) { pTexture = (GTexturePS3Impl*)ptarget; TargetWidth = pTexture->Tex.width; TargetHeight = pTexture->Tex.height; UInt32 pitch = (TargetWidth * 4 + 63) & ~63; Surf.colorFormat = CELL_GCM_SURFACE_A8R8G8B8; // xxx Surf.colorTarget = CELL_GCM_SURFACE_TARGET_0; Surf.colorLocation[0] = pTexture->Tex.location; Surf.colorOffset[0] = pTexture->Tex.offset; Surf.colorPitch[0] = pTexture->Tex.pitch; Surf.colorLocation[1] = CELL_GCM_LOCATION_LOCAL; Surf.colorLocation[2] = CELL_GCM_LOCATION_LOCAL; Surf.colorLocation[3] = CELL_GCM_LOCATION_LOCAL; Surf.colorOffset[1] = 0; Surf.colorOffset[2] = 0; Surf.colorOffset[3] = 0; Surf.colorPitch[1] = 64; Surf.colorPitch[2] = 64; Surf.colorPitch[3] = 64; if (pdepth) pStencilTexture = (GTexturePS3Impl*)pdepth; else if (pstencil) pStencilTexture = (GTexturePS3Impl*)pstencil; if (pStencilTexture) { Surf.depthFormat = CELL_GCM_SURFACE_Z24S8; Surf.depthLocation = pStencilTexture->Tex.location; Surf.depthOffset = pStencilTexture->Tex.offset; Surf.depthPitch = pitch; } else { Surf.depthFormat = CELL_GCM_SURFACE_Z24S8; Surf.depthLocation = CELL_GCM_LOCATION_LOCAL; Surf.depthOffset = 0; Surf.depthPitch = 64; } Surf.type = pTexture->Tex.format & CELL_GCM_TEXTURE_SZ ? CELL_GCM_SURFACE_SWIZZLE : CELL_GCM_SURFACE_PITCH; Surf.antialias = CELL_GCM_SURFACE_CENTER_1; Surf.width = pTexture->Tex.width; Surf.height = pTexture->Tex.height; Surf.x = 0; Surf.y = 0; return 1; } bool GRenderTargetPS3Impl::InitRenderTarget(PS3RenderTargetParams params) { pTexture = 0; pStencilTexture = 0; Surf = *params.Surface; ActualHeight = params.ActualHeight; Tiled = params.Tiled; return 1; } void GRenderTargetPS3Impl::SetRenderTarget(GRenderer::StereoDisplay disp) { if (ActualHeight) { CellGcmSurface SetSurf (Surf); Surf.height = ActualHeight; if (disp == GRenderer::StereoRight) { if (Tiled) { UInt AlignedLineOffset = (30 + ActualHeight) & ~7; SetSurf.colorOffset[0] += Surf.colorPitch[0] * AlignedLineOffset; SetSurf.y += (30 + ActualHeight) - AlignedLineOffset; } else SetSurf.colorOffset[0] += Surf.colorPitch[0] * (30 + ActualHeight); } cellGcmSetSurface(pRenderer->GcmCtx, &SetSurf); } else cellGcmSetSurface(pRenderer->GcmCtx, &Surf); } #include "PS3/PS3Shaders.cpp" // Factory. GRendererPS3* GRendererPS3::CreateRenderer() { return new GRendererPS3Impl; }