/********************************************************************** Filename : GRendererD3D1xImpl.cpp Content : Direct3D 10 Sample renderer implementation Created : Oct 1, 2007 Authors : Andrew Reisse Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved. Notes : Licensees may use this file in accordance with the valid Scaleform Commercial License Agreement provided with the software. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. **********************************************************************/ #include "GImage.h" #include "GAtomic.h" #include "GArray.h" #include "GStd.h" #if defined(GFC_OS_WIN32) #include #endif #include "GRendererCommonImpl.h" #include // for memset() #ifndef GFC_D3D_VERSION #define GFC_D3D_VERSION 10 #endif #if (GFC_D3D_VERSION == 10) #include "GRendererD3D10.h" #if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW) #undef new #endif #include #include #if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW) #define new GFC_DEFINE_NEW #endif #include "ShadersD3D10/hlsl.cpp" #include "D3D1x/D3D10Shaders.cpp" using namespace D3D10; #define GRendererD3D1x GRendererD3D10 #define GTextureD3D1x GTextureD3D10 #define GRenderTargetD3D1x GRenderTargetD3D10 #define GRendererD3D1xImpl GRendererD3D10Impl #define GRenderTargetD3D1xImpl GRenderTargetD3D10Impl #define GTextureD3D1xImpl GTextureD3D10Impl #define D3DxRenderTargetParams D3D10RenderTargetParams // add D3D10_ or ID3D10 before classes and enums that are similar in D3D10/11 #define D3Dx(name) D3D10_##name #define ID3Dx(name) ID3D10##name // context is new in D3D11 #define pContext pDevice #define ID3D1xDeviceContext ID3D10Device #define NULL_SHADER_CLASS #define NULL_SHADER_LINKAGE #define D3DxVIEWPORT_COORD SInt #else // 11 #include "GRendererD3D11.h" #if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW) #undef new #endif #include #include #if defined(GFC_BUILD_DEFINE_NEW) && defined(GFC_DEFINE_NEW) #define new GFC_DEFINE_NEW #endif #include "ShadersD3D10/hlsl.cpp" #include "D3D1x/D3D10Shaders.cpp" using namespace D3D10; #define GRendererD3D1x GRendererD3D11 #define GTextureD3D1x GTextureD3D11 #define GRenderTargetD3D1x GRenderTargetD3D11 #define GRendererD3D1xImpl GRendererD3D11Impl #define GTextureD3D1xImpl GTextureD3D11Impl #define GRenderTargetD3D11Impl GRenderTargetD3D11Impl #define D3DxRenderTargetParams D3D11RenderTargetParams // add D3D11_ or ID3D11 before classes and enums that are similar in D3D10/11 #define D3Dx(name) D3D11_##name #define ID3Dx(name) ID3D11##name #define ID3D1xDeviceContext ID3D11DeviceContext #define NULL_SHADER_CLASS , NULL, 0 #define NULL_SHADER_LINKAGE , NULL #define D3DxVIEWPORT_COORD FLOAT #endif // ***** Classes implemented class GRendererD3D1xImpl; class GTextureD3D1xImpl; // ***** GTextureD3D1xImpl implementation // GTextureD3D1xImpl declaration class GTextureD3D1xImpl : public GTextureD3D1x { public: D3Dx(TEXTURE2D_DESC) Desc; // Renderer GRendererD3D1xImpl* pRenderer; // D3D1x Texture pointer ID3Dx(Texture2D)* pD3DTexture; ID3Dx(ShaderResourceView)* pShaderView; GTextureD3D1xImpl(GRendererD3D1xImpl *prenderer); ~GTextureD3D1xImpl(); // Obtains the renderer that create TextureInfo virtual GRenderer* GetRenderer() const; virtual bool IsDataValid() const; virtual void ReleaseTexture(); // Init Texture implementation. virtual bool InitTexture(ID3Dx(Texture2D) *ptex, bool unused = 0); virtual bool InitTexture(GImageBase* pim, UInt usage); virtual bool InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage); virtual void Update(int level, int n, const UpdateRect *rects, const GImageBase *pim); virtual int Map(int level, int n, MapRect* maps, int flags); virtual bool Unmap(int level, int n, MapRect* maps, int flags); // Remove texture from renderer, notifies of renderer destruction void RemoveFromRenderer(); virtual ID3Dx(Texture2D)* GetNativeTexture() const { return pD3DTexture; } virtual int IsYUVTexture() { return 0; } virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps); }; class GTextureD3D1xImplYUV : public GTextureD3D1xImpl { public: D3Dx(TEXTURE2D_DESC) DescUV; ID3Dx(Texture2D)* pD3DTextureUVA[3]; ID3Dx(ShaderResourceView)* pShaderViewUVA[3]; GTextureD3D1xImplYUV(GRendererD3D1xImpl *prenderer) : GTextureD3D1xImpl(prenderer) { pD3DTextureUVA[0] = pD3DTextureUVA[1] = pD3DTextureUVA[2] = 0; pShaderViewUVA[0] = pShaderViewUVA[1] = pShaderViewUVA[2] = 0; memset (&DescUV, 0, sizeof(DescUV)); } ~GTextureD3D1xImplYUV(); virtual bool InitTexture(ID3Dx(Texture2D) *, bool unused = 0) { GUNUSED(unused); return 0; } virtual bool InitTexture(GImageBase*, UInt) { return 0; } virtual void Update(int, int, const UpdateRect *, const GImageBase *) { } virtual bool InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage); 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 pD3DTextureUVA[2] ? 2 : 1; } virtual void Bind(int stage, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps); }; class GRenderTargetD3D1xImpl : public GRenderTargetD3D1x { public: GPtr pTexture, pStencilTexture; GRendererD3D1xImpl* pRenderer; ID3Dx(RenderTargetView)* pRenderTextureView; ID3Dx(DepthStencilView)* pRTDepthStencil; UInt TargetWidth, TargetHeight; bool IsTemp; GRenderTargetD3D1xImpl(GRendererD3D1xImpl* prenderer); ~GRenderTargetD3D1xImpl(); void ReleaseResources(); void RemoveFromRenderer(); virtual GRenderer* GetRenderer() const; virtual bool InitRenderTarget(GTexture *ptarget, GTexture* pdepth = 0, GTexture* pstencil = 0); virtual bool InitRenderTarget(D3DxRenderTargetParams params); }; // ***** Vertex Declarations and Shaders // Vertex shader declarations we can use enum VDeclType { VD_None, VD_Strip, VD_Glyph, VD_XY16iC32, VD_XY16iCF32, VD_Filter, VD_Count }; // Our vertex coords consist of two signed 16-bit integers, for (x,y) position only. static D3Dx(INPUT_ELEMENT_DESC) StripVertexDecl[] = { {"POSITION", 0, DXGI_FORMAT_R16G16_SINT, 0, 0, D3Dx(INPUT_PER_VERTEX_DATA), 0}, }; static D3Dx(INPUT_ELEMENT_DESC) GlyphVertexDecl[] = { {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 16, D3Dx(INPUT_PER_VERTEX_DATA), 0}, }; // Gouraud vertices used with Edge AA static D3Dx(INPUT_ELEMENT_DESC) VertexDeclXY16iC32[] = { {"POSITION", 0, DXGI_FORMAT_R16G16_SINT, 0, 0, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 4, D3Dx(INPUT_PER_VERTEX_DATA), 0}, }; static D3Dx(INPUT_ELEMENT_DESC) VertexDeclXY16iCF32[] = { {"POSITION", 0, DXGI_FORMAT_R16G16_SINT, 0, 0, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 4, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3Dx(INPUT_PER_VERTEX_DATA), 0}, }; // Filters static D3Dx(INPUT_ELEMENT_DESC) FilterVertexDecl[] = { {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3Dx(INPUT_PER_VERTEX_DATA), 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3Dx(INPUT_PER_VERTEX_DATA), 0}, }; // Vertex declaration lookup table, must correspond to VDeclType +1 struct VDeclSpec { const D3Dx(INPUT_ELEMENT_DESC)* pDecl; UInt Count; }; static VDeclSpec VertexDeclTypeTable[VD_Count-1] = { {StripVertexDecl, 1}, {GlyphVertexDecl, 3}, {VertexDeclXY16iC32, 2}, {VertexDeclXY16iCF32, 3}, {FilterVertexDecl, 2} }; enum VShaderType { VS_None, VS_Strip, VS_Glyph, VS_XY16iC32, VS_XY16iCF32, VS_XY16iCF32_T2, VS_CountSrc, #ifndef GFC_NO_3D VS_3D_None = VS_CountSrc, VS_3D_Strip, VS_3D_Glyph, VS_3D_XY16iC32, VS_3D_XY16iCF32, VS_3D_XY16iCF32_T2, #endif VS_Count }; // Vertex shader text lookup table, must correspond to VShaderType +1 static const char *VertexShaderTextTable[VS_Count-1] = { pStripVShaderText, pGlyphVShaderText, pStripVShaderXY16iC32Text, pStripVShaderXY16iCF32Text, pStripVShaderXY16iCF32_T2Text, #ifndef GFC_NO_3D NULL, pStripVShader3DText, pGlyphVShader3DText, pStripVShader3DXY16iC32Text, pStripVShader3DXY16iCF32Text, pStripVShader3DXY16iCF32_T2Text, #endif }; static const VDeclType VertexShaderDeclTable[VS_Count] = { VD_None, VD_Strip, VD_Glyph, VD_XY16iC32, VD_XY16iCF32, VD_XY16iCF32, #ifndef GFC_NO_3D VD_None, VD_Strip, VD_Glyph, VD_XY16iC32, VD_XY16iCF32, VD_XY16iCF32, #endif }; // 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; } }; // Pixel shaders enum PixelShaderType { PS_None = 0, PS_SolidColor, PS_CxformTexture, PS_CxformTextureMultiply, PS_TextTextureAlpha, PS_TextTextureColor, PS_TextTextureColorMultiply, PS_TextTextureYUV, PS_TextTextureYUVMultiply, PS_TextTextureYUVA, PS_TextTextureYUVAMultiply, PS_CxformGauraud, PS_CxformGauraudNoAddAlpha, PS_CxformGauraudTexture, PS_Cxform2Texture, // Multiplies - must come in same order as other gourauds PS_CxformGauraudMultiply, PS_CxformGauraudMultiplyNoAddAlpha, PS_CxformGauraudMultiplyTexture, PS_CxformMultiply2Texture, PS_Count, }; static const char* PixelShaderInitTable[PS_Count-1] = { // Non-AA Shaders. pSource_PS_SolidColor, pSource_PS_CxformTexture, pSource_PS_CxformTextureMultiply, pSource_PS_TextTextureAlpha, pSource_PS_TextTextureColor, pSource_PS_TextTextureColorMultiply, pSource_PS_TextTextureYUV, pSource_PS_TextTextureYUVMultiply, pSource_PS_TextTextureYUVA, pSource_PS_TextTextureYUVAMultiply, // AA Shaders. pSource_PS_CxformGauraud, pSource_PS_CxformGauraudNoAddAlpha, pSource_PS_CxformGauraudTexture, pSource_PS_Cxform2Texture, pSource_PS_CxformGauraudMultiply, pSource_PS_CxformGauraudMultiplyNoAddAlpha, pSource_PS_CxformGauraudMultiplyTexture, pSource_PS_CxformMultiply2Texture }; // Blending enum BlendStateTypes { BlendDesc_Disable, BlendDesc_Normal, BlendDesc_Add, BlendDesc_Subtract, BlendDesc_Multiply, BlendDesc_Lighten, BlendDesc_Darken, BlendDesc_Stencil, BlendDesc_Count }; #if (GFC_D3D_VERSION == 10) #define BLEND(enable,srcC,dstC,Cop,srcA,dstA,Aop,mask) \ { 0, {enable}, D3Dx(srcC), D3Dx(dstC), D3Dx(Cop), D3Dx(srcA), D3Dx(dstA), D3Dx(Aop), {mask} } #else #define BLEND(enable,srcC,dstC,Cop,srcA,dstA,Aop,mask) \ { 0, 0, {{enable, D3Dx(srcC), D3Dx(dstC), D3Dx(Cop), D3Dx(srcA), D3Dx(dstA), D3Dx(Aop), mask}} } #endif static const D3Dx(BLEND_DESC) BlendDescs[BlendDesc_Count] = { BLEND(0, BLEND_ONE, BLEND_ZERO, BLEND_OP_ADD, BLEND_ONE, BLEND_ZERO, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA, BLEND_OP_ADD, BLEND_ONE, BLEND_INV_SRC_ALPHA, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_SRC_ALPHA, BLEND_ONE, BLEND_OP_ADD, BLEND_ZERO, BLEND_ONE, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_SRC_ALPHA, BLEND_ONE, BLEND_OP_REV_SUBTRACT, BLEND_ZERO, BLEND_ONE, BLEND_OP_REV_SUBTRACT, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_DEST_COLOR,BLEND_ZERO, BLEND_OP_ADD, BLEND_DEST_ALPHA,BLEND_ZERO, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_SRC_ALPHA, BLEND_ONE, BLEND_OP_MAX, BLEND_ONE, BLEND_ONE, BLEND_OP_MAX, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_SRC_ALPHA, BLEND_ONE, BLEND_OP_MIN, BLEND_ONE, BLEND_ONE, BLEND_OP_MIN, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(0, BLEND_SRC_ALPHA, BLEND_ONE, BLEND_OP_ADD, BLEND_ONE, BLEND_ONE, BLEND_OP_ADD, 0) }; static const D3Dx(BLEND_DESC) BlendDescsSrcAc[BlendDesc_Count] = { BLEND(0, BLEND_ONE, BLEND_ZERO, BLEND_OP_ADD, BLEND_ONE, BLEND_ZERO, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_ONE, BLEND_INV_SRC_ALPHA, BLEND_OP_ADD, BLEND_ONE, BLEND_INV_SRC_ALPHA, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_ONE, BLEND_ONE, BLEND_OP_ADD, BLEND_ZERO, BLEND_ONE, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_ONE, BLEND_ONE, BLEND_OP_REV_SUBTRACT, BLEND_ZERO, BLEND_ONE, BLEND_OP_REV_SUBTRACT, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_DEST_COLOR,BLEND_ZERO, BLEND_OP_ADD, BLEND_DEST_ALPHA,BLEND_ZERO, BLEND_OP_ADD, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_ONE, BLEND_ONE, BLEND_OP_MAX, BLEND_ONE, BLEND_ONE, BLEND_OP_MAX, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(1, BLEND_ONE, BLEND_ONE, BLEND_OP_MIN, BLEND_ONE, BLEND_ONE, BLEND_OP_MIN, D3Dx(COLOR_WRITE_ENABLE_ALL)), BLEND(0, BLEND_ONE, BLEND_ONE, BLEND_OP_ADD, BLEND_ONE, BLEND_ONE, BLEND_OP_ADD, 0) }; #undef BLEND static const int BlendModeStates[] = { BlendDesc_Normal, BlendDesc_Normal, BlendDesc_Normal, BlendDesc_Multiply, BlendDesc_Normal, BlendDesc_Lighten, BlendDesc_Darken, BlendDesc_Normal, BlendDesc_Add, BlendDesc_Subtract, BlendDesc_Normal, BlendDesc_Stencil, BlendDesc_Stencil, BlendDesc_Normal, BlendDesc_Normal }; // **** GDynamicVertexStreamD3D1x - Dynamic vertex buffer streaming manager // This class tracks the vertex and index buffers that were set on the // renderer and streams them into dynamic buffers intelligently. // Extra logic is included to handle buffer overruns; for example if // the index buffer passed does not fit into allocated buffer, it // will be streamed and rendered in pieces. class GDynamicVertexStreamD3D1x { public: // Delegate used types for convenience. typedef GRenderer::IndexFormat IndexFormat; typedef GRenderer::VertexFormat VertexFormat; typedef GRenderer::VertexXY16iC32 VertexXY16iC32; typedef GRenderer::VertexXY16iCF32 VertexXY16iCF32; // Default sizes of various buffers - in bytes; Change these to control memory that is used enum { // Warning: if vertex buffer size is not large enough for 65K vertices, // we will fall back to SW indexing for those buffers that don't fit. DefaultVertexBufferSize = 0x10000 * sizeof(VertexXY16iCF32), DefaultIndexBufferSize = 0x18000 * sizeof(UInt16), GlyphBufferVertexCount = 6 * 192, GlyphBufferSize = GlyphBufferVertexCount * sizeof(GGlyphVertex) }; // PrimitiveDesc - package structure for DrawPrimitive args. struct PrimitiveDesc { int BaseVertexIndex, MinVertexIndex, NumVertices; int StartIndex, TriangleCount; PrimitiveDesc() { BaseVertexIndex = 0; MinVertexIndex = 0; NumVertices = 0; StartIndex = 0; TriangleCount = 0; } PrimitiveDesc(int baseVertexIndex, int minVertexIndex, int numVertices, int startIndex, int triangleCount ) { BaseVertexIndex = baseVertexIndex; MinVertexIndex = minVertexIndex; NumVertices = numVertices; StartIndex = startIndex; TriangleCount = triangleCount; } }; private: // Direct3D Device we are using. ID3Dx(Device)* pDevice; #if (GFC_D3D_VERSION == 11) ID3Dx(DeviceContext)* pContext; #endif // Flag set if buffers are released due to lost device. bool LostDevice; // Vertex data pointer const void* pVertexData; const void* pIndexData; VertexFormat VertexFmt; UInt VertexSize; DXGI_FORMAT IndexFmt; UInt VertexCount; UInt IndexCount; // Dynamic Vertex/Index buffer state GPtr pVertexBuffer; GPtr pIndexBuffer; // Allocated sizes UInt VertexBufferSize; UInt IndexBufferSize; // Next available offset in vertex / index buffers UInt NextIBOffset; UInt NextVBOffset; // If vertex data was uploaded into VB, VBDataInBuffer flag is set // and VBDataOffset points to its first byte. bool VBDataInBuffer; bool IBDataInBuffer; UInt VBDataOffset; UInt VBDataIndex; // Used in D3D8 to support multiple arrays in vertex buffer UInt IBDataOffset; // Vertex size of last data in buffer. Can be different from VertexSize // if data was uploaded directly through lock. UInt VertexSizeInBuffer; PrimitiveDesc Primitive; enum RenderMethodType { RM_None, RM_Indexed, RM_IndexedInChunks, RM_NotIndexed, }; RenderMethodType RenderMethod; int MaxTriangleCount; // Helper functions to create/release vertex and index buffers. bool CreateDynamicBuffers(); // Helper, performs un-indexing and copy of data into the target vertex buffer. void InitVerticesFromIndex(void *pvertexDest, int baseVertexIndex, int vertexSize, int startIndex, int triangleCount ); public: GDynamicVertexStreamD3D1x(); ~GDynamicVertexStreamD3D1x(); // Initializes stream, creating buffers. Called at renderer initialization. bool Initialize(ID3Dx(Device) *pdevice, ID3D1xDeviceContext *pcontext) { pDevice = pdevice; pContext = pcontext; if (CreateDynamicBuffers()) return 1; pDevice = 0; pContext = 0; return 0; } void BeginDisplay() { if (LostDevice) CreateDynamicBuffers(); } // Called for a lost device. void ReleaseDynamicBuffers(bool lostDevice = 0); // *** Vertex Upload / Streaming interfaces. // We allow for two types of streaming: // // 1. Direct upload by calling Lock/UnloadVertexBuffer. Such data // can be rendered by calling DrawPrimitive after unlock. // // 2. Indexed upload through SetVertexData + SetIndexData. This data is // rendered through PrepareTriangleData / DrawTriangles. // // Raw buffer locking; also configures the stream. void* LockVertexBuffer(UInt vertexCount, UInt vertexSize); void UnlockVertexBuffer(); void* LockIndexBuffer(UInt indexCount, UInt indexSize); void UnlockIndexBuffer(); // Copies vertices directly into buffer; uses lock. bool InitVertexBufferData(int vertexIndex, int vertexCount); // Indexed data specification, used by PrepareVertexData. void SetVertexData(const void* pvertices, int numVertices, VertexFormat vf); void SetIndexData(const void* pindices, int numIndices, IndexFormat idxf); // Specifies data ranges for DrawTriangles. bool PrepareVertexData(const PrimitiveDesc &prim); // Renders triangles prepared in buffer. void DrawTriangles(); // Data availability check methods. bool HasVertexData() const { return (pVertexData != 0); } bool HasIndexData() const { return (pIndexData != 0); } VertexFormat GetVertexFormat() const { return VertexFmt; } DXGI_FORMAT GetD3DIndexFormat() const { return IndexFmt; } UInt GetStartIndex() const { return VBDataIndex; } }; // *** Dynamic Vertex Stream implementation GDynamicVertexStreamD3D1x::GDynamicVertexStreamD3D1x() { pDevice = 0; pContext = 0; LostDevice = 0; pVertexData = 0; pIndexData = 0; IndexFmt = DXGI_FORMAT_R16_UINT; VertexFmt = GRenderer::Vertex_None; VertexCount = 0; IndexCount = 0; NextIBOffset = 0; NextVBOffset = 0; VBDataInBuffer = 0; IBDataInBuffer = 0; VBDataOffset = 0; IBDataOffset = 0; VBDataIndex = 0; GCOMPILER_ASSERT(DefaultVertexBufferSize >= GlyphBufferSize); } GDynamicVertexStreamD3D1x::~GDynamicVertexStreamD3D1x() { ReleaseDynamicBuffers(); } // Helper function to create vertex and index buffers bool GDynamicVertexStreamD3D1x::CreateDynamicBuffers() { VertexBufferSize = DefaultVertexBufferSize; IndexBufferSize = DefaultIndexBufferSize; if (!pVertexBuffer) { HRESULT createResult; D3Dx(BUFFER_DESC) bd; // Min acceptable buffer must be large enough for glyphs due to logic used in DrawBitmaps. GCOMPILER_ASSERT(DefaultVertexBufferSize/8 >= GlyphBufferSize); memset(&bd, 0, sizeof(D3Dx(BUFFER_DESC))); bd.Usage = D3Dx(USAGE_DYNAMIC); bd.BindFlags = D3Dx(BIND_VERTEX_BUFFER); bd.CPUAccessFlags = D3Dx(CPU_ACCESS_WRITE); // Loop trying several vertex buffer sizes. do { bd.ByteWidth = VertexBufferSize; createResult = pDevice->CreateBuffer(&bd, NULL, &pVertexBuffer.GetRawRef()); // If failed, try smaller size while reasonable. if (FAILED(createResult)) { if (VertexBufferSize > DefaultVertexBufferSize/8) VertexBufferSize = VertexBufferSize / 2; else { GFC_DEBUG_WARNING(1, "GRendererD3D1x - Failed to create dynamic vertex buffer"); VertexBufferSize = 0; return 0; } } } while(FAILED(createResult)); } if (!pIndexBuffer) { HRESULT createResult; D3Dx(BUFFER_DESC) bd; memset(&bd, 0, sizeof(D3Dx(BUFFER_DESC))); bd.Usage = D3Dx(USAGE_DYNAMIC); bd.BindFlags = D3Dx(BIND_INDEX_BUFFER); bd.CPUAccessFlags = D3Dx(CPU_ACCESS_WRITE); do { bd.ByteWidth = IndexBufferSize; createResult = pDevice->CreateBuffer(&bd, NULL, &pIndexBuffer.GetRawRef()); // If failed, try smaller size while reasonable. if (FAILED(createResult)) { if (IndexBufferSize > DefaultIndexBufferSize/4) IndexBufferSize = IndexBufferSize / 2; else { GFC_DEBUG_WARNING(1, "GRendererD3D1x - Failed to create dynamic index buffer"); pVertexBuffer = 0; // Release VB. IndexBufferSize = 0; VertexBufferSize = 0; return 0; } } } while(FAILED(createResult)); } // Initialize buffer pointers to the beginning. NextIBOffset = 0; NextVBOffset = 0; VBDataInBuffer = 0; IBDataInBuffer = 0; VBDataOffset = 0; IBDataOffset = 0; VertexSizeInBuffer = 0; RenderMethod = RM_None; MaxTriangleCount = 0; // After successful creation buffers are no longer lost. LostDevice = 0; return 1; } void GDynamicVertexStreamD3D1x::ReleaseDynamicBuffers(bool lostDevice) { pVertexBuffer = 0; pIndexBuffer = 0; VertexBufferSize = 0; IndexBufferSize = 0; RenderMethod = RM_None; MaxTriangleCount = 0; LostDevice = lostDevice; } void GDynamicVertexStreamD3D1x::SetVertexData(const void* pvertices, int numVertices, VertexFormat vf) { pVertexData = pvertices; VertexFmt = vf; VertexCount = numVertices; VBDataInBuffer = 0; VertexSize = 2 * sizeof(SInt16); switch(VertexFmt) { case GRenderer::Vertex_None: VertexSize = 0; break; case GRenderer::Vertex_XY16iC32: VertexSize = sizeof(GRenderer::VertexXY16iC32); break; case GRenderer::Vertex_XY16iCF32: VertexSize = sizeof(GRenderer::VertexXY16iCF32); break; } } void GDynamicVertexStreamD3D1x::SetIndexData(const void* pindices, int numIndices, IndexFormat idxf) { pIndexData = pindices; IndexCount = numIndices; IBDataInBuffer = 0; switch(idxf) { case GRenderer::Index_None: IndexFmt = DXGI_FORMAT_UNKNOWN; break; case GRenderer::Index_16: IndexFmt = DXGI_FORMAT_R16_UINT; break; case GRenderer::Index_32: IndexFmt = DXGI_FORMAT_R32_UINT; break; } } bool GDynamicVertexStreamD3D1x::PrepareVertexData(const PrimitiveDesc &prim) { if (LostDevice) return 0; Primitive = prim; // Simple heuristic for performance: If VBs are large, use indexed buffers; // otherwise un-index and upload vertices manually. This keeps the Lock // overhead lower, since we only have to lock one buffer instead of two // for small objects. if (VertexCount < 32) { MaxTriangleCount = VertexBufferSize / (VertexSize * 3); RenderMethod = RM_NotIndexed; } else { // Upload vertex data if it is not there. if (!VBDataInBuffer) { // If vertex buffer is not large enough, we must render without indexing. if (VertexCount * VertexSize > VertexBufferSize) { // Will need to render 3 vertices at a time. MaxTriangleCount = VertexBufferSize / (VertexSize * 3); RenderMethod = RM_NotIndexed; return 1; } void * pbuffer = LockVertexBuffer(VertexCount, VertexSize); if (pbuffer) { memcpy(pbuffer, pVertexData, VertexCount * VertexSize); UnlockVertexBuffer(); VBDataInBuffer = 1; } else { RenderMethod = RM_None; return 0; } } // Upload index data. if (!IBDataInBuffer) { UInt indexSize = (IndexFmt == DXGI_FORMAT_R16_UINT) ? sizeof(UInt16) : sizeof(UInt32); if (IndexCount * indexSize > IndexBufferSize) { MaxTriangleCount = IndexBufferSize / (indexSize * 3); RenderMethod = RM_IndexedInChunks; return 1; } void * pbuffer = LockIndexBuffer(IndexCount, indexSize); if (pbuffer) { // Need to know the right index buffer spot. memcpy(pbuffer, pIndexData, IndexCount * indexSize); UnlockIndexBuffer(); IBDataInBuffer = 1; } else { RenderMethod = RM_None; return 0; } } RenderMethod = RM_Indexed; } return 1; } void GDynamicVertexStreamD3D1x::DrawTriangles() { if (RenderMethod == RM_Indexed) { // Draw the mesh with indexed buffers. GASSERT(IndexFmt == DXGI_FORMAT_R16_UINT); pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLELIST)); pContext->DrawIndexed(Primitive.TriangleCount * 3, (IBDataOffset / sizeof(UInt16)) + Primitive.StartIndex, Primitive.BaseVertexIndex); } else if (RenderMethod == RM_NotIndexed) { // Vertex buffer could not fit, render in chunks with software indexing. int triangles = 0; // For now, InitVerticesFromIndex doesn't handle 32 bit buffers. GASSERT(IndexFmt == DXGI_FORMAT_R16_UINT); while(triangles < Primitive.TriangleCount) { int batch = G_Min(Primitive.TriangleCount - triangles, MaxTriangleCount); void * pbuffer = LockVertexBuffer(batch * 3, VertexSize); if (!pbuffer) return; // Copy this batches indices. InitVerticesFromIndex(pbuffer, Primitive.BaseVertexIndex, VertexSize, Primitive.StartIndex + (triangles * 3), batch); UnlockVertexBuffer(); // Draw using uploaded data. pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLELIST)); pContext->Draw(batch * 3, GetStartIndex()); triangles += batch; } } else if (RenderMethod == RM_IndexedInChunks) { // We have vertex buffer, but not index buffer. int triangles = 0; while(triangles < Primitive.TriangleCount) { int batch = G_Min(Primitive.TriangleCount - triangles, MaxTriangleCount); UInt indexSize = (IndexFmt == DXGI_FORMAT_R16_UINT) ? sizeof(UInt16) : sizeof(UInt32); void * pbuffer = LockIndexBuffer(batch * 3, indexSize); if (!pbuffer) return; // Copy this indices for this batch. memcpy( pbuffer, ((UByte*)pIndexData) + (Primitive.StartIndex +(triangles * 3)) * indexSize, batch * 3 * indexSize ); UnlockIndexBuffer(); // Draw using uploaded data. pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLELIST)); pContext->DrawIndexed(batch * 3, (IBDataOffset / sizeof(UInt16)), Primitive.BaseVertexIndex); triangles += batch; } } } // Lock / Unlock logic. void* GDynamicVertexStreamD3D1x::LockVertexBuffer(UInt vertexCount, UInt vertexSize) { if (LostDevice) return 0; UInt size = vertexCount * vertexSize; if ((size > VertexBufferSize) || !pVertexBuffer) { GASSERT(0); return 0; } D3Dx(MAP) lockFlags; void* pbuffer = 0; UInt offset; VBDataInBuffer = 0; // Determine where in the buffer we go. if (size + NextVBOffset + vertexSize < VertexBufferSize) { offset = NextVBOffset; NextVBOffset = offset + size; lockFlags = D3Dx(MAP_WRITE_NO_OVERWRITE); } else { offset = 0; NextVBOffset = size; lockFlags = D3Dx(MAP_WRITE_DISCARD); } #if (GFC_D3D_VERSION == 10) if (FAILED(pVertexBuffer->Map(lockFlags, 0, &pbuffer))) return 0; #else D3Dx(MAPPED_SUBRESOURCE) map; if (FAILED(pContext->Map(pVertexBuffer, 0, lockFlags, 0, &map))) return 0; pbuffer = map.pData; #endif VBDataOffset = offset; VertexSizeInBuffer = vertexSize; return ((UByte*)pbuffer) + offset; } void GDynamicVertexStreamD3D1x::UnlockVertexBuffer() { #if (GFC_D3D_VERSION == 10) pVertexBuffer->Unmap(); #else pContext->Unmap(pVertexBuffer, 0); #endif // Set this buffer offset on a device. pContext->IASetVertexBuffers(0, 1, &pVertexBuffer.GetRawRef(), &VertexSizeInBuffer, &VBDataOffset); } void* GDynamicVertexStreamD3D1x::LockIndexBuffer(UInt indexCount, UInt indexSize) { if (LostDevice) return 0; UInt size = indexCount * indexSize; if ((size > IndexBufferSize) || !pIndexBuffer) { GASSERT(0); return 0; } D3Dx(MAP) lockFlags; void* pbuffer = 0; UInt offset; IBDataInBuffer = 0; // Determine where in the buffer we go. if ((IndexBufferSize - NextIBOffset) > size) { offset = NextIBOffset; NextIBOffset = NextIBOffset + size; lockFlags = D3Dx(MAP_WRITE_NO_OVERWRITE); } else { offset = 0; NextIBOffset = size; lockFlags = D3Dx(MAP_WRITE_DISCARD); } #if (GFC_D3D_VERSION == 10) if (FAILED(pIndexBuffer->Map(lockFlags, 0, (void**)&pbuffer))) return 0; #else D3Dx(MAPPED_SUBRESOURCE) map; if (FAILED(pContext->Map(pIndexBuffer, 0, lockFlags, 0, &map))) return 0; pbuffer = map.pData; #endif IBDataOffset = offset; return ((UByte*)pbuffer) + offset; } void GDynamicVertexStreamD3D1x::UnlockIndexBuffer() { #if (GFC_D3D_VERSION == 10) pIndexBuffer->Unmap(); #else pContext->Unmap(pIndexBuffer, 0); #endif pContext->IASetIndexBuffer(pIndexBuffer, IndexFmt, 0); } bool GDynamicVertexStreamD3D1x::InitVertexBufferData(int vertexIndex, int vertexCount) { GASSERT(UInt(vertexCount + vertexIndex) <= VertexCount); void * pbuffer = LockVertexBuffer(vertexCount, VertexSize); if (!pbuffer) return 0; memcpy(pbuffer, ((UByte*)pVertexData) + vertexIndex * VertexSize, vertexCount * VertexSize); UnlockVertexBuffer(); return 1; } // Performs un-indexing and copy of data into the target vertex buffer. void GDynamicVertexStreamD3D1x::InitVerticesFromIndex( void *pvertexDest, int baseVertexIndex, int vertexSize, int startIndex, int triangleCount ) { UInt16* pindices = ((UInt16*)pIndexData) + startIndex; UByte* pvertices = ((UByte*)pVertexData) + baseVertexIndex * vertexSize; UByte* pdest = (UByte*) pvertexDest; // 32-bit pointer versions. UInt32* pv32 = (UInt32*) pvertices; UInt32* pd32 = (UInt32*) pdest; GCOMPILER_ASSERT(sizeof(UInt32) == 4); int i; switch(vertexSize) { case 4: for(i = 0; i< triangleCount; i++, pd32 += 3) { pd32[0] = pv32[*(pindices + 0)]; pd32[1] = pv32[*(pindices + 1)]; pd32[2] = pv32[*(pindices + 2)]; pindices += 3; } break; case 8: GCOMPILER_ASSERT(sizeof(VertexXY16iC32) == 8); for(i = 0; i< triangleCount; i++, pd32 += 3 * 2) { pd32[0] = pv32[*pindices * 2]; pd32[1] = pv32[*pindices * 2 + 1]; pindices ++; pd32[2] = pv32[*pindices * 2]; pd32[3] = pv32[*pindices * 2 + 1]; pindices ++; pd32[4] = pv32[*pindices * 2]; pd32[5] = pv32[*pindices * 2 + 1]; pindices ++; } break; case 12: GCOMPILER_ASSERT(sizeof(VertexXY16iCF32) == 12); for(i = 0; i< triangleCount; i++, pd32 += 3 * 3) { pd32[0] = pv32[*pindices * 3]; pd32[1] = pv32[*pindices * 3 + 1]; pd32[2] = pv32[*pindices * 3 + 2]; pindices ++; pd32[3] = pv32[*pindices * 3]; pd32[4] = pv32[*pindices * 3 + 1]; pd32[5] = pv32[*pindices * 3 + 2]; pindices ++; pd32[6] = pv32[*pindices * 3]; pd32[7] = pv32[*pindices * 3 + 1]; pd32[8] = pv32[*pindices * 3 + 2]; pindices ++; } break; default: for(i = 0; i< triangleCount; i++, pdest += vertexSize*3) { memcpy(pdest, pvertices + *pindices * vertexSize, vertexSize); pindices ++; memcpy(pdest, pvertices + *pindices * vertexSize, vertexSize); pindices ++; memcpy(pdest, pvertices + *pindices * vertexSize, vertexSize); pindices ++; } break; } } // ***** GRendererD3D1x Implementation class GRendererD3D1xImpl : public GRendererD3D1x { public: // Some renderer state. bool ModeSet; SInt RenderMode; // Shaders and declarations that are currently set. VDeclType VDeclIndex; VShaderType VShaderIndex; // Current pixel shader index PixelShaderType PShaderIndex; // Video Mode Configuration Flags (VMConfigFlags) UInt32 VMCFlags; // render target GPtr pRenderTarget; GPtr pDepthStencil; // Created vertex declarations and shaders. GPtr VertexDecls[VS_Count]; GPtr VertexShaders[VS_Count]; GPtr FilterVertexShaders[VS2_Count]; GPtr VShaderConst; struct VShaderConstData { float mvp[4][4]; float texgen[16]; }; VShaderConstData VShaderData; // Allocated pixel shaders GPtr PixelShaders[PS_Count]; GPtr FilterPixelShaders[FS2_Count]; GPtr PShaderConst; GPtr SamplerStates[4]; GPtr BlendStates[BlendDesc_Count]; GPtr BlendStatesSrcAc[BlendDesc_Count]; GPtr RasterStates[2]; // No multisample, multisample GPtr StencilDisabled, StencilSet, StencilIncr, StencilDecr, StencilTest; // Direct3DDevice ID3Dx(Device)* pDevice; #if (GFC_D3D_VERSION == 11) ID3Dx(DeviceContext)* pContext; #endif bool DrawingMask; UInt32 StencilCounter; ID3Dx(DepthStencilState)* pCurStencilMode; // Output size. Float DisplayWidth; Float DisplayHeight; Matrix UserMatrix; Matrix ViewportMatrix; GViewport ViewRect; Matrix CurrentMatrix; 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 stream / dynamic buffers container. GDynamicVertexStreamD3D1x VertexStream; // Current sample mode BitmapSampleMode SampleMode[2]; typedef GArrayConstPolicy<0, 4, true> NeverShrinkPolicy; typedef GArrayLH BlendModeStackType; // Current blend mode BlendType BlendMode; BlendModeStackType BlendModeStack; // render target struct RTState { GRenderTargetD3D1xImpl* pRT; GMatrix2D ViewMatrix; GMatrix3D ViewMatrix3D; GMatrix3D PerspMatrix3D; const GMatrix3D* pWorldMatrix3D; GViewport ViewRect; SInt RenderMode; ID3Dx(DepthStencilState)* pStencilMode; SInt StencilCounter; RTState() { pRT = 0; pWorldMatrix3D = 0; } RTState(GRenderTargetD3D1xImpl* prt, const GMatrix2D& vm, const GMatrix3D& vm3d, const GMatrix3D &vp3d, const GMatrix3D* pw3d, const GViewport& vp, SInt rm, ID3Dx(DepthStencilState)* psm, SInt sc) : pRT(prt), ViewMatrix(vm), ViewMatrix3D(vm3d), PerspMatrix3D(vp3d), pWorldMatrix3D(pw3d), ViewRect(vp), RenderMode(rm), pStencilMode(psm), StencilCounter(sc) { } }; typedef GArrayLH RTStackType; GRenderTargetD3D1xImpl* pCurRenderTarget; RTStackType RenderTargetStack; GArrayLH > TempRenderTargets; GArrayLH > TempStencilBuffers; // Linked list used for buffer cache testing, otherwise holds no data. CacheNode CacheList; // all D3D calls should be done on the main thread, but textures can be released from // different threads so we collect system resources and release them on the main thread GArray ResourceReleasingQueue; GLock ResourceReleasingQueueLock; // this method is called from GTexture and GRenderTarget destructor to put a system resource into releasing queue void AddResourceForReleasing(ID3Dx(DeviceChild)* ptexture) { if (!ptexture) return; GLock::Locker guard(&ResourceReleasingQueueLock); ResourceReleasingQueue.PushBack(ptexture); } // this method is called from GetTexture, EndDisplay and destructor to actually release // collected system resources void ReleaseQueuedResources() { GLock::Locker guard(&ResourceReleasingQueueLock); for (UInt i = 0; i < ResourceReleasingQueue.GetSize(); ++i) ResourceReleasingQueue[i]->Release(); ResourceReleasingQueue.Clear(); } GRendererD3D1xImpl() { ModeSet = 0; VMCFlags = 0; StencilCounter = 0; VDeclIndex = VD_None; VShaderIndex = VS_None; PShaderIndex = PS_None; SampleMode[0] = Sample_Linear; SampleMode[1] = Sample_Linear; pDevice = 0; pContext = 0; BlendMode = Blend_None; RenderMode = 0; pCurRenderTarget = 0; for (int i = 0; i < 16; i++) { VShaderData.mvp[i>>2][i&3] = 0; VShaderData.texgen[i] = 0; } } ~GRendererD3D1xImpl() { Clear(); } void Clear() { if (pDevice) { UInt n = 0; ID3Dx(Buffer) *buf = 0; ID3Dx(SamplerState)* samp[2] = {0,0}; ID3Dx(ShaderResourceView)* srs[2] = {0,0}; pContext->VSSetShader(0 NULL_SHADER_CLASS); pContext->VSSetConstantBuffers(0, 1, &buf); pContext->PSSetConstantBuffers(0, 1, &buf); pContext->PSSetSamplers(0, 2, samp); pContext->PSSetShaderResources(0, 2, srs); pContext->RSSetState(0); pContext->OMSetBlendState(0, 0, 0xffffffff); pContext->OMSetDepthStencilState(0, 0); pContext->IASetIndexBuffer(0, DXGI_FORMAT_UNKNOWN, 0); pContext->IASetInputLayout(0); pContext->IASetVertexBuffers(0, 1, &buf, &n, &n); pContext->PSSetShader(0 NULL_SHADER_CLASS); pContext->VSSetShader(0 NULL_SHADER_CLASS); } // Remove/notify all textures { GLock::Locker guard(&TexturesLock); while (Textures.pFirst != &Textures) ((GTextureD3D1xImpl*)Textures.pFirst)->RemoveFromRenderer(); while (RenderTargets.pFirst != &RenderTargets) ((GRenderTargetD3D1xImpl*)RenderTargets.pFirst)->RemoveFromRenderer(); } TempRenderTargets.Clear(); TempStencilBuffers.Clear(); pDevice = NULL; pContext = NULL; CacheList.ReleaseList(); if (ModeSet) ResetVideoMode(); ReleaseQueuedResources(); } void ReleaseResources() { Clear(); } bool CreateVertexShader(GPtr *pshaderPtr, GPtr *pdeclPtr, const VDeclSpec *pdeclspec, const char* pshaderText) { if (!*pshaderPtr) { GPtr pshader; GPtr pmsg; HRESULT hr; #if (GFC_D3D_VERSION == 10) hr = D3D10CompileShader(pshaderText, strlen(pshaderText), NULL, NULL, NULL, "main", GRENDERER_VSHADER_PROFILE, 0/*D3Dx(SHADER_DEBUG)*/, &pshader.GetRawRef(), &pmsg.GetRawRef()); #else hr = D3DX11CompileFromMemory(pshaderText, strlen(pshaderText), NULL, NULL, NULL, "main", GRENDERER_VSHADER_PROFILE, 0, 0, NULL, &pshader.GetRawRef(), &pmsg.GetRawRef(), NULL); #endif if (FAILED(hr)) { GFC_DEBUG_WARNING1(1, "GRendererD3D1x - VertexShader errors:\n %s ", (pmsg) ? pmsg->GetBufferPointer() : "unknown"); return 0; } if (!pshader || ((hr = pDevice->CreateVertexShader(pshader->GetBufferPointer(), pshader->GetBufferSize() NULL_SHADER_LINKAGE, &pshaderPtr->GetRawRef())) != S_OK) ) { GFC_DEBUG_WARNING1(1, "GRendererD3D1x - Can't create D3D1x vshader; error code = %d", hr); return 0; } if (!*pdeclPtr) { if (pDevice->CreateInputLayout(pdeclspec->pDecl, pdeclspec->Count, pshader->GetBufferPointer(), pshader->GetBufferSize(), &pdeclPtr->GetRawRef()) != S_OK) { GFC_DEBUG_WARNING(1, "GRendererD3D1x - failed to create input layout"); return 0; } } } return 1; } bool CreatePixelShader(GPtr *pshaderPtr, const char* pshaderText) { if (!*pshaderPtr) { GPtr pshader; GPtr pmsg; HRESULT hr; #if (GFC_D3D_VERSION == 10) hr = D3D10CompileShader(pshaderText, strlen(pshaderText), NULL, NULL, NULL, "main", GRENDERER_PSHADER_PROFILE, 0/*D3Dx(SHADER_DEBUG)*/, &pshader.GetRawRef(), &pmsg.GetRawRef()); #else hr = D3DX11CompileFromMemory(pshaderText, strlen(pshaderText), NULL, NULL, NULL, "main", GRENDERER_PSHADER_PROFILE, 0, 0, NULL, &pshader.GetRawRef(), &pmsg.GetRawRef(), NULL); #endif if (FAILED(hr)) { GFC_DEBUG_WARNING2(1, "GRendererD3D1x - PixelShader errors:\n%s\n%s\n", pshaderText, (pmsg) ? pmsg->GetBufferPointer() : "unknown"); return 0; } if (!pshader || ((hr = pDevice->CreatePixelShader(pshader->GetBufferPointer(), pshader->GetBufferSize() NULL_SHADER_LINKAGE, &pshaderPtr->GetRawRef())) != S_OK) ) { GFC_DEBUG_WARNING1(1, "GRendererD3D1x - Can't create D3D1x pshader; error code = %d", hr); return 0; } } return 1; } // Initialize the shaders we use for SWF mesh rendering. bool InitShaders() { bool success = 1; UInt i; for (i = 1; i < VS_Count; i++) if (VertexShaderTextTable[i-1] && !CreateVertexShader(&VertexShaders[i], &VertexDecls[VertexShaderDeclTable[i]], &VertexDeclTypeTable[VertexShaderDeclTable[i]-1], VertexShaderTextTable[i-1])) { success = 0; break; } for (i = 1; i < PS_Count; i++) { if (PixelShaderInitTable[i-1]) { if (!CreatePixelShader(&PixelShaders[i], PixelShaderInitTable[i-1])) { success = 0; break; } } } for (i = 0; i < VS2_Count; i++) { if (VShaderSources2[i]) if (!CreateVertexShader(&FilterVertexShaders[i], &VertexDecls[VD_Filter], &VertexDeclTypeTable[VD_Filter-1], VShaderSources2[i])) { success = 0; break; } } for (i = 0; i < FS2_Count; i++) { if (FShaderSources2[i]) if (!CreatePixelShader(&FilterPixelShaders[i], FShaderSources2[i])) { success = 0; break; } } // constant buffers D3Dx(BUFFER_DESC) bd; memset(&bd, 0, sizeof(D3Dx(BUFFER_DESC))); bd.Usage = D3Dx(USAGE_DEFAULT); bd.BindFlags = D3Dx(BIND_CONSTANT_BUFFER); bd.ByteWidth = sizeof(VShaderConstData); success &= (pDevice->CreateBuffer(&bd, NULL, &VShaderConst.GetRawRef()) == S_OK); bd.ByteWidth = 128; success &= (pDevice->CreateBuffer(&bd, NULL, &PShaderConst.GetRawRef()) == S_OK); // blending for (i = 1; i < BlendDesc_Count; i++) { success &= (pDevice->CreateBlendState(&BlendDescs[i], &BlendStates[i].GetRawRef()) == S_OK); success &= (pDevice->CreateBlendState(&BlendDescsSrcAc[i], &BlendStatesSrcAc[i].GetRawRef()) == S_OK); } D3Dx(DEPTH_STENCIL_DESC) ds; memset(&ds, 0, sizeof(D3Dx(DEPTH_STENCIL_DESC))); ds.DepthFunc = D3Dx(COMPARISON_ALWAYS); ds.FrontFace.StencilDepthFailOp = ds.BackFace.StencilDepthFailOp = D3Dx(STENCIL_OP_KEEP); ds.FrontFace.StencilFailOp = ds.BackFace.StencilFailOp = D3Dx(STENCIL_OP_KEEP); ds.FrontFace.StencilPassOp = ds.BackFace.StencilPassOp = D3Dx(STENCIL_OP_KEEP); ds.FrontFace.StencilFunc = ds.BackFace.StencilFunc = D3Dx(COMPARISON_ALWAYS); success &= (pDevice->CreateDepthStencilState(&ds, &StencilDisabled.GetRawRef()) == S_OK); ds.StencilEnable = 1; ds.StencilReadMask = 0xff; ds.StencilWriteMask = 0xff; ds.FrontFace.StencilPassOp = ds.BackFace.StencilPassOp = D3Dx(STENCIL_OP_REPLACE); ds.FrontFace.StencilFunc = ds.BackFace.StencilFunc = D3Dx(COMPARISON_ALWAYS); success &= (pDevice->CreateDepthStencilState(&ds, &StencilSet.GetRawRef()) == S_OK); ds.StencilReadMask = 0xff; ds.StencilWriteMask = 0xff; ds.FrontFace.StencilPassOp = ds.BackFace.StencilPassOp = D3Dx(STENCIL_OP_INCR); ds.FrontFace.StencilFunc = ds.BackFace.StencilFunc = D3Dx(COMPARISON_EQUAL); success &= (pDevice->CreateDepthStencilState(&ds, &StencilIncr.GetRawRef()) == S_OK); ds.FrontFace.StencilPassOp = ds.BackFace.StencilPassOp = D3Dx(STENCIL_OP_DECR); ds.FrontFace.StencilFunc = ds.BackFace.StencilFunc = D3Dx(COMPARISON_EQUAL); success &= (pDevice->CreateDepthStencilState(&ds, &StencilDecr.GetRawRef()) == S_OK); ds.StencilWriteMask = 0; ds.FrontFace.StencilPassOp = ds.BackFace.StencilPassOp = D3Dx(STENCIL_OP_KEEP); ds.FrontFace.StencilFunc = ds.BackFace.StencilFunc = D3Dx(COMPARISON_EQUAL); success &= (pDevice->CreateDepthStencilState(&ds, &StencilTest.GetRawRef()) == S_OK); // samplers D3Dx(SAMPLER_DESC) sd; memset(&sd, 0, sizeof(D3Dx(SAMPLER_DESC))); sd.MipLODBias = -0.5; sd.ComparisonFunc = D3Dx(COMPARISON_ALWAYS); sd.Filter = D3Dx(FILTER_MIN_MAG_MIP_POINT); sd.MinLOD = 0; sd.MaxLOD = 0; sd.AddressU = sd.AddressV = sd.AddressW = D3Dx(TEXTURE_ADDRESS_WRAP); success &= (pDevice->CreateSamplerState(&sd, &SamplerStates[0].GetRawRef()) == S_OK); sd.Filter = D3Dx(FILTER_MIN_MAG_MIP_LINEAR); sd.AddressU = sd.AddressV = sd.AddressW = D3Dx(TEXTURE_ADDRESS_WRAP); success &= (pDevice->CreateSamplerState(&sd, &SamplerStates[1].GetRawRef()) == S_OK); sd.Filter = D3Dx(FILTER_MIN_MAG_MIP_POINT); sd.AddressU = sd.AddressV = sd.AddressW = D3Dx(TEXTURE_ADDRESS_CLAMP); success &= (pDevice->CreateSamplerState(&sd, &SamplerStates[2].GetRawRef()) == S_OK); sd.Filter = D3Dx(FILTER_MIN_MAG_MIP_LINEAR); sd.AddressU = sd.AddressV = sd.AddressW = D3Dx(TEXTURE_ADDRESS_CLAMP); success &= (pDevice->CreateSamplerState(&sd, &SamplerStates[3].GetRawRef()) == S_OK); if (!success) { ReleaseShaders(); return 0; } return 1; } void ReleaseShaders() { UInt i; for (i=0; i< VD_Count; i++) VertexDecls[i] = 0; for (i=0; i< VS_Count; i++) VertexShaders[i] = 0; for (i=0; i< PS_Count; i++) PixelShaders[i] = 0; for (i=0; i< BlendDesc_Count; i++) BlendStates[i] = 0; for (i=0; i< 4; i++) SamplerStates[i] = 0; VShaderConst = 0; PShaderConst = 0; RasterStates[0] = 0; RasterStates[1] = 0; StencilDisabled = StencilSet = StencilIncr = StencilDecr = StencilTest = 0; } // Sets a shader to specified type. void SetVertexDecl(VDeclType vd) { if (VDeclIndex != vd) { VDeclIndex = vd; pContext->IASetInputLayout(VertexDecls[vd]); } } void SetVertexShader(VShaderType vtin) { SInt vt = vtin; #ifndef GFC_NO_3D if (pWorldMatrix) vt += VS_CountSrc; #endif if (VShaderIndex != vt) { GASSERT(VertexShaderDeclTable[vt] == VDeclIndex); VShaderIndex = (VShaderType)vt; pContext->VSSetShader(VertexShaders[vt] NULL_SHADER_CLASS); } } // Set shader to a device based on a constant void SetPixelShader(PixelShaderType shaderType) { if (shaderType != PShaderIndex) { pContext->PSSetShader(PixelShaders[shaderType] NULL_SHADER_CLASS); PShaderIndex = shaderType; } } // Utility. Mutates *width, *height and *data to create the // next mip level. static void MakeNextMiplevel(int* width, int* height, UByte* data) { GASSERT(width); GASSERT(height); GASSERT(data); GASSERT_ON_RENDERER_MIPMAP_GEN; int newW = *width >> 1; int newH = *height >> 1; if (newW < 1) newW = 1; if (newH < 1) newH = 1; if (newW * 2 != *width || newH * 2 != *height) { // Image can't be shrunk Along (at least) one // of its dimensions, so don't bother // resampling. Technically we should, but // it's pretty useless at this point. Just // change the image dimensions and leave the // existing pixels. } else { // Resample. Simple average 2x2 --> 1, in-place. for (int j = 0; j < newH; j++) { UByte* out = ((UByte*) data) + j * newW; UByte* in = ((UByte*) data) + (j << 1) * *width; for (int i = 0; i < newW; i++) { int a = (*(in + 0) + *(in + 1) + *(in + 0 + *width) + *(in + 1 + *width)); *(out) = UByte(a >> 2); out++; in += 2; } } } // Munge parameters to reflect the shrunken image. *width = newW; *height = newH; } // Fill helper function: // Applies fill texture by setting it to the specified stage, initializing samplers and vertex constants void ApplyFillTexture(const FillTexture &fill, UInt stageIndex) { GASSERT (fill.pTexture != 0); if (fill.pTexture == 0) return; // avoid crash in release build GTextureD3D1xImpl* ptexture = ((GTextureD3D1xImpl*)fill.pTexture); ptexture->Bind(stageIndex, fill.WrapMode, fill.SampleMode, 0); // Set up the bitmap matrix for texgen. const Matrix& m = fill.TextureMatrix; VShaderData.texgen[stageIndex*8+0] = m.M_[0][0]; VShaderData.texgen[stageIndex*8+1] = m.M_[0][1]; VShaderData.texgen[stageIndex*8+3] = m.M_[0][2]; VShaderData.texgen[stageIndex*8+4] = m.M_[1][0]; VShaderData.texgen[stageIndex*8+5] = m.M_[1][1]; VShaderData.texgen[stageIndex*8+7] = m.M_[1][2]; // actual data upload occurs in SetShaderConstants } void ApplyPShaderCxform(const Cxform &cxform) const { const float mult = 1.0f / 255.0f; float cxformData[4 * 2] = { cxform.M_[0][0], cxform.M_[1][0], cxform.M_[2][0], cxform.M_[3][0], // Cxform color is already pre-multiplied cxform.M_[0][1] * mult, cxform.M_[1][1] * mult, cxform.M_[2][1] * mult, cxform.M_[3][1] * mult }; pContext->UpdateSubresource(PShaderConst, 0, NULL, cxformData, 0, 0); } void SetShaderConstants() { pContext->UpdateSubresource(VShaderConst, 0, NULL, &VShaderData, 0, 0); } class GFxFillStyle { public: enum FillMode { FM_None, FM_Color, FM_Bitmap, FM_Gouraud }; FillMode Mode; GouraudFillType GouraudType; GColor Color; Cxform BitmapColorTransform; bool HasNonzeroAdditiveCxform; FillTexture Fill; FillTexture Fill2; GFxFillStyle() { Mode = FM_None; GouraudType = GFill_Color; Fill.pTexture = 0; Fill2.pTexture = 0; HasNonzeroAdditiveCxform = 0; } // Push our style into Direct3D void Apply(GRendererD3D1xImpl *prenderer) const { GASSERT(Mode != FM_None); // Complex prenderer->ApplyBlendMode(((Color.GetAlpha()== 0xFF) && (prenderer->BlendMode <= Blend_Normal) && (Mode != FM_Gouraud)) ? Blend_None : prenderer->BlendMode); // Non-Edge AA Rendering. if (Mode == FM_Color) { // Solid color fills. prenderer->SetPixelShader(PS_SolidColor); prenderer->ApplyColor(Color); } else if (Mode == FM_Bitmap) { // Gradiend and texture fills. GASSERT(Fill.pTexture != NULL); prenderer->ApplyColor(Color); if (Fill.pTexture == NULL) { // Just in case, for release build. prenderer->SetPixelShader(PS_None); } else { if ((prenderer->BlendMode == Blend_Multiply) || (prenderer->BlendMode == Blend_Darken) ) prenderer->SetPixelShader(PS_CxformTextureMultiply); else prenderer->SetPixelShader(PS_CxformTexture); prenderer->ApplyPShaderCxform(BitmapColorTransform); prenderer->ApplyFillTexture(Fill, 0); } } // Edge AA - relies on Gouraud shading and texture mixing else if (Mode == FM_Gouraud) { PixelShaderType shader = PS_None; // No texture: generate color-shaded triangles. if (Fill.pTexture == NULL) { if (prenderer->VertexStream.GetVertexFormat() == Vertex_XY16iC32) { // Cxform Alpha Add can not be non-zero is this state because // cxform blend equations can not work correctly. // If we hit this assert, it means Vertex_XY16iCF32 should have been used. //GASSERT (BitmapColorTransform.M_[3][1] < 1.0f); shader = PS_CxformGauraudNoAddAlpha; } else { shader = PS_CxformGauraud; } } // We have a textured or multi-textured gouraud case. else { prenderer->ApplyFillTexture(Fill, 0); if ((GouraudType == GFill_1TextureColor) || (GouraudType == GFill_1Texture)) { shader = PS_CxformGauraudTexture; } else { shader = PS_Cxform2Texture; prenderer->ApplyFillTexture(Fill2, 1); } } if ((prenderer->BlendMode == Blend_Multiply) || (prenderer->BlendMode == Blend_Darken) ) { shader = (PixelShaderType)(shader + (PS_CxformGauraudMultiply - PS_CxformGauraud)); // For indexing to work, these should hold: GCOMPILER_ASSERT( (PS_Cxform2Texture - PS_CxformGauraud) == (PS_CxformMultiply2Texture - PS_CxformGauraudMultiply)); GCOMPILER_ASSERT( (PS_CxformGauraudMultiply - PS_CxformGauraud) == (PS_Cxform2Texture - PS_CxformGauraud + 1) ); } prenderer->SetPixelShader(shader); prenderer->ApplyPShaderCxform(BitmapColorTransform); } } 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; } }; // class GFxFillStyle // Style state. enum StyleIndex { FILL_STYLE = 0, LINE_STYLE, STYLE_COUNT }; GFxFillStyle CurrentStyles[STYLE_COUNT]; // Given an image, returns a Pointer to a GTexture struct // that can later be passed to FillStyleX_bitmap(), to set a // bitmap fill style. GTextureD3D1x* CreateTexture() { ReleaseQueuedResources(); GLock::Locker guard(&TexturesLock); return new GTextureD3D1xImpl(this); } GTextureD3D1x* CreateTextureYUV() { ReleaseQueuedResources(); GLock::Locker guard(&TexturesLock); return new GTextureD3D1xImplYUV(this); } GTextureD3D1x* CreateDepthStencilBuffer() { ReleaseQueuedResources(); GLock::Locker guard(&TexturesLock); return new GTextureD3D1xImpl(this); } // Helper function to query renderer capabilities. bool GetRenderCaps(RenderCaps *pcaps) { if (!ModeSet) { GFC_DEBUG_WARNING(1, "GRendererD3D1x::GetRenderCaps fails - video mode not set"); return 0; } pcaps->CapBits = Cap_Index16 | Cap_FillGouraud | Cap_CxformAdd | Cap_FillGouraudTex | Cap_NestedMasks | Cap_TexNonPower2 | Cap_TexNonPower2Wrap | Cap_TexNonPower2Mip | Cap_RenderTargets | Cap_Filter_ColorMatrix | Cap_Filter_Blurs | Cap_RenderTargetNonPow2; pcaps->BlendModes = (1<VertexFormats= (1<MaxTextureSize = 2048; return 1; } GRenderTargetD3D1xImpl* CreateRenderTarget() { ReleaseQueuedResources(); GLock::Locker guard(&TexturesLock); return new GRenderTargetD3D1xImpl(this); } void SetDisplayRenderTarget(GRenderTarget* prt, bool setstate = 1) { GASSERT(RenderTargetStack.GetSize() == 0); pCurRenderTarget = (GRenderTargetD3D1xImpl*)prt; if (setstate) pContext->OMSetRenderTargets(1, &pCurRenderTarget->pRenderTextureView, pCurRenderTarget->pRTDepthStencil); } void PushRenderTarget(const GRectF& frameRect, GRenderTarget* prt) { RenderTargetStack.PushBack(RTState(pCurRenderTarget,ViewportMatrix, #ifndef GFC_NO_3D ViewMatrix, ProjMatrix, pWorldMatrix, #else GMatrix3D(), GMatrix3D(), 0, #endif ViewRect,RenderMode, pCurStencilMode, StencilCounter)); pCurRenderTarget = (GRenderTargetD3D1xImpl*)prt; float dx = frameRect.Width(); float dy = frameRect.Height(); if (dx < 1) { dx = 1; } if (dy < 1) { dy = 1; } ViewportMatrix.SetIdentity(); ViewportMatrix.M_[0][0] = 2.0f / dx; ViewportMatrix.M_[1][1] = -2.0f / dy; ViewportMatrix.M_[0][2] = -1.0f - ViewportMatrix.M_[0][0] * (frameRect.Left); ViewportMatrix.M_[1][2] = 1.0f - ViewportMatrix.M_[1][1] * (frameRect.Top); #ifndef GFC_NO_3D // set 3D view GMatrix3D matView, matPersp; MakeViewAndPersp3D(frameRect, matView, matPersp, DEFAULT_FLASH_FOV, true /*invert Y */); SetView3D(matView); SetPerspective3D(matPersp); SetWorld3D(0); #endif if (pCurRenderTarget->pTexture) ViewRect = GViewport(pCurRenderTarget->pTexture->Desc.Width, pCurRenderTarget->pTexture->Desc.Height, 0,0,pCurRenderTarget->TargetWidth, pCurRenderTarget->TargetHeight, GViewport::View_RenderTextureAlpha); else // Must be external render target, assuming viewport will be set by BeginDisplay. ViewRect = GViewport(64,64,0,0,64,64, GViewport::View_RenderTextureAlpha); RenderMode = GViewport::View_AlphaComposite; ID3Dx(ShaderResourceView) *nulls[] = {0,0,0,0}; pContext->PSSetShaderResources(0, 4, nulls); pContext->OMSetRenderTargets(1, &pCurRenderTarget->pRenderTextureView, pCurRenderTarget->pRTDepthStencil); pContext->OMSetDepthStencilState(StencilDisabled, 0); D3Dx(VIEWPORT) vp; vp.TopLeftX = (D3DxVIEWPORT_COORD)ViewRect.Left; vp.TopLeftY = (D3DxVIEWPORT_COORD)ViewRect.Top; vp.Width = (D3DxVIEWPORT_COORD)ViewRect.Width; vp.Height = (D3DxVIEWPORT_COORD)ViewRect.Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; pContext->RSSetViewports(1, &vp); const float clear[] = {0,0,0,0}; pContext->ClearRenderTargetView(pCurRenderTarget->pRenderTextureView, clear); if (pCurRenderTarget->pRTDepthStencil) pContext->ClearDepthStencilView(pCurRenderTarget->pRTDepthStencil, D3Dx(CLEAR_DEPTH) | D3Dx(CLEAR_STENCIL), 1.0f, 0); ApplyBlendMode(BlendMode); } void PopRenderTarget() { if (pCurRenderTarget->IsTemp) pCurRenderTarget->pStencilTexture = 0; RTState rts = RenderTargetStack.Back(); RenderTargetStack.PopBack(); pCurRenderTarget = rts.pRT; ViewportMatrix = rts.ViewMatrix; ViewRect = rts.ViewRect; RenderMode = rts.RenderMode; pCurStencilMode = rts.pStencilMode; StencilCounter = rts.StencilCounter; #ifndef GFC_NO_3D // restore 3D view matrix SetView3D(rts.ViewMatrix3D); SetPerspective3D(rts.PerspMatrix3D); SetWorld3D(rts.pWorldMatrix3D); #endif ID3Dx(ShaderResourceView) *nulls[] = {0,0,0,0}; pContext->PSSetShaderResources(0, 4, nulls); pContext->OMSetRenderTargets(1, &pCurRenderTarget->pRenderTextureView, pCurRenderTarget->pRTDepthStencil); pContext->OMSetDepthStencilState(pCurStencilMode, StencilCounter); D3Dx(VIEWPORT) vp; vp.TopLeftX = (D3DxVIEWPORT_COORD)ViewRect.Left; vp.TopLeftY = (D3DxVIEWPORT_COORD)ViewRect.Top; vp.Width = (D3DxVIEWPORT_COORD)ViewRect.Width; vp.Height = (D3DxVIEWPORT_COORD)ViewRect.Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; pContext->RSSetViewports(1, &vp); ApplyBlendMode(BlendMode); } GTextureD3D1xImpl* PushTempRenderTarget(const GRectF& frameRect, UInt inw, UInt inh, bool wantStencil) { GASSERT(DrawingMask == 0); GRenderTargetD3D1xImpl* 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->Desc.Width >= SInt(inw) && TempRenderTargets[i]->pTexture->Desc.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 = (GRenderTargetD3D1xImpl *)CreateRenderTarget(); pRT->IsTemp = true; pRT->pTexture = (GTextureD3D1xImpl*) prtt; // will be properly initialized later TempRenderTargets.PushBack(*pRT); prtt->Release(); needsinit = true; } dostencil: GTextureD3D1xImpl* pDepthStencil = 0; if (w < SInt(pRT->pTexture->Desc.Width)) w = SInt(pRT->pTexture->Desc.Width); if (h < SInt(pRT->pTexture->Desc.Height)) h = SInt(pRT->pTexture->Desc.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]->Desc.Width == UInt(w) && TempStencilBuffers[i]->Desc.Height == UInt(h)) { 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 = (GTextureD3D1xImpl*) CreateDepthStencilBuffer(); pDepthStencil->InitDynamicTexture(w, h, GImage::Image_DepthStencil, 0, GTexture::Usage_RenderTarget); TempStencilBuffers.PushBack(*pDepthStencil); } } needsinit = true; } if (needsinit) { if (w != SInt(pRT->pTexture->Desc.Width) || h != SInt(pRT->pTexture->Desc.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->Desc.Width * TempRenderTargets[i]->pTexture->Desc.Height) <= keepArea) { NewTempRenderTargets.PushBack(TempRenderTargets[i]); keepArea -= TempRenderTargets[i]->pTexture->Desc.Width * TempRenderTargets[i]->pTexture->Desc.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]->Desc.Width * TempStencilBuffers[i]->Desc.Height) <= stencilArea) { NewTempStencilBuffers.PushBack(TempStencilBuffers[i]); stencilArea -= TempStencilBuffers[i]->Desc.Width * TempStencilBuffers[i]->Desc.Height; } TempStencilBuffers.Clear(); TempStencilBuffers.Append(NewTempStencilBuffers.GetDataPtr(), NewTempStencilBuffers.GetSize()); } UInt CheckFilterSupport(const BlurFilterParams& params) { UInt flags = FilterSupport_Ok; if (params.Passes > 1 || (params.BlurX * params.BlurY > 32)) flags |= FilterSupport_Multipass; return flags; } static inline void SetUniform(Float* uniforms, FragShaderType2 shader, FragShader2Uniform var, const Float *val, UInt size) { memcpy(uniforms + FShaderUniforms[shader][var], val, size*sizeof(Float)); } static inline void SetUniform(Float* uniforms, FragShaderType2 shader, FragShader2Uniform var, Float x, Float y) { uniforms[FShaderUniforms[shader][var] + 0] = x; uniforms[FShaderUniforms[shader][var] + 1] = y; } static inline void SetUniform(Float* uniforms, FragShaderType2 shader, FragShader2Uniform var, Float x, Float y, Float z, Float w) { uniforms[FShaderUniforms[shader][var] + 0] = x; uniforms[FShaderUniforms[shader][var] + 1] = y; uniforms[FShaderUniforms[shader][var] + 2] = z; uniforms[FShaderUniforms[shader][var] + 3] = w; } void DrawBlurRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& indestrect, const BlurFilterParams& params, bool islast) { GUNUSED(islast); GASSERT(!DrawingMask); GPtr psrcinRef = (GTextureD3D1xImpl*)psrcin; GPtr psrc = (GTextureD3D1xImpl*)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; PShaderIndex = PS_None; VShaderIndex = VS_None; SetVertexDecl(VD_Filter); pContext->VSSetShader(FilterVertexShaders[VS2_VVatc] NULL_SHADER_CLASS); for (UInt i = (params.Mode & Filter_LastPassOnly) ? last : 0; i < n; i++) { UInt passi = (i == last) ? 2 : (i&1); const BlurFilterParams& pparams = pass[passi]; pContext->PSSetShader(FilterPixelShaders[passis[passi]] NULL_SHADER_CLASS); GTextureD3D1xImpl* pnextsrc; if (i != n - 1) { pnextsrc = PushTempRenderTarget(GRectF(-1,-1,1,1), bufWidth, bufHeight, 0); ApplyMatrix(GMatrix2D::Identity); destrect = GRectF(-1,-1,1,1); } else { pnextsrc = 0; ApplyMatrix(CurrentMatrix); destrect = indestrect; } srcrect = GRectF(insrcrect.Left * 1.0f/psrc->Desc.Width, insrcrect.Top * 1.0f/psrc->Desc.Height, insrcrect.Right * 1.0f/psrc->Desc.Width, insrcrect.Bottom * 1.0f/psrc->Desc.Height); if (i < last) ApplyBlendMode(Blend_Normal, true); else ApplyBlendMode(BlendMode, true); const float mult = 1.0f / 255.0f; Float uniforms[32]; memset(uniforms, 0x80, 128); if (FShaderUniforms[passis[passi]][FSU_cxadd] >= 0) { if (i == n - 1) { float cxformData[4 * 2] = { params.cxform.M_[0][0] * params.cxform.M_[3][0], params.cxform.M_[1][0] * params.cxform.M_[3][0], params.cxform.M_[2][0] * params.cxform.M_[3][0], params.cxform.M_[3][0], params.cxform.M_[0][1] * mult * params.cxform.M_[3][0], params.cxform.M_[1][1] * mult * params.cxform.M_[3][0], params.cxform.M_[2][1] * mult * params.cxform.M_[3][0], params.cxform.M_[3][1] * mult }; SetUniform(uniforms, passis[passi], FSU_cxmul, cxformData, 4); SetUniform(uniforms, passis[passi], FSU_cxadd, cxformData+4, 4); } else { const float add[] = {0,0,0,0}; const float mul[] = {1,1,1,1}; SetUniform(uniforms, passis[passi], FSU_cxmul, mul, 4); SetUniform(uniforms, passis[passi], FSU_cxadd, add, 4); } } Float SizeX = UInt(pparams.BlurX-1) * 0.5f; Float SizeY = UInt(pparams.BlurY-1) * 0.5f; if (passis[passi] == FS2_FBox1Blur || passis[passi] == FS2_FBox1BlurMul) { const float fsize[] = {pparams.BlurX > 1 ? SizeX : SizeY, 0, 0, 1.0f/((SizeX*2+1)*(SizeY*2+1))}; SetUniform(uniforms, passis[passi], FSU_fsize, fsize, 4); SetUniform(uniforms, passis[passi], FSU_texscale, pparams.BlurX > 1 ? 1.0f/psrc->Desc.Width : 0, pparams.BlurY > 1 ? 1.0f/psrc->Desc.Height : 0); } else { const float fsize[] = {SizeX, SizeY, 0, 1.0f/((SizeX*2+1)*(SizeY*2+1))}; SetUniform(uniforms, passis[passi], FSU_fsize, fsize, 4); SetUniform(uniforms, passis[passi], FSU_texscale, 1.0f/psrc->Desc.Width, 1.0f/psrc->Desc.Height); } if (FShaderUniforms[passis[passi]][FSU_offset] >= 0) { SetUniform(uniforms, passis[passi], FSU_offset, -params.Offset.x, -params.Offset.y); SetUniform(uniforms, passis[passi], FSU_srctexscale, psrc->Desc.Width/Float(psrcinRef->Desc.Width), psrc->Desc.Height/Float(psrcinRef->Desc.Height)); } if (FShaderUniforms[passis[passi]][FSU_scolor] >= 0) { SetUniform(uniforms, passis[passi], FSU_scolor, params.Color.GetRed() * mult, params.Color.GetGreen() * mult, params.Color.GetBlue() * mult, params.Color.GetAlpha() * mult); if (FShaderUniforms[passis[passi]][FSU_scolor2] >= 0) SetUniform(uniforms, passis[passi], FSU_scolor2, params.Color2.GetRed() * mult, params.Color2.GetGreen() * mult, params.Color2.GetBlue() * mult, params.Color2.GetAlpha() * mult); } pContext->UpdateSubresource(PShaderConst, 0, NULL, uniforms, 0, 0); SetShaderConstants(); psrc->Bind(FShaderUniforms[passis[passi]][FSU_tex], Wrap_Clamp, Sample_Linear, false); if (FShaderUniforms[passis[passi]][FSU_srctex] >= 0) ((GTextureD3D1xImpl*)psrcin)->Bind(FShaderUniforms[passis[passi]][FSU_srctex], Wrap_Clamp, Sample_Linear, false); Float strip[16] = { destrect.Left, destrect.Top, srcrect.Left, srcrect.Top, destrect.Right, destrect.Top, srcrect.Right, srcrect.Top, destrect.Left, destrect.Bottom, srcrect.Left, srcrect.Bottom, destrect.Right, destrect.Bottom, srcrect.Right, srcrect.Bottom, }; // Copy vertices to dynamic buffer and draw. void *pbuffer = VertexStream.LockVertexBuffer(4, sizeof(Float)*4); if (pbuffer) { memcpy(pbuffer, &strip[0], 16 * sizeof(Float)); VertexStream.UnlockVertexBuffer(); pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)); pContext->Draw(4, VertexStream.GetStartIndex()); } if (i != n - 1) { PopRenderTarget(); psrc = pnextsrc; } } ApplyBlendMode(BlendMode); FilterCnt.AddCount(n); RenderStats.Filters += n; } void DrawColorMatrixRect(GTexture* psrcin, const GRectF& insrcrect, const GRectF& destrect, const Float *matrix, bool islast) { GUNUSED(islast); GTextureD3D1xImpl* psrc = (GTextureD3D1xImpl*) psrcin; PShaderIndex = PS_None; VShaderIndex = VS_None; SetVertexDecl(VD_Filter); pContext->VSSetShader(FilterVertexShaders[VS2_VVatc] NULL_SHADER_CLASS); if (BlendMode == Blend_Darken || BlendMode == Blend_Multiply) pContext->PSSetShader(FilterPixelShaders[FS2_FCMatrixMul] NULL_SHADER_CLASS); else pContext->PSSetShader(FilterPixelShaders[FS2_FCMatrix] NULL_SHADER_CLASS); GRectF srcrect = GRectF(insrcrect.Left * 1.0f/psrc->Desc.Width, insrcrect.Top * 1.0f/psrc->Desc.Height, insrcrect.Right * 1.0f/psrc->Desc.Width, insrcrect.Bottom * 1.0f/psrc->Desc.Height); ApplyMatrix(CurrentMatrix); SetShaderConstants(); ApplyBlendMode(BlendMode, true); Float uniforms[20]; memcpy(uniforms + FShaderUniforms[FS2_FCMatrix][FSU_cxmul], matrix, 64); memcpy(uniforms + FShaderUniforms[FS2_FCMatrix][FSU_cxadd], matrix+16, 16); pContext->UpdateSubresource(PShaderConst, 0, NULL, uniforms, 0, 0); psrc->Bind(0, Wrap_Clamp, Sample_Linear, false); Float strip[16] = { destrect.Left, destrect.Top, srcrect.Left, srcrect.Top, destrect.Right, destrect.Top, srcrect.Right, srcrect.Top, destrect.Left, destrect.Bottom, srcrect.Left, srcrect.Bottom, destrect.Right, destrect.Bottom, srcrect.Right, srcrect.Bottom, }; // Copy vertices to dynamic buffer and draw. void *pbuffer = VertexStream.LockVertexBuffer(4, sizeof(Float)*4); if (pbuffer) { memcpy(pbuffer, &strip[0], 16 * sizeof(Float)); VertexStream.UnlockVertexBuffer(); pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)); pContext->Draw(4, VertexStream.GetStartIndex()); } ApplyBlendMode(BlendMode); FilterCnt.AddCount(1); RenderStats.Filters += 1; } // 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 (viewport.Left, viewport.Top, viewport.Left + // viewportWidth, viewport.Top + viewportHeight) defines the // window coordinates taken up by the movie. // // The Rectangle (x0, y0, x1, y1) defines the pixel // coordinates of the movie that correspond to the viewport // bounds. void BeginDisplay( GColor backgroundColor, const GViewport &vpin, Float x0, Float x1, Float y0, Float y1) { if (!ModeSet) { GFC_DEBUG_WARNING(1, "GRendererD3D1x::BeginDisplay failed - video mode not set"); return; } RenderMode = (vpin.Flags & GViewport::View_AlphaComposite); DisplayWidth = fabsf(x1 - x0); DisplayHeight = fabsf(y1 - y0); pContext->OMGetRenderTargets(1, &pRenderTarget.GetRawRef(), &pDepthStencil.GetRawRef()); // clamp viewport to buffer GViewport surfaceDesc(vpin); surfaceDesc.Width = vpin.BufferWidth; surfaceDesc.Height= vpin.BufferHeight; // Matrix to map from SWF Movie (TWIPs) coords to // viewport coordinates. float dx = x1 - x0; float dy = y1 - y0; if (dx < 1) { dx = 1; } if (dy < 1) { dy = 1; } float clipX0 = x0; float clipX1 = x1; float clipY0 = y0; float clipY1 = y1; // In some cases destination source coordinates can be outside D3D viewport. // D3D will not allow that; however, it is useful to support. // So if this happens, clamp the viewport and re-calculate source values. GViewport viewport(vpin); int viewportX0_save = viewport.Left; int viewportY0_save = viewport.Top; int viewportW_save = viewport.Width; int viewportH_save = viewport.Height; if (viewport.Left < 0) { clipX0 = x0 + ((-viewport.Left) * dx) / (float) viewport.Width; if ((-viewport.Left) >= viewport.Width) viewport.Width = 0; else viewport.Width += viewport.Left; viewport.Left = 0; } if (viewportX0_save + viewportW_save > (int) surfaceDesc.Width) { clipX1 = x0 + ((surfaceDesc.Width - viewportX0_save) * dx) / (float) viewportW_save; viewport.Width = surfaceDesc.Width - viewport.Left; } if (viewport.Top < 0) { clipY0 = y0 + ((-viewport.Top) * dy) / (float) viewport.Height; if ((-viewport.Top) >= viewport.Height) viewport.Height = 0; else viewport.Height += viewport.Top; viewport.Top = 0; } if (viewportY0_save + viewportH_save > (int) surfaceDesc.Height) { clipY1 = y0 + ((surfaceDesc.Height - viewportY0_save) * dy) / (float) viewportH_save; viewport.Height = surfaceDesc.Height - viewport.Top; } dx = clipX1 - clipX0; dy = clipY1 - clipY0; // Viewport. D3Dx(VIEWPORT) vp; vp.MinDepth = 0.0f; vp.MaxDepth = 0.0f; if (viewport.Flags & GViewport::View_UseScissorRect) { int scissorX0_save = viewport.ScissorLeft; int scissorY0_save = viewport.ScissorTop; if (viewport.ScissorLeft < viewport.Left) viewport.ScissorLeft = viewport.Left; if (viewport.ScissorTop < viewport.Top) viewport.ScissorTop = viewport.Top; viewport.ScissorWidth -= (viewport.ScissorLeft - scissorX0_save); viewport.ScissorHeight -= (viewport.ScissorTop - scissorY0_save); if (viewport.ScissorLeft + viewport.ScissorWidth > viewport.Left + viewport.Width) viewport.ScissorWidth = viewport.Left + viewport.Width - viewport.ScissorLeft; if (viewport.ScissorTop + viewport.ScissorHeight > viewport.Top + viewport.Height) viewport.ScissorHeight = viewport.Top + viewport.Height - viewport.ScissorTop; vp.TopLeftX = (D3DxVIEWPORT_COORD)viewport.ScissorLeft; vp.TopLeftY = (D3DxVIEWPORT_COORD)viewport.ScissorTop; vp.Width = (D3DxVIEWPORT_COORD)viewport.ScissorWidth; vp.Height = (D3DxVIEWPORT_COORD)viewport.ScissorHeight; clipX0 = clipX0 + dx / viewport.Width * (viewport.ScissorLeft - viewport.Left); clipY0 = clipY0 + dy / viewport.Height * (viewport.ScissorTop - viewport.Top); dx = dx / viewport.Width * viewport.ScissorWidth; dy = dy / viewport.Height * viewport.ScissorHeight; } else { vp.TopLeftX = (D3DxVIEWPORT_COORD)viewport.Left; vp.TopLeftY = (D3DxVIEWPORT_COORD)viewport.Top; vp.Width = (D3DxVIEWPORT_COORD)viewport.Width; vp.Height = (D3DxVIEWPORT_COORD)viewport.Height; } pContext->RSSetViewports(1, &vp); ViewRect = viewport; ViewportMatrix.SetIdentity(); ViewportMatrix.M_[0][0] = 2.0f / dx; ViewportMatrix.M_[1][1] = -2.0f / dy; // MA: it does not seem necessary to subtract viewport. // Need to work through details of viewport cutting, there still seems to // be a 1-pixel issue at edges. Could that be due to edge fill rules ? ViewportMatrix.M_[0][2] = -1.0f - ViewportMatrix.M_[0][0] * (clipX0); ViewportMatrix.M_[1][2] = 1.0f - ViewportMatrix.M_[1][1] * (clipY0); ViewportMatrix *= UserMatrix; // Blending render states. BlendModeStack.Clear(); BlendModeStack.Reserve(16); ApplyBlendMode(BlendMode); // Must reset both stages since ApplySampleMode modifies both. SampleMode[0] = Sample_Linear; SampleMode[1] = Sample_Linear; pContext->RSSetState(RasterStates[VMCFlags & VMConfig_Multisample ? 1 : 0]); pContext->OMSetDepthStencilState(StencilDisabled, 0); pContext->VSSetConstantBuffers(0, 1, &VShaderConst.GetRawRef()); pContext->PSSetConstantBuffers(0, 1, &PShaderConst.GetRawRef()); // Vertex format. // Set None so that shader is forced to be set and then set to default: strip. VDeclIndex = VD_None; VShaderIndex = VS_None; PShaderIndex = PS_None; SetVertexDecl(VD_Strip); SetVertexShader(VS_Strip); // Start with solid shader. SetPixelShader(PS_SolidColor); // Configure vertex/index buffers. VertexStream.BeginDisplay(); StencilCounter = 0; DrawingMask = 0; pCurStencilMode = StencilDisabled; #ifndef GFC_NO_3D pWorldMatrix = 0; #endif // Clear the background, if background color has alpha > 0. if (backgroundColor.GetAlpha() > 0) { // Draw a big quad. ApplyColor(backgroundColor); SetMatrix(Matrix::Identity); ApplyMatrix(CurrentMatrix); SetShaderConstants(); // The and x/y matrix are scaled by 20 twips already, so it // should ok to just convert these values into integers. struct Vertex { SInt16 x, y; }; Vertex strip[4] = { { (SInt16)x0, (SInt16)y0 }, { (SInt16)x1, (SInt16)y0 }, { (SInt16)x0, (SInt16)y1 }, { (SInt16)x1, (SInt16)y1 } }; // Copy vertices to dynamic buffer and draw. void *pbuffer = VertexStream.LockVertexBuffer(4, sizeof(Vertex)); if (pbuffer) { memcpy(pbuffer, &strip[0], 4 * sizeof(Vertex)); VertexStream.UnlockVertexBuffer(); pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)); pContext->Draw(4, VertexStream.GetStartIndex()); } } } // Clean up after rendering a frame. Client program is still // responsible for calling Present() or glSwapBuffers() void EndDisplay() { if (pDevice) { GFC_DEBUG_WARNING(StencilCounter > 0, "GRenderer::EndDisplay - BeginSubmitMask/DisableSubmitMask mismatch"); } pRenderTarget = 0; pDepthStencil = 0; ReleaseQueuedResources(); } // Set the current transform for mesh & line-strip rendering. void SetMatrix(const Matrix& m) { CurrentMatrix = m; } void SetUserMatrix(const Matrix& m) { UserMatrix = m; } // Set the current color transform for mesh & line-strip rendering. void SetCxform(const Cxform& cx) { CurrentCxform = cx; } void ApplyBlendMode(BlendType mode, bool sourceAc = 0) { if (!pDevice) return; if (DrawingMask) return; // For debug build GASSERT(((UInt) mode) < 15); // For release if (((UInt) mode) >= 15) mode = Blend_None; const float one[4] = {1,1,1,1}; if (sourceAc) pContext->OMSetBlendState(BlendStatesSrcAc[BlendModeStates[mode]], one, 0xffffffff); else pContext->OMSetBlendState(BlendStates[BlendModeStates[mode]], one, 0xffffffff); } void ApplySampleMode(UInt stage, BitmapSampleMode mode, BitmapWrapMode wrap, bool useMipmaps = false) { GUNUSED(useMipmaps); pContext->PSSetSamplers(stage, 1, &SamplerStates[mode+wrap*2].GetRawRef()); } // 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, "GRendererD3D1x::PopBlendMode - blend mode stack is empty"); } #ifndef GFC_NO_3D virtual void SetPerspective3D(const GMatrix3D &projMatIn) { ProjMatrix = projMatIn; UVPMatricesChanged = 1; } virtual void SetView3D(const GMatrix3D &viewMatIn) { ViewMatrix = viewMatIn; UVPMatricesChanged = 1; } virtual void SetWorld3D(const GMatrix3D *pWorldMatIn) { pWorldMatrix = pWorldMatIn; } #endif // multiply current GRenderer::Matrix with d3d GRenderer::Matrix void ApplyMatrix(const Matrix& matIn) { #ifndef GFC_NO_3D if (pWorldMatrix) { if (UVPMatricesChanged) { UVPMatricesChanged = 0; UVPMatrix = UserMatrix; UVPMatrix.Append(ViewMatrix); UVPMatrix.Append(ProjMatrix); if (RenderTargetStack.GetSize()) Adjust3DMatrixForRT(UVPMatrix, ProjMatrix, RenderTargetStack[0].ViewMatrix, ViewportMatrix); } GMatrix3D Final = GMatrix3D(matIn); Final.Append(*pWorldMatrix); Final.Append(UVPMatrix); Final.Transpose(); memcpy(VShaderData.mvp, Final.GetAsFloatArray(), sizeof(Float)*16); } else #endif { Matrix m(ViewportMatrix); m.Prepend(matIn); VShaderData.mvp[0][0] = m.M_[0][0]; VShaderData.mvp[0][1] = m.M_[0][1]; VShaderData.mvp[0][2] = 0; VShaderData.mvp[0][3] = m.M_[0][2]; VShaderData.mvp[1][0] = m.M_[1][0]; VShaderData.mvp[1][1] = m.M_[1][1]; VShaderData.mvp[1][2] = 0; VShaderData.mvp[1][3] = m.M_[1][2]; VShaderData.mvp[2][0] = 0; VShaderData.mvp[2][1] = 0; VShaderData.mvp[2][2] = 1.0f; VShaderData.mvp[2][3] = 0; VShaderData.mvp[3][0] = 0; VShaderData.mvp[3][1] = 0; VShaderData.mvp[3][2] = 0; VShaderData.mvp[3][3] = 1.0f; } } // Set the given color. void ApplyColor(GColor c) { const float mult = 1.0f / 255.0f; const float alpha = c.GetAlpha() * mult; // Set color. float rgba[8] = { c.GetRed() * mult, c.GetGreen() * mult, c.GetBlue() * mult, alpha, 0,0,0,0 }; if ((BlendMode == Blend_Multiply) || (BlendMode == Blend_Darken)) { rgba[0] = gflerp(1.0f, rgba[0], alpha); rgba[1] = gflerp(1.0f, rgba[1], alpha); rgba[2] = gflerp(1.0f, rgba[2], alpha); } pContext->UpdateSubresource(PShaderConst, 0, NULL, rgba, 0, 0); } // 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) { // Dynamic buffer stream update. VertexStream.SetVertexData(pvertices, numVertices, vf); // 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) { VertexStream.SetIndexData(pindices, numIndices, idxf); // Test cache buffer management support. CacheList.VerifyCachedData( this, pcache, CacheNode::Buffer_Index, (pindices!=0), numIndices, (((pindices!=0)&&numIndices) ? *((UInt16*)pindices) : 0) ); } void ReleaseCachedData(CachedData *pdata, CachedDataType type) { // Releases cached data that was allocated from the cache providers. CacheList.ReleaseCachedData(pdata, type); } void DrawIndexedTriList(int baseVertexIndex, int minVertexIndex, int numVertices, int startIndex, int triangleCount) { if (!ModeSet) return; // Must have vertex data. if (!VertexStream.HasVertexData() || !VertexStream.HasIndexData() || (VertexStream.GetD3DIndexFormat() == DXGI_FORMAT_UNKNOWN)) { GFC_DEBUG_WARNING(!VertexStream.HasVertexData(), "GRendererD3D1x::DrawIndexedTriList failed, vertex data not specified"); GFC_DEBUG_WARNING(!VertexStream.HasIndexData(), "GRendererD3D1x::DrawIndexedTriList failed, index data not specified"); GFC_DEBUG_WARNING((VertexStream.GetD3DIndexFormat() == DXGI_FORMAT_UNKNOWN), "GRendererD3D1x::DrawIndexedTriList failed, index buffer format not specified"); return; } bool isGouraud = (CurrentStyles[FILL_STYLE].Mode == GFxFillStyle::FM_Gouraud); VertexFormat vfmt = VertexStream.GetVertexFormat(); // Ensure we have the right vertex size. // MA: Should we loosen some rules to allow for other decl/shader combinations? if (isGouraud) { if (vfmt == Vertex_XY16iC32) { SetVertexDecl(VD_XY16iC32); SetVertexShader(VS_XY16iC32); } else if (vfmt == Vertex_XY16iCF32) { SetVertexDecl(VD_XY16iCF32); if (CurrentStyles[FILL_STYLE].GouraudType != GFill_2Texture) SetVertexShader(VS_XY16iCF32); else SetVertexShader(VS_XY16iCF32_T2); } } else { GASSERT(vfmt == Vertex_XY16i); SetVertexDecl(VD_Strip); SetVertexShader(VS_Strip); } // Set up current style. CurrentStyles[FILL_STYLE].Apply(this); ApplyMatrix(CurrentMatrix); SetShaderConstants(); // Upload data to buffers and draw. GDynamicVertexStreamD3D1x::PrimitiveDesc prim(baseVertexIndex, minVertexIndex, numVertices, startIndex, triangleCount); if (!VertexStream.PrepareVertexData(prim)) return; VertexStream.DrawTriangles(); RenderStats.Triangles += triangleCount; RenderStats.Primitives++; DPTriangleCnt.AddCount(1); TriangleCnt.AddCount(triangleCount); } // Draw the line strip formed by the sequence of points. void DrawLineStrip(int baseVertexIndex, int lineCount) { if (!ModeSet) return; if (!VertexStream.HasVertexData()) // Must have vertex data. { GFC_DEBUG_WARNING(1, "GRendererD3D1x::DrawLineStrip failed, vertex data not specified"); return; } // MA: Should loosen some rules to allow for other decl/shader combinations? GASSERT(VertexStream.GetVertexFormat() == Vertex_XY16i); SetVertexDecl(VD_Strip); SetVertexShader(VS_Strip); // Set up current style. CurrentStyles[LINE_STYLE].Apply(this); ApplyMatrix(CurrentMatrix); SetShaderConstants(); if (!VertexStream.InitVertexBufferData(baseVertexIndex, lineCount + 1)) return; pContext->IASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_LINESTRIP)); pContext->Draw(lineCount + 1, VertexStream.GetStartIndex()); RenderStats.Lines += lineCount; RenderStats.Primitives++; DPLineCnt.AddCount(1); LineCnt.AddCount(lineCount); } // Draw a set of rectangles textured with the given bitmap, with the given color. // Apply given transform; ignore any currently set transforms. // // Intended for textured glyph rendering. void DrawBitmaps(BitmapDesc* pbitmapList, int listSize, int startIndex, int count, const GTexture* pti, const Matrix& m, CacheProvider *pcache) { if (!ModeSet || !pbitmapList || !pti) return; GTextureD3D1xImpl* ptexture = (GTextureD3D1xImpl*)pti; // Texture must be valid for rendering if (!ptexture->pD3DTexture && !ptexture->CallRecreate()) { GFC_DEBUG_WARNING(1, "GRendererD3D1x::DrawBitmaps failed, empty texture specified/could not recreate texture"); 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) ); ApplyPShaderCxform(CurrentCxform); // Complex ApplyBlendMode(BlendMode); // Set texture. if (ptexture->IsYUVTexture() == 2) { if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureYUVAMultiply); else SetPixelShader(PS_TextTextureYUVA); } else if (ptexture->IsYUVTexture() == 1) { if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureYUVMultiply); else SetPixelShader(PS_TextTextureYUV); } else { if (ptexture->Desc.Format == DXGI_FORMAT_A8_UNORM) SetPixelShader(PS_TextTextureAlpha); else if (BlendMode == Blend_Multiply || BlendMode == Blend_Darken) SetPixelShader(PS_TextTextureColorMultiply); else SetPixelShader(PS_TextTextureColor); } ptexture->Bind(0, Wrap_Clamp, Sample_Linear, 0 == ptexture->IsYUVTexture()); // Switch to non-texgen shader. SetVertexDecl(VD_Glyph); SetVertexShader(VS_Glyph); ApplyMatrix(m); SetShaderConstants(); int ibitmap = 0, ivertex = 0; GCOMPILER_ASSERT((GDynamicVertexStreamD3D1x::GlyphBufferVertexCount%6) == 0); while (ibitmap < count) { // Lock the vertex buffer for glyphs. int vertexCount = G_Min((count-ibitmap) * 6, GDynamicVertexStreamD3D1x::GlyphBufferVertexCount); void * pbuffer = VertexStream.LockVertexBuffer(vertexCount, sizeof(GGlyphVertex)); if (!pbuffer) return; for(ivertex = 0; (ivertex < vertexCount) && (ibitmapIASetPrimitiveTopology(D3Dx(PRIMITIVE_TOPOLOGY_TRIANGLELIST)); pContext->Draw(ivertex, VertexStream.GetStartIndex()); RenderStats.Primitives++; DPTriangleCnt.AddCount(1); } } RenderStats.Triangles += count * 2; TriangleCnt.AddCount(count * 2); } void BeginSubmitMask(SubmitMaskMode maskMode) { if (!ModeSet) return; DrawingMask = 1; // disable color writes const float one[4] = {1,1,1,1}; pContext->OMSetBlendState(BlendStates[BlendDesc_Stencil], one, 0xffffffff); switch(maskMode) { case Mask_Clear: pContext->ClearDepthStencilView(pDepthStencil, D3Dx(CLEAR_STENCIL), 0, 0); pContext->OMSetDepthStencilState(StencilSet, 1); pCurStencilMode = StencilSet; StencilCounter = 1; break; case Mask_Increment: pContext->OMSetDepthStencilState(StencilIncr, StencilCounter); pCurStencilMode = StencilIncr; StencilCounter++; break; case Mask_Decrement: pContext->OMSetDepthStencilState(StencilDecr, StencilCounter); pCurStencilMode = StencilDecr; StencilCounter--; break; } RenderStats.Masks++; MaskCnt.AddCount(1); } void EndSubmitMask() { if (!ModeSet) return; DrawingMask = 0; ApplyBlendMode(BlendMode); pCurStencilMode = StencilTest; pContext->OMSetDepthStencilState(StencilTest, StencilCounter); } void DisableMask() { if (!ModeSet) return; DrawingMask = 0; StencilCounter = 0; ApplyBlendMode(BlendMode); pCurStencilMode = StencilDisabled; pContext->OMSetDepthStencilState(StencilDisabled, 0); } virtual void GetRenderStats(Stats *pstats, bool resetStats) { if (pstats) memcpy(pstats, &RenderStats, sizeof(Stats)); if (resetStats) RenderStats.Clear(); } // StatBag statistics virtual void GetStats(GStatBag* pbag, bool reset) { if (pbag) { pbag->Add(GStatRender_Texture_VMem, &TextureVMem); pbag->Add(GStatRender_Buffer_VMem, &BufferVMem); pbag->Add(GStatRender_TextureUpload_Cnt, &TextureUploadCnt); pbag->Add(GStatRender_TextureUpdate_Cnt, &TextureUpdateCnt); pbag->Add(GStatRender_DP_Line_Cnt, &DPLineCnt); pbag->Add(GStatRender_DP_Triangle_Cnt, &DPTriangleCnt); pbag->Add(GStatRender_Line_Cnt, &LineCnt); pbag->Add(GStatRender_Triangle_Cnt, &TriangleCnt); pbag->Add(GStatRender_Mask_Cnt, &MaskCnt); pbag->Add(GStatRender_Filter_Cnt, &FilterCnt); } if (reset) { TextureUploadCnt.Reset(); TextureUpdateCnt.Reset(); DPLineCnt.Reset(); DPTriangleCnt.Reset(); LineCnt.Reset(); TriangleCnt.Reset(); MaskCnt.Reset(); FilterCnt.Reset(); } } // GRendererD3D1x interface implementation virtual bool SetDependentVideoMode( ID3Dx(Device)* pd3dDevice, #if (GFC_D3D_VERSION == 11) ID3Dx(DeviceContext)* pd3dContext, #endif UInt32 vmConfigFlags) { if (!pd3dDevice) return 0; #if (GFC_D3D_VERSION == 10) ID3D1xDeviceContext* pd3dContext = pd3dDevice; #else if (!pd3dContext) return 0; #endif VMCFlags = vmConfigFlags; if (pDevice && pd3dDevice != pDevice) { GFC_DEBUG_ERROR(1, "GRendererD3D1x - different device passed to SetDependentVideoMode"); return 0; } else if (!pDevice) { pDevice = pd3dDevice; pDevice->AddRef(); if (!InitShaders() || !VertexStream.Initialize(pd3dDevice, pd3dContext)) { ReleaseShaders(); pDevice->Release(); pDevice = 0; pContext = 0; return 0; } } #if (GFC_D3D_VERSION == 11) if (pContext) pContext->Release(); pContext = pd3dContext; pContext->AddRef(); #endif UInt rasterstate = VMCFlags & VMConfig_Multisample ? 1 : 0; if (!RasterStates[rasterstate]) { D3Dx(RASTERIZER_DESC) rs; memset(&rs, 0, sizeof(D3Dx(RASTERIZER_DESC))); rs.FillMode = D3Dx(FILL_SOLID); rs.CullMode = D3Dx(CULL_NONE); rs.ScissorEnable = 0; rs.MultisampleEnable = VMCFlags & VMConfig_Multisample ? 1 : 0; rs.AntialiasedLineEnable = 1; if (FAILED(pDevice->CreateRasterizerState(&rs, &RasterStates[rasterstate].GetRawRef()))) return 0; } ModeSet = 1; return 1; } // Returns back to original mode (cleanup) virtual bool ResetVideoMode() { if (!ModeSet) return 1; ModeSet = 0; return 1; } virtual DisplayStatus CheckDisplayStatus() const { if (!ModeSet) return DisplayStatus_NoModeSet; return DisplayStatus_Ok; } // Direct3D Access // Return various Dirext3D related information virtual ID3Dx(Device)* GetDirect3DDevice() const { return pDevice; } #if (GFC_D3D_VERSION == 11) ID3D11DeviceContext* GetDirect3DContext() const { return pContext; } #endif }; // class GRendererD3D1xImpl // ***** GTextureD3D1x implementation GTextureD3D1xImpl::GTextureD3D1xImpl(GRendererD3D1xImpl *prenderer) : GTextureD3D1x(&prenderer->Textures) { pRenderer = prenderer; pD3DTexture = 0; pShaderView = 0; memset (&Desc, 0, sizeof(Desc)); } GTextureD3D1xImpl::~GTextureD3D1xImpl() { if (pD3DTexture) { GASSERT(pRenderer); if (pRenderer) { pRenderer->AddResourceForReleasing(pD3DTexture); pRenderer->AddResourceForReleasing(pShaderView); } else { // this should never happen pD3DTexture->Release(); pShaderView->Release(); } } if (!pRenderer) return; GLock::Locker guard(&pRenderer->TexturesLock); if(pFirst) RemoveNode(); } // Obtains the renderer that create TextureInfo GRenderer* GTextureD3D1xImpl::GetRenderer() const { return pRenderer; } bool GTextureD3D1xImpl::IsDataValid() const { return (pD3DTexture != 0); } void GTextureD3D1xImpl::ReleaseTexture() { if (pD3DTexture) { pD3DTexture->Release(); pD3DTexture = 0; } if (pShaderView) { pShaderView->Release(); pShaderView = 0; } } // Remove texture from renderer, notifies renderer destruction void GTextureD3D1xImpl::RemoveFromRenderer() { pRenderer = 0; if (AddRef_NotZero()) { ReleaseTexture(); CallHandlers(ChangeHandler::Event_RendererReleased); // we don't need to take textureslock here because this method // is called from within lock if (pNext) // We may have been released by user RemoveNode(); Release(); } else { if (pNext) // We may have been released by user RemoveNode(); } } // Does a bilinear resampling to create the dest image. // Source can be 4,3, or 1 bytes/pixel. Destination is either 1 or 4 bytes/pixel. static void SoftwareResample( UByte* pDst, int dstWidth, int dstHeight, int dstPitch, UByte* pSrc, int srcWidth, int srcHeight, int srcPitch, int bytesPerPixel ) { switch(bytesPerPixel) { case 4: GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch, pSrc, srcWidth, srcHeight, srcPitch, GRenderer::ResizeRgbaToRgba); break; case 3: GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch, pSrc, srcWidth, srcHeight, srcPitch, GRenderer::ResizeRgbToRgba); break; case 1: GRenderer::ResizeImage(pDst, dstWidth, dstHeight, dstPitch, pSrc, srcWidth, srcHeight, srcPitch, GRenderer::ResizeGray); break; } } bool GTextureD3D1xImpl::InitTexture(ID3Dx(Texture2D) *ptex, bool unused) { GUNUSED(unused); if (!pRenderer || !pRenderer->pDevice) return 0; if (pD3DTexture) { pD3DTexture->Release(); pD3DTexture = 0; } if (pShaderView) { pShaderView->Release(); pShaderView = 0; } if (ptex) { pD3DTexture = ptex; ptex->AddRef(); ptex->GetDesc(&Desc); HRESULT result = pRenderer->pDevice->CreateShaderResourceView(pD3DTexture, NULL, &pShaderView); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create shader view"); pD3DTexture->Release(); pD3DTexture = 0; return 0; } } CallHandlers(ChangeHandler::Event_DataChange); return 1; } // NOTE: This function destroys pim's data in the process of making mipmaps. bool GTextureD3D1xImpl::InitTexture(GImageBase* pim, UInt usage) { GUNUSED(usage); if (!pRenderer || !pRenderer->pDevice) return 0; // Delete old data if (pD3DTexture) { pD3DTexture->Release(); pD3DTexture = 0; } if (!pim || !pim->pData) { // Kill texture CallHandlers(ChangeHandler::Event_DataChange); return 1; } // Determine format UInt bytesPerPixel = 0; if (pim->Format == GImage::Image_ARGB_8888) { bytesPerPixel = 4; Desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; } else if (pim->Format == GImage::Image_RGB_888) { bytesPerPixel = 3; Desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; } else if (pim->Format == GImage::Image_A_8) { bytesPerPixel = 1; Desc.Format = DXGI_FORMAT_A8_UNORM; } else if (pim->Format == GImage::Image_DXT1) { Desc.Format = DXGI_FORMAT_BC1_UNORM; bytesPerPixel = 1; } else if (pim->Format == GImage::Image_DXT3) { Desc.Format = DXGI_FORMAT_BC2_UNORM; bytesPerPixel = 1; } else if (pim->Format == GImage::Image_DXT5) { Desc.Format = DXGI_FORMAT_BC3_UNORM; bytesPerPixel = 1; } else { // Unsupported format GASSERT(0); return 0; } UInt w = pim->Width; UInt h = pim->Height; UInt levelsNeeded; if (pim->IsDataCompressed() || pim->MipMapCount > 1) levelsNeeded = G_Max(1, pim->MipMapCount); else { levelsNeeded = 1; UInt mipw = w, miph = h; while (mipw > 1 || miph > 1) { mipw >>= 1; miph >>= 1; levelsNeeded++; } } GPtr presampleImage; // Set the actual data. if (!pim->IsDataCompressed() && (w != pim->Width || h != pim->Height || pim->Format == GImage::Image_RGB_888)) { GASSERT_ON_RENDERER_RESAMPLING; // Resample the image to new size if (presampleImage = *new GImage( ((pim->Format == GImage::Image_RGB_888) ? GImage::Image_ARGB_8888 : pim->Format), w, h)) { if (w != pim->Width || h != pim->Height) { // Resample will store an extra Alpha byte for RGB_888 -> RGBA_8888 SoftwareResample(presampleImage->pData, w, h, presampleImage->Pitch, pim->pData, pim->Width, pim->Height, pim->Pitch, bytesPerPixel); } else if (pim->Format == GImage::Image_RGB_888) { // Need to insert an alpha byte in the image data for (UInt y = 0; y < h; y++) { UByte* scanline = pim->GetScanline(y); UByte* pdest = presampleImage->GetScanline(y); for (UInt x = 0; x < w; x++) { pdest[x * 4 + 0] = scanline[x * 3 + 0]; // red pdest[x * 4 + 1] = scanline[x * 3 + 1]; // green pdest[x * 4 + 2] = scanline[x * 3 + 2]; // blue pdest[x * 4 + 3] = 255; // alpha } } } if (pim->MipMapCount > 1) { GFC_DEBUG_WARNING(1, "GRendererD3D1x - Existing mipmap levels have been skipped due to resampling"); levelsNeeded = 1; } } } if (pim->MipMapCount <= 1 && bytesPerPixel != 1) levelsNeeded = 1; // else // levelsNeeded = G_Max(1u, levelsNeeded - 1); Desc.ArraySize = 1; Desc.MipLevels = levelsNeeded; Desc.Width = w; Desc.Height = h; Desc.BindFlags = D3Dx(BIND_SHADER_RESOURCE); Desc.Usage = D3Dx(USAGE_DEFAULT); Desc.CPUAccessFlags = 0; Desc.SampleDesc.Count = 1; Desc.SampleDesc.Quality = 0; if (pim->IsDataCompressed() || pim->MipMapCount > 1) { GImageBase* psourceImage= presampleImage ? presampleImage.GetPtr() : pim; D3Dx(SUBRESOURCE_DATA) data[16]; for(UInt level = 0; level < levelsNeeded; level++) { UInt mipW, mipH, mipPitch; data[level].pSysMem = psourceImage->GetMipMapLevelData(level, &mipW, &mipH, &mipPitch); data[level].SysMemPitch = mipPitch; } HRESULT result = pRenderer->pDevice->CreateTexture2D(&Desc, data, &pD3DTexture); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create texture"); return 0; } } else if (levelsNeeded == 1) { D3Dx(SUBRESOURCE_DATA) data; data.pSysMem = presampleImage ? presampleImage->pData : pim->pData; data.SysMemPitch = presampleImage ? presampleImage->Pitch : pim->Pitch; HRESULT result = pRenderer->pDevice->CreateTexture2D(&Desc, &data, &pD3DTexture); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create texture"); return 0; } } else { HRESULT result = pRenderer->pDevice->CreateTexture2D(&Desc, NULL, &pD3DTexture); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create texture"); return 0; } // Will need a buffer for destructive mipmap generation if ((bytesPerPixel == 1) && (levelsNeeded >1) && !presampleImage) { GASSERT_ON_RENDERER_MIPMAP_GEN; // A bit hacky, needs to be more general presampleImage = *GImage::CreateImage(pim->Format, pim->Width, pim->Height); GASSERT(pim->Pitch == presampleImage->Pitch); memcpy(presampleImage->pData, pim->pData, pim->Height * pim->Pitch); } GImageBase* psourceImage= presampleImage ? presampleImage.GetPtr() : pim; int mipw = psourceImage->Width, miph; for(UInt level = 0; levelpContext->UpdateSubresource(pD3DTexture, level, NULL, psourceImage->pData, mipw * bytesPerPixel, 0); // For Alpha-only images, we might need to generate the next mipmap level. if (level< (levelsNeeded-1)) { if (psourceImage == pim) { presampleImage = new GImage(*pim); psourceImage = presampleImage.GetPtr(); } GRendererD3D1xImpl::MakeNextMiplevel(&mipw, &miph, psourceImage->pData); } } } HRESULT result = pRenderer->pDevice->CreateShaderResourceView(pD3DTexture, NULL, &pShaderView); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create shader view"); pD3DTexture->Release(); pD3DTexture = 0; return 0; } CallHandlers(ChangeHandler::Event_DataChange); return 1; } bool GTextureD3D1xImpl::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage) { if (!pRenderer || !pRenderer->pDevice) return 0; if (pD3DTexture) pD3DTexture->Release(); // Determine format UInt bytesPerPixel = 0; if (format == GImage::Image_ARGB_8888) { Desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; bytesPerPixel = 4; } else if (format == GImage::Image_A_8) { Desc.Format = DXGI_FORMAT_A8_UNORM; bytesPerPixel = 1; } else if (format == GImage::Image_DepthStencil) { Desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; bytesPerPixel = 4; } else { // Unsupported format GASSERT(0); return 0; } Desc.ArraySize = 1; Desc.MipLevels = 1+mipmaps; Desc.Width = width; Desc.Height = height; Desc.BindFlags = D3Dx(BIND_SHADER_RESOURCE); if (usage & Usage_Map) { Desc.Usage = D3Dx(USAGE_DYNAMIC); Desc.CPUAccessFlags = D3Dx(CPU_ACCESS_WRITE); } else { Desc.Usage = D3Dx(USAGE_DEFAULT); Desc.CPUAccessFlags = 0; } if (usage & Usage_RenderTarget) { if (format == GImage::Image_DepthStencil) Desc.BindFlags = D3Dx(BIND_DEPTH_STENCIL); else Desc.BindFlags |= D3Dx(BIND_RENDER_TARGET); } Desc.SampleDesc.Count = 1; Desc.SampleDesc.Quality = 0; UByte *p = (UByte*) GALLOC(bytesPerPixel*Desc.Width*Desc.Height, GStat_Default_Mem); memset (p, 64, bytesPerPixel*Desc.Width*Desc.Height); D3Dx(SUBRESOURCE_DATA) data; data.pSysMem = p; data.SysMemPitch = bytesPerPixel*Desc.Width; HRESULT result = pRenderer->pDevice->CreateTexture2D(&Desc, &data, &pD3DTexture); GFREE(p); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create texture"); return 0; } if (format != GImage::Image_DepthStencil) { result = pRenderer->pDevice->CreateShaderResourceView(pD3DTexture, NULL, &pShaderView); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create shader view"); pD3DTexture->Release(); pD3DTexture = 0; return 0; } } return 1; } void GTextureD3D1xImpl::Update(int level, int n, const UpdateRect *rects, const GImageBase *pim) { if (!pD3DTexture) { GFC_DEBUG_WARNING(1, "GTextureD3D1x::Update failed, no texture"); return; } for (int i = 0; i < n; i++) { D3Dx(BOX) dest = { rects[i].dest.x, rects[i].dest.y, 0, rects[i].dest.x + rects[i].src.Width(), rects[i].dest.y + rects[i].src.Height(), 1 }; pRenderer->pContext->UpdateSubresource(pD3DTexture, level, &dest, pim->pData + pim->Pitch * rects[i].src.Top + pim->GetBytesPerPixel() * rects[i].src.Left, pim->Pitch, 0); } } int GTextureD3D1xImpl::Map(int level, int n, MapRect* maps, int flags) { GUNUSED2(n,flags); GASSERT(n > 0 && maps); int h = Desc.Height; int w = Desc.Width; for (int i = 0; i < level; i++) { h >>= 1; if (h < 1) h = 1; w >>= 1; if (w < 1) w = 1; } #if (GFC_D3D_VERSION == 10) D3Dx(MAPPED_TEXTURE2D) rect; pD3DTexture->Map(level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #else D3Dx(MAPPED_SUBRESOURCE) rect; pRenderer->pContext->Map(pD3DTexture, level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #endif maps[0].pData = (UByte*)rect.pData; maps[0].pitch = rect.RowPitch; maps[0].width = w; maps[0].height = h; return 1; } bool GTextureD3D1xImpl::Unmap(int level, int n, MapRect* maps, int flags) { GUNUSED3(n,maps,flags); #if (GFC_D3D_VERSION == 10) pD3DTexture->Unmap(level); #else pRenderer->pContext->Unmap(pD3DTexture, level); #endif return 1; } void GTextureD3D1xImpl::Bind(int stageIndex, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps) { pRenderer->pContext->PSSetShaderResources(stageIndex, 1, &pShaderView); pRenderer->ApplySampleMode(stageIndex, SampleMode, WrapMode, useMipmaps); } GTextureD3D1xImplYUV::~GTextureD3D1xImplYUV() { for (int i = 0; i < 3; i++) if (pD3DTextureUVA[i]) { GASSERT(pRenderer); if (pRenderer) { pRenderer->AddResourceForReleasing(pD3DTextureUVA[i]); pRenderer->AddResourceForReleasing(pShaderViewUVA[i]); } else { // it should never happen pD3DTextureUVA[i]->Release(); pShaderViewUVA[i]->Release(); } } } void GTextureD3D1xImplYUV::ReleaseTexture() { GTextureD3D1xImpl::ReleaseTexture(); for (int i = 0; i < 3; i++) if (pD3DTextureUVA[i]) { pD3DTextureUVA[i]->Release(); pD3DTextureUVA[i] = 0; pShaderViewUVA[i]->Release(); pShaderViewUVA[i] = 0; } } bool GTextureD3D1xImplYUV::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage) { if (!pRenderer || !pRenderer->pDevice) return 0; GUNUSED(usage); ReleaseTexture(); Desc.Format = DXGI_FORMAT_A8_UNORM; Desc.ArraySize = 1; Desc.MipLevels = 1+mipmaps; Desc.Width = width; Desc.Height = height; Desc.BindFlags = D3Dx(BIND_SHADER_RESOURCE); Desc.Usage = D3Dx(USAGE_DYNAMIC); Desc.CPUAccessFlags = D3Dx(CPU_ACCESS_WRITE); Desc.SampleDesc.Count = 1; Desc.SampleDesc.Quality = 0; DescUV = Desc; DescUV.Width = Desc.Width >> 1; DescUV.Height = Desc.Height >> 1; UByte *p = (UByte*) GALLOC(width*height, GStat_Default_Mem); D3Dx(SUBRESOURCE_DATA) data; data.pSysMem = p; data.SysMemPitch = Desc.Width; HRESULT result = pRenderer->pDevice->CreateTexture2D(&Desc, &data, &pD3DTexture); result |= pRenderer->pDevice->CreateShaderResourceView(pD3DTexture, NULL, &pShaderView); result |= pRenderer->pDevice->CreateTexture2D(&DescUV, &data, &pD3DTextureUVA[0]); result |= pRenderer->pDevice->CreateShaderResourceView(pD3DTextureUVA[0], NULL, &pShaderViewUVA[0]); result |= pRenderer->pDevice->CreateTexture2D(&DescUV, &data, &pD3DTextureUVA[1]); result |= pRenderer->pDevice->CreateShaderResourceView(pD3DTextureUVA[1], NULL, &pShaderViewUVA[1]); if (format == GImage::Image_ARGB_8888) { result |= pRenderer->pDevice->CreateTexture2D(&Desc, &data, &pD3DTextureUVA[2]); result |= pRenderer->pDevice->CreateShaderResourceView(pD3DTextureUVA[2], NULL, &pShaderViewUVA[2]); } GFREE(p); if (result != S_OK) { GFC_DEBUG_ERROR(1, "GTextureD3D1x - Can't create YUV texture"); ReleaseTexture(); return 0; } return 1; } int GTextureD3D1xImplYUV::Map(int level, int n, MapRect* maps, int flags) { GUNUSED(flags); GASSERT(n >= (pD3DTextureUVA[2] ? 4 : 3) && maps); int h = Desc.Height; int w = Desc.Width; for (int i = 0; i < level; i++) { h >>= 1; if (h < 1) h = 1; w >>= 1; if (w < 1) w = 1; } n = pD3DTextureUVA[2] ? 4 : 3; #if (GFC_D3D_VERSION == 10) D3Dx(MAPPED_TEXTURE2D) rect; pD3DTexture->Map(level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #else D3Dx(MAPPED_SUBRESOURCE) rect; pRenderer->pContext->Map(pD3DTexture, level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #endif maps[0].pData = (UByte*)rect.pData; maps[0].pitch = rect.RowPitch; maps[0].width = w; maps[0].height = h; for (int i = 1; i < n; i++) { #if (GFC_D3D_VERSION == 10) pD3DTextureUVA[i-1]->Map(level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #else pRenderer->pContext->Map(pD3DTextureUVA[i-1], level, (flags & Map_KeepOld) ? D3Dx(MAP_WRITE) : D3Dx(MAP_WRITE_DISCARD), 0, &rect); #endif maps[i].pData = (UByte*)rect.pData; maps[i].pitch = rect.RowPitch; maps[i].width = (i == 3) ? w : (w >> 1); maps[i].height = (i == 3) ? h : (h >> 1); } return n; } bool GTextureD3D1xImplYUV::Unmap(int level, int n, MapRect* maps, int flags) { GUNUSED3(n,maps,flags); #if (GFC_D3D_VERSION == 10) pD3DTexture->Unmap(level); for (int i = 0; i < 3; i++) if (pD3DTextureUVA[i]) pD3DTextureUVA[i]->Unmap(level); #else pRenderer->pContext->Unmap(pD3DTexture, level); for (int i = 0; i < 3; i++) if (pD3DTextureUVA[i]) pRenderer->pContext->Unmap(pD3DTextureUVA[i], level); #endif return 1; } void GTextureD3D1xImplYUV::Bind(int stageIndex, GRenderer::BitmapWrapMode WrapMode, GRenderer::BitmapSampleMode SampleMode, bool useMipmaps) { GASSERT(stageIndex == 0); GUNUSED(stageIndex); pRenderer->pContext->PSSetShaderResources(0, 1, &pShaderView); pRenderer->ApplySampleMode(0, SampleMode, WrapMode, useMipmaps); pRenderer->pContext->PSSetShaderResources(1, 1, &pShaderViewUVA[0]); pRenderer->ApplySampleMode(1, SampleMode, WrapMode, useMipmaps); pRenderer->pContext->PSSetShaderResources(2, 1, &pShaderViewUVA[1]); pRenderer->ApplySampleMode(2, SampleMode, WrapMode, useMipmaps); if (pD3DTextureUVA[2]) { pRenderer->pContext->PSSetShaderResources(3, 1, &pShaderViewUVA[2]); pRenderer->ApplySampleMode(3, SampleMode, WrapMode, useMipmaps); } } GRenderTargetD3D1xImpl::GRenderTargetD3D1xImpl(GRendererD3D1xImpl* prenderer) : GRenderTargetD3D1x(&prenderer->RenderTargets), pRenderer(prenderer) { pRenderTextureView = 0; pRTDepthStencil = 0; } GRenderTargetD3D1xImpl::~GRenderTargetD3D1xImpl() { if (!pRenderer) return; ReleaseResources(); GLock::Locker guard(&pRenderer->TexturesLock); if (pFirst) RemoveNode(); } GRenderer* GRenderTargetD3D1xImpl::GetRenderer() const { return pRenderer; } void GRenderTargetD3D1xImpl::RemoveFromRenderer() { if (AddRef_NotZero()) { ReleaseResources(); pRenderer = 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(); pRenderer = 0; } } void GRenderTargetD3D1xImpl::ReleaseResources() { if (pTexture) { if (pRenderer) { pRenderer->AddResourceForReleasing(pRenderTextureView); pRenderer->AddResourceForReleasing(pRTDepthStencil); } else { if (pRenderTextureView) pRenderTextureView->Release(); if (pRTDepthStencil) pRTDepthStencil->Release(); } } pRenderTextureView = 0; pRTDepthStencil = 0; } bool GRenderTargetD3D1xImpl::InitRenderTarget(D3DxRenderTargetParams params) { ReleaseResources(); pRenderTextureView = params.pRenderTextureView; pRTDepthStencil = params.pRTDepthStencil; CallHandlers(ChangeHandler::Event_DataChange); return true; } bool GRenderTargetD3D1xImpl::InitRenderTarget(GTexture *ptarget, GTexture* pdepth, GTexture* pstencil) { ReleaseResources(); pTexture = (GTextureD3D1xImpl*)ptarget; TargetWidth = pTexture->Desc.Width; TargetHeight = pTexture->Desc.Height; if (FAILED(pRenderer->pDevice->CreateRenderTargetView(pTexture->pD3DTexture, NULL, &pRenderTextureView))) return 0; if (pdepth) pStencilTexture = (GTextureD3D1xImpl*)pdepth; else if (pstencil) pStencilTexture = (GTextureD3D1xImpl*)pstencil; else { pStencilTexture = 0; pRTDepthStencil = 0; } if (pStencilTexture) { D3Dx(DEPTH_STENCIL_VIEW_DESC) dsv; memset(&dsv, 0, sizeof(dsv)); dsv.Format = pStencilTexture->Desc.Format; dsv.ViewDimension = D3Dx(DSV_DIMENSION_TEXTURE2D); dsv.Texture2D.MipSlice = 0; if (FAILED(pRenderer->pDevice->CreateDepthStencilView( pStencilTexture->pD3DTexture, &dsv, &pRTDepthStencil))) return 0; } CallHandlers(ChangeHandler::Event_DataChange); return 1; } // Factory. GRendererD3D1x* GRendererD3D1x::CreateRenderer() { return new GRendererD3D1xImpl; }