From 38f1e61fcb058da39b1ee55f6eceb5aa92bd443e Mon Sep 17 00:00:00 2001 From: ShawnHargreaves_cp Date: Fri, 12 Oct 2012 09:45:08 -0700 Subject: [PATCH] Added PrimitiveBatch for drawing user primitives. Also now set debug object names for all D3D resources (for PIX and debug layer leak reporting) PrimitiveBatch is a helper for easily and efficiently drawing dynamically generated geometry such as lines or trianges. It fills the same role as the legacy D3D9 APIs DrawPrimitiveUP and DrawIndexedPrimitiveUP. Dynamic submission is a highly effective pattern for drawing procedural geometry, and convenient for debug rendering, but is not nearly as efficient as static vertex buffers. Excessive dynamic submission is a common source of performance problems in apps. PrimitiveBatch manages the vertex and index buffers for you, using DISCARD and NO_OVERWRITE hints to avoid stalling the GPU pipeline. It automatically merges adjacent draw requests, so if you call DrawLine 100 times in a row, only a single GPU draw call will be generated. PrimitiveBatch is responsible for setting the vertex buffer, index buffer, and primitive topology, then issuing the final draw call. Unlike the higher level SpriteBatch helper, it does not provide shaders, set the input layout, or set any state objects. PrimitiveBatch is often used in conjunction with BasicEffect and the structures from VertexTypes.h, but it can work with any other shader or vertex formats of your own. --- DirectXTK_Desktop_2010.vcxproj | 2 + DirectXTK_Desktop_2010.vcxproj.filters | 6 + DirectXTK_Desktop_2012.vcxproj | 2 + DirectXTK_Desktop_2012.vcxproj.filters | 6 + DirectXTK_Windows8.vcxproj | 2 + DirectXTK_Windows8.vcxproj.filters | 6 + Inc/PrimitiveBatch.h | 142 +++++++++++ Readme.txt | 95 +++++++- Src/CommonStates.cpp | 28 ++- Src/ConstantBuffer.h | 2 + Src/DDSTextureLoader.cpp | 31 +-- Src/EffectCommon.cpp | 18 +- Src/GeometricPrimitive.cpp | 4 + Src/PlatformHelpers.h | 11 + Src/PrimitiveBatch.cpp | 322 +++++++++++++++++++++++++ Src/SpriteBatch.cpp | 8 + Src/SpriteFont.cpp | 7 +- Src/WICTextureLoader.cpp | 19 +- 18 files changed, 652 insertions(+), 59 deletions(-) create mode 100644 Inc/PrimitiveBatch.h create mode 100644 Src/PrimitiveBatch.cpp diff --git a/DirectXTK_Desktop_2010.vcxproj b/DirectXTK_Desktop_2010.vcxproj index 65b2fc779..bbe2570d7 100644 --- a/DirectXTK_Desktop_2010.vcxproj +++ b/DirectXTK_Desktop_2010.vcxproj @@ -25,6 +25,7 @@ + @@ -58,6 +59,7 @@ + diff --git a/DirectXTK_Desktop_2010.vcxproj.filters b/DirectXTK_Desktop_2010.vcxproj.filters index a99b1ae47..cc3ad0b2d 100644 --- a/DirectXTK_Desktop_2010.vcxproj.filters +++ b/DirectXTK_Desktop_2010.vcxproj.filters @@ -24,6 +24,9 @@ Inc + + Inc + Inc @@ -83,6 +86,9 @@ Src + + Src + Src diff --git a/DirectXTK_Desktop_2012.vcxproj b/DirectXTK_Desktop_2012.vcxproj index dd1ad6016..b331e501c 100644 --- a/DirectXTK_Desktop_2012.vcxproj +++ b/DirectXTK_Desktop_2012.vcxproj @@ -25,6 +25,7 @@ + @@ -58,6 +59,7 @@ + diff --git a/DirectXTK_Desktop_2012.vcxproj.filters b/DirectXTK_Desktop_2012.vcxproj.filters index fd6c04d55..4fb5ea43b 100644 --- a/DirectXTK_Desktop_2012.vcxproj.filters +++ b/DirectXTK_Desktop_2012.vcxproj.filters @@ -24,6 +24,9 @@ Inc + + Inc + Inc @@ -83,6 +86,9 @@ Src + + Src + Src diff --git a/DirectXTK_Windows8.vcxproj b/DirectXTK_Windows8.vcxproj index a54297a95..2cc316851 100644 --- a/DirectXTK_Windows8.vcxproj +++ b/DirectXTK_Windows8.vcxproj @@ -33,6 +33,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/DirectXTK_Windows8.vcxproj.filters b/DirectXTK_Windows8.vcxproj.filters index b90982919..8934a478a 100644 --- a/DirectXTK_Windows8.vcxproj.filters +++ b/DirectXTK_Windows8.vcxproj.filters @@ -24,6 +24,9 @@ Inc + + Inc + Inc @@ -83,6 +86,9 @@ Src + + Src + Src diff --git a/Inc/PrimitiveBatch.h b/Inc/PrimitiveBatch.h new file mode 100644 index 000000000..5e4b4a561 --- /dev/null +++ b/Inc/PrimitiveBatch.h @@ -0,0 +1,142 @@ +//-------------------------------------------------------------------------------------- +// File: PrimitiveBatch.h +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable: 4005) +#include +#pragma warning(pop) + + +namespace DirectX +{ + namespace Internal + { + // Base class, not to be used directly: clients should access this via the derived PrimitiveBatch. + class PrimitiveBatchBase + { + protected: + PrimitiveBatchBase(_In_ ID3D11DeviceContext* deviceContext, size_t maxIndices, size_t maxVertices, size_t vertexSize); + PrimitiveBatchBase(PrimitiveBatchBase&& moveFrom); + PrimitiveBatchBase& operator= (PrimitiveBatchBase&& moveFrom); + virtual ~PrimitiveBatchBase(); + + public: + // Begin/End a batch of primitive drawing operations. + void Begin(); + void End(); + + protected: + // Internal, untyped drawing method. + void Draw(D3D11_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Out_ void** pMappedVertices); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + // Prevent copying. + PrimitiveBatchBase(PrimitiveBatchBase const&); + PrimitiveBatchBase& operator= (PrimitiveBatchBase const&); + }; + } + + + // Template makes the API typesafe, eg. PrimitiveBatch. + template + class PrimitiveBatch : public Internal::PrimitiveBatchBase + { + static const size_t DefaultBatchSize = 2048; + + public: + PrimitiveBatch(_In_ ID3D11DeviceContext* deviceContext, size_t maxIndices = DefaultBatchSize * 3, size_t maxVertices = DefaultBatchSize) + : PrimitiveBatchBase(deviceContext, maxIndices, maxVertices, sizeof(TVertex)) + { } + + PrimitiveBatch(PrimitiveBatch&& moveFrom) + : PrimitiveBatchBase(std::move(moveFrom)) + { } + + PrimitiveBatch& operator= (PrimitiveBatch&& moveFrom) + { + PrimitiveBatchBase::operator=(std::move(moveFrom)); + return *this; + } + + + // Similar to the D3D9 API DrawPrimitiveUP. + void Draw(D3D11_PRIMITIVE_TOPOLOGY topology, _In_reads_(vertexCount) TVertex const* vertices, size_t vertexCount) + { + void* mappedVertices; + + PrimitiveBatchBase::Draw(topology, false, nullptr, 0, vertexCount, &mappedVertices); + + memcpy(mappedVertices, vertices, vertexCount * sizeof(TVertex)); + } + + + // Similar to the D3D9 API DrawIndexedPrimitiveUP. + void DrawIndexed(D3D11_PRIMITIVE_TOPOLOGY topology, _In_reads_(indexCount) uint16_t const* indices, size_t indexCount, _In_reads_(vertexCount) TVertex const* vertices, size_t vertexCount) + { + void* mappedVertices; + + PrimitiveBatchBase::Draw(topology, true, indices, indexCount, vertexCount, &mappedVertices); + + memcpy(mappedVertices, vertices, vertexCount * sizeof(TVertex)); + } + + + void DrawLine(TVertex const& v1, TVertex const& v2) + { + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D11_PRIMITIVE_TOPOLOGY_LINELIST, false, nullptr, 0, 2, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + } + + + void DrawTriangle(TVertex const& v1, TVertex const& v2, TVertex const& v3) + { + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, false, nullptr, 0, 3, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + mappedVertices[2] = v3; + } + + + void DrawQuad(TVertex const& v1, TVertex const& v2, TVertex const& v3, TVertex const& v4) + { + static const uint16_t quadIndices[] = { 0, 1, 2, 0, 2, 3 }; + + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, true, quadIndices, 6, 4, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + mappedVertices[2] = v3; + mappedVertices[3] = v4; + } + }; +} diff --git a/Readme.txt b/Readme.txt index d45707c11..041fa1b2c 100644 --- a/Readme.txt +++ b/Readme.txt @@ -4,7 +4,7 @@ DirectXTK - the DirectX Tool Kit Copyright (c) Microsoft Corporation. All rights reserved. -October 2, 2012 +October 12, 2012 This package contains the "DirectX Tool Kit", a collection of helper classes for writing Direct3D 11 C++ code for Windows Store apps, Windows 8 Win32 desktop @@ -26,6 +26,7 @@ Inc\ SpriteBatch.h - simple & efficient 2D sprite rendering SpriteFont.h - bitmap based text rendering Effects.h - set of built-in shaders for common rendering tasks + PrimitiveBatch.h - simple and efficient way to draw user primitives GeometricPrimitive.h - draws basic shapes such as cubes and spheres CommonStates.h - factory providing commonly used D3D state objects VertexTypes.h - structures for commonly used vertex data formats @@ -45,6 +46,7 @@ All content and source code for this package are bound to the Microsoft Public L http://go.microsoft.com/fwlink/?LinkId=248929 + ----------- SpriteBatch ----------- @@ -332,6 +334,88 @@ Further reading: +-------------- +PrimitiveBatch +-------------- + +This is a helper for easily and efficiently drawing dynamically generated +geometry such as lines or trianges. It fills the same role as the legacy D3D9 +APIs DrawPrimitiveUP and DrawIndexedPrimitiveUP. Dynamic submission is a highly +effective pattern for drawing procedural geometry, and convenient for debug +rendering, but is not nearly as efficient as static vertex buffers. Excessive +dynamic submission is a common source of performance problems in apps. + +PrimitiveBatch manages the vertex and index buffers for you, using DISCARD and +NO_OVERWRITE hints to avoid stalling the GPU pipeline. It automatically merges +adjacent draw requests, so if you call DrawLine 100 times in a row, only a +single GPU draw call will be generated. + +PrimitiveBatch is responsible for setting the vertex buffer, index buffer, and +primitive topology, then issuing the final draw call. Unlike the higher level +SpriteBatch helper, it does not provide shaders, set the input layout, or set +any state objects. PrimitiveBatch is often used in conjunction with BasicEffect +and the structures from VertexTypes.h, but it can work with any other shader or +vertex formats of your own. + +To initialize a PrimitiveBatch for drawing VertexPositionColor data: + + std::unique_ptr> primitiveBatch(new PrimitiveBatch(deviceContext)); + +To set up a suitable BasicEffect and input layout: + + std::unique_ptr basicEffect(new BasicEffect(device)); + + basicEffect->SetProjection(XMMatrixOrthographicOffCenterRH(0, screenHeight, screenWidth, 0, 0, 1)); + basicEffect->SetVertexColorEnabled(true); + + void const* shaderByteCode; + size_t byteCodeLength; + + basicEffect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength); + + ComPtr inputLayout; + + device->CreateInputLayout(VertexPositionColor::InputElements, + VertexPositionColor::InputElementCount, + shaderByteCode, byteCodeLength, + &inputLayout); + +To draw a line: + + basicEffect->Apply(deviceContext); + deviceContext->IASetInputLayout(inputLayout.Get()); + + primitiveBatch->Begin(); + primitiveBatch->DrawLine(VertexPositionColor(...), VertexPositionColor(...)); + primitiveBatch->End(); + +PrimitiveBatch provides five drawing methods: + + - DrawLine(v1, v2) + - DrawTriangle(v1, v2, v3) + - DrawQuad(v1, v2, v3, v4) + - Draw(topology, vertices, vertexCount) + - DrawIndexed(topology, indices, indexCount, vertices, vertexCount) + +Optimization: + + For best performance, draw as much as possible inside the fewest separate + Begin/End blocks. This will reduce overhead and maximize potential for + batching. + + The PrimitiveBatch constructor allows you to specify what size index and + vertex buffers to allocate. You may want to tweak these values to fit your + workload, or if you only intend to draw non-indexed geometry, specify + maxIndices = 0 to entirely skip creating the index buffer. + +Threading model: + + Each PrimitiveBatch instance only supports drawing from one thread at a + time, but you can simultaneously submit primitives on multiple threads if + you create a separate PrimitiveBatch instance per D3D11 deferred context. + + + ------------------ GeometricPrimitive ------------------ @@ -529,9 +613,9 @@ Further reading: ----------------- +---------- ScreenGrab ----------------- +---------- ScreenGrab.h contains routines for writing out a texture, usually a render-target, to either a .dds file or a WIC-supported bitmap file (BMP, JPEG, PNG, TIFF, etc.). @@ -567,7 +651,6 @@ Capturing a screenshot: hr = SaveWICTextureToFile( pContext, backBuffer, GUID_ContainerFormatBmp, L"SCREENSHOT.BMP" ) ); } - Threading model: Since these functions use ID3D11DeviceContext, they are not thread-safe. @@ -583,6 +666,10 @@ Further reading: RELEASE HISTORY --------------- +October 12, 2012 + Added PrimitiveBatch for drawing user primitives + Debug object names for all D3D resources (for PIX and debug layer leak reporting) + October 2, 2012 Added ScreenGrab module Added CreateGeoSphere for drawing a geodesic sphere diff --git a/Src/CommonStates.cpp b/Src/CommonStates.cpp index c29d1c35e..c07d7b7c8 100644 --- a/Src/CommonStates.cpp +++ b/Src/CommonStates.cpp @@ -82,7 +82,12 @@ HRESULT CommonStates::Impl::CreateBlendState(D3D11_BLEND srcBlend, D3D11_BLEND d desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - return device->CreateBlendState(&desc, pResult); + HRESULT hr = device->CreateBlendState(&desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:CommonStates"); + + return hr; } @@ -107,7 +112,12 @@ HRESULT CommonStates::Impl::CreateDepthStencilState(bool enable, bool writeEnabl desc.BackFace = desc.FrontFace; - return device->CreateDepthStencilState(&desc, pResult); + HRESULT hr = device->CreateDepthStencilState(&desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:CommonStates"); + + return hr; } @@ -122,7 +132,12 @@ HRESULT CommonStates::Impl::CreateRasterizerState(D3D11_CULL_MODE cullMode, D3D1 desc.DepthClipEnable = true; desc.MultisampleEnable = true; - return device->CreateRasterizerState(&desc, pResult); + HRESULT hr = device->CreateRasterizerState(&desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:CommonStates"); + + return hr; } @@ -142,7 +157,12 @@ HRESULT CommonStates::Impl::CreateSamplerState(D3D11_FILTER filter, D3D11_TEXTUR desc.MaxAnisotropy = 16; desc.ComparisonFunc = D3D11_COMPARISON_NEVER; - return device->CreateSamplerState(&desc, pResult); + HRESULT hr = device->CreateSamplerState(&desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:CommonStates"); + + return hr; } diff --git a/Src/ConstantBuffer.h b/Src/ConstantBuffer.h index 595254597..a1228393d 100644 --- a/Src/ConstantBuffer.h +++ b/Src/ConstantBuffer.h @@ -39,6 +39,8 @@ namespace DirectX ThrowIfFailed( device->CreateBuffer(&desc, nullptr, &mConstantBuffer) ); + + SetDebugObjectName(mConstantBuffer.Get(), "DirectXTK"); } diff --git a/Src/DDSTextureLoader.cpp b/Src/DDSTextureLoader.cpp index b5433efb2..fad637616 100644 --- a/Src/DDSTextureLoader.cpp +++ b/Src/DDSTextureLoader.cpp @@ -762,11 +762,7 @@ static HRESULT CreateD3DResources( _In_ ID3D11Device* d3dDevice, } else { -#if defined(_DEBUG) || defined(PROFILE) - tex->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("DDSTextureLoader")-1, - "DDSTextureLoader" ); -#endif + SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } @@ -846,12 +842,7 @@ static HRESULT CreateD3DResources( _In_ ID3D11Device* d3dDevice, } else { -#if defined(_DEBUG) || defined(PROFILE) - tex->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("DDSTextureLoader")-1, - "DDSTextureLoader" - ); -#endif + SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } @@ -903,11 +894,7 @@ static HRESULT CreateD3DResources( _In_ ID3D11Device* d3dDevice, } else { -#if defined(_DEBUG) || defined(PROFILE) - tex->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("DDSTextureLoader")-1, - "DDSTextureLoader" ); -#endif + SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } @@ -1206,23 +1193,15 @@ HRESULT DirectX::CreateDDSTextureFromMemory( _In_ ID3D11Device* d3dDevice, maxsize ); -#if defined(_DEBUG) || defined(PROFILE) if (texture != 0 && *texture != 0) { - (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("DDSTextureLoader")-1, - "DDSTextureLoader" - ); + SetDebugObjectName(*texture, "DDSTextureLoader"); } if (textureView != 0 && *textureView != 0) { - (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("DDSTextureLoader")-1, - "DDSTextureLoader" - ); + SetDebugObjectName(*textureView, "DDSTextureLoader"); } -#endif return hr; } diff --git a/Src/EffectCommon.cpp b/Src/EffectCommon.cpp index e384c9b63..9559e618e 100644 --- a/Src/EffectCommon.cpp +++ b/Src/EffectCommon.cpp @@ -356,9 +356,14 @@ void EffectLights::EnableDefaultLighting(_In_ IEffectLights* effect) // Gets or lazily creates the specified vertex shader permutation. ID3D11VertexShader* EffectDeviceResources::DemandCreateVertexShader(_Inout_ ComPtr& vertexShader, ShaderBytecode const& bytecode) { - return DemandCreate(vertexShader, mMutex, [&](ID3D11VertexShader** pResult) + return DemandCreate(vertexShader, mMutex, [&](ID3D11VertexShader** pResult) -> HRESULT { - return mDevice->CreateVertexShader(bytecode.code, bytecode.length, nullptr, pResult); + HRESULT hr = mDevice->CreateVertexShader(bytecode.code, bytecode.length, nullptr, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:Effect"); + + return hr; }); } @@ -366,8 +371,13 @@ ID3D11VertexShader* EffectDeviceResources::DemandCreateVertexShader(_Inout_ ComP // Gets or lazily creates the specified pixel shader permutation. ID3D11PixelShader* EffectDeviceResources::DemandCreatePixelShader(_Inout_ ComPtr& pixelShader, ShaderBytecode const& bytecode) { - return DemandCreate(pixelShader, mMutex, [&](ID3D11PixelShader** pResult) + return DemandCreate(pixelShader, mMutex, [&](ID3D11PixelShader** pResult) -> HRESULT { - return mDevice->CreatePixelShader(bytecode.code, bytecode.length, nullptr, pResult); + HRESULT hr = mDevice->CreatePixelShader(bytecode.code, bytecode.length, nullptr, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, "DirectXTK:Effect"); + + return hr; }); } diff --git a/Src/GeometricPrimitive.cpp b/Src/GeometricPrimitive.cpp index 2b6de57a3..696230e81 100644 --- a/Src/GeometricPrimitive.cpp +++ b/Src/GeometricPrimitive.cpp @@ -111,6 +111,8 @@ static void CreateInputLayout(_In_ ID3D11Device* device, IEffect* effect, _Out_ shaderByteCode, byteCodeLength, pInputLayout) ); + + SetDebugObjectName(*pInputLayout, "DirectXTK:GeometricPrimitive"); } @@ -216,6 +218,8 @@ static void CreateBuffer(_In_ ID3D11Device* device, T const& data, D3D11_BIND_FL ThrowIfFailed( device->CreateBuffer(&bufferDesc, &dataDesc, pBuffer) ); + + SetDebugObjectName(*pBuffer, "DirectXTK:GeometricPrimitive"); } diff --git a/Src/PlatformHelpers.h b/Src/PlatformHelpers.h index 65de5664b..dc55e24d6 100644 --- a/Src/PlatformHelpers.h +++ b/Src/PlatformHelpers.h @@ -77,6 +77,16 @@ namespace DirectX } + // Helper sets a D3D resource name string (used by PIX and debug layer leak reporting). + template + inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char (&name)[TNameLength]) + { + #if defined(_DEBUG) || defined(PROFILE) + resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name); + #endif + } + + // Helper smart-pointers struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; @@ -84,6 +94,7 @@ namespace DirectX inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; } + template class ScopedObject { public: diff --git a/Src/PrimitiveBatch.cpp b/Src/PrimitiveBatch.cpp new file mode 100644 index 000000000..e6486df17 --- /dev/null +++ b/Src/PrimitiveBatch.cpp @@ -0,0 +1,322 @@ +//-------------------------------------------------------------------------------------- +// File: PrimitiveBatch.cpp +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PrimitiveBatch.h" +#include "PlatformHelpers.h" + +using namespace DirectX; +using namespace DirectX::Internal; +using namespace Microsoft::WRL; + + +// Internal PrimitiveBatch implementation class. +class PrimitiveBatchBase::Impl +{ +public: + Impl(_In_ ID3D11DeviceContext* deviceContext, size_t maxIndices, size_t maxVertices, size_t vertexSize); + + void Begin(); + void End(); + + void Draw(D3D11_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Out_ void** pMappedVertices); + +private: + void FlushBatch(); + + ComPtr mDeviceContext; + ComPtr mIndexBuffer; + ComPtr mVertexBuffer; + + size_t mMaxIndices; + size_t mMaxVertices; + size_t mVertexSize; + + bool mInBeginEndPair; + + D3D11_PRIMITIVE_TOPOLOGY mCurrentTopology; + bool mCurrentlyIndexed; + + size_t mCurrentIndex; + size_t mCurrentVertex; + + size_t mBaseIndex; + size_t mBaseVertex; + + D3D11_MAPPED_SUBRESOURCE mMappedIndices; + D3D11_MAPPED_SUBRESOURCE mMappedVertices; +}; + + +// Helper for creating a D3D vertex or index buffer. +static void CreateBuffer(_In_ ID3D11Device* device, size_t bufferSize, D3D11_BIND_FLAG bindFlag, _Out_ ID3D11Buffer** pBuffer) +{ + D3D11_BUFFER_DESC desc = { 0 }; + + desc.ByteWidth = (UINT)bufferSize; + desc.BindFlags = bindFlag; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + ThrowIfFailed( + device->CreateBuffer(&desc, nullptr, pBuffer) + ); + + SetDebugObjectName(*pBuffer, "DirectXTK:PrimitiveBatch"); +} + + +// Constructor. +PrimitiveBatchBase::Impl::Impl(_In_ ID3D11DeviceContext* deviceContext, size_t maxIndices, size_t maxVertices, size_t vertexSize) + : mDeviceContext(deviceContext), + mMaxIndices(maxIndices), + mMaxVertices(maxVertices), + mVertexSize(vertexSize), + mInBeginEndPair(false), + mCurrentTopology(D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED), + mCurrentlyIndexed(false), + mCurrentIndex(0), + mCurrentVertex(0), + mBaseIndex(0), + mBaseVertex(0) +{ + ComPtr device; + + deviceContext->GetDevice(&device); + + // If you only intend to draw non-indexed geometry, specify maxIndices = 0 to skip creating the index buffer. + if (maxIndices > 0) + { + CreateBuffer(device.Get(), maxIndices * sizeof(uint16_t), D3D11_BIND_INDEX_BUFFER, &mIndexBuffer); + } + + // Create the vertex buffer. + CreateBuffer(device.Get(), maxVertices * vertexSize, D3D11_BIND_VERTEX_BUFFER, &mVertexBuffer); +} + + +// Begins a batch of primitive drawing operations. +void PrimitiveBatchBase::Impl::Begin() +{ + if (mInBeginEndPair) + throw std::exception("Cannot nest Begin calls"); + + // Bind the index buffer. + if (mMaxIndices > 0) + { + mDeviceContext->IASetIndexBuffer(mIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0); + } + + // Bind the vertex buffer. + auto vertexBuffer = mVertexBuffer.Get(); + UINT vertexStride = (UINT)mVertexSize; + UINT vertexOffset = 0; + + mDeviceContext->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset); + + // If this is a deferred D3D context, reset position so the first Map calls will use D3D11_MAP_WRITE_DISCARD. + if (mDeviceContext->GetType() == D3D11_DEVICE_CONTEXT_DEFERRED) + { + mCurrentIndex = 0; + mCurrentVertex = 0; + } + + mInBeginEndPair = true; +} + + +// Ends a batch of primitive drawing operations. +void PrimitiveBatchBase::Impl::End() +{ + if (!mInBeginEndPair) + throw std::exception("Begin must be called before End"); + + FlushBatch(); + + mInBeginEndPair = false; +} + + +// Can we combine adjacent primitives using this topology into a single draw call? +static bool CanBatchPrimitives(D3D11_PRIMITIVE_TOPOLOGY topology) +{ + switch (topology) + { + case D3D11_PRIMITIVE_TOPOLOGY_POINTLIST: + case D3D11_PRIMITIVE_TOPOLOGY_LINELIST: + case D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST: + // Lists can easily be merged. + return true; + + default: + // Strips cannot. + return false; + } + + // We could also merge indexed strips by inserting degenerates, + // but that's not always a perf win, so let's keep things simple. +} + + +// Helper for locking a vertex or index buffer. +static void LockBuffer(_In_ ID3D11DeviceContext* deviceContext, _In_ ID3D11Buffer* buffer, size_t currentPosition, _Out_ size_t* basePosition, _Out_ D3D11_MAPPED_SUBRESOURCE* mappedResource) +{ + D3D11_MAP mapType = (currentPosition == 0) ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + + ThrowIfFailed( + deviceContext->Map(buffer, 0, mapType, 0, mappedResource) + ); + + *basePosition = currentPosition; +} + + +// Adds new geometry to the batch. +void PrimitiveBatchBase::Impl::Draw(D3D11_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Out_ void** pMappedVertices) +{ + if (isIndexed && !indices) + throw std::exception("Indices cannot be null"); + + if (indexCount >= mMaxIndices) + throw std::exception("Too many indices"); + + if (vertexCount >= mMaxVertices) + throw std::exception("Too many vertices"); + + if (!mInBeginEndPair) + throw std::exception("Begin must be called before Draw"); + + // Can we merge this primitive in with an existing batch, or must we flush first? + bool wrapIndexBuffer = (mCurrentIndex + indexCount > mMaxIndices); + bool wrapVertexBuffer = (mCurrentVertex + vertexCount > mMaxVertices); + + if ((topology != mCurrentTopology) || + (isIndexed != mCurrentlyIndexed) || + !CanBatchPrimitives(topology) || + wrapIndexBuffer || wrapVertexBuffer) + { + FlushBatch(); + } + + if (wrapIndexBuffer) + mCurrentIndex = 0; + + if (wrapVertexBuffer) + mCurrentVertex = 0; + + // If we are not already in a batch, lock the buffers. + if (mCurrentTopology == D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) + { + if (isIndexed) + { + LockBuffer(mDeviceContext.Get(), mIndexBuffer.Get(), mCurrentIndex, &mBaseIndex, &mMappedIndices); + } + + LockBuffer(mDeviceContext.Get(), mVertexBuffer.Get(), mCurrentVertex, &mBaseVertex, &mMappedVertices); + + mCurrentTopology = topology; + mCurrentlyIndexed = isIndexed; + } + + // Copy over the index data. + if (isIndexed) + { + uint16_t* outputIndices = (uint16_t*)mMappedIndices.pData + mCurrentIndex; + + for (size_t i = 0; i < indexCount; i++) + { + outputIndices[i] = (uint16_t)(indices[i] + mCurrentVertex - mBaseVertex); + } + + mCurrentIndex += indexCount; + } + + // Return the output vertex data location. + *pMappedVertices = (uint8_t*)mMappedVertices.pData + (mCurrentVertex * mVertexSize); + + mCurrentVertex += vertexCount; +} + + +// Sends queued primitives to the graphics device. +void PrimitiveBatchBase::Impl::FlushBatch() +{ + // Early out if there is nothing to flush. + if (mCurrentTopology == D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) + return; + + mDeviceContext->IASetPrimitiveTopology(mCurrentTopology); + + mDeviceContext->Unmap(mVertexBuffer.Get(), 0); + + if (mCurrentlyIndexed) + { + // Draw indexed geometry. + mDeviceContext->Unmap(mIndexBuffer.Get(), 0); + + mDeviceContext->DrawIndexed((UINT)(mCurrentIndex - mBaseIndex), (UINT)mBaseIndex, (UINT)mBaseVertex); + } + else + { + // Draw non-indexed geometry. + mDeviceContext->Draw((UINT)(mCurrentVertex - mBaseVertex), (UINT)mBaseVertex); + } + + mCurrentTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + + +// Public constructor. +PrimitiveBatchBase::PrimitiveBatchBase(_In_ ID3D11DeviceContext* deviceContext, size_t maxIndices, size_t maxVertices, size_t vertexSize) + : pImpl(new Impl(deviceContext, maxIndices, maxVertices, vertexSize)) +{ +} + + +// Move constructor. +PrimitiveBatchBase::PrimitiveBatchBase(PrimitiveBatchBase&& moveFrom) + : pImpl(std::move(moveFrom.pImpl)) +{ +} + + +// Move assignment. +PrimitiveBatchBase& PrimitiveBatchBase::operator= (PrimitiveBatchBase&& moveFrom) +{ + pImpl = std::move(moveFrom.pImpl); + return *this; +} + + +// Public destructor. +PrimitiveBatchBase::~PrimitiveBatchBase() +{ +} + + +void PrimitiveBatchBase::Begin() +{ + pImpl->Begin(); +} + + +void PrimitiveBatchBase::End() +{ + pImpl->End(); +} + + +void PrimitiveBatchBase::Draw(D3D11_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Out_ void** pMappedVertices) +{ + pImpl->Draw(topology, isIndexed, indices, indexCount, vertexCount, pMappedVertices); +} diff --git a/Src/SpriteBatch.cpp b/Src/SpriteBatch.cpp index 074faa83b..e9bd34e1e 100644 --- a/Src/SpriteBatch.cpp +++ b/Src/SpriteBatch.cpp @@ -239,6 +239,10 @@ void SpriteBatch::Impl::DeviceResources::CreateShaders(_In_ ID3D11Device* device sizeof(SpriteEffect_SpriteVertexShader), &inputLayout) ); + + SetDebugObjectName(vertexShader.Get(), "DirectXTK:SpriteBatch"); + SetDebugObjectName(pixelShader.Get(), "DirectXTK:SpriteBatch"); + SetDebugObjectName(inputLayout.Get(), "DirectXTK:SpriteBatch"); } @@ -260,6 +264,8 @@ void SpriteBatch::Impl::DeviceResources::CreateIndexBuffer(_In_ ID3D11Device* de ThrowIfFailed( device->CreateBuffer(&indexBufferDesc, &indexDataDesc, &indexBuffer) ); + + SetDebugObjectName(indexBuffer.Get(), "DirectXTK:SpriteBatch"); } @@ -309,6 +315,8 @@ void SpriteBatch::Impl::ContextResources::CreateVertexBuffer() ThrowIfFailed( GetDevice(deviceContext.Get())->CreateBuffer(&vertexBufferDesc, nullptr, &vertexBuffer) ); + + SetDebugObjectName(vertexBuffer.Get(), "DirectXTK:SpriteBatch"); } diff --git a/Src/SpriteFont.cpp b/Src/SpriteFont.cpp index f0a296a02..f210450a6 100644 --- a/Src/SpriteFont.cpp +++ b/Src/SpriteFont.cpp @@ -118,11 +118,8 @@ SpriteFont::Impl::Impl(_In_ ID3D11Device* device, _In_ BinaryReader* reader) device->CreateShaderResourceView(texture2D.Get(), &viewDesc, &texture) ); -#if defined(_DEBUG) || defined(PROFILE) - const char debugLabel[] = "SpriteFont"; - texture->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(debugLabel) - 1, debugLabel); - texture2D->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(debugLabel) - 1, debugLabel); -#endif + SetDebugObjectName(texture.Get(), "DirectXTK:SpriteFont"); + SetDebugObjectName(texture2D.Get(), "DirectXTK:SpriteFont"); } diff --git a/Src/WICTextureLoader.cpp b/Src/WICTextureLoader.cpp index bdaad69fd..1ff84fd4c 100644 --- a/Src/WICTextureLoader.cpp +++ b/Src/WICTextureLoader.cpp @@ -516,12 +516,7 @@ static HRESULT CreateTextureFromWIC( _In_ ID3D11Device* d3dDevice, } else { -#if defined(_DEBUG) || defined(PROFILE) - tex->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("WICTextureLoader")-1, - "WICTextureLoader" - ); -#endif + SetDebugObjectName(tex, "WICTextureLoader"); tex->Release(); } } @@ -583,23 +578,15 @@ HRESULT DirectX::CreateWICTextureFromMemory( _In_ ID3D11Device* d3dDevice, if ( FAILED(hr)) return hr; -#if defined(_DEBUG) || defined(PROFILE) if (texture != 0 && *texture != 0) { - (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("WICTextureLoader")-1, - "WICTextureLoader" - ); + SetDebugObjectName(*texture, "WICTextureLoader"); } if (textureView != 0 && *textureView != 0) { - (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, - sizeof("WICTextureLoader")-1, - "WICTextureLoader" - ); + SetDebugObjectName(*textureView, "WICTextureLoader"); } -#endif return hr; }