4329 lines
140 KiB
C++
4329 lines
140 KiB
C++
/**********************************************************************
|
|
|
|
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 <windows.h>
|
|
#endif
|
|
|
|
#include "GRendererCommonImpl.h"
|
|
#include <string.h> // 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 <D3D10.h>
|
|
#include <D3DX10.h>
|
|
#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 <D3D11.h>
|
|
#include <D3DX11.h>
|
|
#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<GTextureD3D1xImpl> 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<ID3Dx(Buffer)> pVertexBuffer;
|
|
GPtr<ID3Dx(Buffer)> 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<int>(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<int>(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<ID3Dx(RenderTargetView)> pRenderTarget;
|
|
GPtr<ID3Dx(DepthStencilView)> pDepthStencil;
|
|
|
|
// Created vertex declarations and shaders.
|
|
GPtr<ID3Dx(InputLayout)> VertexDecls[VS_Count];
|
|
GPtr<ID3Dx(VertexShader)> VertexShaders[VS_Count];
|
|
GPtr<ID3Dx(VertexShader)> FilterVertexShaders[VS2_Count];
|
|
GPtr<ID3Dx(Buffer)> VShaderConst;
|
|
|
|
struct VShaderConstData
|
|
{
|
|
float mvp[4][4];
|
|
float texgen[16];
|
|
};
|
|
VShaderConstData VShaderData;
|
|
|
|
// Allocated pixel shaders
|
|
GPtr<ID3Dx(PixelShader)> PixelShaders[PS_Count];
|
|
GPtr<ID3Dx(PixelShader)> FilterPixelShaders[FS2_Count];
|
|
GPtr<ID3Dx(Buffer)> PShaderConst;
|
|
GPtr<ID3Dx(SamplerState)> SamplerStates[4];
|
|
|
|
GPtr<ID3Dx(BlendState)> BlendStates[BlendDesc_Count];
|
|
GPtr<ID3Dx(BlendState)> BlendStatesSrcAc[BlendDesc_Count];
|
|
GPtr<ID3Dx(RasterizerState)> RasterStates[2]; // No multisample, multisample
|
|
GPtr<ID3Dx(DepthStencilState)> 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<BlendType, GStat_Default_Mem, NeverShrinkPolicy> 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<RTState, GStat_Default_Mem, NeverShrinkPolicy> RTStackType;
|
|
|
|
GRenderTargetD3D1xImpl* pCurRenderTarget;
|
|
RTStackType RenderTargetStack;
|
|
GArrayLH<GPtr<GRenderTargetD3D1xImpl> > TempRenderTargets;
|
|
GArrayLH<GPtr<GTextureD3D1xImpl> > 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<ID3Dx(DeviceChild)*> 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<ID3Dx(VertexShader)> *pshaderPtr, GPtr<ID3Dx(InputLayout)> *pdeclPtr, const VDeclSpec *pdeclspec,
|
|
const char* pshaderText)
|
|
{
|
|
if (!*pshaderPtr)
|
|
{
|
|
GPtr<ID3D10Blob> pshader;
|
|
GPtr<ID3D10Blob> 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<ID3Dx(PixelShader)> *pshaderPtr, const char* pshaderText)
|
|
{
|
|
if (!*pshaderPtr)
|
|
{
|
|
GPtr<ID3D10Blob> pshader;
|
|
GPtr<ID3D10Blob> 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<<Blend_None) | (1<<Blend_Normal) |
|
|
(1<<Blend_Multiply) | (1<<Blend_Lighten) | (1<<Blend_Darken) |
|
|
(1<<Blend_Add) | (1<<Blend_Subtract);
|
|
pcaps->VertexFormats= (1<<Vertex_None) | (1<<Vertex_XY16i) | (1<<Vertex_XY16iC32) | (1<<Vertex_XY16iCF32);
|
|
|
|
pcaps->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<GPtr<GRenderTargetD3D1xImpl> > NewTempRenderTargets;
|
|
GArray<GPtr<GTextureD3D1xImpl> > 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<GTextureD3D1xImpl> psrcinRef = (GTextureD3D1xImpl*)psrcin;
|
|
GPtr<GTextureD3D1xImpl> 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<int>((count-ibitmap) * 6, GDynamicVertexStreamD3D1x::GlyphBufferVertexCount);
|
|
void * pbuffer = VertexStream.LockVertexBuffer(vertexCount, sizeof(GGlyphVertex));
|
|
if (!pbuffer)
|
|
return;
|
|
|
|
for(ivertex = 0; (ivertex < vertexCount) && (ibitmap<count); ibitmap++, ivertex+= 6)
|
|
{
|
|
BitmapDesc & bd = pbitmapList[ibitmap + startIndex];
|
|
GGlyphVertex* pv = ((GGlyphVertex*)pbuffer) + ivertex;
|
|
|
|
// Triangle 1.
|
|
pv[0].SetVertex2D(bd.Coords.Left, bd.Coords.Top, bd.TextureCoords.Left, bd.TextureCoords.Top, bd.Color);
|
|
pv[1].SetVertex2D(bd.Coords.Right, bd.Coords.Top, bd.TextureCoords.Right, bd.TextureCoords.Top, bd.Color);
|
|
pv[2].SetVertex2D(bd.Coords.Left, bd.Coords.Bottom, bd.TextureCoords.Left, bd.TextureCoords.Bottom, bd.Color);
|
|
// Triangle 2.
|
|
pv[3].SetVertex2D(bd.Coords.Left, bd.Coords.Bottom, bd.TextureCoords.Left, bd.TextureCoords.Bottom, bd.Color);
|
|
pv[4].SetVertex2D(bd.Coords.Right, bd.Coords.Top, bd.TextureCoords.Right, bd.TextureCoords.Top, bd.Color);
|
|
pv[5].SetVertex2D(bd.Coords.Right, bd.Coords.Bottom,bd.TextureCoords.Right, bd.TextureCoords.Bottom, bd.Color);
|
|
}
|
|
|
|
VertexStream.UnlockVertexBuffer();
|
|
|
|
// Draw the generated triangles.
|
|
if (ivertex)
|
|
{
|
|
pContext->IASetPrimitiveTopology(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<UInt>(1, pim->MipMapCount);
|
|
else
|
|
{
|
|
levelsNeeded = 1;
|
|
UInt mipw = w, miph = h;
|
|
while (mipw > 1 || miph > 1)
|
|
{
|
|
mipw >>= 1;
|
|
miph >>= 1;
|
|
levelsNeeded++;
|
|
}
|
|
}
|
|
|
|
GPtr<GImage> presampleImage;
|
|
|
|
// Set the actual data.
|
|
if (!pim->IsDataCompressed() && (w != pim->Width || h != pim->Height ||
|
|
pim->Format == GImage::Image_RGB_888))
|
|
{
|
|
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; level<levelsNeeded; level++)
|
|
{
|
|
pRenderer->pContext->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;
|
|
}
|