From 973ee146a8a37039f5d8f6db24c967c8035c81c6 Mon Sep 17 00:00:00 2001 From: Michael Vance Date: Mon, 11 Apr 2016 21:23:05 -0400 Subject: [PATCH] Support for a new 'Statistics' pane. Notes ====== - Create a (hopefully) cross-backend performance statistics abstraction as part of FetchFrameInfo. This currently collects statistics about constant buffer binds, sampler binds, resource binds, client and server style resource updates (e.g. Map and UpdateSubresource), index & vertex buffer binds, and draws and dispatches. In my captures this covers approximately half of all API traffic. The rest is often shader sets, and then usual RS, OM, etc., that aren't currently tracked. During READING state parsing on the wrapped device context, record statistics about the calls and store them into the current frame info. We inspect objects occasionally to get things like their type for recording. It may be useful to expand this in the future to check bind types, etc. On the GL/Vulkan backends the stats data is never initialized and we display the same statistics data as before. - Add a new statistics pane in the UI. A variety of shim arbitration/marshalling is provided to get the statistics data across the managed/native boundary. Removed the old statistics menu item. Currently the statistics pane is just a text log of various gathered data, with ASCII art (woo!) histograms. However in the future we would like to have image based historgrams as well as support for gathering statistics on a mask of stages, etc. - Remove 'diagnostic' events (e.g. push/pop/marker) from the API call count as it distorts the API:draw call ratio. - Add _First and _Count to ShaderResourceType and ShaderStageType for weak enumeration (weak due to int cast requirement). - Provide utility functions Log2Floor for 32 and 64 bit types. These require OS specific clz, which are provided for Windows/VS and Linux/gcc/clang. TODO ====== - UI toolkit based historgram, ability to set stages enabled for statistics, etc. - Revisit necessity of gathering statistics across frames--apparently there is no multi-frame capture in a single log anymore? - Revisit min/max/etc. gathering on the data--is there some way to improve this to not be so mechanical? Perhaps with field enumeration? However if moving to C++/Qt in future, possibly not a good time investment. --- renderdoc/api/replay/data_types.h | 110 +++ renderdoc/api/replay/replay_enums.h | 4 + renderdoc/common/common.cpp | 14 + renderdoc/common/common.h | 5 + renderdoc/common/globalconfig.h | 6 + renderdoc/core/replay_proxy.cpp | 127 ++- renderdoc/driver/d3d11/d3d11_context.cpp | 202 +++++ renderdoc/driver/d3d11/d3d11_context.h | 32 + .../driver/d3d11/d3d11_context1_wrap.cpp | 6 + renderdoc/driver/d3d11/d3d11_context_wrap.cpp | 155 +++- renderdoc/driver/d3d11/d3d11_device.cpp | 25 + renderdoc/driver/gl/gl_driver.cpp | 1 + renderdoc/driver/vulkan/vk_core.cpp | 1 + renderdoc/os/linux_specific.h | 14 + renderdoc/os/os_specific.h | 9 + renderdoc/os/win32_specific.h | 20 + renderdoc/serialise/serialiser.h | 7 + renderdocui/Code/Core.cs | 14 +- renderdocui/Interop/Enums.cs | 3 + renderdocui/Interop/FetchInfo.cs | 115 +++ renderdocui/Windows/MainWindow.Designer.cs | 129 ++- renderdocui/Windows/MainWindow.cs | 146 +--- .../Windows/StatisticsViewer.Designer.cs | 63 ++ renderdocui/Windows/StatisticsViewer.cs | 785 ++++++++++++++++++ renderdocui/Windows/StatisticsViewer.resx | 120 +++ renderdocui/renderdocui.csproj | 9 + 26 files changed, 1880 insertions(+), 242 deletions(-) create mode 100644 renderdocui/Windows/StatisticsViewer.Designer.cs create mode 100644 renderdocui/Windows/StatisticsViewer.cs create mode 100644 renderdocui/Windows/StatisticsViewer.resx diff --git a/renderdoc/api/replay/data_types.h b/renderdoc/api/replay/data_types.h index 42510ec85..f7e50bc94 100644 --- a/renderdoc/api/replay/data_types.h +++ b/renderdoc/api/replay/data_types.h @@ -134,6 +134,115 @@ struct DebugMessage rdctype::str description; }; +enum BucketRecordType +{ + BUCKET_RECORD_TYPE_LINEAR, + BUCKET_RECORD_TYPE_POW2, + + BUCKET_RECORD_TYPE_COUNT, +}; + +struct FetchFrameConstantBindStats +{ + enum Constants + { + BUCKET_TYPE = BUCKET_RECORD_TYPE_POW2, + BUCKET_COUNT = 31, + }; + uint32_t calls; + uint32_t sets; + uint32_t nulls; + rdctype::array slots; + rdctype::array sizes; +}; + +struct FetchFrameSamplerBindStats +{ + uint32_t calls; + uint32_t sets; + uint32_t nulls; + rdctype::array slots; +}; + +struct FetchFrameResourceBindStats +{ + uint32_t calls; + uint32_t sets; + uint32_t nulls; + rdctype::array types; + rdctype::array slots; +}; + +struct FetchFrameUpdateStats +{ + enum Constants + { + BUCKET_TYPE = BUCKET_RECORD_TYPE_POW2, + BUCKET_COUNT = 31, + }; + uint32_t calls; + uint32_t clients; + uint32_t servers; + rdctype::array types; + rdctype::array sizes; +}; + +struct FetchFrameDrawStats +{ + enum Constants + { + BUCKET_TYPE = BUCKET_RECORD_TYPE_LINEAR, + BUCKET_SIZE = 1, + BUCKET_COUNT = 16, + }; + uint32_t calls; + uint32_t instanced; + uint32_t indirect; + rdctype::array counts; +}; + +struct FetchFrameDispatchStats +{ + uint32_t calls; + uint32_t indirect; +}; + +struct FetchFrameIndexBindStats +{ + uint32_t calls; + uint32_t sets; + uint32_t nulls; +}; + +struct FetchFrameVertexBindStats +{ + uint32_t calls; + uint32_t sets; + uint32_t nulls; + rdctype::array slots; +}; + +struct FetchFrameLayoutBindStats +{ + uint32_t calls; + uint32_t sets; + uint32_t nulls; +}; + +struct FetchFrameStatistics +{ + uint32_t recorded; + FetchFrameConstantBindStats constants[eShaderStage_Count]; + FetchFrameSamplerBindStats samplers[eShaderStage_Count]; + FetchFrameResourceBindStats resources[eShaderStage_Count]; + FetchFrameUpdateStats updates; + FetchFrameDrawStats draws; + FetchFrameDispatchStats dispatches; + FetchFrameIndexBindStats indices; + FetchFrameVertexBindStats vertices; + FetchFrameLayoutBindStats layouts; +}; + struct FetchFrameInfo { uint32_t frameNumber; @@ -141,6 +250,7 @@ struct FetchFrameInfo uint64_t fileOffset; uint64_t captureTime; ResourceId immContextId; + FetchFrameStatistics stats; rdctype::array debugMessages; }; diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 8714f82cd..1110454a8 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -73,6 +73,7 @@ enum ShaderResourceType eResType_Texture3D, eResType_TextureCube, eResType_TextureCubeArray, + eResType_Count, }; enum ShaderBindType @@ -286,6 +287,7 @@ enum TextureCreationFlags enum ShaderStageType { eShaderStage_Vertex = 0, + eShaderStage_First = eShaderStage_Vertex, eShaderStage_Hull, eShaderStage_Tess_Control = eShaderStage_Hull, @@ -299,6 +301,8 @@ enum ShaderStageType eShaderStage_Fragment = eShaderStage_Pixel, eShaderStage_Compute, + + eShaderStage_Count, }; enum ShaderStageBits diff --git a/renderdoc/common/common.cpp b/renderdoc/common/common.cpp index 390bfc4fa..9894b64ad 100644 --- a/renderdoc/common/common.cpp +++ b/renderdoc/common/common.cpp @@ -239,6 +239,20 @@ uint32_t CalcNumMips(int w, int h, int d) return mipLevels; } +uint32_t Log2Floor(uint32_t value) +{ + RDCASSERT(value > 0); + return 31 - Bits::CountLeadingZeroes(value); +} + +#if RDC64BIT +uint64_t Log2Floor(uint64_t value) +{ + RDCASSERT(value > 0); + return 63 - Bits::CountLeadingZeroes(value); +} +#endif + static string &logfile() { static string fn; diff --git a/renderdoc/common/common.h b/renderdoc/common/common.h index b90f06577..3654c411e 100644 --- a/renderdoc/common/common.h +++ b/renderdoc/common/common.h @@ -95,6 +95,11 @@ inline T AlignUpPtr(T x, A a) { return (T)AlignUp( (uintptr_t)x, (uin bool FindDiffRange(void *a, void *b, size_t bufSize, size_t &diffStart, size_t &diffEnd); uint32_t CalcNumMips(int Width, int Height, int Depth); +uint32_t Log2Floor(uint32_t value); +#if RDC64BIT +uint64_t Log2Floor(uint64_t value); +#endif + ///////////////////////////////////////////////// // Debugging features diff --git a/renderdoc/common/globalconfig.h b/renderdoc/common/globalconfig.h index 71c3b7514..dd6da35a3 100644 --- a/renderdoc/common/globalconfig.h +++ b/renderdoc/common/globalconfig.h @@ -26,6 +26,12 @@ #pragma once +///////////////////////////////////////////////// +// Build/machine configuration +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define RDC64BIT 1 +#endif + ///////////////////////////////////////////////// // Global constants enum diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index c1dc1bf29..5ecab69cb 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1049,6 +1049,128 @@ void Serialiser::Serialise(const char *name, FetchDrawcall &el) SIZE_CHECK(FetchDrawcall, 216); } +template<> +void Serialiser::Serialise(const char *name, FetchFrameConstantBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + Serialise("", el.slots); + Serialise("", el.sizes); + + SIZE_CHECK(FetchFrameConstantBindStats, 28); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameSamplerBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + Serialise("", el.slots); + + SIZE_CHECK(FetchFrameSamplerBindStats, 20); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameResourceBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + Serialise("", el.types); + Serialise("", el.slots); + + SIZE_CHECK(FetchFrameResourceBindStats, 28); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameUpdateStats &el) +{ + Serialise("", el.calls); + Serialise("", el.clients); + Serialise("", el.servers); + Serialise("", el.types); + Serialise("", el.sizes); + + SIZE_CHECK(FetchFrameUpdateStats, 28); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameDrawStats &el) +{ + Serialise("", el.calls); + Serialise("", el.instanced); + Serialise("", el.indirect); + Serialise("", el.counts); + + SIZE_CHECK(FetchFrameDrawStats, 20); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameDispatchStats &el) +{ + Serialise("", el.calls); + Serialise("", el.indirect); + + SIZE_CHECK(FetchFrameDispatchStats, 8); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameIndexBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + + SIZE_CHECK(FetchFrameIndexBindStats, 12); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameVertexBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + Serialise("", el.slots); + + SIZE_CHECK(FetchFrameVertexBindStats, 20); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameLayoutBindStats &el) +{ + Serialise("", el.calls); + Serialise("", el.sets); + Serialise("", el.nulls); + + SIZE_CHECK(FetchFrameLayoutBindStats, 12); +} + +template<> +void Serialiser::Serialise(const char *name, FetchFrameStatistics &el) +{ + Serialise("", el.recorded); + // #mivance note this is technically error-prone from the perspective + // that we're passing references to pointers, but as we're really + // dealing with arrays,t hey'll never be NULL and need to be assigned + // to, so this is fine + FetchFrameConstantBindStats* constants = el.constants; + SerialiseComplexArray("", constants); + FetchFrameSamplerBindStats* samplers = el.samplers; + SerialiseComplexArray("", samplers); + FetchFrameResourceBindStats* resources = el.resources; + SerialiseComplexArray("", resources); + Serialise("", el.updates); + Serialise("", el.draws); + Serialise("", el.dispatches); + Serialise("", el.indices); + Serialise("", el.vertices); + Serialise("", el.layouts); + + SIZE_CHECK(FetchFrameStatistics, 560); +} + template<> void Serialiser::Serialise(const char *name, FetchFrameInfo &el) { @@ -1057,9 +1179,10 @@ void Serialiser::Serialise(const char *name, FetchFrameInfo &el) Serialise("", el.fileOffset); Serialise("", el.captureTime); Serialise("", el.immContextId); + Serialise("", el.stats); Serialise("", el.debugMessages); - SIZE_CHECK(FetchFrameInfo, 40); + SIZE_CHECK(FetchFrameInfo, 600); } template<> @@ -1068,7 +1191,7 @@ void Serialiser::Serialise(const char *name, FetchFrameRecord &el) Serialise("", el.frameInfo); Serialise("", el.drawcallList); - SIZE_CHECK(FetchFrameRecord, 56); + SIZE_CHECK(FetchFrameRecord, 616); } template<> diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 4141d8ba4..627c544ea 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -1385,3 +1385,205 @@ HRESULT STDMETHODCALLTYPE WrappedID3D11DeviceContext::QueryInterface( REFIID rii return RefCounter::QueryInterface(riid, ppvObject); } + +#pragma region Record Statistics + +void WrappedID3D11DeviceContext::RecordIndexBindStats(ID3D11Buffer* Buffer) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameIndexBindStats& indices = stats.indices; + indices.calls += 1; + indices.sets += (Buffer != NULL); + indices.nulls += (Buffer == NULL); +} + +void WrappedID3D11DeviceContext::RecordVertexBindStats(UINT NumBuffers, ID3D11Buffer* Buffers[]) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameVertexBindStats& vertices = stats.vertices; + vertices.calls += 1; + RDCASSERT(NumBuffers < vertices.slots.size()); + vertices.slots[NumBuffers] += 1; + + for (UINT i = 0; i < NumBuffers; i++) + { + if (Buffers[i]) + vertices.sets += 1; + else + vertices.nulls += 1; + } +} + +void WrappedID3D11DeviceContext::RecordLayoutBindStats(ID3D11InputLayout* Layout) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameLayoutBindStats& layouts = stats.layouts; + layouts.calls += 1; + layouts.sets += (Layout != NULL); + layouts.nulls += (Layout == NULL); +} + +void WrappedID3D11DeviceContext::RecordConstantStats(ShaderStageType stage, UINT NumBuffers, ID3D11Buffer* Buffers[]) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + RDCASSERT(stage < ARRAY_COUNT( stats.constants)); + FetchFrameConstantBindStats& constants = stats.constants[stage]; + constants.calls += 1; + RDCASSERT(NumBuffers < constants.slots.size()); + constants.slots[NumBuffers] += 1; + + for (UINT i = 0; i < NumBuffers; i++) + { + if (Buffers[i]) + { + constants.sets += 1; + + D3D11_BUFFER_DESC desc; + Buffers[i]->GetDesc(&desc); + uint32_t bufferSize = desc.ByteWidth; + size_t bucket = BucketForRecordPow2(bufferSize); + RDCASSERT(bucket < constants.sizes.size()); + constants.sizes[bucket] += 1; + } + else + { + constants.nulls += 1; + } + } +} + +void WrappedID3D11DeviceContext::RecordResourceStats(ShaderStageType stage, UINT NumResources, ID3D11ShaderResourceView* Resources[]) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + RDCASSERT(stage < ARRAY_COUNT(stats.resources)); + FetchFrameResourceBindStats& resources = stats.resources[stage]; + resources.calls += 1; + RDCASSERT(NumResources < resources.slots.size()); + resources.slots[NumResources] += 1; + + const ShaderResourceType mapping[] = { + eResType_None, + eResType_Buffer, + eResType_Texture1D, + eResType_Texture1DArray, + eResType_Texture2D, + eResType_Texture2DArray, + eResType_Texture2DMS, + eResType_Texture2DMSArray, + eResType_Texture3D, + eResType_TextureCube, + eResType_TextureCubeArray, + eResType_Buffer, + }; + RDCCOMPILE_ASSERT(ARRAY_COUNT(mapping) == D3D_SRV_DIMENSION_BUFFEREX + 1, "Update mapping table."); + + for (UINT i = 0; i < NumResources; i++) + { + if (Resources[i]) + { + resources.sets += 1; + + D3D11_SHADER_RESOURCE_VIEW_DESC desc; + Resources[i]->GetDesc(&desc); + RDCASSERT(desc.ViewDimension < ARRAY_COUNT(mapping)); + ShaderResourceType type = mapping[desc.ViewDimension]; + // #mivance surprisingly this is not asserted in operator[] for + // rdctype::array so I'm being paranoid + RDCASSERT((int)type < (int)resources.types.size()); + resources.types[type] += 1; + } + else + { + resources.nulls += 1; + } + } +} + +void WrappedID3D11DeviceContext::RecordSamplerStats(ShaderStageType stage, UINT NumSamplers, ID3D11SamplerState* Samplers[]) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + RDCASSERT(stage < ARRAY_COUNT(stats.samplers)); + FetchFrameSamplerBindStats& samplers = stats.samplers[stage]; + samplers.calls += 1; + RDCASSERT(NumSamplers < samplers.slots.size()); + samplers.slots[NumSamplers] += 1; + + for (UINT i = 0; i < NumSamplers; i++) + { + if (Samplers[i]) + samplers.sets += 1; + else + samplers.nulls += 1; + } +} + +void WrappedID3D11DeviceContext::RecordUpdateStats(ID3D11Resource* res, uint32_t Size, bool Server) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameUpdateStats& updates = stats.updates; + + if (res == NULL) + return; + + updates.calls += 1; + updates.clients += (Server == false); + updates.servers += (Server == true); + + const ShaderResourceType mapping[] = { + eResType_None, // D3D11_RESOURCE_DIMENSION_UNKNOWN = 0, + eResType_Buffer, // D3D11_RESOURCE_DIMENSION_BUFFER = 1, + eResType_Texture1D, // D3D11_RESOURCE_DIMENSION_TEXTURE1D = 2, + eResType_Texture2D, // D3D11_RESOURCE_DIMENSION_TEXTURE2D = 3, + eResType_Texture3D, // D3D11_RESOURCE_DIMENSION_TEXTURE3D = 4 + }; + + D3D11_RESOURCE_DIMENSION dim; + res->GetType(&dim); + RDCASSERT(dim < ARRAY_COUNT(mapping)); + ShaderResourceType type = mapping[dim]; + RDCASSERT((int)type < (int)updates.types.size()); + updates.types[type] += 1; + + // #mivance it might be nice to query the buffer to differentiate + // between bindings for constant buffers + + size_t bucket = BucketForRecordPow2( Size ); + updates.sizes[bucket] += 1; +} + +void WrappedID3D11DeviceContext::RecordDrawStats(bool instanced, bool indirect, UINT InstanceCount) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameDrawStats& draws = stats.draws; + + draws.calls += 1; + draws.instanced += (uint32_t)instanced; + draws.indirect += (uint32_t)indirect; + + if (instanced) + { + size_t bucket = BucketForRecordLinear(InstanceCount); + RDCASSERT(bucket < draws.counts.size()); + draws.counts[bucket] += 1; + } +} + +void WrappedID3D11DeviceContext::RecordDispatchStats(bool indirect) +{ + FetchFrameRecord& record = m_pDevice->GetFrameRecord().back(); + FetchFrameStatistics& stats = record.frameInfo.stats; + FetchFrameDispatchStats& dispatches = stats.dispatches; + + dispatches.calls += 1; + dispatches.indirect += (uint32_t)indirect; +} + +#pragma endregion diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index 74f553a9f..24292138d 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -124,6 +124,28 @@ struct DrawcallTreeNode } }; +template +size_t BucketForRecordLinear(size_t value) +{ + RDCCOMPILE_ASSERT( T::BUCKET_TYPE == BUCKET_RECORD_TYPE_LINEAR, "Incorrect bucket type for record query." ); + const size_t size = T::BUCKET_SIZE; + const size_t count = T::BUCKET_COUNT; + const size_t maximum = size * count; + const size_t index = (value < maximum) ? (value / size) : (count - 1); + return index; +} + +template +size_t BucketForRecordPow2(size_t value) +{ + RDCCOMPILE_ASSERT( T::BUCKET_TYPE == BUCKET_RECORD_TYPE_POW2, "Incorrect bucket type for record query." ); + const size_t count = T::BUCKET_COUNT; + RDCCOMPILE_ASSERT(count <= (sizeof(size_t) * 8), "Unexpected correspondence between bucket size and sizeof(size_t)"); + const size_t maximum = (size_t)1 << count; + const size_t index = (value < maximum) ? (size_t)(Log2Floor(value)) : (count - 1); + return index; +} + class WrappedID3D11DeviceContext : public RefCounter, public ID3D11DeviceContext2 { private: @@ -222,6 +244,16 @@ private: void AddDrawcall(FetchDrawcall draw, bool hasEvents); void RefreshDrawcallIDs(DrawcallTreeNode &node); + void RecordIndexBindStats(ID3D11Buffer* Buffer); + void RecordVertexBindStats(UINT NumBuffers, ID3D11Buffer* Buffers[]); + void RecordLayoutBindStats(ID3D11InputLayout* Layout); + void RecordConstantStats(ShaderStageType stage, UINT NumBuffers, ID3D11Buffer* Buffers[]); + void RecordResourceStats(ShaderStageType stage, UINT NumResources, ID3D11ShaderResourceView* Resources[]); + void RecordSamplerStats(ShaderStageType stage, UINT NumSamplers, ID3D11SamplerState* Samplers[]); + void RecordUpdateStats(ID3D11Resource* res, uint32_t Size, bool Server); + void RecordDrawStats(bool instanced, bool indirect, UINT InstanceCount); + void RecordDispatchStats(bool indirect); + //////////////////////////////////////////////////////////////// // implement InterceptorSystem privately, since it is not thread safe (like all other context functions) IMPLEMENT_FUNCTION_SERIALISED(void, SetMarker(uint32_t col, const wchar_t *name)); diff --git a/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp index 1c54af644..83beb92f7 100644 --- a/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp @@ -137,6 +137,9 @@ bool WrappedID3D11DeviceContext::Serialise_UpdateSubresource1(ID3D11Resource *pD if(HasDestBox) pBox = &box; + if(m_State == READING) + RecordUpdateStats(DestResource, SourceDataLength, true); + if(flags == 0) { m_pRealContext->UpdateSubresource(m_pDevice->GetResourceManager()->UnwrapResource(DestResource), DestSubresource, pBox, @@ -223,6 +226,9 @@ bool WrappedID3D11DeviceContext::Serialise_UpdateSubresource1(ID3D11Resource *pD UINT SourceRowPitch = GetByteSize(subWidth, 1, 1, fmt, 0); UINT SourceDepthPitch = GetByteSize(subWidth, subHeight, 1, fmt, 0); + if(m_State == READING) + RecordUpdateStats(DestResource, SourceRowPitch * subHeight + SourceDepthPitch * subWidth * subHeight, true); + if(flags == 0) { m_pRealContext->UpdateSubresource(m_pDevice->GetResourceManager()->UnwrapResource(DestResource), DestSubresource, NULL, diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index 65b72e8da..ec50000bd 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -342,6 +342,10 @@ bool WrappedID3D11DeviceContext::Serialise_IASetInputLayout(ID3D11InputLayout *p pInputLayout = NULL; if(m_pDevice->GetResourceManager()->HasLiveResource(InputLayout)) pInputLayout = (ID3D11InputLayout *)m_pDevice->GetResourceManager()->GetLiveResource(InputLayout); + + if(m_State == READING) + RecordLayoutBindStats(pInputLayout); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->IA.Layout, pInputLayout); m_pRealContext->IASetInputLayout(UNWRAP(WrappedID3D11InputLayout, pInputLayout)); VerifyState(); @@ -404,6 +408,9 @@ bool WrappedID3D11DeviceContext::Serialise_IASetVertexBuffers(UINT StartSlot_, U if(m_State <= EXECUTING) { + if(m_State == READING) + RecordVertexBindStats(NumBuffers, Buffers); + m_CurrentPipelineState->Change(m_CurrentPipelineState->IA.Strides, Strides, StartSlot, NumBuffers); m_CurrentPipelineState->Change(m_CurrentPipelineState->IA.Offsets, Offsets, StartSlot, NumBuffers); m_pRealContext->IASetVertexBuffers(StartSlot, NumBuffers, Buffers, Strides, Offsets); @@ -459,6 +466,10 @@ bool WrappedID3D11DeviceContext::Serialise_IASetIndexBuffer(ID3D11Buffer *pIndex pIndexBuffer = NULL; if(m_pDevice->GetResourceManager()->HasLiveResource(Buffer)) pIndexBuffer = (ID3D11Buffer *)m_pDevice->GetResourceManager()->GetLiveResource(Buffer); + + if(m_State == READING) + RecordIndexBindStats(pIndexBuffer); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->IA.IndexBuffer, pIndexBuffer); m_CurrentPipelineState->Change(m_CurrentPipelineState->IA.IndexFormat, Format); m_CurrentPipelineState->Change(m_CurrentPipelineState->IA.IndexOffset, Offset); @@ -619,6 +630,9 @@ bool WrappedID3D11DeviceContext::Serialise_VSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Vertex, NumBuffers, Buffers); + m_pRealContext->VSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); } @@ -682,6 +696,9 @@ bool WrappedID3D11DeviceContext::Serialise_VSSetShaderResources(UINT StartSlot_, for(UINT i=0; i < NumViews; i++) Views[i] = UNWRAP(WrappedID3D11ShaderResourceView, Views[i]); + if(m_State == READING) + RecordResourceStats(eShaderStage_Vertex, NumViews, Views); + m_pRealContext->VSSetShaderResources(StartSlot, NumViews, Views); VerifyState(); } @@ -732,30 +749,33 @@ bool WrappedID3D11DeviceContext::Serialise_VSSetSamplers(UINT StartSlot_, UINT N SERIALISE_ELEMENT(uint32_t, StartSlot, StartSlot_); SERIALISE_ELEMENT(uint32_t, NumSamplers, NumSamplers_); - ID3D11SamplerState **Sampler = new ID3D11SamplerState *[NumSamplers]; + ID3D11SamplerState **Samplers = new ID3D11SamplerState *[NumSamplers]; for(UINT i=0; i < NumSamplers; i++) { SERIALISE_ELEMENT(ResourceId, id, GetIDForResource(ppSamplers[i])); if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(id)) - Sampler[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); + Samplers[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); else - Sampler[i] = NULL; + Samplers[i] = NULL; } if(m_State <= EXECUTING) { - m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->VS.Samplers, Sampler, StartSlot, NumSamplers); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->VS.Samplers, Samplers, StartSlot, NumSamplers); for(UINT i=0; i < NumSamplers; i++) - Sampler[i] = UNWRAP(WrappedID3D11SamplerState, Sampler[i]); + Samplers[i] = UNWRAP(WrappedID3D11SamplerState, Samplers[i]); - m_pRealContext->VSSetSamplers(StartSlot, NumSamplers, Sampler); + if(m_State == READING) + RecordSamplerStats(eShaderStage_Vertex, NumSamplers, Samplers); + + m_pRealContext->VSSetSamplers(StartSlot, NumSamplers, Samplers); VerifyState(); } - SAFE_DELETE_ARRAY(Sampler); + SAFE_DELETE_ARRAY(Samplers); return true; } @@ -978,6 +998,9 @@ bool WrappedID3D11DeviceContext::Serialise_HSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Hull, NumBuffers, Buffers); + if(m_State <= EXECUTING) m_pRealContext->HSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); @@ -1044,6 +1067,9 @@ bool WrappedID3D11DeviceContext::Serialise_HSSetShaderResources(UINT StartSlot_, for(UINT i=0; i < NumViews; i++) Views[i] = UNWRAP(WrappedID3D11ShaderResourceView, Views[i]); + if(m_State == READING) + RecordResourceStats(eShaderStage_Hull, NumViews, Views); + m_pRealContext->HSSetShaderResources(StartSlot, NumViews, Views); VerifyState(); } @@ -1094,30 +1120,33 @@ bool WrappedID3D11DeviceContext::Serialise_HSSetSamplers(UINT StartSlot_, UINT N SERIALISE_ELEMENT(uint32_t, StartSlot, StartSlot_); SERIALISE_ELEMENT(uint32_t, NumSamplers, NumSamplers_); - ID3D11SamplerState **Sampler = new ID3D11SamplerState *[NumSamplers]; + ID3D11SamplerState **Samplers = new ID3D11SamplerState *[NumSamplers]; for(UINT i=0; i < NumSamplers; i++) { SERIALISE_ELEMENT(ResourceId, id, GetIDForResource(ppSamplers[i])); if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(id)) - Sampler[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); + Samplers[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); else - Sampler[i] = NULL; + Samplers[i] = NULL; } if(m_State <= EXECUTING) { - m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->HS.Samplers, Sampler, StartSlot, NumSamplers); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->HS.Samplers, Samplers, StartSlot, NumSamplers); for(UINT i=0; i < NumSamplers; i++) - Sampler[i] = UNWRAP(WrappedID3D11SamplerState, Sampler[i]); + Samplers[i] = UNWRAP(WrappedID3D11SamplerState, Samplers[i]); - m_pRealContext->HSSetSamplers(StartSlot, NumSamplers, Sampler); + if(m_State == READING) + RecordSamplerStats(eShaderStage_Hull, NumSamplers, Samplers); + + m_pRealContext->HSSetSamplers(StartSlot, NumSamplers, Samplers); VerifyState(); } - SAFE_DELETE_ARRAY(Sampler); + SAFE_DELETE_ARRAY(Samplers); return true; } @@ -1340,6 +1369,9 @@ bool WrappedID3D11DeviceContext::Serialise_DSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Domain, NumBuffers, Buffers); + m_pRealContext->DSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); } @@ -1405,6 +1437,9 @@ bool WrappedID3D11DeviceContext::Serialise_DSSetShaderResources(UINT StartSlot_, for(UINT i=0; i < NumViews; i++) Views[i] = UNWRAP(WrappedID3D11ShaderResourceView, Views[i]); + if(m_State == READING) + RecordResourceStats(eShaderStage_Domain, NumViews, Views); + m_pRealContext->DSSetShaderResources(StartSlot, NumViews, Views); VerifyState(); } @@ -1455,30 +1490,33 @@ bool WrappedID3D11DeviceContext::Serialise_DSSetSamplers(UINT StartSlot_, UINT N SERIALISE_ELEMENT(uint32_t, StartSlot, StartSlot_); SERIALISE_ELEMENT(uint32_t, NumSamplers, NumSamplers_); - ID3D11SamplerState **Sampler = new ID3D11SamplerState *[NumSamplers]; + ID3D11SamplerState **Samplers = new ID3D11SamplerState *[NumSamplers]; for(UINT i=0; i < NumSamplers; i++) { SERIALISE_ELEMENT(ResourceId, id, GetIDForResource(ppSamplers[i])); if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(id)) - Sampler[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); + Samplers[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); else - Sampler[i] = NULL; + Samplers[i] = NULL; } if(m_State <= EXECUTING) { - m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->DS.Samplers, Sampler, StartSlot, NumSamplers); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->DS.Samplers, Samplers, StartSlot, NumSamplers); for(UINT i=0; i < NumSamplers; i++) - Sampler[i] = UNWRAP(WrappedID3D11SamplerState, Sampler[i]); + Samplers[i] = UNWRAP(WrappedID3D11SamplerState, Samplers[i]); - m_pRealContext->DSSetSamplers(StartSlot, NumSamplers, Sampler); + if(m_State == READING) + RecordSamplerStats(eShaderStage_Domain, NumSamplers, Samplers); + + m_pRealContext->DSSetSamplers(StartSlot, NumSamplers, Samplers); VerifyState(); } - SAFE_DELETE_ARRAY(Sampler); + SAFE_DELETE_ARRAY(Samplers); return true; } @@ -1701,6 +1739,9 @@ bool WrappedID3D11DeviceContext::Serialise_GSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Geometry, NumBuffers, Buffers); + m_pRealContext->GSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); } @@ -1766,6 +1807,9 @@ bool WrappedID3D11DeviceContext::Serialise_GSSetShaderResources(UINT StartSlot_, for(UINT i=0; i < NumViews; i++) Views[i] = UNWRAP(WrappedID3D11ShaderResourceView, Views[i]); + if(m_State == READING) + RecordResourceStats(eShaderStage_Geometry, NumViews, Views); + m_pRealContext->GSSetShaderResources(StartSlot, NumViews, Views); VerifyState(); } @@ -1816,30 +1860,33 @@ bool WrappedID3D11DeviceContext::Serialise_GSSetSamplers(UINT StartSlot_, UINT N SERIALISE_ELEMENT(uint32_t, StartSlot, StartSlot_); SERIALISE_ELEMENT(uint32_t, NumSamplers, NumSamplers_); - ID3D11SamplerState **Sampler = new ID3D11SamplerState *[NumSamplers]; + ID3D11SamplerState **Samplers = new ID3D11SamplerState *[NumSamplers]; for(UINT i=0; i < NumSamplers; i++) { SERIALISE_ELEMENT(ResourceId, id, GetIDForResource(ppSamplers[i])); if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(id)) - Sampler[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); + Samplers[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); else - Sampler[i] = NULL; + Samplers[i] = NULL; } if(m_State <= EXECUTING) { - m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->GS.Samplers, Sampler, StartSlot, NumSamplers); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->GS.Samplers, Samplers, StartSlot, NumSamplers); for(UINT i=0; i < NumSamplers; i++) - Sampler[i] = UNWRAP(WrappedID3D11SamplerState, Sampler[i]); + Samplers[i] = UNWRAP(WrappedID3D11SamplerState, Samplers[i]); - m_pRealContext->GSSetSamplers(StartSlot, NumSamplers, Sampler); + if(m_State == READING) + RecordSamplerStats(eShaderStage_Geometry, NumSamplers, Samplers); + + m_pRealContext->GSSetSamplers(StartSlot, NumSamplers, Samplers); VerifyState(); } - SAFE_DELETE_ARRAY(Sampler); + SAFE_DELETE_ARRAY(Samplers); return true; } @@ -2467,6 +2514,9 @@ bool WrappedID3D11DeviceContext::Serialise_PSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Pixel, NumBuffers, Buffers); + m_pRealContext->PSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); } @@ -2530,6 +2580,9 @@ bool WrappedID3D11DeviceContext::Serialise_PSSetShaderResources(UINT StartSlot_, for(UINT i=0; i < NumViews; i++) Views[i] = UNWRAP(WrappedID3D11ShaderResourceView, Views[i]); + if(m_State == READING) + RecordResourceStats(eShaderStage_Pixel, NumViews, Views); + m_pRealContext->PSSetShaderResources(StartSlot, NumViews, Views); VerifyState(); } @@ -2578,26 +2631,29 @@ bool WrappedID3D11DeviceContext::Serialise_PSSetSamplers(UINT StartSlot_, UINT N SERIALISE_ELEMENT(uint32_t, StartSlot, StartSlot_); SERIALISE_ELEMENT(uint32_t, NumSamplers, NumSamplers_); - ID3D11SamplerState *Sampler[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT]; + ID3D11SamplerState *Samplers[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT]; for(UINT i=0; i < NumSamplers; i++) { SERIALISE_ELEMENT(ResourceId, id, GetIDForResource(ppSamplers[i])); if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(id)) - Sampler[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); + Samplers[i] = (ID3D11SamplerState *)m_pDevice->GetResourceManager()->GetLiveResource(id); else - Sampler[i] = NULL; + Samplers[i] = NULL; } if(m_State <= EXECUTING) { - m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->PS.Samplers, Sampler, StartSlot, NumSamplers); + m_CurrentPipelineState->ChangeRefRead(m_CurrentPipelineState->PS.Samplers, Samplers, StartSlot, NumSamplers); for(UINT i=0; i < NumSamplers; i++) - Sampler[i] = UNWRAP(WrappedID3D11SamplerState, Sampler[i]); + Samplers[i] = UNWRAP(WrappedID3D11SamplerState, Samplers[i]); - m_pRealContext->PSSetSamplers(StartSlot, NumSamplers, Sampler); + if(m_State == READING) + RecordSamplerStats(eShaderStage_Pixel, NumSamplers, Samplers); + + m_pRealContext->PSSetSamplers(StartSlot, NumSamplers, Samplers); VerifyState(); } @@ -3429,6 +3485,9 @@ bool WrappedID3D11DeviceContext::Serialise_DrawIndexedInstanced(UINT IndexCountP if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDrawStats(true, false, InstanceCount); + m_pRealContext->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); } @@ -3492,6 +3551,9 @@ bool WrappedID3D11DeviceContext::Serialise_DrawInstanced(UINT VertexCountPerInst if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDrawStats(true, false, InstanceCount); + m_pRealContext->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); } @@ -3552,6 +3614,9 @@ bool WrappedID3D11DeviceContext::Serialise_DrawIndexed(UINT IndexCount_, UINT St if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDrawStats(false, false, 1); + m_pRealContext->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); } @@ -3609,6 +3674,9 @@ bool WrappedID3D11DeviceContext::Serialise_Draw(UINT VertexCount_, UINT StartVer if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDrawStats(false, false, 1); + m_pRealContext->Draw(VertexCount, StartVertexLocation); } @@ -3664,6 +3732,9 @@ bool WrappedID3D11DeviceContext::Serialise_DrawAuto() if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDrawStats(false, false, 1); + // spec says that only the first vertex buffer is used if(m_CurrentPipelineState->IA.VBs[0] == NULL) { @@ -3808,6 +3879,8 @@ bool WrappedID3D11DeviceContext::Serialise_DrawIndexedInstancedIndirect(ID3D11Bu draw.baseVertex = args->BaseVertexLocation; draw.instanceOffset = args->StartInstanceLocation; + RecordDrawStats(true, true, draw.numInstances); + name = "DrawIndexedInstancedIndirect(<" + ToStr::Get(draw.numIndices) + ", " + ToStr::Get(draw.numInstances) + ">)"; } @@ -3885,6 +3958,8 @@ bool WrappedID3D11DeviceContext::Serialise_DrawInstancedIndirect(ID3D11Buffer *p draw.numInstances = uargs[1]; draw.vertexOffset = uargs[2]; draw.instanceOffset = uargs[3]; + + RecordDrawStats(true, true, draw.numInstances); } draw.name = name; @@ -4065,6 +4140,9 @@ bool WrappedID3D11DeviceContext::Serialise_CSSetConstantBuffers(UINT StartSlot_, for(UINT i=0; i < NumBuffers; i++) Buffers[i] = UNWRAP(WrappedID3D11Buffer, Buffers[i]); + if(m_State == READING) + RecordConstantStats(eShaderStage_Compute, NumBuffers, Buffers); + m_pRealContext->CSSetConstantBuffers(StartSlot, NumBuffers, Buffers); VerifyState(); } @@ -4492,6 +4570,9 @@ bool WrappedID3D11DeviceContext::Serialise_Dispatch(UINT ThreadGroupCountX_, UIN if(m_State <= EXECUTING) { + if(m_State == READING) + RecordDispatchStats(false); + m_pRealContext->Dispatch(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ); } @@ -4562,6 +4643,9 @@ bool WrappedID3D11DeviceContext::Serialise_DispatchIndirect(ID3D11Buffer *pBuffe if(m_State <= EXECUTING && m_pDevice->GetResourceManager()->HasLiveResource(BufferForArgs)) { + if(m_State == READING) + RecordDispatchStats(true); + m_pRealContext->DispatchIndirect(UNWRAP(WrappedID3D11Buffer, m_pDevice->GetResourceManager()->GetLiveResource(BufferForArgs)), AlignedByteOffsetForArgs); } @@ -6971,6 +7055,9 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT ID3D11Resource *res = (ID3D11Resource *)m_pDevice->GetResourceManager()->GetLiveResource(mapIdx.resource); + if((m_State == READING) && (DiffStart < DiffEnd)) + RecordUpdateStats(res, DiffEnd - DiffStart, false); + if(DiffStart >= DiffEnd) { // do nothing diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index e9bbf07cf..a024dadf2 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -1004,6 +1004,31 @@ void WrappedID3D11Device::Serialise_CaptureScope(uint64_t offset) record.frameInfo.firstEvent = m_pImmediateContext->GetEventID(); record.frameInfo.frameNumber = FrameNumber; record.frameInfo.immContextId = GetResourceManager()->GetOriginalID(m_pImmediateContext->GetResourceID()); + + FetchFrameStatistics& stats = record.frameInfo.stats; + RDCEraseEl(stats); + + // #mivance GL/Vulkan don't set this so don't get stats in window + stats.recorded = 1; + + for(uint32_t stage = eShaderStage_First; stage < eShaderStage_Count; stage++) + { + create_array(stats.constants[stage].slots, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 1); + create_array(stats.constants[stage].sizes, FetchFrameConstantBindStats::BUCKET_COUNT); + + create_array(stats.samplers[stage].slots, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT + 1); + + create_array(stats.resources[stage].types, eResType_Count); + create_array(stats.resources[stage].slots, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT + 1); + } + + create_array(stats.updates.types, eResType_Count); + create_array(stats.updates.sizes, FetchFrameUpdateStats::BUCKET_COUNT); + + create_array(stats.draws.counts, FetchFrameDrawStats::BUCKET_COUNT); + + create_array(stats.vertices.slots, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT + 1); + m_FrameRecord.push_back(record); GetResourceManager()->CreateInitialContents(); diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 470aa4ce3..e40a1b156 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -2353,6 +2353,7 @@ void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd) FetchFrameRecord record; record.frameInfo.frameNumber = m_FrameCounter+1; record.frameInfo.captureTime = Timing::GetUnixTimestamp(); + RDCEraseEl(record.frameInfo.stats); m_FrameRecord.push_back(record); GetResourceManager()->ClearReferencedResources(); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 256f9969f..08969481f 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -724,6 +724,7 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) FetchFrameRecord record; record.frameInfo.frameNumber = m_FrameCounter+1; record.frameInfo.captureTime = Timing::GetUnixTimestamp(); + RDCEraseEl(record.frameInfo.stats); m_FrameRecord.push_back(record); GetResourceManager()->ClearReferencedResources(); diff --git a/renderdoc/os/linux_specific.h b/renderdoc/os/linux_specific.h index 6d7bd0f2b..3a7d89d4d 100644 --- a/renderdoc/os/linux_specific.h +++ b/renderdoc/os/linux_specific.h @@ -55,3 +55,17 @@ namespace Threading typedef CriticalSectionTemplate CriticalSection; }; +namespace Bits +{ + inline uint32_t CountLeadingZeroes(uint32_t value) + { + return __builtin_clz(value); + } + +#if RDC64BIT + inline uint64_t CountLeadingZeroes(uint64_t value) + { + return __builtin_clzl(value); + } +#endif +}; diff --git a/renderdoc/os/os_specific.h b/renderdoc/os/os_specific.h index 4502f35c1..1be88e3a4 100644 --- a/renderdoc/os/os_specific.h +++ b/renderdoc/os/os_specific.h @@ -40,6 +40,7 @@ #include #include #include + using std::string; using std::vector; using std::map; @@ -314,6 +315,14 @@ namespace OSUtility void WriteOutput(int channel, const char *str); }; +namespace Bits +{ + inline uint32_t CountLeadingZeroes(uint32_t value); +#if RDC64BIT + inline uint64_t CountLeadingZeroes(uint64_t value); +#endif +}; + // must #define: // __PRETTY_FUNCTION_SIGNATURE__ - undecorated function signature // GetEmbeddedResource(name_with_underscores_ext) - function/inline that returns the given file in a std::string diff --git a/renderdoc/os/win32_specific.h b/renderdoc/os/win32_specific.h index 34b1cfb91..2b87d18a0 100644 --- a/renderdoc/os/win32_specific.h +++ b/renderdoc/os/win32_specific.h @@ -27,6 +27,7 @@ #pragma once #include +#include #include "data/resource.h" #define __PRETTY_FUNCTION_SIGNATURE__ __FUNCSIG__ @@ -54,3 +55,22 @@ namespace Threading { typedef CriticalSectionTemplate CriticalSection; }; + +namespace Bits +{ + inline uint32_t CountLeadingZeroes(uint32_t value) + { + DWORD index; + BOOLEAN result = _BitScanReverse(&index, value); + return (result == TRUE) ? (index ^ 31) : 32; + } + +#if RDC64BIT + inline uint64_t CountLeadingZeroes(uint64_t value) + { + DWORD index; + BOOLEAN result = _BitScanReverse64(&index, value); + return (result == TRUE) ? (index ^ 63) : 64; + } +#endif +}; diff --git a/renderdoc/serialise/serialiser.h b/renderdoc/serialise/serialiser.h index e61d8f124..788a98639 100644 --- a/renderdoc/serialise/serialiser.h +++ b/renderdoc/serialise/serialiser.h @@ -388,6 +388,13 @@ class Serialiser // // If serialising in, el will either be set to NULL or allocated, the // existing value will be overwritten. + template + void SerialiseComplexArray(const char* name, T *&el) + { + uint32_t n = (uint32_t)Num; + SerialiseComplexArray(name, el, n); + } + template void SerialiseComplexArray(const char *name, T *&el, uint32_t &Num) { diff --git a/renderdocui/Code/Core.cs b/renderdocui/Code/Core.cs index 7ac4ef9eb..3a8086bc5 100644 --- a/renderdocui/Code/Core.cs +++ b/renderdocui/Code/Core.cs @@ -89,6 +89,7 @@ namespace renderdocui.Code private TimelineBar m_TimelineBar = null; private TextureViewer m_TextureViewer = null; private PipelineStateViewer m_PipelineStateViewer = null; + private StatisticsViewer m_StatisticsViewer = null; #endregion @@ -809,7 +810,18 @@ namespace renderdocui.Code return m_TimelineBar; } - public void AddLogProgressListener(ILogLoadProgressListener p) + public StatisticsViewer GetStatisticsViewer() + { + if (m_StatisticsViewer == null || m_StatisticsViewer.IsDisposed) + { + m_StatisticsViewer = new StatisticsViewer(this); + AddLogViewer(m_StatisticsViewer); + } + + return m_StatisticsViewer; + } + + public void AddLogProgressListener(ILogLoadProgressListener p) { m_ProgressListeners.Add(p); } diff --git a/renderdocui/Interop/Enums.cs b/renderdocui/Interop/Enums.cs index 59f87958f..be80c682a 100644 --- a/renderdocui/Interop/Enums.cs +++ b/renderdocui/Interop/Enums.cs @@ -283,6 +283,7 @@ namespace renderdoc public enum ShaderStageType { Vertex = 0, + First = Vertex, Hull, Tess_Control = Hull, @@ -296,6 +297,8 @@ namespace renderdoc Fragment = Pixel, Compute, + + Count, }; [Flags] diff --git a/renderdocui/Interop/FetchInfo.cs b/renderdocui/Interop/FetchInfo.cs index 03e3b9fd1..720a1f480 100644 --- a/renderdocui/Interop/FetchInfo.cs +++ b/renderdocui/Interop/FetchInfo.cs @@ -277,6 +277,119 @@ namespace renderdoc }; [StructLayout(LayoutKind.Sequential)] + public class FetchFrameConstantBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] slots; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] sizes; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameSamplerBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] slots; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameResourceBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] types; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] slots; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameUpdateStats + { + public UInt32 calls; + public UInt32 clients; + public UInt32 servers; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] types; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] sizes; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameDrawStats + { + public UInt32 calls; + public UInt32 instanced; + public UInt32 indirect; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] counts; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameDispatchStats + { + public UInt32 calls; + public UInt32 indirect; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameIndexBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameVertexBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public UInt32[] slots; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameLayoutBindStats + { + public UInt32 calls; + public UInt32 sets; + public UInt32 nulls; + }; + + [StructLayout(LayoutKind.Sequential)] + public class FetchFrameStatistics + { + public UInt32 recorded; + [CustomMarshalAs(CustomUnmanagedType.FixedArray, FixedLength = (int)ShaderStageType.Count)] + public FetchFrameConstantBindStats[] constants; + [CustomMarshalAs(CustomUnmanagedType.FixedArray, FixedLength = (int)ShaderStageType.Count)] + public FetchFrameSamplerBindStats[] samplers; + [CustomMarshalAs(CustomUnmanagedType.FixedArray, FixedLength = (int)ShaderStageType.Count)] + public FetchFrameResourceBindStats[] resources; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameUpdateStats updates; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameDrawStats draws; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameDispatchStats dispatches; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameIndexBindStats indices; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameVertexBindStats vertices; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameLayoutBindStats layouts; + }; + +[StructLayout(LayoutKind.Sequential)] public class FetchFrameInfo { public UInt32 frameNumber; @@ -284,6 +397,8 @@ namespace renderdoc public UInt64 fileOffset; public UInt64 captureTime; public ResourceId immContextId; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public FetchFrameStatistics stats; [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] public DebugMessage[] debugMessages; diff --git a/renderdocui/Windows/MainWindow.Designer.cs b/renderdocui/Windows/MainWindow.Designer.cs index 38fa9a751..119f85a5d 100644 --- a/renderdocui/Windows/MainWindow.Designer.cs +++ b/renderdocui/Windows/MainWindow.Designer.cs @@ -89,15 +89,17 @@ this.meshOutputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.debugMessagesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timelineToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statisticsViewerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.resolveSymbolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.logStatisticsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.manageReplayDevicesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewDocsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewLogFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.showTipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); this.sendErrorReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator(); this.checkForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -114,8 +116,6 @@ this.statusProgress = new System.Windows.Forms.ToolStripProgressBar(); this.dockPanel = new WeifenLuo.WinFormsUI.Docking.DockPanel(); this.saveDialog = new System.Windows.Forms.SaveFileDialog(); - this.toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); - this.showTipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); this.toolStripContainer1.BottomToolStripPanel.SuspendLayout(); this.toolStripContainer1.ContentPanel.SuspendLayout(); @@ -154,41 +154,41 @@ this.toolStripSeparator5, this.exitToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20); + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); this.fileToolStripMenuItem.Text = "&File"; // // captureLogToolStripMenuItem // this.captureLogToolStripMenuItem.Name = "captureLogToolStripMenuItem"; this.captureLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); - this.captureLogToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.captureLogToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.captureLogToolStripMenuItem.Text = "&Capture Log"; this.captureLogToolStripMenuItem.Click += new System.EventHandler(this.captureLogToolStripMenuItem_Click); // // attachToInstanceToolStripMenuItem // this.attachToInstanceToolStripMenuItem.Name = "attachToInstanceToolStripMenuItem"; - this.attachToInstanceToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.attachToInstanceToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.attachToInstanceToolStripMenuItem.Text = "&Attach to Running Instance"; this.attachToInstanceToolStripMenuItem.Click += new System.EventHandler(this.attachToInstanceToolStripMenuItem_Click); // // injectIntoProcessToolStripMenuItem // this.injectIntoProcessToolStripMenuItem.Name = "injectIntoProcessToolStripMenuItem"; - this.injectIntoProcessToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.injectIntoProcessToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.injectIntoProcessToolStripMenuItem.Text = "&Inject into Process"; this.injectIntoProcessToolStripMenuItem.Click += new System.EventHandler(this.injectIntoProcessToolStripMenuItem_Click); // // toolStripSeparator10 // this.toolStripSeparator10.Name = "toolStripSeparator10"; - this.toolStripSeparator10.Size = new System.Drawing.Size(203, 6); + this.toolStripSeparator10.Size = new System.Drawing.Size(215, 6); // // openLogToolStripMenuItem // this.openLogToolStripMenuItem.Name = "openLogToolStripMenuItem"; this.openLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.openLogToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.openLogToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.openLogToolStripMenuItem.Text = "&Open Log"; this.openLogToolStripMenuItem.Click += new System.EventHandler(this.openLogToolStripMenuItem_Click); // @@ -197,7 +197,7 @@ this.saveLogToolStripMenuItem.Enabled = false; this.saveLogToolStripMenuItem.Name = "saveLogToolStripMenuItem"; this.saveLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); - this.saveLogToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.saveLogToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.saveLogToolStripMenuItem.Text = "&Save Log"; this.saveLogToolStripMenuItem.Click += new System.EventHandler(this.saveLogToolStripMenuItem_Click); // @@ -205,14 +205,14 @@ // this.closeLogToolStripMenuItem.Name = "closeLogToolStripMenuItem"; this.closeLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W))); - this.closeLogToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.closeLogToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.closeLogToolStripMenuItem.Text = "C&lose Log"; this.closeLogToolStripMenuItem.Click += new System.EventHandler(this.closeLogToolStripMenuItem_Click); // // toolStripSeparator4 // this.toolStripSeparator4.Name = "toolStripSeparator4"; - this.toolStripSeparator4.Size = new System.Drawing.Size(203, 6); + this.toolStripSeparator4.Size = new System.Drawing.Size(215, 6); // // recentFilesToolStripMenuItem // @@ -220,18 +220,18 @@ this.toolStripSeparator6, this.clearHistoryToolStripMenuItem}); this.recentFilesToolStripMenuItem.Name = "recentFilesToolStripMenuItem"; - this.recentFilesToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.recentFilesToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.recentFilesToolStripMenuItem.Text = "&Recent Logs"; // // toolStripSeparator6 // this.toolStripSeparator6.Name = "toolStripSeparator6"; - this.toolStripSeparator6.Size = new System.Drawing.Size(133, 6); + this.toolStripSeparator6.Size = new System.Drawing.Size(139, 6); // // clearHistoryToolStripMenuItem // this.clearHistoryToolStripMenuItem.Name = "clearHistoryToolStripMenuItem"; - this.clearHistoryToolStripMenuItem.Size = new System.Drawing.Size(136, 22); + this.clearHistoryToolStripMenuItem.Size = new System.Drawing.Size(142, 22); this.clearHistoryToolStripMenuItem.Text = "&Clear History"; this.clearHistoryToolStripMenuItem.Click += new System.EventHandler(this.clearHistoryToolStripMenuItem_Click); // @@ -241,31 +241,31 @@ this.toolStripSeparator7, this.clearHistoryToolStripMenuItem1}); this.recentCapturesToolStripMenuItem.Name = "recentCapturesToolStripMenuItem"; - this.recentCapturesToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.recentCapturesToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.recentCapturesToolStripMenuItem.Text = "R&ecent Capture Settings"; // // toolStripSeparator7 // this.toolStripSeparator7.Name = "toolStripSeparator7"; - this.toolStripSeparator7.Size = new System.Drawing.Size(133, 6); + this.toolStripSeparator7.Size = new System.Drawing.Size(139, 6); // // clearHistoryToolStripMenuItem1 // this.clearHistoryToolStripMenuItem1.Name = "clearHistoryToolStripMenuItem1"; - this.clearHistoryToolStripMenuItem1.Size = new System.Drawing.Size(136, 22); + this.clearHistoryToolStripMenuItem1.Size = new System.Drawing.Size(142, 22); this.clearHistoryToolStripMenuItem1.Text = "&Clear History"; this.clearHistoryToolStripMenuItem1.Click += new System.EventHandler(this.clearHistoryToolStripMenuItem1_Click); // // toolStripSeparator5 // this.toolStripSeparator5.Name = "toolStripSeparator5"; - this.toolStripSeparator5.Size = new System.Drawing.Size(203, 6); + this.toolStripSeparator5.Size = new System.Drawing.Size(215, 6); // // exitToolStripMenuItem // this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; this.exitToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4))); - this.exitToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.exitToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.exitToolStripMenuItem.Text = "E&xit"; this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); // @@ -282,9 +282,10 @@ this.aPIInspectorToolStripMenuItem, this.meshOutputToolStripMenuItem, this.debugMessagesToolStripMenuItem, - this.timelineToolStripMenuItem}); + this.timelineToolStripMenuItem, + this.statisticsViewerToolStripMenuItem}); this.windowToolStripMenuItem.Name = "windowToolStripMenuItem"; - this.windowToolStripMenuItem.Size = new System.Drawing.Size(57, 20); + this.windowToolStripMenuItem.Size = new System.Drawing.Size(63, 20); this.windowToolStripMenuItem.Text = "&Window"; // // toolStripMenuItem1 @@ -299,13 +300,13 @@ this.layoutSave5, this.layoutSave6}); this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(192, 22); + this.toolStripMenuItem1.Size = new System.Drawing.Size(203, 22); this.toolStripMenuItem1.Text = "&Save Layout"; // // layoutSaveDefault // this.layoutSaveDefault.Name = "layoutSaveDefault"; - this.layoutSaveDefault.Size = new System.Drawing.Size(145, 22); + this.layoutSaveDefault.Size = new System.Drawing.Size(151, 22); this.layoutSaveDefault.Tag = "0"; this.layoutSaveDefault.Text = "&Default Layout"; this.layoutSaveDefault.Click += new System.EventHandler(this.saveLayout_Click); @@ -313,12 +314,12 @@ // toolStripSeparator3 // this.toolStripSeparator3.Name = "toolStripSeparator3"; - this.toolStripSeparator3.Size = new System.Drawing.Size(142, 6); + this.toolStripSeparator3.Size = new System.Drawing.Size(148, 6); // // layoutSave1 // this.layoutSave1.Name = "layoutSave1"; - this.layoutSave1.Size = new System.Drawing.Size(145, 22); + this.layoutSave1.Size = new System.Drawing.Size(151, 22); this.layoutSave1.Tag = "1"; this.layoutSave1.Text = "Layout &1"; this.layoutSave1.Click += new System.EventHandler(this.saveLayout_Click); @@ -326,7 +327,7 @@ // layoutSave2 // this.layoutSave2.Name = "layoutSave2"; - this.layoutSave2.Size = new System.Drawing.Size(145, 22); + this.layoutSave2.Size = new System.Drawing.Size(151, 22); this.layoutSave2.Tag = "2"; this.layoutSave2.Text = "Layout &2"; this.layoutSave2.Click += new System.EventHandler(this.saveLayout_Click); @@ -334,7 +335,7 @@ // layoutSave3 // this.layoutSave3.Name = "layoutSave3"; - this.layoutSave3.Size = new System.Drawing.Size(145, 22); + this.layoutSave3.Size = new System.Drawing.Size(151, 22); this.layoutSave3.Tag = "3"; this.layoutSave3.Text = "Layout &3"; this.layoutSave3.Click += new System.EventHandler(this.saveLayout_Click); @@ -342,7 +343,7 @@ // layoutSave4 // this.layoutSave4.Name = "layoutSave4"; - this.layoutSave4.Size = new System.Drawing.Size(145, 22); + this.layoutSave4.Size = new System.Drawing.Size(151, 22); this.layoutSave4.Tag = "4"; this.layoutSave4.Text = "Layout &4"; this.layoutSave4.Click += new System.EventHandler(this.saveLayout_Click); @@ -350,7 +351,7 @@ // layoutSave5 // this.layoutSave5.Name = "layoutSave5"; - this.layoutSave5.Size = new System.Drawing.Size(145, 22); + this.layoutSave5.Size = new System.Drawing.Size(151, 22); this.layoutSave5.Tag = "5"; this.layoutSave5.Text = "Layout &5"; this.layoutSave5.Click += new System.EventHandler(this.saveLayout_Click); @@ -358,7 +359,7 @@ // layoutSave6 // this.layoutSave6.Name = "layoutSave6"; - this.layoutSave6.Size = new System.Drawing.Size(145, 22); + this.layoutSave6.Size = new System.Drawing.Size(151, 22); this.layoutSave6.Tag = "6"; this.layoutSave6.Text = "Layout &6"; this.layoutSave6.Click += new System.EventHandler(this.saveLayout_Click); @@ -375,13 +376,13 @@ this.layoutLoad5, this.layoutLoad6}); this.saveLayoutToolStripMenuItem.Name = "saveLayoutToolStripMenuItem"; - this.saveLayoutToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.saveLayoutToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.saveLayoutToolStripMenuItem.Text = "&Load Layout"; // // layoutLoadDefault // this.layoutLoadDefault.Name = "layoutLoadDefault"; - this.layoutLoadDefault.Size = new System.Drawing.Size(145, 22); + this.layoutLoadDefault.Size = new System.Drawing.Size(151, 22); this.layoutLoadDefault.Tag = "0"; this.layoutLoadDefault.Text = "&Default Layout"; this.layoutLoadDefault.Click += new System.EventHandler(this.loadLayout_Click); @@ -389,12 +390,12 @@ // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; - this.toolStripSeparator2.Size = new System.Drawing.Size(142, 6); + this.toolStripSeparator2.Size = new System.Drawing.Size(148, 6); // // layoutLoad1 // this.layoutLoad1.Name = "layoutLoad1"; - this.layoutLoad1.Size = new System.Drawing.Size(145, 22); + this.layoutLoad1.Size = new System.Drawing.Size(151, 22); this.layoutLoad1.Tag = "1"; this.layoutLoad1.Text = "Layout &1"; this.layoutLoad1.Click += new System.EventHandler(this.loadLayout_Click); @@ -402,7 +403,7 @@ // layoutLoad2 // this.layoutLoad2.Name = "layoutLoad2"; - this.layoutLoad2.Size = new System.Drawing.Size(145, 22); + this.layoutLoad2.Size = new System.Drawing.Size(151, 22); this.layoutLoad2.Tag = "2"; this.layoutLoad2.Text = "Layout &2"; this.layoutLoad2.Click += new System.EventHandler(this.loadLayout_Click); @@ -410,7 +411,7 @@ // layoutLoad3 // this.layoutLoad3.Name = "layoutLoad3"; - this.layoutLoad3.Size = new System.Drawing.Size(145, 22); + this.layoutLoad3.Size = new System.Drawing.Size(151, 22); this.layoutLoad3.Tag = "3"; this.layoutLoad3.Text = "Layout &3"; this.layoutLoad3.Click += new System.EventHandler(this.loadLayout_Click); @@ -418,7 +419,7 @@ // layoutLoad4 // this.layoutLoad4.Name = "layoutLoad4"; - this.layoutLoad4.Size = new System.Drawing.Size(145, 22); + this.layoutLoad4.Size = new System.Drawing.Size(151, 22); this.layoutLoad4.Tag = "4"; this.layoutLoad4.Text = "Layout &4"; this.layoutLoad4.Click += new System.EventHandler(this.loadLayout_Click); @@ -426,7 +427,7 @@ // layoutLoad5 // this.layoutLoad5.Name = "layoutLoad5"; - this.layoutLoad5.Size = new System.Drawing.Size(145, 22); + this.layoutLoad5.Size = new System.Drawing.Size(151, 22); this.layoutLoad5.Tag = "5"; this.layoutLoad5.Text = "Layout &5"; this.layoutLoad5.Click += new System.EventHandler(this.loadLayout_Click); @@ -434,7 +435,7 @@ // layoutLoad6 // this.layoutLoad6.Name = "layoutLoad6"; - this.layoutLoad6.Size = new System.Drawing.Size(145, 22); + this.layoutLoad6.Size = new System.Drawing.Size(151, 22); this.layoutLoad6.Tag = "6"; this.layoutLoad6.Text = "Layout &6"; this.layoutLoad6.Click += new System.EventHandler(this.loadLayout_Click); @@ -442,106 +443,105 @@ // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(189, 6); + this.toolStripSeparator1.Size = new System.Drawing.Size(200, 6); // // pythonShellToolStripMenuItem // this.pythonShellToolStripMenuItem.Name = "pythonShellToolStripMenuItem"; - this.pythonShellToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.pythonShellToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.pythonShellToolStripMenuItem.Text = "P&ython Shell"; this.pythonShellToolStripMenuItem.Click += new System.EventHandler(this.pythonShellToolStripMenuItem_Click); // // eventViewerToolStripMenuItem // this.eventViewerToolStripMenuItem.Name = "eventViewerToolStripMenuItem"; - this.eventViewerToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.eventViewerToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.eventViewerToolStripMenuItem.Text = "&Event Viewer"; this.eventViewerToolStripMenuItem.Click += new System.EventHandler(this.eventViewerToolStripMenuItem_Click); // // textureToolStripMenuItem // this.textureToolStripMenuItem.Name = "textureToolStripMenuItem"; - this.textureToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.textureToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.textureToolStripMenuItem.Text = "&Texture Viewer"; this.textureToolStripMenuItem.Click += new System.EventHandler(this.textureToolStripMenuItem_Click); // // D3D11PipelineStateToolStripMenuItem // this.D3D11PipelineStateToolStripMenuItem.Name = "D3D11PipelineStateToolStripMenuItem"; - this.D3D11PipelineStateToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.D3D11PipelineStateToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.D3D11PipelineStateToolStripMenuItem.Text = "&Pipeline State"; this.D3D11PipelineStateToolStripMenuItem.Click += new System.EventHandler(this.PipelineStateToolStripMenuItem_Click); // // aPIInspectorToolStripMenuItem // this.aPIInspectorToolStripMenuItem.Name = "aPIInspectorToolStripMenuItem"; - this.aPIInspectorToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.aPIInspectorToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.aPIInspectorToolStripMenuItem.Text = "&API Inspector"; this.aPIInspectorToolStripMenuItem.Click += new System.EventHandler(this.APIInspectorToolStripMenuItem_Click); // // meshOutputToolStripMenuItem // this.meshOutputToolStripMenuItem.Name = "meshOutputToolStripMenuItem"; - this.meshOutputToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.meshOutputToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.meshOutputToolStripMenuItem.Text = "&Mesh Output"; this.meshOutputToolStripMenuItem.Click += new System.EventHandler(this.meshOutputToolStripMenuItem_Click); // // debugMessagesToolStripMenuItem // this.debugMessagesToolStripMenuItem.Name = "debugMessagesToolStripMenuItem"; - this.debugMessagesToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.debugMessagesToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.debugMessagesToolStripMenuItem.Text = "Log Errors and &Warnings"; this.debugMessagesToolStripMenuItem.Click += new System.EventHandler(this.debugMessagesToolStripMenuItem_Click); // // timelineToolStripMenuItem // this.timelineToolStripMenuItem.Name = "timelineToolStripMenuItem"; - this.timelineToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.timelineToolStripMenuItem.Size = new System.Drawing.Size(203, 22); this.timelineToolStripMenuItem.Text = "T&imeline"; this.timelineToolStripMenuItem.Click += new System.EventHandler(this.timelineToolStripMenuItem_Click); // + // statisticsViewerToolStripMenuItem + // + this.statisticsViewerToolStripMenuItem.Name = "statisticsViewerToolStripMenuItem"; + this.statisticsViewerToolStripMenuItem.Size = new System.Drawing.Size(203, 22); + this.statisticsViewerToolStripMenuItem.Text = "Statisti&cs Viewer"; + this.statisticsViewerToolStripMenuItem.Click += new System.EventHandler(this.statisticsViewerToolStripMenuItem_Click); + // // toolsToolStripMenuItem // this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.resolveSymbolsToolStripMenuItem, - this.logStatisticsToolStripMenuItem, this.toolStripSeparator11, this.optionsToolStripMenuItem, this.manageReplayDevicesToolStripMenuItem}); this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem"; - this.toolsToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.toolsToolStripMenuItem.Size = new System.Drawing.Size(48, 20); this.toolsToolStripMenuItem.Text = "&Tools"; // // resolveSymbolsToolStripMenuItem // this.resolveSymbolsToolStripMenuItem.Name = "resolveSymbolsToolStripMenuItem"; - this.resolveSymbolsToolStripMenuItem.Size = new System.Drawing.Size(188, 22); + this.resolveSymbolsToolStripMenuItem.Size = new System.Drawing.Size(198, 22); this.resolveSymbolsToolStripMenuItem.Text = "&Resolve Symbols"; this.resolveSymbolsToolStripMenuItem.Click += new System.EventHandler(this.resolveSymbolsToolStripMenuItem_Click); // - // logStatisticsToolStripMenuItem - // - this.logStatisticsToolStripMenuItem.Name = "logStatisticsToolStripMenuItem"; - this.logStatisticsToolStripMenuItem.Size = new System.Drawing.Size(188, 22); - this.logStatisticsToolStripMenuItem.Text = "&Log Statistics"; - this.logStatisticsToolStripMenuItem.Click += new System.EventHandler(this.logStatisticsToolStripMenuItem_Click); - // // toolStripSeparator11 // this.toolStripSeparator11.Name = "toolStripSeparator11"; - this.toolStripSeparator11.Size = new System.Drawing.Size(185, 6); + this.toolStripSeparator11.Size = new System.Drawing.Size(195, 6); // // optionsToolStripMenuItem // this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; - this.optionsToolStripMenuItem.Size = new System.Drawing.Size(188, 22); + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(198, 22); this.optionsToolStripMenuItem.Text = "&Options"; this.optionsToolStripMenuItem.Click += new System.EventHandler(this.optionsToolStripMenuItem_Click); // // manageReplayDevicesToolStripMenuItem // this.manageReplayDevicesToolStripMenuItem.Name = "manageReplayDevicesToolStripMenuItem"; - this.manageReplayDevicesToolStripMenuItem.Size = new System.Drawing.Size(188, 22); + this.manageReplayDevicesToolStripMenuItem.Size = new System.Drawing.Size(198, 22); this.manageReplayDevicesToolStripMenuItem.Text = "&Manage Replay Devices"; this.manageReplayDevicesToolStripMenuItem.Click += new System.EventHandler(this.manageReplayDevicesToolStripMenuItem_Click); // @@ -693,7 +693,7 @@ // this.statusText.DoubleClickEnabled = true; this.statusText.Name = "statusText"; - this.statusText.Size = new System.Drawing.Size(63, 17); + this.statusText.Size = new System.Drawing.Size(64, 17); this.statusText.Text = "Status Text"; this.statusText.DoubleClick += new System.EventHandler(this.status_DoubleClick); // @@ -873,7 +873,6 @@ private System.Windows.Forms.ToolStripProgressBar statusProgress; private System.Windows.Forms.ToolStripMenuItem sendErrorReportToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem viewDocsToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem logStatisticsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator9; private System.Windows.Forms.ToolStripMenuItem updateToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; @@ -889,6 +888,6 @@ private System.Windows.Forms.ToolStripMenuItem checkForUpdatesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem showTipsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator12; - + private System.Windows.Forms.ToolStripMenuItem statisticsViewerToolStripMenuItem; } } \ No newline at end of file diff --git a/renderdocui/Windows/MainWindow.cs b/renderdocui/Windows/MainWindow.cs index 030efba79..f9a6fce4f 100644 --- a/renderdocui/Windows/MainWindow.cs +++ b/renderdocui/Windows/MainWindow.cs @@ -160,8 +160,6 @@ namespace renderdocui.Windows m_InitRemoteIdent = remoteIdent; OwnTemporaryLog = temp; - logStatisticsToolStripMenuItem.Enabled = false; - resolveSymbolsToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Text = "Resolve Symbols"; @@ -264,8 +262,6 @@ namespace renderdocui.Windows m_MessageTick.Dispose(); m_MessageTick = null; - logStatisticsToolStripMenuItem.Enabled = false; - resolveSymbolsToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Text = "Resolve Symbols"; @@ -366,8 +362,6 @@ namespace renderdocui.Windows })); }); - logStatisticsToolStripMenuItem.Enabled = true; - saveLogToolStripMenuItem.Enabled = true; SetTitle(); @@ -1356,139 +1350,6 @@ namespace renderdocui.Windows } } - private void CountDrawsDispatches(FetchDrawcall draw, ref int numDraws, ref int numDispatches) - { - if ((draw.flags & DrawcallFlags.Drawcall) != 0) - { - numDraws++; - } - if ((draw.flags & DrawcallFlags.Dispatch) != 0) - { - numDraws++; - numDispatches++; - } - - if(draw.children != null) - { - foreach (var d in draw.children) - CountDrawsDispatches(d, ref numDraws, ref numDispatches); - } - } - - private void logStatisticsToolStripMenuItem_Click(object sender, EventArgs e) - { - long fileSize = (new FileInfo(m_Core.LogFileName)).Length; - - int firstIdx = 0; - - var firstDrawcall = m_Core.CurDrawcalls[firstIdx]; - while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) - firstDrawcall = firstDrawcall.children[0]; - - while (firstDrawcall.events.Length == 0) - { - if (firstDrawcall.next != null) - { - firstDrawcall = firstDrawcall.next; - while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) - firstDrawcall = firstDrawcall.children[0]; - } - else - { - firstDrawcall = m_Core.CurDrawcalls[++firstIdx]; - while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) - firstDrawcall = firstDrawcall.children[0]; - } - } - - UInt64 persistantData = (UInt64)fileSize - firstDrawcall.events[0].fileOffset; - - var lastDraw = m_Core.CurDrawcalls[m_Core.CurDrawcalls.Length - 1]; - while (lastDraw.children != null && lastDraw.children.Length > 0) - lastDraw = lastDraw.children[lastDraw.children.Length - 1]; - - uint numAPIcalls = lastDraw.eventID; - - int numDrawcalls = 0; - int numDispatches = 0; - - foreach(var d in m_Core.CurDrawcalls) - CountDrawsDispatches(d, ref numDrawcalls, ref numDispatches); - - int numTextures = m_Core.CurTextures.Length; - int numBuffers = m_Core.CurBuffers.Length; - - ulong IBBytes = 0; - ulong VBBytes = 0; - ulong BufBytes = 0; - foreach(var b in m_Core.CurBuffers) - { - BufBytes += b.byteSize; - - if((b.creationFlags & BufferCreationFlags.IB) != 0) - IBBytes += b.byteSize; - if((b.creationFlags & BufferCreationFlags.VB) != 0) - VBBytes += b.byteSize; - } - - ulong RTBytes = 0; - ulong TexBytes = 0; - ulong LargeTexBytes = 0; - - int numRTs = 0; - float texW = 0, texH = 0; - float largeTexW = 0, largeTexH = 0; - int texCount = 0, largeTexCount = 0; - foreach (var t in m_Core.CurTextures) - { - if ((t.creationFlags & (TextureCreationFlags.RTV|TextureCreationFlags.DSV)) != 0) - { - numRTs++; - - RTBytes += t.byteSize; - } - else - { - texW += (float)t.width; - texH += (float)t.height; - texCount++; - - TexBytes += t.byteSize; - - if (t.width > 32 && t.height > 32) - { - largeTexW += (float)t.width; - largeTexH += (float)t.height; - largeTexCount++; - - LargeTexBytes += t.byteSize; - } - } - } - - texW /= texCount; - texH /= texCount; - - largeTexW /= largeTexCount; - largeTexH /= largeTexCount; - - string msg = - String.Format("Stats for {0}.\n\nFile size: {1:N2}MB\nPersistant Data (approx): {2:N2}MB\n\n", - Path.GetFileName(m_Core.LogFileName), - (float)fileSize / (1024.0f * 1024.0f), (float)persistantData / (1024.0f * 1024.0f)) + - String.Format("Draw calls: {0} ({1} of them are dispatches)\nAPI calls: {2}\nAPI:Draw call ratio: {3}\n\n", - numDrawcalls, numDispatches, numAPIcalls, (float)numAPIcalls / (float)numDrawcalls) + - String.Format("{0} Textures - {1:N2} MB ({2:N2} MB over 32x32), {3} RTs - {4:N2} MB.\nAvg. tex dimension: {5}x{6} ({7}x{8} over 32x32)\n", - numTextures, (float)TexBytes / (1024.0f * 1024.0f), (float)LargeTexBytes / (1024.0f * 1024.0f), - numRTs, (float)RTBytes / (1024.0f * 1024.0f), - texW, texH, largeTexW, largeTexH) + - String.Format("{0} Buffers - {1:N2} MB total {2:N2} MB IBs {3:N2} MB VBs.\n", - numBuffers, (float)BufBytes / (1024.0f * 1024.0f), (float)IBBytes / (1024.0f * 1024.0f), (float)VBBytes / (1024.0f * 1024.0f)) + - String.Format("{0} MB - Grand total GPU buffer + texture load", (float)(TexBytes + BufBytes + RTBytes) / (1024.0f * 1024.0f)); - - MessageBox.Show(msg); - } - private void recentLogMenuItem_Click(object sender, EventArgs e) { ToolStripDropDownItem item = (ToolStripDropDownItem)sender; @@ -1664,6 +1525,11 @@ namespace renderdocui.Windows m_Core.GetTimelineBar().Show(dockPanel); } + private void statisticsViewerToolStripMenuItem_Click(object sender, EventArgs e) + { + m_Core.GetStatisticsViewer().Show(dockPanel); + } + #endregion #region Symbol resolving @@ -1732,6 +1598,6 @@ namespace renderdocui.Windows e.Effect = DragDropEffects.None; } - #endregion + #endregion } } diff --git a/renderdocui/Windows/StatisticsViewer.Designer.cs b/renderdocui/Windows/StatisticsViewer.Designer.cs new file mode 100644 index 000000000..09d38c04c --- /dev/null +++ b/renderdocui/Windows/StatisticsViewer.Designer.cs @@ -0,0 +1,63 @@ +namespace renderdocui.Windows +{ + partial class StatisticsViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.statisticsLog = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // statisticsLog + // + this.statisticsLog.BackColor = System.Drawing.SystemColors.ControlLightLight; + this.statisticsLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.statisticsLog.Location = new System.Drawing.Point(0, 0); + this.statisticsLog.Name = "statisticsLog"; + this.statisticsLog.ReadOnly = true; + this.statisticsLog.Size = new System.Drawing.Size(812, 482); + this.statisticsLog.TabIndex = 0; + this.statisticsLog.Text = ""; + // + // StatisticsViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(812, 482); + this.Controls.Add(this.statisticsLog); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "StatisticsViewer"; + this.Text = "Statistics"; + this.Load += new System.EventHandler(this.StatisticsViewer_Load); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox statisticsLog; + } +} \ No newline at end of file diff --git a/renderdocui/Windows/StatisticsViewer.cs b/renderdocui/Windows/StatisticsViewer.cs new file mode 100644 index 000000000..1366bc997 --- /dev/null +++ b/renderdocui/Windows/StatisticsViewer.cs @@ -0,0 +1,785 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using WeifenLuo.WinFormsUI.Docking; +using renderdocui.Code; +using renderdoc; + +namespace renderdocui.Windows +{ + public partial class StatisticsViewer : DockContent, ILogViewerForm + { + private Core m_Core; + + public StatisticsViewer(Core core) + { + InitializeComponent(); + + Icon = global::renderdocui.Properties.Resources.icon; + + m_Core = core; + + statisticsLog.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + } + + public void OnLogfileClosed() + { + statisticsLog.Clear(); + } + + private static readonly int HistogramWidth = 128; + private static readonly string Stars = String.Concat(Enumerable.Repeat("*", HistogramWidth)); + + private string Pow2IndexAsReadable(int index) + { + long value = 1L << index; + + if (value >= (1024 * 1024)) + { + float slice = (float)value / (1024 * 1024); + return String.Format("{0}MB", Formatter.Format(slice)); + } + else if (value >= 1024) + { + float slice = (float)value / 1024; + return String.Format("{0}KB", Formatter.Format(slice)); + } + else + { + return String.Format("{0}B", Formatter.Format(value)); + } + } + + private int SliceForString(string s, UInt32 value, UInt32 maximum) + { + if (value == 0) + return 0; + + float ratio = (float)value / maximum; + int slice = (int)(ratio * s.Length); + return Math.Max(1, slice); + } + + private string CountOrEmpty(UInt32 count) + { + if (count == 0) + return ""; + else + return String.Format("({0})", count); + } + + private void AppendDrawStatistics(FetchFrameInfo[] frameList) + { + // #mivance see AppendConstantBindStatistics + FetchFrameDrawStats template = frameList[0].stats.draws; + + FetchFrameDrawStats totalUpdates = new FetchFrameDrawStats(); + totalUpdates.counts = new UInt32[template.counts.Length]; + + foreach (var f in frameList) + { + FetchFrameDrawStats draws = f.stats.draws; + + totalUpdates.calls += draws.calls; + totalUpdates.instanced += draws.instanced; + totalUpdates.indirect += draws.indirect; + + System.Diagnostics.Debug.Assert(totalUpdates.counts.Length == draws.counts.Length); + for (var t = 0; t < draws.counts.Length; t++) + totalUpdates.counts[t] += draws.counts[t]; + } + + statisticsLog.AppendText("\n*** Draw Statistics ***\n\n"); + + statisticsLog.AppendText(String.Format("Total calls: {0}, instanced: {1}, indirect: {2}\n", totalUpdates.calls, totalUpdates.instanced, totalUpdates.indirect)); + + if ( totalUpdates.instanced > 0 ) + { + statisticsLog.AppendText("\nHistogram of instance counts:\n"); + UInt32 maxCount = 0; + int maxWithValue = 0; + int maximum = totalUpdates.counts.Length; + for (var s = 1; s < maximum; s++) + { + UInt32 value = totalUpdates.counts[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 1; s <= maxWithValue; s++) + { + UInt32 count = totalUpdates.counts[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,2}{1,2}: {2} {3}\n", (s == maximum - 1) ? ">=" : "", s, Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + } + + private void AppendDispatchStatistics(FetchFrameInfo[] frameList) + { + FetchFrameDispatchStats totalUpdates = new FetchFrameDispatchStats(); + + foreach (var f in frameList) + { + FetchFrameDispatchStats dispatches = f.stats.dispatches; + + totalUpdates.calls += dispatches.calls; + totalUpdates.indirect += dispatches.indirect; + } + + statisticsLog.AppendText("\n*** Dispatch Statistics ***\n\n"); + + statisticsLog.AppendText(String.Format("Total calls: {0}, indirect: {1}\n", totalUpdates.calls, totalUpdates.indirect)); + } + + private void AppendInputAssemblerStatistics(FetchFrameInfo[] frameList) + { + FetchFrameIndexBindStats totalIndexStats = new FetchFrameIndexBindStats(); + + foreach (var f in frameList) + { + FetchFrameIndexBindStats indices = f.stats.indices; + + totalIndexStats.calls += indices.calls; + totalIndexStats.sets += indices.sets; + totalIndexStats.nulls += indices.nulls; + } + + FetchFrameLayoutBindStats totalLayoutStats = new FetchFrameLayoutBindStats(); + + foreach (var f in frameList) + { + FetchFrameLayoutBindStats layouts = f.stats.layouts; + + totalLayoutStats.calls += layouts.calls; + totalLayoutStats.sets += layouts.sets; + totalLayoutStats.nulls += layouts.nulls; + } + + // #mivance see AppendConstantBindStatistics + FetchFrameVertexBindStats template = frameList[0].stats.vertices; + FetchFrameVertexBindStats totalVertexStats = new FetchFrameVertexBindStats(); + totalVertexStats.slots = new UInt32[template.slots.Length]; + + foreach (var f in frameList) + { + FetchFrameVertexBindStats vertices = f.stats.vertices; + + totalVertexStats.calls += vertices.calls; + totalVertexStats.sets += vertices.sets; + totalVertexStats.nulls += vertices.nulls; + + System.Diagnostics.Debug.Assert(totalVertexStats.slots.Length == vertices.slots.Length); + for (var s = 0; s < vertices.slots.Length; s++) + { + totalVertexStats.slots[s] += vertices.slots[s]; + } + } + + statisticsLog.AppendText("\n*** Input Assembler Statistics ***\n\n"); + + statisticsLog.AppendText(String.Format("Total index calls: {0}, non-null index sets: {1}, null index sets: {2}\n", totalIndexStats.calls, totalIndexStats.sets, totalIndexStats.nulls)); + statisticsLog.AppendText(String.Format("Total layout calls: {0}, non-null layout sets: {1}, null layout sets: {2}\n", totalLayoutStats.calls, totalLayoutStats.sets, totalLayoutStats.nulls)); + statisticsLog.AppendText(String.Format("Total vertex calls: {0}, non-null vertex sets: {1}, null vertex sets: {2}\n", totalVertexStats.calls, totalVertexStats.sets, totalVertexStats.nulls)); + + statisticsLog.AppendText("\nHistogram of aggregate vertex slot counts per invocation:\n"); + UInt32 maxCount = 0; + int maxWithValue = 0; + for (var s = 1; s < totalVertexStats.slots.Length; s++) + { + UInt32 value = totalVertexStats.slots[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 1; s <= maxWithValue; s++) + { + UInt32 count = totalVertexStats.slots[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,2}: {1} {2}\n", s, Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + + private void AppendConstantBindStatistics(FetchFrameInfo[] frameList) + { + System.Diagnostics.Debug.Assert(frameList.Length > 0); + + // #mivance C++-side we guarantee all stages will have the same slots + // and sizes count, so pattern off of the first frame's first stage + FetchFrameConstantBindStats template = frameList[0].stats.constants[0]; + + // #mivance there is probably a way to iterate the fields via + // GetType()/GetField() and build a sort of dynamic min/max/average + // structure for a given type with known integral types (or arrays + // thereof), but given we're heading for a Qt/C++ rewrite of the UI + // perhaps best not to dwell too long on that + FetchFrameConstantBindStats[] totalConstantsPerStage = new FetchFrameConstantBindStats[(int)ShaderStageType.Count]; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalConstantsPerStage[s] = new FetchFrameConstantBindStats(); + totalConstantsPerStage[s].slots = new UInt32[template.slots.Length]; + totalConstantsPerStage[s].sizes = new UInt32[template.sizes.Length]; + } + + foreach (var f in frameList) + { + FetchFrameConstantBindStats[] constants = f.stats.constants; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalConstantsPerStage[s].calls += constants[s].calls; + totalConstantsPerStage[s].sets += constants[s].sets; + totalConstantsPerStage[s].nulls += constants[s].nulls; + + System.Diagnostics.Debug.Assert(totalConstantsPerStage[s].slots.Length == constants[s].slots.Length); + for (var l = 0; l < constants[s].slots.Length; l++) + { + totalConstantsPerStage[s].slots[l] += constants[s].slots[l]; + } + + System.Diagnostics.Debug.Assert(totalConstantsPerStage[s].sizes.Length == constants[s].sizes.Length); + for (var z = 0; z < constants[s].sizes.Length; z++) + { + totalConstantsPerStage[s].sizes[z] += constants[s].sizes[z]; + } + } + } + + FetchFrameConstantBindStats totalConstantsForAllStages = new FetchFrameConstantBindStats(); + totalConstantsForAllStages.slots = new UInt32[totalConstantsPerStage[0].slots.Length]; + totalConstantsForAllStages.sizes = new UInt32[totalConstantsPerStage[0].sizes.Length]; + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + FetchFrameConstantBindStats perStage = totalConstantsPerStage[s]; + totalConstantsForAllStages.calls += perStage.calls; + totalConstantsForAllStages.sets += perStage.sets; + totalConstantsForAllStages.nulls += perStage.nulls; + for (var l = 0; l < perStage.slots.Length; l++) + { + totalConstantsForAllStages.slots[l] += perStage.slots[l]; + } + for (var z = 0; z < perStage.sizes.Length; z++) + { + totalConstantsForAllStages.sizes[z] += perStage.sizes[z]; + } + } + + statisticsLog.AppendText("\n*** Constant Bind Statistics ***\n\n"); + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + statisticsLog.AppendText(String.Format("{0} calls: {1}, non-null buffer sets: {2}, null buffer sets: {3}\n", + m_Core.CurPipelineState.Abbrev((ShaderStageType)s), totalConstantsPerStage[s].calls, + totalConstantsPerStage[s].sets, totalConstantsPerStage[s].nulls)); + } + + statisticsLog.AppendText(String.Format("Total calls: {0}, non-null buffer sets: {1}, null buffer sets: {2}\n", + totalConstantsForAllStages.calls, totalConstantsForAllStages.sets, totalConstantsForAllStages.nulls)); + + statisticsLog.AppendText("\nHistogram of aggregate slot counts per invocation across all stages:\n"); + UInt32 maxCount = 0; + int maxWithValue = 0; + for (var s = 1; s < totalConstantsForAllStages.slots.Length; s++) + { + UInt32 value = totalConstantsForAllStages.slots[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 1; s <= maxWithValue; s++) + { + UInt32 count = totalConstantsForAllStages.slots[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,2}: {1} {2}\n", s, Stars.Substring(0, slice), CountOrEmpty(count))); + } + + statisticsLog.AppendText("\nHistogram of aggregate constant buffer sizes across all stages:\n"); + maxCount = 0; + maxWithValue = 0; + for (var s = 0; s < totalConstantsForAllStages.sizes.Length; s++) + { + UInt32 value = totalConstantsForAllStages.sizes[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 0; s <= maxWithValue; s++) + { + UInt32 count = totalConstantsForAllStages.sizes[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,8}: {1} {2}\n", Pow2IndexAsReadable(s), Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + + private void AppendSamplerBindStatistics(FetchFrameInfo[] frameList) + { + // #mivance see AppendConstantBindStatistics + FetchFrameSamplerBindStats template = frameList[0].stats.samplers[0]; + + FetchFrameSamplerBindStats[] totalSamplersPerStage = new FetchFrameSamplerBindStats[(int)ShaderStageType.Count]; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalSamplersPerStage[s] = new FetchFrameSamplerBindStats(); + totalSamplersPerStage[s].slots = new UInt32[template.slots.Length]; + } + + foreach (var f in frameList) + { + FetchFrameSamplerBindStats[] resources = f.stats.samplers; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalSamplersPerStage[s].calls += resources[s].calls; + totalSamplersPerStage[s].sets += resources[s].sets; + totalSamplersPerStage[s].nulls += resources[s].nulls; + + System.Diagnostics.Debug.Assert(totalSamplersPerStage[s].slots.Length == resources[s].slots.Length); + for (var l = 0; l < resources[s].slots.Length; l++) + { + totalSamplersPerStage[s].slots[l] += resources[s].slots[l]; + } + } + } + + FetchFrameSamplerBindStats totalSamplersForAllStages = new FetchFrameSamplerBindStats(); + totalSamplersForAllStages.slots = new UInt32[totalSamplersPerStage[0].slots.Length]; + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + FetchFrameSamplerBindStats perStage = totalSamplersPerStage[s]; + totalSamplersForAllStages.calls += perStage.calls; + totalSamplersForAllStages.sets += perStage.sets; + totalSamplersForAllStages.nulls += perStage.nulls; + for (var l = 0; l < perStage.slots.Length; l++) + { + totalSamplersForAllStages.slots[l] += perStage.slots[l]; + } + } + + statisticsLog.AppendText("\n*** Sampler Bind Statistics ***\n\n"); + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + statisticsLog.AppendText(String.Format("{0} calls: {1}, non-null sampler sets: {2}, null sampler sets: {3}\n", + m_Core.CurPipelineState.Abbrev((ShaderStageType)s), totalSamplersPerStage[s].calls, + totalSamplersPerStage[s].sets, totalSamplersPerStage[s].nulls)); + } + + statisticsLog.AppendText(String.Format("Total calls: {0}, non-null sampler sets: {1}, null sampler sets: {2}\n", + totalSamplersForAllStages.calls, totalSamplersForAllStages.sets, totalSamplersForAllStages.nulls)); + + statisticsLog.AppendText("\nHistogram of aggregate slot counts per invocation across all stages:\n"); + UInt32 maxCount = 0; + int maxWithValue = 0; + for (var s = 1; s < totalSamplersForAllStages.slots.Length; s++) + { + UInt32 value = totalSamplersForAllStages.slots[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 1; s <= maxWithValue; s++) + { + UInt32 count = totalSamplersForAllStages.slots[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,2}: {1} {2}\n", s, Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + + private void AppendResourceBindStatistics(FetchFrameInfo[] frameList) + { + System.Diagnostics.Debug.Assert(frameList.Length > 0); + + // #mivance see AppendConstantBindStatistics + FetchFrameResourceBindStats template = frameList[0].stats.resources[0]; + + FetchFrameResourceBindStats[] totalResourcesPerStage = new FetchFrameResourceBindStats[(int)ShaderStageType.Count]; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalResourcesPerStage[s] = new FetchFrameResourceBindStats(); + totalResourcesPerStage[s].types = new UInt32[template.types.Length]; + totalResourcesPerStage[s].slots = new UInt32[template.slots.Length]; + } + + foreach (var f in frameList) + { + FetchFrameResourceBindStats[] resources = f.stats.resources; + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + totalResourcesPerStage[s].calls += resources[s].calls; + totalResourcesPerStage[s].sets += resources[s].sets; + totalResourcesPerStage[s].nulls += resources[s].nulls; + + System.Diagnostics.Debug.Assert(totalResourcesPerStage[s].types.Length == resources[s].types.Length); + for (var z = 0; z < resources[s].types.Length; z++) + { + totalResourcesPerStage[s].types[z] += resources[s].types[z]; + } + + System.Diagnostics.Debug.Assert(totalResourcesPerStage[s].slots.Length == resources[s].slots.Length); + for (var l = 0; l < resources[s].slots.Length; l++) + { + totalResourcesPerStage[s].slots[l] += resources[s].slots[l]; + } + } + } + + FetchFrameResourceBindStats totalResourcesForAllStages = new FetchFrameResourceBindStats(); + totalResourcesForAllStages.types = new UInt32[totalResourcesPerStage[0].types.Length]; + totalResourcesForAllStages.slots = new UInt32[totalResourcesPerStage[0].slots.Length]; + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + FetchFrameResourceBindStats perStage = totalResourcesPerStage[s]; + totalResourcesForAllStages.calls += perStage.calls; + totalResourcesForAllStages.sets += perStage.sets; + totalResourcesForAllStages.nulls += perStage.nulls; + for (var t = 0; t < perStage.types.Length; t++) + { + totalResourcesForAllStages.types[t] += perStage.types[t]; + } + for (var l = 0; l < perStage.slots.Length; l++) + { + totalResourcesForAllStages.slots[l] += perStage.slots[l]; + } + } + + statisticsLog.AppendText("\n*** Resource Bind Statistics ***\n\n"); + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + statisticsLog.AppendText(String.Format("{0} calls: {1} non-null resource sets: {2} null resource sets: {3}\n", + m_Core.CurPipelineState.Abbrev((ShaderStageType)s), totalResourcesPerStage[s].calls, + totalResourcesPerStage[s].sets, totalResourcesPerStage[s].nulls)); + } + + statisticsLog.AppendText(String.Format("Total calls: {0} non-null resource sets: {1} null resource sets: {2}\n", + totalResourcesForAllStages.calls, totalResourcesForAllStages.sets, + totalResourcesForAllStages.nulls)); + + UInt32 maxCount = 0; + int maxWithCount = 0; + + statisticsLog.AppendText("\nHistogram of resource types across all stages:\n"); + for (var s = 0; s < totalResourcesForAllStages.types.Length; s++) + { + UInt32 count = totalResourcesForAllStages.types[s]; + if (count > 0) + maxWithCount = s; + maxCount = Math.Max(maxCount, count); + } + + for (var s = 0; s <= maxWithCount; s++) + { + UInt32 count = totalResourcesForAllStages.types[s]; + int slice = SliceForString(Stars, count, maxCount); + ShaderResourceType type = (ShaderResourceType)s; + statisticsLog.AppendText(String.Format("{0,16}: {1} {2}\n", type.ToString(), Stars.Substring(0, slice), CountOrEmpty(count))); + } + + maxCount = 0; + maxWithCount = 0; + + statisticsLog.AppendText("\nHistogram of aggregate slot counts per invocation across all stages:\n"); + for (var s = 1; s < totalResourcesForAllStages.slots.Length; s++) + { + UInt32 count = totalResourcesForAllStages.slots[s]; + if (count > 0) + maxWithCount = s; + maxCount = Math.Max(maxCount, count); + } + + for (var s = 1; s <= maxWithCount; s++) + { + UInt32 count = totalResourcesForAllStages.slots[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,3}: {1} {2}\n", s, Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + + private void AppendUpdateStatistics(FetchFrameInfo[] frameList) + { + // #mivance see AppendConstantBindStatistics + FetchFrameUpdateStats template = frameList[0].stats.updates; + + FetchFrameUpdateStats totalUpdates = new FetchFrameUpdateStats(); + totalUpdates.types = new UInt32[template.types.Length]; + totalUpdates.sizes = new UInt32[template.sizes.Length]; + + foreach (var f in frameList) + { + FetchFrameUpdateStats updates = f.stats.updates; + + totalUpdates.calls += updates.calls; + totalUpdates.clients += updates.clients; + totalUpdates.servers += updates.servers; + + System.Diagnostics.Debug.Assert(totalUpdates.types.Length == updates.types.Length); + for (var t = 0; t < updates.types.Length; t++) + totalUpdates.types[t] += updates.types[t]; + + System.Diagnostics.Debug.Assert(totalUpdates.sizes.Length == updates.sizes.Length); + for (var t = 0; t < updates.sizes.Length; t++) + totalUpdates.sizes[t] += updates.sizes[t]; + } + + statisticsLog.AppendText("\n*** Resource Update Statistics ***\n\n"); + + statisticsLog.AppendText(String.Format("Total calls: {0}, client-updated memory: {1}, server-updated memory: {2}\n", totalUpdates.calls, totalUpdates.clients, totalUpdates.servers)); + + statisticsLog.AppendText("\nHistogram of updated resource type:\n"); + UInt32 maxCount = 0; + int maxWithValue = 0; + for (var s = 1; s < totalUpdates.types.Length; s++) + { + UInt32 value = totalUpdates.types[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 1; s <= maxWithValue; s++) + { + UInt32 count = totalUpdates.types[s]; + int slice = SliceForString(Stars, count, maxCount); + ShaderResourceType type = (ShaderResourceType)s; + statisticsLog.AppendText(String.Format("{0,16}: {1} {2}\n", type.ToString(), Stars.Substring(0, slice), CountOrEmpty(count))); + } + + statisticsLog.AppendText("\nHistogram of updated resource size:\n"); + maxCount = 0; + maxWithValue = 0; + for (var s = 0; s < totalUpdates.sizes.Length; s++) + { + UInt32 value = totalUpdates.sizes[s]; + if (value > 0) + maxWithValue = s; + maxCount = Math.Max(maxCount, value); + } + + for (var s = 0; s <= maxWithValue; s++) + { + UInt32 count = totalUpdates.sizes[s]; + int slice = SliceForString(Stars, count, maxCount); + statisticsLog.AppendText(String.Format("{0,8}: {1} {2}\n", Pow2IndexAsReadable(s), Stars.Substring(0, slice), CountOrEmpty(count))); + } + } + + private void AppendDetailedInformation(FetchFrameInfo[] frameList) + { + AppendDrawStatistics(frameList); + AppendDispatchStatistics(frameList); + AppendInputAssemblerStatistics(frameList); + AppendConstantBindStatistics(frameList); + AppendSamplerBindStatistics(frameList); + AppendResourceBindStatistics(frameList); + AppendUpdateStatistics(frameList); + } + + private void CountContributingEvents(FetchDrawcall draw, ref uint drawCount, ref uint dispatchCount, ref uint diagnosticCount) + { + const uint diagnosticMask = (uint)DrawcallFlags.SetMarker | (uint)DrawcallFlags.PushMarker | (uint)DrawcallFlags.PopMarker; + uint diagnosticMasked = (uint)draw.flags & diagnosticMask; + + if (diagnosticMasked != 0) + diagnosticCount += 1; + + if ((draw.flags & DrawcallFlags.Drawcall) != 0) + drawCount += 1; + + if ((draw.flags & DrawcallFlags.Dispatch) != 0) + dispatchCount += 1; + + if (draw.children != null) + { + foreach (var c in draw.children) + CountContributingEvents(c, ref drawCount, ref dispatchCount, ref diagnosticCount); + } + } + + public void OnLogfileLoaded() + { + statisticsLog.Clear(); + + long fileSize = (new FileInfo(m_Core.LogFileName)).Length; + + int firstIdx = 0; + + var firstDrawcall = m_Core.CurDrawcalls[firstIdx]; + while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) + firstDrawcall = firstDrawcall.children[0]; + + while (firstDrawcall.events.Length == 0) + { + if (firstDrawcall.next != null) + { + firstDrawcall = firstDrawcall.next; + while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) + firstDrawcall = firstDrawcall.children[0]; + } + else + { + firstDrawcall = m_Core.CurDrawcalls[++firstIdx]; + while (firstDrawcall.children != null && firstDrawcall.children.Length > 0) + firstDrawcall = firstDrawcall.children[0]; + } + } + + UInt64 persistentData = (UInt64)fileSize - firstDrawcall.events[0].fileOffset; + + var lastDraw = m_Core.CurDrawcalls[m_Core.CurDrawcalls.Length - 1]; + while (lastDraw.children != null && lastDraw.children.Length > 0) + lastDraw = lastDraw.children[lastDraw.children.Length - 1]; + + uint drawCount = 0; + uint dispatchCount = 0; + uint diagnosticCount = 0; + foreach (var d in m_Core.CurDrawcalls) + CountContributingEvents(d, ref drawCount, ref dispatchCount, ref diagnosticCount); + + uint numAPIcalls = lastDraw.eventID - diagnosticCount; + + // #mivance only recording this for comparison vis a vis draw call + // iteration, we want to preserve the old stats data for the + // backends which aren't recording statistics + bool statsRecorded = false; + uint numDraws = 0; + uint numDispatches = 0; + uint numIndexVertexSets = 0; + uint numConstantSets = 0; + uint numSamplerSets = 0; + uint numResourceSets = 0; + uint numResourceUpdates = 0; + + FetchFrameInfo[] frameList = m_Core.FrameInfo; + + foreach (var f in frameList) + { + if (f.stats.recorded == 0) + continue; + + statsRecorded = true; + + for (var s = (int)ShaderStageType.First; s < (int)ShaderStageType.Count; s++) + { + numConstantSets += f.stats.constants[s].calls; + numSamplerSets += f.stats.samplers[s].calls; + numResourceSets += f.stats.resources[s].calls; + } + + numResourceUpdates += f.stats.updates.calls; + numIndexVertexSets += (f.stats.indices.calls + f.stats.vertices.calls + f.stats.layouts.calls); + numDraws += f.stats.draws.calls; + numDispatches += f.stats.dispatches.calls; + } + + int numTextures = m_Core.CurTextures.Length; + int numBuffers = m_Core.CurBuffers.Length; + + ulong IBBytes = 0; + ulong VBBytes = 0; + ulong BufBytes = 0; + foreach (var b in m_Core.CurBuffers) + { + BufBytes += b.byteSize; + + if ((b.creationFlags & BufferCreationFlags.IB) != 0) + IBBytes += b.byteSize; + if ((b.creationFlags & BufferCreationFlags.VB) != 0) + VBBytes += b.byteSize; + } + + ulong RTBytes = 0; + ulong TexBytes = 0; + ulong LargeTexBytes = 0; + + int numRTs = 0; + float texW = 0, texH = 0; + float largeTexW = 0, largeTexH = 0; + int texCount = 0, largeTexCount = 0; + foreach (var t in m_Core.CurTextures) + { + if ((t.creationFlags & (TextureCreationFlags.RTV | TextureCreationFlags.DSV)) != 0) + { + numRTs++; + + RTBytes += t.byteSize; + } + else + { + texW += (float)t.width; + texH += (float)t.height; + texCount++; + + TexBytes += t.byteSize; + + if (t.width > 32 && t.height > 32) + { + largeTexW += (float)t.width; + largeTexH += (float)t.height; + largeTexCount++; + + LargeTexBytes += t.byteSize; + } + } + } + + texW /= texCount; + texH /= texCount; + + largeTexW /= largeTexCount; + largeTexH /= largeTexCount; + + string header = String.Format("Stats for {0}.\n\nFile size: {1:N2}MB\nPersistent Data (approx): {2:N2}MB\n", + Path.GetFileName(m_Core.LogFileName), + (float)fileSize / (1024.0f * 1024.0f), (float)persistentData / (1024.0f * 1024.0f)); + string draws = String.Format("Draw calls: {0}\nDispatch calls: {1}\n", + drawCount, dispatchCount); + string calls = statsRecorded ? String.Format("API calls: {0}\n\tIndex/vertex bind calls: {1}\n\tConstant bind calls: {2}\n\tSampler bind calls: {3}\n\tResource bind calls: {4}\n\tResource update calls: {5}\n", + numAPIcalls, numIndexVertexSets, numConstantSets, numSamplerSets, numResourceSets, numResourceUpdates) : ""; + string ratio = String.Format("API:Draw call ratio: {0}\n\n", (float)numAPIcalls / (float)drawCount); + string textures = String.Format("{0} Textures - {1:N2} MB ({2:N2} MB over 32x32), {3} RTs - {4:N2} MB.\nAvg. tex dimension: {5}x{6} ({7}x{8} over 32x32)\n", + numTextures, (float)TexBytes / (1024.0f * 1024.0f), (float)LargeTexBytes / (1024.0f * 1024.0f), + numRTs, (float)RTBytes / (1024.0f * 1024.0f), + texW, texH, largeTexW, largeTexH); + string buffers = String.Format("{0} Buffers - {1:N2} MB total {2:N2} MB IBs {3:N2} MB VBs.\n", + numBuffers, (float)BufBytes / (1024.0f * 1024.0f), (float)IBBytes / (1024.0f * 1024.0f), (float)VBBytes / (1024.0f * 1024.0f)); + string load = String.Format("{0} MB - Grand total GPU buffer + texture load.\n", (float)(TexBytes + BufBytes + RTBytes) / (1024.0f * 1024.0f)); + + statisticsLog.AppendText(header); + + statisticsLog.AppendText("\n*** Summary ***\n\n"); + statisticsLog.AppendText(draws); + statisticsLog.AppendText(calls); + statisticsLog.AppendText(ratio); + statisticsLog.AppendText(textures); + statisticsLog.AppendText(buffers); + statisticsLog.AppendText(load); + + if (statsRecorded) + AppendDetailedInformation(frameList); + + statisticsLog.Select(0, 0); + } + + public void OnEventSelected(UInt32 frameID, UInt32 eventID) + { + + } + + private void StatisticsViewer_Load(object sender, EventArgs e) + { + + } + } +} diff --git a/renderdocui/Windows/StatisticsViewer.resx b/renderdocui/Windows/StatisticsViewer.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/renderdocui/Windows/StatisticsViewer.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/renderdocui/renderdocui.csproj b/renderdocui/renderdocui.csproj index 0b3845a4e..618e13199 100644 --- a/renderdocui/renderdocui.csproj +++ b/renderdocui/renderdocui.csproj @@ -325,6 +325,12 @@ ShaderViewer.cs + + Form + + + StatisticsViewer.cs + Form @@ -433,6 +439,9 @@ ShaderViewer.cs + + StatisticsViewer.cs + TextureViewer.cs