#endif
using namespace Microsoft::WRL;
-using namespace BasicSprites;
+namespace BasicSprites
+{
+
-SpriteBatch::SpriteBatch() :
- m_capacity(0)
+struct ReplicationVertex
{
-}
+ float4 pos;
+ float2 tex;
+ unsigned int color;
+};
-void SpriteBatch::Initialize(
- _In_ ID3D11Device1* d3dDevice,
- _In_ int capacity = 1024
- )
+struct InstancingVertex
{
- m_d3dDevice = d3dDevice;
- m_d3dDevice->GetImmediateContext1(&m_d3dContext);
- m_capacity = capacity;
+ float4 pos;
+ float2 tex;
+};
- // Determine the technique that will be used to render the sprites.
- auto featureLevel = m_d3dDevice->GetFeatureLevel();
+struct InstanceData
+{
+ float4 origin;
+ float2 offset;
+ float rotation;
+ float4 textureCellInfo;
+ unsigned int color;
+};
- if (featureLevel >= D3D_FEATURE_LEVEL_10_0)
- {
- // On DirectX 10+ devices, the Geometry Shader allows the sprite vertices to be
- // generated on the GPU, significantly reducing memory bandwidth requirements.
- m_technique = RenderTechnique::GeometryShader;
- }
- else if (featureLevel >= D3D_FEATURE_LEVEL_9_3)
- {
- // On DirectX 9.3+ devices, instancing allows shared sprite geometry with unique
- // per-sprite instance parameters, eliminating redundant data transfer.
- m_technique = RenderTechnique::Instancing;
- }
- else
- {
- // On devices that do not support Instancing, sprite vertex data must be replicated
- // in order to achieve the desired effect.
- m_technique = RenderTechnique::Replication;
+struct TextureMapElement
+{
+ Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> srv;
+ float2 size;
+ float2 textureCellSize;
+};
- if (capacity > static_cast<int>(Parameters::MaximumCapacityCompatible))
- {
- // The index buffer format for feature-level 9.1 devices may only be 16 bits.
- // With 4 vertices per sprite, this allows a maximum of (1 << 16) / 4 sprites.
- throw ref new Platform::InvalidArgumentException();
- }
- }
+struct SpritePrepInfo
+{
+ ReplicationVertex replicationVertex[4];
+ InstanceData instanceData;
+ ID3D11ShaderResourceView* textureView;
+ ID3D11BlendState1* blendState;
+ float2 textureSize;
+ float2 textureCellSize;
+ float2 textureCellOffset;
+};
+
+struct SpriteRunInfo
+{
+ ID3D11ShaderResourceView* textureView;
+ ID3D11BlendState1* blendState;
+// float4 textureCellInfo;
+ unsigned int numSprites;
+};
- // Create the texture sampler.
-
- D3D11_SAMPLER_DESC samplerDesc;
- ZeroMemory(&samplerDesc, sizeof(samplerDesc));
-// samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
- samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
-
- samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
- samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
- samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
- samplerDesc.MipLODBias = 0.0f;
- samplerDesc.MaxAnisotropy = 0;
- samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
- samplerDesc.BorderColor[0] = 0.0f;
- samplerDesc.BorderColor[1] = 0.0f;
- samplerDesc.BorderColor[2] = 0.0f;
- samplerDesc.BorderColor[3] = 0.0f;
- samplerDesc.MinLOD = 0.0f;
- samplerDesc.MaxLOD = FLT_MAX;
-
- DX::ThrowIfFailed(
- m_d3dDevice->CreateSamplerState(
- &samplerDesc,
- &m_linearSampler
- )
- );
+struct SpriteDrawerBase : public SpriteDrawer
+{
+ SpriteDrawerBase(){}
+
+ virtual void Initialize(
+ ID3D11Device1* d3dDevice,
+ int capacity
+ )
+ {
+ m_d3dDevice = d3dDevice;
+ m_d3dDevice->GetImmediateContext1(&m_d3dContext);
+ m_capacity = capacity;
+
+ // Create the texture sampler.
+
+ D3D11_SAMPLER_DESC samplerDesc;
+ ZeroMemory(&samplerDesc, sizeof(samplerDesc));
+ // samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+
+ samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+ samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+ samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+ samplerDesc.MipLODBias = 0.0f;
+ samplerDesc.MaxAnisotropy = 0;
+ samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+ samplerDesc.BorderColor[0] = 0.0f;
+ samplerDesc.BorderColor[1] = 0.0f;
+ samplerDesc.BorderColor[2] = 0.0f;
+ samplerDesc.BorderColor[3] = 0.0f;
+ samplerDesc.MinLOD = 0.0f;
+ samplerDesc.MaxLOD = FLT_MAX;
+
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateSamplerState(
+ &samplerDesc,
+ &m_linearSampler
+ )
+ );
+
+ // Create the blend states.
+
+ D3D11_BLEND_DESC1 blendDesc;
+ ZeroMemory(&blendDesc, sizeof(blendDesc));
+ blendDesc.AlphaToCoverageEnable = false;
+ blendDesc.IndependentBlendEnable = false;
+ blendDesc.RenderTarget[0].BlendEnable = true;
+ blendDesc.RenderTarget[0].LogicOpEnable = false;
+ blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
+ blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+ blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+ blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
+ blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBlendState1(
+ &blendDesc,
+ &m_blendStateAlpha
+ )
+ );
+
+ blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
+
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBlendState1(
+ &blendDesc,
+ &m_blendStateAdditive
+ )
+ );
+
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBuffer(
+ &CD3D11_BUFFER_DESC(
+ 16, // Constant buffer sizes must be a multiple of 16 bytes. 16 is sufficient for the required float2 data.
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE
+ ),
+ nullptr,
+ &textureOffset_
+ )
+ );
+
+
+ };
+
+ virtual void AddTexture(
+ ID3D11Texture2D* texture,float2 textureCellSize
+ )
+ {
+ TextureMapElement mapElement;
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateShaderResourceView(
+ texture,
+ &CD3D11_SHADER_RESOURCE_VIEW_DESC(
+ texture,
+ D3D11_SRV_DIMENSION_TEXTURE2D
+ ),
+ &mapElement.srv
+ )
+ );
+
+ D3D11_TEXTURE2D_DESC textureDesc;
+ texture->GetDesc(&textureDesc);
+ mapElement.size = float2(
+ static_cast<float>(textureDesc.Width),
+ static_cast<float>(textureDesc.Height)
+ );
+ if(textureCellSize.x == 0 && textureCellSize.y == 0)
+ {
+ mapElement.textureCellSize = mapElement.size;
+ } else {
+ mapElement.textureCellSize = textureCellSize;
+ }
+ m_textureMap[texture] = mapElement;
+
+ }
+
+ virtual void RemoveTexture(
+ ID3D11Texture2D* texture
+ )
+ {
+ m_textureMap.erase(texture);
+ }
+
+ virtual void Begin()
+ {
+ // Reset internal sprite data.
+
+ m_numSpritesDrawn = 0;
+ m_spriteRuns.clear();
+ spritePrepInfos_.clear();
+ // Get the current render target dimensions and logical DPI.
+
+ ComPtr<ID3D11RenderTargetView> renderTargetView;
+ m_d3dContext->OMGetRenderTargets(
+ 1,
+ &renderTargetView,
+ nullptr
+ );
+
+ ComPtr<ID3D11Resource> renderTarget;
+ renderTargetView->GetResource(&renderTarget);
+
+ ComPtr<ID3D11Texture2D> renderTargetTexture;
+ renderTarget.As(&renderTargetTexture);
+
+ D3D11_TEXTURE2D_DESC renderTargetTextureDesc;
+ renderTargetTexture->GetDesc(&renderTargetTextureDesc);
+
+ m_renderTargetSize = float2(
+ static_cast<float>(renderTargetTextureDesc.Width),
+ static_cast<float>(renderTargetTextureDesc.Height)
+ );
+
+ m_dpi = Windows::Graphics::Display::DisplayProperties::LogicalDpi;
+ }
+
+ virtual void End(){};
+
+ virtual void Draw(
+ ID3D11Texture2D* texture,
+ float4 position,
+ PositionUnits positionUnits,
+ float2 size,
+ SizeUnits sizeUnits,
+ float4 color,
+ float rotation,
+ BlendMode blendMode,
+ float2 textureCellOffset
+ )
+ {
+ // Fail if drawing this sprite would exceed the capacity of the sprite batch.
+ if (m_numSpritesDrawn >= m_capacity)
+ {
+ throw ref new Platform::OutOfBoundsException();
+ }
- // Create the blend states.
-
- D3D11_BLEND_DESC1 blendDesc;
- ZeroMemory(&blendDesc, sizeof(blendDesc));
- blendDesc.AlphaToCoverageEnable = false;
- blendDesc.IndependentBlendEnable = false;
- blendDesc.RenderTarget[0].BlendEnable = true;
- blendDesc.RenderTarget[0].LogicOpEnable = false;
- blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
- blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
- blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
- blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
- blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
- blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
- blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
-
- DX::ThrowIfFailed(
- m_d3dDevice->CreateBlendState1(
- &blendDesc,
- &m_blendStateAlpha
- )
- );
+ SpritePrepInfo info;
+ InitSpritePrepInfo(info,texture,position,positionUnits,size,sizeUnits,color,rotation,blendMode,textureCellOffset);
+ AddSpritePrepInfo(info);
+
+ };
+protected:
+ unsigned int MakeUnorm(float4 color)
+ {
+ unsigned int r = max(0, min(255, static_cast<unsigned int>(color.r * 255.0f)));
+ unsigned int g = max(0, min(255, static_cast<unsigned int>(color.g * 255.0f)));
+ unsigned int b = max(0, min(255, static_cast<unsigned int>(color.b * 255.0f)));
+ unsigned int a = max(0, min(255, static_cast<unsigned int>(color.a * 255.0f)));
+ return
+ (a << 24) |
+ (b << 16) |
+ (g << 8) |
+ r;
+ }
+
+ float4 StandardOrigin(float4 position, PositionUnits positionUnits, float2 renderTargetSize, float dpi)
+ {
+ float4 origin;
+
+ origin.z = position.z;
+ origin.w = position.w;
+
+ if (positionUnits == PositionUnits::Pixels)
+ {
+ origin.x = (position.x / renderTargetSize.x) * 2.0f - 1.0f;
+ origin.y = 1.0f - (position.y / renderTargetSize.y) * 2.0f;
+ }
+ else if (positionUnits == PositionUnits::DIPs)
+ {
+ origin.x = ((position.x * dpi / 96.0f) / renderTargetSize.x) * 2.0f - 1.0f;
+ origin.y = 1.0f - ((position.y * dpi / 96.0f) / renderTargetSize.y) * 2.0f;
+ }
+ else if (positionUnits == PositionUnits::Normalized)
+ {
+ origin.x = position.x * 2.0f - 1.0f;
+ origin.y = 1.0f - position.y * 2.0f;
+ }
+ else if (positionUnits == PositionUnits::UniformWidth)
+ {
+ origin.x = position.x * 2.0f - 1.0f;
+ origin.y = 1.0f - position.y * (renderTargetSize.x / renderTargetSize.y) * 2.0f;
+ }
+ else if (positionUnits == PositionUnits::UniformHeight)
+ {
+ origin.x = position.x * (renderTargetSize.y / renderTargetSize.x) * 2.0f - 1.0f;
+ origin.y = 1.0f - position.y * 2.0f;
+ }
+ return origin;
+
+ }
+
+ float2 StandardOffset(float2 size, SizeUnits sizeUnits, float2 spriteSize, float dpi)
+ {
+ float2 offset;
+ if (sizeUnits == SizeUnits::Pixels)
+ {
+ offset = size;
+ }
+ else if (sizeUnits == SizeUnits::DIPs)
+ {
+ offset = size * dpi / 96.0f;
+ }
+ else if (sizeUnits == SizeUnits::Normalized)
+ {
+ offset = spriteSize * size;
+ }
+ return offset;
+
+ }
+
+ void InitSpritePrepInfo(
+ SpritePrepInfo& info,
+ ID3D11Texture2D* texture,
+ float4 position,
+ PositionUnits positionUnits,
+ float2 size,
+ SizeUnits sizeUnits,
+ float4 color,
+ float rotation,
+ BlendMode blendMode,
+ float2 textureCellOffset
+ )
+ {
+ InitSpritePrepInfo(info,texture,blendMode,textureCellOffset);
+
+ // Add the sprite to the buffer.
+
+ float4 origin = StandardOrigin(position, positionUnits, m_renderTargetSize, m_dpi);
+ float2 offset = StandardOffset(size, sizeUnits, info.textureSize, m_dpi);
- blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
+ info.instanceData.origin = origin;
+ info.instanceData.offset = offset;
+ info.instanceData.rotation = rotation;
+ info.instanceData.color = MakeUnorm(color);
+ }
+
+ void InitSpritePrepInfo
+ (
+ SpritePrepInfo& info,
+ ID3D11Texture2D* texture,
+ BlendMode blendMode,
+ float2 textureCellOffset
+ )
+ {
+ // Retrieve information about the sprite.
+ TextureMapElement element = m_textureMap[texture];
+ ID3D11ShaderResourceView* textureView = element.srv.Get();
+ float2 textureSize = element.size;
+ ID3D11BlendState1* blendState = blendMode == BlendMode::Additive ? m_blendStateAdditive.Get() : m_blendStateAlpha.Get();
+
+ // Fail if the texture has not previously been added to the sprite batch.
+ if (textureView == nullptr)
+ {
+ throw ref new Platform::NullReferenceException();
+ }
- DX::ThrowIfFailed(
- m_d3dDevice->CreateBlendState1(
- &blendDesc,
- &m_blendStateAdditive
- )
+ info.textureView = textureView;
+ info.blendState = blendState;
+ info.textureSize = textureSize;
+ info.textureCellSize = element.textureCellSize;
+ info.textureCellOffset = textureCellOffset;
+ }
+
+ void AddSpritePrepInfo(SpritePrepInfo& info)
+ {
+ if(spritePrepInfos_.empty()){
+ spritePrepInfos_.push_back(info);
+ } else {
+ std::list<SpritePrepInfo>::const_iterator it = spritePrepInfos_.cbegin();
+ while(it != spritePrepInfos_.end())
+ {
+ if(it->instanceData.origin.z > info.instanceData.origin.z)
+ {
+ ++it;
+ } else {
+ if(it->instanceData.origin.z == info.instanceData.origin.z)
+ {
+ if(it->textureView == info.textureView && it->blendState == info.blendState)
+ {
+ spritePrepInfos_.insert(it,info);
+ break;
+ } else {
+ ++it;
+ }
+ } else {
+ spritePrepInfos_.insert(it,info);
+ break;
+ }
+ }
+ }
+
+ if(it == spritePrepInfos_.end())
+ {
+ spritePrepInfos_.push_back(info);
+ }
+ }
+ m_numSpritesDrawn++;
+ }
+
+ void AddSpriteRun(std::unique_ptr<InstanceData[]>& instanceData)
+ {
+ // Save the final sprite run info.
+ int index = 0;
+ int spriteInRun= 0;
+ std::for_each(spritePrepInfos_.begin(),spritePrepInfos_.end(),
+ [&](SpritePrepInfo& info)
+ {
+ if(index != 0
+ && (m_spriteRuns.end() - 1)->textureView == info.textureView && (m_spriteRuns.end() - 1)->blendState == info.blendState)
+ {
+ ++spriteInRun;
+ (m_spriteRuns.end() - 1)->numSprites = spriteInRun;
+ } else {
+ spriteInRun = 1;
+ SpriteRunInfo runInfo;
+ runInfo.textureView = info.textureView;
+ runInfo.blendState = info.blendState;
+ runInfo.numSprites = spriteInRun;
+ m_spriteRuns.push_back(runInfo);
+ }
+
+ info.instanceData.textureCellInfo.x = info.textureCellOffset.x / info.textureSize.x;
+ info.instanceData.textureCellInfo.y = info.textureCellOffset.y / info.textureSize.y;
+ info.instanceData.textureCellInfo.z = info.textureCellSize.x / info.textureSize.x;
+ info.instanceData.textureCellInfo.w = info.textureCellSize.y / info.textureSize.y;
+
+ instanceData[index] = info.instanceData;
+ ++index;
+ }
+ );
+
+ }
+
+ Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> m_indexBuffer;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> m_vertexBuffer;
+ Microsoft::WRL::ComPtr<ID3D11BlendState1> m_blendStateAlpha;
+ Microsoft::WRL::ComPtr<ID3D11BlendState1> m_blendStateAdditive;
+ Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
+ Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertexShader;
+ Microsoft::WRL::ComPtr<ID3D11GeometryShader> m_geometryShader;
+ Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixelShader;
+ Microsoft::WRL::ComPtr<ID3D11SamplerState> m_linearSampler;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> m_renderTargetInfoCbuffer;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> textureOffset_;
+
+ std::map<ID3D11Texture2D*, TextureMapElement> m_textureMap;
+ std::vector<SpriteRunInfo> m_spriteRuns;
+ std::list<SpritePrepInfo> spritePrepInfos_;
+
+ ID3D11ShaderResourceView* m_currentTextureView;
+ ID3D11BlendState1* m_currentBlendState;
+ float2 m_renderTargetSize;
+ int m_capacity;
+// int m_spritesInRun;
+ int m_numSpritesDrawn;
+ float m_dpi;
+};
+
+struct GeometryShaderSpriteDrawer : public SpriteDrawerBase
+{
+public:
+ GeometryShaderSpriteDrawer(){};
+ virtual void Initialize(
+ ID3D11Device1* d3dDevice,
+ int capacity
+ )
+ {
+ SpriteDrawerBase::Initialize(d3dDevice,capacity);
+
+ BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());
+ D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
+ {
+ { "TRANSFORM", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ { "TRANSFORM", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ { "TRANSFORM", 2, DXGI_FORMAT_R32_FLOAT, 0, 24, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ { "TRANSFORM", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 28, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 44, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ };
+ loader->LoadShader(
+ "BasicSprites.GeometryShader.vs.cso",
+ layoutDesc,
+ ARRAYSIZE(layoutDesc),
+ &m_vertexShader,
+ &m_inputLayout
+ );
+ loader->LoadShader(
+ "BasicSprites.GeometryShader.gs.cso",
+ &m_geometryShader
+ );
+
+ loader->LoadShader(
+ "BasicSprites.ps.cso",
+ &m_pixelShader
);
- BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());
+ // Create the instance data buffer.
+
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBuffer(
+ &CD3D11_BUFFER_DESC(
+ m_capacity * sizeof(InstanceData),
+ D3D11_BIND_VERTEX_BUFFER,
+ D3D11_USAGE_DEFAULT,
+ 0
+ ),
+ nullptr,
+ &m_instanceDataBuffer
+ )
+ );
+
+ m_instanceData.reset(new InstanceData[m_capacity]);
+
+ // Both the Geometry Shader and Instancing techniques scale geometry in shader code.
+ // As a result, they require information about the render target size.
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBuffer(
+ &CD3D11_BUFFER_DESC(
+ 16, // Constant buffer sizes must be a multiple of 16 bytes. 16 is sufficient for the required float2 data.
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE
+ ),
+ nullptr,
+ &m_renderTargetInfoCbuffer
+ )
+ );
+
+ }
+
+
+ virtual void End()
+ {
+ // If no sprites were drawn, do nothing.
+ if (m_numSpritesDrawn == 0)
+ {
+ return;
+ }
- if (m_technique == RenderTechnique::GeometryShader)
- {
- D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
- {
- { "TRANSFORM", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
- { "TRANSFORM", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
- { "TRANSFORM", 2, DXGI_FORMAT_R32_FLOAT, 0, 24, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
- { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
- };
- loader->LoadShader(
- "BasicSprites.GeometryShader.vs.cso",
- layoutDesc,
- ARRAYSIZE(layoutDesc),
- &m_vertexShader,
- &m_inputLayout
- );
- loader->LoadShader(
- "BasicSprites.GeometryShader.gs.cso",
- &m_geometryShader
- );
- }
- else if (m_technique == RenderTechnique::Instancing)
- {
+ AddSpriteRun(m_instanceData);
+
+ // Update the buffer data.
+
+ m_d3dContext->UpdateSubresource(
+ m_instanceDataBuffer.Get(),
+ 0,
+ &CD3D11_BOX(0, 0, 0, sizeof(InstanceData) * m_numSpritesDrawn, 1, 1),
+ m_instanceData.get(),
+ 0,
+ 0
+ );
+
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+ m_d3dContext->Map(
+ m_renderTargetInfoCbuffer.Get(),
+ 0,
+ D3D11_MAP_WRITE_DISCARD,
+ 0,
+ &mappedSubresource
+ );
+ *static_cast<float2*>(mappedSubresource.pData) = m_renderTargetSize;
+ m_d3dContext->Unmap(
+ m_renderTargetInfoCbuffer.Get(),
+ 0
+ );
+
+ // Set the pipeline state
+
+ m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
+ m_d3dContext->GSSetShader(
+ m_geometryShader.Get(),
+ nullptr,
+ 0
+ );
+ m_d3dContext->IASetInputLayout(m_inputLayout.Get());
+
+ m_d3dContext->VSSetShader(
+ m_vertexShader.Get(),
+ nullptr,
+ 0
+ );
+
+ m_d3dContext->GSSetConstantBuffers(
+ 0,
+ 1,
+ m_renderTargetInfoCbuffer.GetAddressOf()
+ );
+
+ m_d3dContext->PSSetShader(
+ m_pixelShader.Get(),
+ nullptr,
+ 0
+ );
+
+ m_d3dContext->PSSetSamplers(
+ 0,
+ 1,
+ m_linearSampler.GetAddressOf()
+ );
+
+ unsigned int stride = sizeof(InstanceData);
+ unsigned int offset = 0;
+ m_d3dContext->IASetVertexBuffers(
+ 0,
+ 1,
+ m_instanceDataBuffer.GetAddressOf(),
+ &stride,
+ &offset
+ );
+
+ // Draw each sprite run
+
+ unsigned int indexBase = 0;
+ for (auto runIterator = m_spriteRuns.begin(); runIterator != m_spriteRuns.end(); runIterator++)
+ {
+ m_d3dContext->PSSetShaderResources(
+ 0,
+ 1,
+ &runIterator->textureView
+ );
+
+ const FLOAT blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ m_d3dContext->OMSetBlendState(
+ runIterator->blendState,
+ nullptr,
+ 0xFFFFFFFF
+ );
+
+ unsigned int instancesToDraw = runIterator->numSprites;
+ m_d3dContext->DrawInstanced(
+ 1,
+ instancesToDraw,
+ 0,
+ indexBase
+ );
+ indexBase += instancesToDraw;
+ }
+ };
+
+private:
+ std::unique_ptr<InstanceData[]> m_instanceData;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> m_instanceDataBuffer;
+};
+
+struct InstancingSpriteDrawer : public SpriteDrawerBase
+{
+ virtual void Initialize(
+ ID3D11Device1* d3dDevice,
+ int capacity
+ )
+ {
+ SpriteDrawerBase::Initialize(d3dDevice,capacity);
+ BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());
D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TRANSFORM", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "TRANSFORM", 1, DXGI_FORMAT_R32G32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "TRANSFORM", 2, DXGI_FORMAT_R32_FLOAT, 1, 24, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
- { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 28, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
+ { "TRANSFORM", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 28, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
+ { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 44, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
};
loader->LoadShader(
"BasicSprites.Instancing.vs.cso",
&m_vertexShader,
&m_inputLayout
);
- }
- else if (m_technique == RenderTechnique::Replication)
- {
- D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
- {
- { "POSITIONT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
- { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
- { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
- };
- loader->LoadShader(
- "BasicSprites.Replication.vs.cso",
- layoutDesc,
- ARRAYSIZE(layoutDesc),
- &m_vertexShader,
- &m_inputLayout
- );
- }
- loader->LoadShader(
+ loader->LoadShader(
"BasicSprites.ps.cso",
&m_pixelShader
);
- // Create buffers.
-
- if (m_technique == RenderTechnique::GeometryShader)
- {
- // Create the instance data buffer.
-
- DX::ThrowIfFailed(
- m_d3dDevice->CreateBuffer(
- &CD3D11_BUFFER_DESC(
- m_capacity * sizeof(InstanceData),
- D3D11_BIND_VERTEX_BUFFER,
- D3D11_USAGE_DEFAULT,
- 0
- ),
- nullptr,
- &m_instanceDataBuffer
- )
- );
-
- m_instanceData.reset(new InstanceData[m_capacity]);
- }
- else if (m_technique == RenderTechnique::Instancing)
- {
- // Create the vertex buffer.
-
InstancingVertex vertexBufferData[] =
{
{ float4(-1.0f, 1.0f , 0.0f , 0.0f), float2(0.0f, 0.0f) },
{ float4(-1.0f, -1.0f , 0.0f , 0.0f), float2(0.0f, 1.0f) },
{ float4( 1.0f, -1.0f , 0.0f , 0.0f), float2(1.0f, 1.0f) }
};
+ // Create the vertex buffer.
D3D11_SUBRESOURCE_DATA vertexInitialData;
vertexInitialData.pSysMem = vertexBufferData;
&m_indexBuffer
)
);
- }
- else if (m_technique == RenderTechnique::Replication)
- {
+
+ // Both the Geometry Shader and Instancing techniques scale geometry in shader code.
+ // As a result, they require information about the render target size.
+ DX::ThrowIfFailed(
+ m_d3dDevice->CreateBuffer(
+ &CD3D11_BUFFER_DESC(
+ 16, // Constant buffer sizes must be a multiple of 16 bytes. 16 is sufficient for the required float2 data.
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE
+ ),
+ nullptr,
+ &m_renderTargetInfoCbuffer
+ )
+ );
+
+ };
+
+
+ virtual void End()
+ {
+ // If no sprites were drawn, do nothing.
+ if (m_numSpritesDrawn == 0)
+ {
+ return;
+ }
+
+
+ AddSpriteRun(m_instanceData);
+
+ // Update the buffer data.
+
+ m_d3dContext->UpdateSubresource(
+ m_instanceDataBuffer.Get(),
+ 0,
+ &CD3D11_BOX(0, 0, 0, sizeof(InstanceData) * m_numSpritesDrawn, 1, 1),
+ m_instanceData.get(),
+ 0,
+ 0
+ );
+
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+ m_d3dContext->Map(
+ m_renderTargetInfoCbuffer.Get(),
+ 0,
+ D3D11_MAP_WRITE_DISCARD,
+ 0,
+ &mappedSubresource
+ );
+ *static_cast<float2*>(mappedSubresource.pData) = m_renderTargetSize;
+ m_d3dContext->Unmap(
+ m_renderTargetInfoCbuffer.Get(),
+ 0
+ );
+
+ // Set the pipeline state
+
+ m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ m_d3dContext->IASetIndexBuffer(
+ m_indexBuffer.Get(),
+ DXGI_FORMAT_R32_UINT,
+ 0
+ );
+
+ m_d3dContext->IASetInputLayout(m_inputLayout.Get());
+
+ m_d3dContext->VSSetShader(
+ m_vertexShader.Get(),
+ nullptr,
+ 0
+ );
+
+ m_d3dContext->VSSetConstantBuffers(
+ 0,
+ 1,
+ m_renderTargetInfoCbuffer.GetAddressOf()
+ );
+
+ m_d3dContext->PSSetShader(
+ m_pixelShader.Get(),
+ nullptr,
+ 0
+ );
+
+ m_d3dContext->PSSetSamplers(
+ 0,
+ 1,
+ m_linearSampler.GetAddressOf()
+ );
+
+ unsigned int stride = sizeof(InstancingVertex);
+ unsigned int offset = 0;
+ m_d3dContext->IASetVertexBuffers(
+ 0,
+ 1,
+ m_vertexBuffer.GetAddressOf(),
+ &stride,
+ &offset
+ );
+
+ // Draw each sprite run
+
+ unsigned int indexBase = 0;
+ for (auto runIterator = m_spriteRuns.begin(); runIterator != m_spriteRuns.end(); runIterator++)
+ {
+ m_d3dContext->PSSetShaderResources(
+ 0,
+ 1,
+ &runIterator->textureView
+ );
+
+ const FLOAT blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ m_d3dContext->OMSetBlendState(
+ runIterator->blendState,
+ nullptr,
+ 0xFFFFFFFF
+ );
+
+ unsigned int instancesToDraw = runIterator->numSprites;
+ unsigned int stride = sizeof(InstanceData);
+ unsigned int offset = indexBase * stride;
+ // Instance data offset must be zero for the draw call on feature level 9.3 and below.
+ // Instead, set the offset in the input assembler.
+ m_d3dContext->IASetVertexBuffers(
+ 1,
+ 1,
+ m_instanceDataBuffer.GetAddressOf(),
+ &stride,
+ &offset
+ );
+ m_d3dContext->DrawIndexedInstanced(
+ 6,
+ instancesToDraw,
+ 0,
+ 0,
+ 0
+ );
+ indexBase += instancesToDraw;
+ }
+ }
+
+private:
+ std::unique_ptr<InstanceData[]> m_instanceData;
+ Microsoft::WRL::ComPtr<ID3D11Buffer> m_instanceDataBuffer;
+};
+
+struct ReplicationSpriteDrawer : public SpriteDrawerBase
+{
+
+ virtual void Initialize(
+ ID3D11Device1* d3dDevice,
+ int capacity
+ )
+ {
+ SpriteDrawerBase::Initialize(d3dDevice,capacity);
+ BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());
+
+ D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
+ {
+ { "POSITIONT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
+ };
+ loader->LoadShader(
+ "BasicSprites.Replication.vs.cso",
+ layoutDesc,
+ ARRAYSIZE(layoutDesc),
+ &m_vertexShader,
+ &m_inputLayout
+ );
+
+ loader->LoadShader(
+ "BasicSprites.ps.cso",
+ &m_pixelShader
+ );
+
// Create the vertex buffer.
DX::ThrowIfFailed(
&m_indexBuffer
)
);
- }
-
- if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
- {
- // Both the Geometry Shader and Instancing techniques scale geometry in shader code.
- // As a result, they require information about the render target size.
- DX::ThrowIfFailed(
- m_d3dDevice->CreateBuffer(
- &CD3D11_BUFFER_DESC(
- 16, // Constant buffer sizes must be a multiple of 16 bytes. 16 is sufficient for the required float2 data.
- D3D11_BIND_CONSTANT_BUFFER,
- D3D11_USAGE_DYNAMIC,
- D3D11_CPU_ACCESS_WRITE
- ),
- nullptr,
- &m_renderTargetInfoCbuffer
- )
- );
- }
-}
-
-void SpriteBatch::AddTexture(
- _In_ ID3D11Texture2D* texture
- )
-{
- TextureMapElement mapElement;
- DX::ThrowIfFailed(
- m_d3dDevice->CreateShaderResourceView(
- texture,
- &CD3D11_SHADER_RESOURCE_VIEW_DESC(
- texture,
- D3D11_SRV_DIMENSION_TEXTURE2D
- ),
- &mapElement.srv
- )
- );
-
- D3D11_TEXTURE2D_DESC textureDesc;
- texture->GetDesc(&textureDesc);
- mapElement.size = float2(
- static_cast<float>(textureDesc.Width),
- static_cast<float>(textureDesc.Height)
- );
-
- m_textureMap[texture] = mapElement;
-}
-
-void SpriteBatch::RemoveTexture(
- _In_ ID3D11Texture2D* texture
- )
-{
- m_textureMap.erase(texture);
-}
-
-void SpriteBatch::Begin()
-{
- // Reset internal sprite data.
-
- m_numSpritesDrawn = 0;
- m_spritesInRun = 0;
- m_spriteRuns.clear();
- spritePrepInfos_.clear();
- // Get the current render target dimensions and logical DPI.
-
- ComPtr<ID3D11RenderTargetView> renderTargetView;
- m_d3dContext->OMGetRenderTargets(
- 1,
- &renderTargetView,
- nullptr
- );
-
- ComPtr<ID3D11Resource> renderTarget;
- renderTargetView->GetResource(&renderTarget);
-
- ComPtr<ID3D11Texture2D> renderTargetTexture;
- renderTarget.As(&renderTargetTexture);
-
- D3D11_TEXTURE2D_DESC renderTargetTextureDesc;
- renderTargetTexture->GetDesc(&renderTargetTextureDesc);
-
- m_renderTargetSize = float2(
- static_cast<float>(renderTargetTextureDesc.Width),
- static_cast<float>(renderTargetTextureDesc.Height)
- );
- m_dpi = Windows::Graphics::Display::DisplayProperties::LogicalDpi;
-}
+ };
-void SpriteBatch::End()
-{
+ virtual void End()
+ {
// If no sprites were drawn, do nothing.
if (m_numSpritesDrawn == 0)
{
m_spriteRuns.push_back(runInfo);
}
- if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
- {
- m_instanceData[index] = info.instanceData;
- } else if (m_technique == RenderTechnique::Replication) {
- m_vertexData[index * 4 + 0] = info.replicationVertex[0];
- m_vertexData[index * 4 + 1] = info.replicationVertex[1];
- m_vertexData[index * 4 + 2] = info.replicationVertex[2];
- m_vertexData[index * 4 + 3] = info.replicationVertex[3];
- }
+ m_vertexData[index * 4 + 0] = info.replicationVertex[0];
+ m_vertexData[index * 4 + 1] = info.replicationVertex[1];
+ m_vertexData[index * 4 + 2] = info.replicationVertex[2];
+ m_vertexData[index * 4 + 3] = info.replicationVertex[3];
+
++index;
}
);
// Update the buffer data.
-
- if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
- {
- m_d3dContext->UpdateSubresource(
- m_instanceDataBuffer.Get(),
- 0,
- &CD3D11_BOX(0, 0, 0, sizeof(InstanceData) * m_numSpritesDrawn, 1, 1),
- m_instanceData.get(),
- 0,
- 0
- );
- }
- else if (m_technique == RenderTechnique::Replication)
- {
m_d3dContext->UpdateSubresource(
m_vertexBuffer.Get(),
0,
0,
0
);
- }
-
- if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
- {
- D3D11_MAPPED_SUBRESOURCE mappedSubresource;
- m_d3dContext->Map(
- m_renderTargetInfoCbuffer.Get(),
- 0,
- D3D11_MAP_WRITE_DISCARD,
- 0,
- &mappedSubresource
- );
- *static_cast<float2*>(mappedSubresource.pData) = m_renderTargetSize;
- m_d3dContext->Unmap(
- m_renderTargetInfoCbuffer.Get(),
- 0
- );
- }
// Set the pipeline state
- if (m_technique == RenderTechnique::Instancing)
- {
- m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
- m_d3dContext->IASetIndexBuffer(
- m_indexBuffer.Get(),
- DXGI_FORMAT_R32_UINT,
- 0
- );
- }
- else if (m_technique == RenderTechnique::Replication)
- {
m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_d3dContext->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0
);
- }
- else if (m_technique == RenderTechnique::GeometryShader)
- {
- m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
- m_d3dContext->GSSetShader(
- m_geometryShader.Get(),
- nullptr,
- 0
- );
- }
m_d3dContext->IASetInputLayout(m_inputLayout.Get());
0
);
- if (m_technique == RenderTechnique::GeometryShader)
- {
- m_d3dContext->GSSetConstantBuffers(
- 0,
- 1,
- m_renderTargetInfoCbuffer.GetAddressOf()
- );
- }
- else if (m_technique == RenderTechnique::Instancing)
- {
- m_d3dContext->VSSetConstantBuffers(
- 0,
- 1,
- m_renderTargetInfoCbuffer.GetAddressOf()
- );
- }
-
m_d3dContext->PSSetShader(
m_pixelShader.Get(),
nullptr,
m_linearSampler.GetAddressOf()
);
- if (m_technique == RenderTechnique::GeometryShader)
- {
- unsigned int stride = sizeof(InstanceData);
- unsigned int offset = 0;
- m_d3dContext->IASetVertexBuffers(
- 0,
- 1,
- m_instanceDataBuffer.GetAddressOf(),
- &stride,
- &offset
- );
- }
- else if (m_technique == RenderTechnique::Instancing)
- {
- unsigned int stride = sizeof(InstancingVertex);
- unsigned int offset = 0;
- m_d3dContext->IASetVertexBuffers(
- 0,
- 1,
- m_vertexBuffer.GetAddressOf(),
- &stride,
- &offset
- );
- }
- else if (m_technique == RenderTechnique::Replication)
- {
unsigned int stride = sizeof(ReplicationVertex);
unsigned int offset = 0;
m_d3dContext->IASetVertexBuffers(
&stride,
&offset
);
- }
// Draw each sprite run
0xFFFFFFFF
);
- if (m_technique == RenderTechnique::GeometryShader)
- {
- unsigned int instancesToDraw = runIterator->numSprites;
- m_d3dContext->DrawInstanced(
- 1,
- instancesToDraw,
- 0,
- indexBase
- );
- indexBase += instancesToDraw;
- }
- else if (m_technique == RenderTechnique::Instancing)
- {
- unsigned int instancesToDraw = runIterator->numSprites;
- unsigned int stride = sizeof(InstanceData);
- unsigned int offset = indexBase * stride;
- // Instance data offset must be zero for the draw call on feature level 9.3 and below.
- // Instead, set the offset in the input assembler.
- m_d3dContext->IASetVertexBuffers(
- 1,
- 1,
- m_instanceDataBuffer.GetAddressOf(),
- &stride,
- &offset
- );
- m_d3dContext->DrawIndexedInstanced(
- 6,
- instancesToDraw,
- 0,
- 0,
- 0
- );
- indexBase += instancesToDraw;
- }
- else if (m_technique == RenderTechnique::Replication)
- {
unsigned int indicesToDraw = runIterator->numSprites * 6;
m_d3dContext->DrawIndexed(indicesToDraw, indexBase, 0);
indexBase += indicesToDraw;
- }
}
-}
+ }
+
+ virtual void Draw(
+ ID3D11Texture2D* texture,
+ float4 position,
+ PositionUnits positionUnits,
+ float2 size,
+ SizeUnits sizeUnits,
+ float4 color,
+ float rotation,
+ BlendMode blendMode,
+ float2 textureCellOffset
+ )
+ {
+ // Fail if drawing this sprite would exceed the capacity of the sprite batch.
+ if (m_numSpritesDrawn >= m_capacity)
+ {
+ throw ref new Platform::OutOfBoundsException();
+ }
+
+ SpritePrepInfo info;
+ InitSpritePrepInfo(info,texture,blendMode,textureCellOffset);
+
+ // Unless this is the first sprite run, save out the previous run info if a new run is required.
+ //if (
+ // m_numSpritesDrawn > 0 && (
+ // textureView != m_currentTextureView ||
+ // blendState != m_currentBlendState
+ // )
+ // )
+ //{
+ // SpriteRunInfo runInfo;
+ // runInfo.textureView = m_currentTextureView;
+ // runInfo.blendState = m_currentBlendState;
+ // runInfo.numSprites = m_spritesInRun;
+ // m_spriteRuns.push_back(runInfo);
+ // m_spritesInRun = 0; // Reset for the next sprite run.
+ //}
+ //m_currentTextureView = textureView;
+ //m_currentBlendState = blendState;
+
+ // Add the sprite to the buffer.
+
+ float4 origin = StandardOrigin(position, positionUnits, m_renderTargetSize, m_dpi);
+ float2 offset = StandardOffset(size, sizeUnits, info.textureSize, m_dpi);
+
+ float2 offsets[4] =
+ {
+ float2(-offset.x, offset.y),
+ float2( offset.x, offset.y),
+ float2(-offset.x, -offset.y),
+ float2( offset.x, -offset.y)
+ };
+
+ float sinRotation = sinf(rotation);
+ float cosRotation = cosf(rotation);
+
+ for (int i = 0; i < 4; i++)
+ {
+ offsets[i] = float2(
+ offsets[i].x * cosRotation - offsets[i].y * sinRotation,
+ offsets[i].x * sinRotation + offsets[i].y * cosRotation
+ );
+ offsets[i].x /= m_renderTargetSize.x;
+ offsets[i].y /= m_renderTargetSize.y;
+ }
-unsigned int SpriteBatch::MakeUnorm(float4 color)
+ float textureCellOffsetX = info.textureCellOffset.x / info.textureSize.x;
+ float textureCellOffsetY = info.textureCellOffset.y / info.textureSize.y;
+ float textureCellOffsetWidth = info.textureCellSize.x / info.textureSize.x;
+ float textureCellOffsetHeight = info.textureCellSize.y / info.textureSize.y;
+
+ // Write vertex buffer data.
+
+ //ReplicationVertex* singleSpriteVertices = &m_vertexData[m_numSpritesDrawn * 4];
+ unsigned int colorUnorm = MakeUnorm(color);
+ info.replicationVertex[0].pos = float4(origin.x + offsets[0].x,origin.y + offsets[0].y,origin.z,origin.w);
+ info.replicationVertex[1].pos = float4(origin.x + offsets[1].x,origin.y + offsets[1].y,origin.z,origin.w);
+ info.replicationVertex[2].pos = float4(origin.x + offsets[2].x,origin.y + offsets[2].y,origin.z,origin.w);
+ info.replicationVertex[3].pos = float4(origin.x + offsets[3].x,origin.y + offsets[3].y,origin.z,origin.w);
+ info.replicationVertex[0].color = colorUnorm;
+ info.replicationVertex[1].color = colorUnorm;
+ info.replicationVertex[2].color = colorUnorm;
+ info.replicationVertex[3].color = colorUnorm;
+ info.replicationVertex[0].tex = float2(textureCellOffsetX,textureCellOffsetY);
+ info.replicationVertex[1].tex = float2(textureCellOffsetX + textureCellOffsetWidth, textureCellOffsetY);
+ info.replicationVertex[2].tex = float2(textureCellOffsetX,textureCellOffsetY + textureCellOffsetHeight);
+ info.replicationVertex[3].tex = float2(textureCellOffsetX + textureCellOffsetWidth,textureCellOffsetY + textureCellOffsetHeight);
+
+ AddSpritePrepInfo(info);
+ }
+private:
+ std::unique_ptr<ReplicationVertex[]> m_vertexData;
+};
+
+SpriteBatch::SpriteBatch()
{
- unsigned int r = max(0, min(255, static_cast<unsigned int>(color.r * 255.0f)));
- unsigned int g = max(0, min(255, static_cast<unsigned int>(color.g * 255.0f)));
- unsigned int b = max(0, min(255, static_cast<unsigned int>(color.b * 255.0f)));
- unsigned int a = max(0, min(255, static_cast<unsigned int>(color.a * 255.0f)));
- return
- (a << 24) |
- (b << 16) |
- (g << 8) |
- r;
}
-float4 SpriteBatch::StandardOrigin(float4 position, PositionUnits positionUnits, float2 renderTargetSize, float dpi)
+void SpriteBatch::Initialize(
+ _In_ ID3D11Device1* d3dDevice,
+ _In_ int capacity = 1024
+ )
{
- float4 origin;
-
- origin.z = position.z;
- origin.w = position.w;
+ //m_d3dDevice = d3dDevice;
+ //m_d3dDevice->GetImmediateContext1(&m_d3dContext);
+ //m_capacity = capacity;
- if (positionUnits == PositionUnits::Pixels)
- {
- origin.x = (position.x / renderTargetSize.x) * 2.0f - 1.0f;
- origin.y = 1.0f - (position.y / renderTargetSize.y) * 2.0f;
- }
- else if (positionUnits == PositionUnits::DIPs)
- {
- origin.x = ((position.x * dpi / 96.0f) / renderTargetSize.x) * 2.0f - 1.0f;
- origin.y = 1.0f - ((position.y * dpi / 96.0f) / renderTargetSize.y) * 2.0f;
- }
- else if (positionUnits == PositionUnits::Normalized)
+ // Determine the technique that will be used to render the sprites.
+ auto featureLevel = d3dDevice->GetFeatureLevel();
+
+ if (featureLevel >= D3D_FEATURE_LEVEL_10_0)
{
- origin.x = position.x * 2.0f - 1.0f;
- origin.y = 1.0f - position.y * 2.0f;
+ // On DirectX 10+ devices, the Geometry Shader allows the sprite vertices to be
+ // generated on the GPU, significantly reducing memory bandwidth requirements.
+ drawer_.reset(new GeometryShaderSpriteDrawer());
+ drawer_->Initialize(d3dDevice,capacity);
}
- else if (positionUnits == PositionUnits::UniformWidth)
+ else if (featureLevel >= D3D_FEATURE_LEVEL_9_3)
{
- origin.x = position.x * 2.0f - 1.0f;
- origin.y = 1.0f - position.y * (renderTargetSize.x / renderTargetSize.y) * 2.0f;
+ // On DirectX 9.3+ devices, instancing allows shared sprite geometry with unique
+ // per-sprite instance parameters, eliminating redundant data transfer.
+ drawer_.reset(new InstancingSpriteDrawer());
+ drawer_->Initialize(d3dDevice,capacity);
}
- else if (positionUnits == PositionUnits::UniformHeight)
+ else
{
- origin.x = position.x * (renderTargetSize.y / renderTargetSize.x) * 2.0f - 1.0f;
- origin.y = 1.0f - position.y * 2.0f;
+ // On devices that do not support Instancing, sprite vertex data must be replicated
+ // in order to achieve the desired effect.
+ // m_technique = RenderTechnique::Replication;
+
+ if (capacity > static_cast<int>(Parameters::MaximumCapacityCompatible))
+ {
+ // The index buffer format for feature-level 9.1 devices may only be 16 bits.
+ // With 4 vertices per sprite, this allows a maximum of (1 << 16) / 4 sprites.
+ throw ref new Platform::InvalidArgumentException();
+ }
+ drawer_.reset(new ReplicationSpriteDrawer());
+ drawer_->Initialize(d3dDevice,capacity);
+
}
- return origin;
+
}
-float2 SpriteBatch::StandardOffset(float2 size, SizeUnits sizeUnits, float2 spriteSize, float dpi)
+void SpriteBatch::AddTexture(
+ _In_ ID3D11Texture2D* texture,float2 textureCellSize
+ )
{
- float2 offset;
- if (sizeUnits == SizeUnits::Pixels)
- {
- offset = size;
- }
- else if (sizeUnits == SizeUnits::DIPs)
- {
- offset = size * dpi / 96.0f;
- }
- else if (sizeUnits == SizeUnits::Normalized)
- {
- offset = spriteSize * size;
- }
- return offset;
+ drawer_->AddTexture(texture,textureCellSize);
}
+void SpriteBatch::RemoveTexture(
+ _In_ ID3D11Texture2D* texture
+ )
+{
+ drawer_->RemoveTexture(texture);
+}
+
+void SpriteBatch::Begin()
+{
+ drawer_->Begin();
+}
+
+void SpriteBatch::End()
+{
+ drawer_->End();
+}
+
+
+
+
void SpriteBatch::Draw(
_In_ ID3D11Texture2D* texture,
_In_ float4 position,
_In_ BlendMode blendMode
)
{
- // Fail if drawing this sprite would exceed the capacity of the sprite batch.
- if (m_numSpritesDrawn >= m_capacity)
- {
- throw ref new Platform::OutOfBoundsException();
- }
-
- // Retrieve information about the sprite.
- TextureMapElement element = m_textureMap[texture];
- ID3D11ShaderResourceView* textureView = element.srv.Get();
- float2 textureSize = element.size;
- ID3D11BlendState1* blendState = blendMode == BlendMode::Additive ? m_blendStateAdditive.Get() : m_blendStateAlpha.Get();
-
- // Fail if the texture has not previously been added to the sprite batch.
- if (textureView == nullptr)
- {
- throw ref new Platform::NullReferenceException();
- }
-
- SpritePrepInfo info;
-
- // Unless this is the first sprite run, save out the previous run info if a new run is required.
- //if (
- // m_numSpritesDrawn > 0 && (
- // textureView != m_currentTextureView ||
- // blendState != m_currentBlendState
- // )
- // )
- //{
- // SpriteRunInfo runInfo;
- // runInfo.textureView = m_currentTextureView;
- // runInfo.blendState = m_currentBlendState;
- // runInfo.numSprites = m_spritesInRun;
- // m_spriteRuns.push_back(runInfo);
- // m_spritesInRun = 0; // Reset for the next sprite run.
- //}
- //m_currentTextureView = textureView;
- //m_currentBlendState = blendState;
-
- info.textureView = textureView;
- info.blendState = blendState;
-
- // Add the sprite to the buffer.
-
- float4 origin = StandardOrigin(position, positionUnits, m_renderTargetSize, m_dpi);
- float2 offset = StandardOffset(size, sizeUnits, textureSize, m_dpi);
-
-
- if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
- {
-
- info.instanceData.origin = origin;
- info.instanceData.offset = offset;
- info.instanceData.rotation = rotation;
- info.instanceData.color = MakeUnorm(color);
- if(spritePrepInfos_.empty()){
- spritePrepInfos_.push_back(info);
- } else {
- std::list<SpritePrepInfo>::const_iterator it = spritePrepInfos_.cbegin();
- while(it != spritePrepInfos_.end())
- {
- if(it->instanceData.origin.z > info.instanceData.origin.z)
- {
- ++it;
- } else {
- if(it->instanceData.origin.z == info.instanceData.origin.z)
- {
- if(it->textureView == info.textureView)
- {
- spritePrepInfos_.insert(it,info);
- break;
- } else {
- ++it;
- }
- } else {
- spritePrepInfos_.insert(it,info);
- break;
- }
- }
- }
-
- if(it == spritePrepInfos_.end())
- {
- spritePrepInfos_.push_back(info);
- }
- }
-
- //m_instanceData[m_numSpritesDrawn].origin = origin;
- //m_instanceData[m_numSpritesDrawn].offset = offset;
- //m_instanceData[m_numSpritesDrawn].rotation = rotation;
- //m_instanceData[m_numSpritesDrawn].color = MakeUnorm(color);
- }
- else if (m_technique == RenderTechnique::Replication)
- {
- float2 offsets[4] =
- {
- float2(-offset.x, offset.y),
- float2( offset.x, offset.y),
- float2(-offset.x, -offset.y),
- float2( offset.x, -offset.y)
- };
-
- float sinRotation = sinf(rotation);
- float cosRotation = cosf(rotation);
-
- for (int i = 0; i < 4; i++)
- {
- offsets[i] = float2(
- offsets[i].x * cosRotation - offsets[i].y * sinRotation,
- offsets[i].x * sinRotation + offsets[i].y * cosRotation
- );
- offsets[i].x /= m_renderTargetSize.x;
- offsets[i].y /= m_renderTargetSize.y;
- }
-
- // Write vertex buffer data.
-
- //ReplicationVertex* singleSpriteVertices = &m_vertexData[m_numSpritesDrawn * 4];
- unsigned int colorUnorm = MakeUnorm(color);
- info.replicationVertex[0].pos = float4(origin.x + offsets[0].x,origin.y + offsets[0].y,origin.z,origin.w);
- info.replicationVertex[1].pos = float4(origin.x + offsets[1].x,origin.y + offsets[1].y,origin.z,origin.w);
- info.replicationVertex[2].pos = float4(origin.x + offsets[2].x,origin.y + offsets[2].y,origin.z,origin.w);
- info.replicationVertex[3].pos = float4(origin.x + offsets[3].x,origin.y + offsets[3].y,origin.z,origin.w);
- info.replicationVertex[0].color = colorUnorm;
- info.replicationVertex[1].color = colorUnorm;
- info.replicationVertex[2].color = colorUnorm;
- info.replicationVertex[3].color = colorUnorm;
- info.replicationVertex[0].tex = float2(0.0f, 0.0f);
- info.replicationVertex[1].tex = float2(1.0f, 0.0f);
- info.replicationVertex[2].tex = float2(0.0f, 1.0f);
- info.replicationVertex[3].tex = float2(1.0f, 1.0f);
- std::list<SpritePrepInfo>::const_iterator it = spritePrepInfos_.cbegin();
- while(it != spritePrepInfos_.end())
- {
- if(it->replicationVertex[0].pos.z < info.replicationVertex[0].pos.z)
- {
- ++it;
- } else {
- if(it->replicationVertex[0].pos.z == info.replicationVertex[0].pos.z)
- {
- if(it->textureView == info.textureView)
- {
- spritePrepInfos_.insert(it,info);
- break;
- } else {
- ++it;
- }
- } else {
- spritePrepInfos_.insert(it,info);
- break;
- }
- }
- }
-
- if(it == spritePrepInfos_.end())
- {
- spritePrepInfos_.push_back(info);
- }
+ Draw(
+ texture,
+ position,
+ positionUnits,
+ size,
+ sizeUnits,
+ color,
+ rotation,
+ blendMode,
+ float2(0.0f,0.0f)
+ );
- }
+}
-// m_spritesInRun++;
- m_numSpritesDrawn++;
+void SpriteBatch::Draw(
+ _In_ ID3D11Texture2D* texture,
+ _In_ float4 position,
+ _In_ PositionUnits positionUnits,
+ _In_ float2 size,
+ _In_ SizeUnits sizeUnits,
+ _In_ float4 color,
+ _In_ float rotation,
+ _In_ BlendMode blendMode,
+ _In_ float2 textureOffset
+ )
+{
+ drawer_->Draw(texture,position,positionUnits,size,sizeUnits,color,rotation,blendMode,textureOffset);
}
+}
\ No newline at end of file