Implement bindless feedback for DXBC shaders on D3D12 with patching

This commit is contained in:
baldurk
2021-07-15 16:41:23 +01:00
parent 95ea12b0e5
commit 46266a9dd6
18 changed files with 904 additions and 68 deletions
+40 -2
View File
@@ -282,6 +282,15 @@ struct View
:type: TextureSwizzle4
)");
TextureSwizzle4 swizzle;
DOCUMENT(R"(``True`` if this binding element is dynamically used.
If set to ``False`` this means that the binding was available to the shader but during execution it
was not referenced. The data gathered for setting this variable is conservative, meaning that only
accesses through arrays will have this calculated to reduce the required feedback bandwidth - single
non-arrayed descriptors may have this value set to ``True`` even if the shader did not use them,
since single descriptors may only be dynamically skipped by control flow.
)");
bool dynamicallyUsed = true;
DOCUMENT("The :class:`D3DBufferViewFlags` set for the buffer.");
D3DBufferViewFlags bufferFlags = D3DBufferViewFlags::NoFlags;
DOCUMENT("If the view has a hidden counter, this stores the current value of the counter.");
@@ -464,8 +473,9 @@ struct RootSignatureRange
bool operator==(const RootSignatureRange &o) const
{
return immediate == o.immediate && rootElement == o.rootElement && visibility == o.visibility &&
registerSpace == o.registerSpace && constantBuffers == o.constantBuffers &&
samplers == o.samplers && views == o.views;
registerSpace == o.registerSpace && dynamicallyUsedCount == o.dynamicallyUsedCount &&
firstUsedIndex == o.firstUsedIndex && lastUsedIndex == o.lastUsedIndex &&
constantBuffers == o.constantBuffers && samplers == o.samplers && views == o.views;
}
bool operator<(const RootSignatureRange &o) const
{
@@ -477,6 +487,12 @@ struct RootSignatureRange
return visibility < o.visibility;
if(!(registerSpace == o.registerSpace))
return registerSpace < o.registerSpace;
if(!(dynamicallyUsedCount == o.dynamicallyUsedCount))
return dynamicallyUsedCount < o.dynamicallyUsedCount;
if(!(firstUsedIndex == o.firstUsedIndex))
return firstUsedIndex < o.firstUsedIndex;
if(!(lastUsedIndex == o.lastUsedIndex))
return lastUsedIndex < o.lastUsedIndex;
if(!(constantBuffers == o.constantBuffers))
return constantBuffers < o.constantBuffers;
if(!(samplers == o.samplers))
@@ -496,6 +512,28 @@ struct RootSignatureRange
ShaderStageMask visibility = ShaderStageMask::All;
DOCUMENT("The register space of this element.");
uint32_t registerSpace;
DOCUMENT(R"(Lists how many bindings in :data:`views` are dynamically used. Useful to avoid
redundant iteration to determine whether any bindings are present.
For more information see :data:`D3D12View.dynamicallyUsed`.
)");
uint32_t dynamicallyUsedCount = ~0U;
DOCUMENT(R"(Gives the index of the first binding in :data:`views` that is dynamically used. Useful
to avoid redundant iteration in very large descriptor arrays with a small subset that are used.
For more information see :data:`D3D12View.dynamicallyUsed`.
)");
int32_t firstUsedIndex = 0;
DOCUMENT(R"(Gives the index of the first binding in :data:`views` that is dynamically used. Useful
to avoid redundant iteration in very large descriptor arrays with a small subset that are used.
.. note::
This may be set to a higher value than the number of bindings, if no dynamic use information is
available. Ensure that this is an additional check on the bind and the count is still respected.
For more information see :data:`D3D12View.dynamicallyUsed`.
)");
int32_t lastUsedIndex = 0x7fffffff;
DOCUMENT(R"(The constant buffers in this range.
:type: List[D3D12ConstantBuffer]
+42 -4
View File
@@ -1272,6 +1272,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
rdcarray<BoundResource> &val = ret.back().resources;
ret.back().dynamicallyUsedCount = 0;
for(size_t i = 0; i < m_D3D12->rootElements.size(); ++i)
{
const D3D12Pipe::RootSignatureRange &element = m_D3D12->rootElements[i];
@@ -1281,7 +1283,19 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
if(element.type == BindType::ReadOnlyResource &&
element.registerSpace == (uint32_t)bind.bindset)
{
for(size_t j = 0; j < element.views.size(); ++j)
size_t firstIdx = 0;
size_t count = element.views.size();
if(onlyUsed && val.empty())
{
firstIdx = (uint32_t)element.firstUsedIndex;
count = std::min(count - firstIdx,
size_t(element.lastUsedIndex - element.firstUsedIndex + 1));
ret.back().firstIndex = (int32_t)firstIdx;
}
for(size_t j = firstIdx; j < count; ++j)
{
const D3D12Pipe::View &view = element.views[j];
if(view.bind >= start && view.bind < end)
@@ -1289,9 +1303,13 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
val.push_back(BoundResource());
BoundResource &b = val.back();
b.resourceId = view.resourceId;
b.dynamicallyUsed = view.dynamicallyUsed;
b.firstMip = (int)view.firstMip;
b.firstSlice = (int)view.firstSlice;
b.typeCast = view.viewFormat.compType;
if(view.dynamicallyUsed)
ret.back().dynamicallyUsedCount++;
}
}
}
@@ -1352,7 +1370,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
if(onlyUsed)
{
firstIdx = (uint32_t)bind.firstUsedIndex;
count = std::min(count, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
count =
std::min(count - firstIdx, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
}
rdcarray<BoundResource> &val = ret.back().resources;
@@ -1454,6 +1473,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
rdcarray<BoundResource> &val = ret.back().resources;
ret.back().dynamicallyUsedCount = 0;
for(size_t i = 0; i < m_D3D12->rootElements.size(); ++i)
{
const D3D12Pipe::RootSignatureRange &element = m_D3D12->rootElements[i];
@@ -1463,7 +1484,19 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
if(element.type == BindType::ReadWriteResource &&
element.registerSpace == (uint32_t)bind.bindset)
{
for(size_t j = 0; j < element.views.size(); ++j)
size_t firstIdx = 0;
size_t count = element.views.size();
if(onlyUsed && val.empty())
{
firstIdx = (uint32_t)element.firstUsedIndex;
count = std::min(count - firstIdx,
size_t(element.lastUsedIndex - element.firstUsedIndex + 1));
ret.back().firstIndex = (int32_t)firstIdx;
}
for(size_t j = firstIdx; j < count; ++j)
{
const D3D12Pipe::View &view = element.views[j];
if(view.bind >= start && view.bind < end)
@@ -1471,9 +1504,13 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
val.push_back(BoundResource());
BoundResource &b = val.back();
b.resourceId = view.resourceId;
b.dynamicallyUsed = view.dynamicallyUsed;
b.firstMip = (int)view.firstMip;
b.firstSlice = (int)view.firstSlice;
b.typeCast = view.viewFormat.compType;
if(view.dynamicallyUsed)
ret.back().dynamicallyUsedCount++;
}
}
}
@@ -1530,7 +1567,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
if(onlyUsed)
{
firstIdx = (uint32_t)bind.firstUsedIndex;
count = std::min(count, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
count =
std::min(count - firstIdx, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
}
rdcarray<BoundResource> &val = ret.back().resources;
+2 -2
View File
@@ -421,7 +421,7 @@ DOCUMENT(R"(A single source component for a destination texture swizzle.
The fixed value ``1``.
)");
enum class TextureSwizzle : uint32_t
enum class TextureSwizzle : uint8_t
{
Red,
Green,
@@ -4077,7 +4077,7 @@ DOCUMENT(R"(A set of flags for D3D buffer view properties.
The buffer is used with a structured buffer with associated hidden counter.
)");
enum class D3DBufferViewFlags : uint32_t
enum class D3DBufferViewFlags : uint8_t
{
NoFlags = 0x0,
Raw = 0x1,
+5
View File
@@ -231,6 +231,7 @@ struct DescriptorBinding
bool operator==(const DescriptorBinding &o) const
{
return descriptorCount == o.descriptorCount && dynamicallyUsedCount == o.dynamicallyUsedCount &&
firstUsedIndex == o.firstUsedIndex && lastUsedIndex == o.lastUsedIndex &&
type == o.type && stageFlags == o.stageFlags && binds == o.binds;
}
bool operator<(const DescriptorBinding &o) const
@@ -239,6 +240,10 @@ struct DescriptorBinding
return descriptorCount < o.descriptorCount;
if(!(dynamicallyUsedCount == o.dynamicallyUsedCount))
return dynamicallyUsedCount < o.dynamicallyUsedCount;
if(!(firstUsedIndex == o.firstUsedIndex))
return firstUsedIndex < o.firstUsedIndex;
if(!(lastUsedIndex == o.lastUsedIndex))
return lastUsedIndex < o.lastUsedIndex;
if(!(type == o.type))
return type < o.type;
if(!(stageFlags == o.stageFlags))
@@ -185,6 +185,8 @@
<ClInclude Include="d3d11_video.h">
<Filter>Wrapped</Filter>
</ClInclude>
<ClInclude Include="d3d11_hooks.h" />
<ClInclude Include="d3d11_hooks.h">
<Filter>Hooks</Filter>
</ClInclude>
</ItemGroup>
</Project>
+2
View File
@@ -64,6 +64,8 @@ enum CBVUAVSRVSlot
TMP_UAV,
FEEDBACK_CLEAR_UAV,
MSAA_SRV2x,
MSAA_SRV4x,
MSAA_SRV8x,
+93 -20
View File
@@ -168,6 +168,7 @@ void D3D12Replay::CreateResources()
void D3D12Replay::DestroyResources()
{
ClearPostVSCache();
ClearFeedbackCache();
m_General.Release();
m_TexRender.Release();
@@ -176,6 +177,8 @@ void D3D12Replay::DestroyResources()
m_PixelPick.Release();
m_Histogram.Release();
SAFE_RELEASE(m_BindlessFeedback.FeedbackBuffer);
SAFE_RELEASE(m_SOBuffer);
SAFE_RELEASE(m_SOStagingBuffer);
SAFE_RELEASE(m_SOPatchedIndexBuffer);
@@ -926,7 +929,7 @@ ShaderStageMask ToShaderStageMask(D3D12_SHADER_VISIBILITY vis)
}
}
void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSig,
void D3D12Replay::FillRootElements(uint32_t eventId, const D3D12RenderState::RootSignature &rootSig,
const ShaderBindpointMapping *mappings[(uint32_t)ShaderStage::Count],
rdcarray<D3D12Pipe::RootSignatureRange> &rootElements)
{
@@ -935,6 +938,12 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
D3D12ResourceManager *rm = m_pDevice->GetResourceManager();
const D3D12DynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
const D3D12FeedbackBindIdentifier *curUsage = usage.used.begin();
const D3D12FeedbackBindIdentifier *lastUsage = usage.used.end();
D3D12FeedbackBindIdentifier curIdentifier;
WrappedID3D12RootSignature *sig =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rootSig.rootsig);
@@ -944,6 +953,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
for(size_t rootEl = 0; rootEl < sig->sig.Parameters.size(); rootEl++)
{
curIdentifier.rootEl = rootEl;
const D3D12RootSignatureParameter &p = sig->sig.Parameters[rootEl];
if(p.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS)
@@ -1027,6 +1038,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
element.views.push_back(D3D12Pipe::View(p.Descriptor.ShaderRegister));
D3D12Pipe::View &view = element.views.back();
view.dynamicallyUsed = true;
if(rootEl < rootSig.sigelems.size())
{
const D3D12RenderState::SignatureElement &e = rootSig.sigelems[rootEl];
@@ -1065,6 +1078,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
element.views.push_back(D3D12Pipe::View(p.Descriptor.ShaderRegister));
D3D12Pipe::View &view = element.views.back();
view.dynamicallyUsed = true;
if(rootEl < rootSig.sigelems.size())
{
const D3D12RenderState::SignatureElement &e = rootSig.sigelems[rootEl];
@@ -1104,6 +1119,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
{
const D3D12_DESCRIPTOR_RANGE1 &range = p.ranges[r];
curIdentifier.rangeIndex = r;
// Here we diverge slightly from how root signatures store data. A descriptor table can
// contain multiple ranges which can each contain different types. D3D12Pipe treats
// each range as a separate RootElement
@@ -1146,10 +1163,13 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
// Find shader binds that map any or all of this range to see if we can trim it
for(ShaderStage stage = ShaderStage::Vertex; stage < ShaderStage::Count; ++stage)
{
const ShaderBindpointMapping *mapping = mappings[(uint32_t)stage];
if((element.visibility & MaskForStage(stage)) != ShaderStageMask::Unknown)
{
// This range is visible to this shader stage, check its mappings
const rdcarray<Bindpoint> &bps = mappings[(uint32_t)stage]->readOnlyResources;
const rdcarray<Bindpoint> &bps = range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV
? mapping->readWriteResources
: mapping->readOnlyResources;
for(size_t b = 0; b < bps.size(); ++b)
{
if(bps[b].bindset == (int32_t)element.registerSpace &&
@@ -1237,9 +1257,16 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
}
}
}
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV ||
range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
{
element.type = BindType::ReadOnlyResource;
if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
element.type = BindType::ReadOnlyResource;
else
element.type = BindType::ReadWriteResource;
if(usage.valid)
element.dynamicallyUsedCount = 0;
for(UINT i = 0; i < num; i++, shaderReg++)
{
@@ -1247,22 +1274,59 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
D3D12Pipe::View &view = element.views.back();
view.tableIndex = offset + i;
if(desc)
if(usage.valid)
{
FillResourceView(view, desc);
desc++;
}
}
}
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
{
element.type = BindType::ReadWriteResource;
curIdentifier.descIndex = i;
for(UINT i = 0; i < num; i++, shaderReg++)
{
element.views.push_back(D3D12Pipe::View(shaderReg));
D3D12Pipe::View &view = element.views.back();
view.tableIndex = offset + i;
// if the usage data is valid we expect entries for all descriptors that are used.
// Note that because of the messed up root signature/binding split, some of these
// entries will just be statically inserted and not actually from dynamic feedback
//
// usage data is only valid when at least one array exists to have feedback
if(curUsage >= lastUsage)
{
// if we exhausted the list, all other elements are unused
view.dynamicallyUsed = false;
}
else
{
// we never saw the current value of curUsage (which is odd, we should have
// when iterating over all descriptors. This could only happen if there's some
// layout mismatch or a feedback bug that lead to an invalid entry in the list).
// Keep advancing until we get to one that is >= our current bind
while(*curUsage < curIdentifier && curUsage < lastUsage)
{
curUsage++;
}
// the next used bind is equal to this one. Mark it as dynamically used, and consume
if(curUsage < lastUsage && *curUsage == curIdentifier)
{
view.dynamicallyUsed = true;
curUsage++;
}
// the next used bind is after the current one, this is not used.
else if(curUsage < lastUsage && curIdentifier < *curUsage)
{
view.dynamicallyUsed = false;
}
}
}
else
{
view.dynamicallyUsed = true;
}
if(view.dynamicallyUsed)
{
element.dynamicallyUsedCount++;
// we iterate in forward order, so we can unconditinoally set the last bind to the
// current one, and only set the first bind if we haven't encountered one before
element.lastUsedIndex = i;
if(element.firstUsedIndex < 0)
element.firstUsedIndex = i;
}
if(desc)
{
@@ -1340,6 +1404,13 @@ void D3D12Replay::SavePipelineState(uint32_t eventId)
{
const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
D3D12MarkerRegion::Begin(m_pDevice->GetQueue(),
StringFormat::Fmt("FetchShaderFeedback for %u", eventId));
FetchShaderFeedback(eventId);
D3D12MarkerRegion::End(m_pDevice->GetQueue());
D3D12Pipe::State &state = m_PipelineState;
/////////////////////////////////////////////////
@@ -1464,12 +1535,12 @@ void D3D12Replay::SavePipelineState(uint32_t eventId)
if(pipe && pipe->IsCompute())
{
FillRootElements(rs.compute, mappings, state.rootElements);
FillRootElements(eventId, rs.compute, mappings, state.rootElements);
state.rootSignatureResourceId = rm->GetOriginalID(rs.compute.rootsig);
}
else if(pipe)
{
FillRootElements(rs.graphics, mappings, state.rootElements);
FillRootElements(eventId, rs.graphics, mappings, state.rootElements);
state.rootSignatureResourceId = rm->GetOriginalID(rs.graphics.rootsig);
}
}
@@ -2991,6 +3062,7 @@ void D3D12Replay::ReplaceResource(ResourceId from, ResourceId to)
RefreshDerivedReplacements();
ClearPostVSCache();
ClearFeedbackCache();
}
void D3D12Replay::RemoveReplacement(ResourceId id)
@@ -3002,6 +3074,7 @@ void D3D12Replay::RemoveReplacement(ResourceId id)
RefreshDerivedReplacements();
ClearPostVSCache();
ClearFeedbackCache();
}
}
+38 -2
View File
@@ -50,6 +50,27 @@ enum TexDisplayFlags
eTexDisplay_RemapSInt = 0x40,
};
struct D3D12FeedbackBindIdentifier
{
size_t rootEl;
size_t rangeIndex;
UINT descIndex;
bool operator<(const D3D12FeedbackBindIdentifier &o) const
{
if(rootEl != o.rootEl)
return rootEl < o.rootEl;
if(rangeIndex != o.rangeIndex)
return rangeIndex < o.rangeIndex;
return descIndex < o.descIndex;
}
bool operator==(const D3D12FeedbackBindIdentifier &o) const
{
return rootEl == o.rootEl && rangeIndex == o.rangeIndex && descIndex == o.descIndex;
}
};
class D3D12Replay : public IReplayDriver
{
public:
@@ -214,14 +235,16 @@ public:
void FileChanged() {}
AMDCounters *GetAMDCounters() { return m_pAMDCounters; }
private:
void FillRootElements(const D3D12RenderState::RootSignature &rootSig,
void FillRootElements(uint32_t eventId, const D3D12RenderState::RootSignature &rootSig,
const ShaderBindpointMapping *mappings[(uint32_t)ShaderStage::Count],
rdcarray<D3D12Pipe::RootSignatureRange> &rootElements);
void FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor *desc);
bool CreateSOBuffers();
void ClearPostVSCache();
bool CreateSOBuffers();
void FetchShaderFeedback(uint32_t eventId);
void ClearFeedbackCache();
void RefreshDerivedReplacements();
@@ -238,6 +261,19 @@ private:
m_OutputHeight = (float)h;
}
struct D3D12DynamicShaderFeedback
{
bool compute = false, valid = false;
rdcarray<D3D12FeedbackBindIdentifier> used;
};
struct Feedback
{
ID3D12Resource *FeedbackBuffer = NULL;
std::unordered_map<uint32_t, D3D12DynamicShaderFeedback> Usage;
} m_BindlessFeedback;
struct D3D12PostVSData
{
struct InstData
@@ -0,0 +1,604 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "core/settings.h"
#include "driver/shaders/dxbc/dxbc_bytecode_editor.h"
#include "d3d12_command_queue.h"
#include "d3d12_debug.h"
#include "d3d12_device.h"
#include "d3d12_replay.h"
#include "d3d12_resources.h"
#include "d3d12_shader_cache.h"
RDOC_CONFIG(rdcstr, D3D12_Debug_FeedbackDumpDirPath, "",
"Path to dump bindless feedback annotation generated DXBC/DXIL files.");
RDOC_CONFIG(bool, D3D12_Experimental_BindlessFeedback, true,
"EXPERIMENTAL: Enable fetching from GPU which descriptors were dynamically used in "
"descriptor arrays.");
struct D3D12FeedbackKey
{
DXBCBytecode::OperandType type;
Bindpoint bind;
bool operator<(const D3D12FeedbackKey &key) const
{
if(type != key.type)
return type < key.type;
return bind < key.bind;
}
};
struct D3D12FeedbackSlot
{
public:
D3D12FeedbackSlot()
{
slot = 0;
used = 0;
}
void SetSlot(uint32_t s) { slot = s; }
void SetStaticUsed() { used = 0x1; }
bool StaticUsed() const { return used != 0x0; }
uint32_t Slot() const { return slot; }
private:
uint32_t slot : 31;
uint32_t used : 1;
};
static bool AnnotateShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
const std::map<D3D12FeedbackKey, D3D12FeedbackSlot> &slots,
bytebuf &editedBlob)
{
using namespace DXBCBytecode;
using namespace DXBCBytecode::Edit;
ProgramEditor editor(dxbc, editedBlob);
// get ourselves a temp
uint32_t t = editor.AddTemp();
// declare the output UAV
ResourceDecl desc;
desc.compType = CompType::UInt;
desc.type = TextureType::Buffer;
desc.raw = true;
ResourceIdentifier u = {~0U, ~0U};
for(size_t i = 0; i < editor.GetNumInstructions(); i++)
{
const Operation &op = editor.GetInstruction(i);
for(const Operand &operand : op.operands)
{
if(operand.type != TYPE_RESOURCE && operand.type != TYPE_UNORDERED_ACCESS_VIEW)
continue;
const Declaration *decl =
editor.FindDeclaration(operand.type, (uint32_t)operand.indices[0].index);
if(!decl)
{
RDCERR("Couldn't find declaration for %d operand identifier %u", operand.type,
(uint32_t)operand.indices[0].index);
continue;
}
// ignore non-arrayed declarations
if(decl->operand.indices[1].index == decl->operand.indices[2].index)
continue;
// the operand should be relative addressing like r0.x + 6 for a t6 resource being indexed
// with [r0.x]
RDCASSERT(operand.indices[1].relative &&
operand.indices[1].index == decl->operand.indices[1].index);
D3D12FeedbackKey key;
key.type = operand.type;
key.bind.bindset = decl->space;
key.bind.bind = (int32_t)decl->operand.indices[1].index;
auto it = slots.find(key);
if(it == slots.end())
{
RDCERR("Couldn't find reserved base slot for %d at space %u and bind %u", key.type,
key.bind.bindset, key.bind.bind);
continue;
}
// should be getting a scalar index
if(operand.indices[1].operand.comps[1] != 0xff ||
operand.indices[1].operand.comps[2] != 0xff || operand.indices[1].operand.comps[3] != 0xff)
{
RDCERR("Unexpected vector index for resource: %s",
operand.toString(dxbc->GetReflection(), ToString::None).c_str());
continue;
}
if(u.first == ~0U && u.second == ~0U)
u = editor.DeclareUAV(desc, space, 0, 0);
// resource base plus index
editor.InsertOperation(i++, oper(OPCODE_IADD, {temp(t).swizzle(0), imm(it->second.Slot()),
operand.indices[1].operand}));
// multiply by 4 for byte index
editor.InsertOperation(i++,
oper(OPCODE_ISHL, {temp(t).swizzle(0), temp(t).swizzle(0), imm(2)}));
// atomic or the slot
editor.InsertOperation(i++, oper(OPCODE_ATOMIC_OR, {uav(u), temp(t).swizzle(0), imm(~0U)}));
// only one resource operand per instruction
break;
}
}
if(u.first != ~0U || u.second != ~0U)
{
editor.InsertOperation(0, oper(OPCODE_MOV, {temp(t).swizzle(0), imm(0)}));
editor.InsertOperation(1, oper(OPCODE_ATOMIC_OR, {uav(u), temp(t).swizzle(0), imm(~0U)}));
return true;
}
return false;
}
static void AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_t space,
uint32_t maxDescriptors,
std::map<D3D12FeedbackKey, D3D12FeedbackSlot> &slots, uint32_t &numSlots,
bytebuf &editedBlob, D3D12_SHADER_BYTECODE &desc)
{
if(!shad)
return;
ShaderReflection &refl = shad->GetDetails();
const ShaderBindpointMapping &mapping = shad->GetMapping();
for(const ShaderResource &ro : refl.readOnlyResources)
{
const Bindpoint &bind = mapping.readOnlyResources[ro.bindPoint];
if(bind.arraySize > 1)
{
D3D12FeedbackKey key;
key.type = DXBCBytecode::TYPE_RESOURCE;
key.bind = bind;
slots[key].SetSlot(numSlots);
numSlots += RDCMIN(maxDescriptors, bind.arraySize);
}
else if(bind.arraySize <= 1 && bind.used)
{
// since the eventual descriptor range iteration won't know which descriptors map to arrays
// and which to fixed slots, it can't mark fixed descriptors as dynamically used itself. So
// instead we don't reserve a slot and set the top bit for these binds to indicate that
// they're fixed used. This allows for overlap between an array and a fixed resource which is
// allowed
D3D12FeedbackKey key;
key.type = DXBCBytecode::TYPE_RESOURCE;
key.bind = bind;
slots[key].SetStaticUsed();
}
}
for(const ShaderResource &rw : refl.readWriteResources)
{
const Bindpoint &bind = mapping.readWriteResources[rw.bindPoint];
if(bind.arraySize > 1)
{
D3D12FeedbackKey key;
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
key.bind = bind;
slots[key].SetSlot(numSlots);
numSlots += RDCMIN(maxDescriptors, bind.arraySize);
}
else if(bind.arraySize <= 1 && bind.used)
{
// since the eventual descriptor range iteration won't know which descriptors map to arrays
// and which to fixed slots, it can't mark fixed descriptors as dynamically used itself. So
// instead we don't reserve a slot and set the top bit for these binds to indicate that
// they're fixed used. This allows for overlap between an array and a fixed resource which is
// allowed
D3D12FeedbackKey key;
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
key.bind = bind;
slots[key].SetStaticUsed();
}
}
if(shad->GetDXBC()->m_Version.Major > 6)
{
RDCERR("DXIL shaders are not supported for bindless feedback currently");
}
else
{
// only SM5.1 can have dynamic array indexing
if(shad->GetDXBC()->m_Version.Major == 5 && shad->GetDXBC()->m_Version.Minor == 1)
{
if(AnnotateShader(shad->GetDXBC(), space, slots, editedBlob))
{
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/before_dxbc_" +
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
shad->GetDXBC()->GetShaderBlob());
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/after_dxbc_" +
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
editedBlob);
desc.pShaderBytecode = editedBlob.data();
desc.BytecodeLength = editedBlob.size();
}
}
}
}
void D3D12Replay::FetchShaderFeedback(uint32_t eventId)
{
if(m_BindlessFeedback.Usage.find(eventId) != m_BindlessFeedback.Usage.end())
return;
if(!D3D12_Experimental_BindlessFeedback())
return;
// create it here so we won't re-run any code if the event is re-selected. We'll mark it as valid
// if it actually has any data in it later.
D3D12DynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
const ActionDescription *action = m_pDevice->GetAction(eventId);
if(action == NULL || !(action->flags & (ActionFlags::Dispatch | ActionFlags::Drawcall)))
return;
result.compute = bool(action->flags & ActionFlags::Dispatch);
D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
D3D12ResourceManager *rm = m_pDevice->GetResourceManager();
WrappedID3D12PipelineState *pipe =
(WrappedID3D12PipelineState *)rm->GetCurrentAs<ID3D12PipelineState>(rs.pipe);
D3D12RootSignature modsig;
bytebuf editedBlob[5];
D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc;
pipe->Fill(pipeDesc);
uint32_t space = 1;
uint32_t maxDescriptors = 0;
for(ResourceId id : rs.heaps)
{
D3D12_DESCRIPTOR_HEAP_DESC desc = rm->GetCurrentAs<ID3D12DescriptorHeap>(id)->GetDesc();
if(desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
{
maxDescriptors = desc.NumDescriptors;
RDCDEBUG("Clamping any unbounded ranges to %u descriptors", maxDescriptors);
break;
}
}
std::map<D3D12FeedbackKey, D3D12FeedbackSlot> slots[6];
// reserve the first 4 dwords for debug info and a validity flag
uint32_t numSlots = 4;
if(result.compute)
{
ID3D12RootSignature *sig = rm->GetCurrentAs<ID3D12RootSignature>(rs.compute.rootsig);
modsig = ((WrappedID3D12RootSignature *)sig)->sig;
space = modsig.maxSpaceIndex;
AddArraySlots(pipe->CS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.CS);
}
else
{
ID3D12RootSignature *sig = rm->GetCurrentAs<ID3D12RootSignature>(rs.graphics.rootsig);
modsig = ((WrappedID3D12RootSignature *)sig)->sig;
space = modsig.maxSpaceIndex;
AddArraySlots(pipe->VS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.VS);
AddArraySlots(pipe->HS(), space, maxDescriptors, slots[1], numSlots, editedBlob[1], pipeDesc.HS);
AddArraySlots(pipe->DS(), space, maxDescriptors, slots[2], numSlots, editedBlob[2], pipeDesc.DS);
AddArraySlots(pipe->GS(), space, maxDescriptors, slots[3], numSlots, editedBlob[3], pipeDesc.GS);
AddArraySlots(pipe->PS(), space, maxDescriptors, slots[4], numSlots, editedBlob[4], pipeDesc.PS);
}
// if numSlots was 0, none of the resources were arrayed so we have nothing to do. Silently return
if(numSlots == 0)
return;
// need to be able to add a descriptor of our UAV without hitting the 64 DWORD limit
if(modsig.dwordLength > 62)
{
RDCWARN("Root signature is 64 DWORDS, adding feedback buffer might fail");
}
// add root UAV element
modsig.Parameters.push_back(D3D12RootSignatureParameter());
{
D3D12RootSignatureParameter &param = modsig.Parameters.back();
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE;
param.Descriptor.RegisterSpace = space;
param.Descriptor.ShaderRegister = 0;
}
if(m_BindlessFeedback.FeedbackBuffer == NULL ||
m_BindlessFeedback.FeedbackBuffer->GetDesc().Width < numSlots * sizeof(uint32_t))
{
SAFE_RELEASE(m_BindlessFeedback.FeedbackBuffer);
D3D12_RESOURCE_DESC desc = {};
desc.Alignment = 0;
desc.DepthOrArraySize = 1;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Height = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Width = numSlots * sizeof(uint32_t);
D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
HRESULT hr = m_pDevice->CreateCommittedResource(
&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL,
__uuidof(ID3D12Resource), (void **)&m_BindlessFeedback.FeedbackBuffer);
if(m_BindlessFeedback.FeedbackBuffer == NULL || FAILED(hr))
{
RDCERR("Couldn't create feedback buffer with %u slots: %s", numSlots, ToStr(hr).c_str());
return;
}
m_BindlessFeedback.FeedbackBuffer->SetName(L"m_BindlessFeedback.FeedbackBuffer");
}
{
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
uavDesc.Format = DXGI_FORMAT_R32_UINT;
uavDesc.Buffer.CounterOffsetInBytes = 0;
// start with elements after the counter
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = numSlots;
uavDesc.Buffer.StructureByteStride = 0;
m_pDevice->CreateUnorderedAccessView(m_BindlessFeedback.FeedbackBuffer, NULL, &uavDesc,
GetDebugManager()->GetCPUHandle(FEEDBACK_CLEAR_UAV));
m_pDevice->CreateUnorderedAccessView(m_BindlessFeedback.FeedbackBuffer, NULL, &uavDesc,
GetDebugManager()->GetUAVClearHandle(FEEDBACK_CLEAR_UAV));
ID3D12GraphicsCommandList *list = m_pDevice->GetNewList();
UINT zeroes[4] = {0, 0, 0, 0};
list->ClearUnorderedAccessViewUint(GetDebugManager()->GetGPUHandle(FEEDBACK_CLEAR_UAV),
GetDebugManager()->GetUAVClearHandle(FEEDBACK_CLEAR_UAV),
m_BindlessFeedback.FeedbackBuffer, zeroes, 0, NULL);
list->Close();
}
ID3D12RootSignature *annotatedSig = NULL;
{
ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig);
HRESULT hr =
m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(),
__uuidof(ID3D12RootSignature), (void **)&annotatedSig);
if(annotatedSig == NULL || FAILED(hr))
{
RDCERR("Couldn't create feedback modified root signature: %s", ToStr(hr).c_str());
return;
}
}
ID3D12PipelineState *annotatedPipe = NULL;
{
pipeDesc.pRootSignature = annotatedSig;
HRESULT hr = m_pDevice->CreatePipeState(pipeDesc, &annotatedPipe);
if(annotatedPipe == NULL || FAILED(hr))
{
SAFE_RELEASE(annotatedSig);
RDCERR("Couldn't create feedback modified pipeline: %s", ToStr(hr).c_str());
return;
}
}
D3D12RenderState prev = rs;
rs.pipe = GetResID(annotatedPipe);
if(result.compute)
{
rs.compute.rootsig = GetResID(annotatedSig);
size_t idx = modsig.Parameters.size() - 1;
rs.compute.sigelems.resize_for_index(idx);
rs.compute.sigelems[idx] =
D3D12RenderState::SignatureElement(eRootUAV, GetResID(m_BindlessFeedback.FeedbackBuffer), 0);
}
else
{
rs.graphics.rootsig = GetResID(annotatedSig);
size_t idx = modsig.Parameters.size() - 1;
rs.graphics.sigelems.resize_for_index(idx);
rs.graphics.sigelems[idx] =
D3D12RenderState::SignatureElement(eRootUAV, GetResID(m_BindlessFeedback.FeedbackBuffer), 0);
}
m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw);
m_pDevice->ExecuteLists();
m_pDevice->FlushLists();
SAFE_RELEASE(annotatedPipe);
SAFE_RELEASE(annotatedSig);
rs = prev;
bytebuf results;
GetDebugManager()->GetBufferData(m_BindlessFeedback.FeedbackBuffer, 0, 0, results);
if(results.size() < numSlots * sizeof(uint32_t))
{
RDCERR("Results buffer not the right size!");
}
else
{
uint32_t *slotsData = (uint32_t *)results.data();
result.valid = true;
// now we iterate over descriptor ranges and find which (of potentially multiple) registers each
// descriptor maps to and store the index if it's dynamically or statically used. We do this
// here so it only happens once instead of doing it when looking up the data.
D3D12FeedbackKey curKey;
D3D12FeedbackBindIdentifier curIdentifier = {};
// don't iterate the last signature element because that's ours!
for(size_t rootEl = 0; rootEl < modsig.Parameters.size() - 1; rootEl++)
{
curIdentifier.rootEl = rootEl;
const D3D12RootSignatureParameter &p = modsig.Parameters[rootEl];
// only tables need feedback data, others all are treated as dynamically used
if(p.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
{
for(size_t r = 0; r < p.ranges.size(); r++)
{
const D3D12_DESCRIPTOR_RANGE1 &range = p.ranges[r];
curIdentifier.rangeIndex = r;
curKey.bind.bindset = range.RegisterSpace;
curKey.bind.bind = range.BaseShaderRegister;
UINT num = range.NumDescriptors;
uint32_t visMask = 0;
// see which shader's binds we should look up for this range
switch(p.ShaderVisibility)
{
case D3D12_SHADER_VISIBILITY_ALL: visMask = result.compute ? 0x1 : 0xff; break;
case D3D12_SHADER_VISIBILITY_VERTEX: visMask = 1 << 0; break;
case D3D12_SHADER_VISIBILITY_HULL: visMask = 1 << 1; break;
case D3D12_SHADER_VISIBILITY_DOMAIN: visMask = 1 << 2; break;
case D3D12_SHADER_VISIBILITY_GEOMETRY: visMask = 1 << 3; break;
case D3D12_SHADER_VISIBILITY_PIXEL: visMask = 1 << 4; break;
default: RDCERR("Unexpected shader visibility %d", p.ShaderVisibility); return;
}
// set the key type
if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
curKey.type = DXBCBytecode::TYPE_RESOURCE;
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
curKey.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
for(uint32_t st = 0; st < 5; st++)
{
if(visMask & (1 << st))
{
// the feedback entries start here
auto slotIt = slots[st].lower_bound(curKey);
curIdentifier.descIndex = 0;
// iterate over the declared range. This could be unbounded, so we might exit
// another way
for(uint32_t i = 0; i < num; i++)
{
// stop when we've run out of recorded used slots
if(slotIt == slots[st].end())
break;
Bindpoint bind = slotIt->first.bind;
// stop if the next used slot is in another space or is another type
if(bind.bindset > curKey.bind.bindset || slotIt->first.type != curKey.type)
break;
// if the next bind is definitely outside this range, early out now instead of
// iterating fruitlessly
if((uint32_t)bind.bind > range.BaseShaderRegister + num)
break;
int32_t lastBind = bind.bind + (int32_t)RDCCLAMP(bind.arraySize, 1U, maxDescriptors);
// if this slot's array covers the current bind, check the result
if(bind.bind <= curKey.bind.bind && curKey.bind.bind < lastBind)
{
// if it's static used by having a fixed result declared, it's used
const bool staticUsed = slotIt->second.StaticUsed();
// otherwise check the feedback we got
const uint32_t baseSlot = slotIt->second.Slot();
const uint32_t arrayIndex = curKey.bind.bind - bind.bind;
if(staticUsed || slotsData[baseSlot + arrayIndex])
result.used.push_back(curIdentifier);
}
curKey.bind.bind++;
curIdentifier.descIndex++;
// if we've passed this slot, move to the next one. Because we're iterating a
// contiguous range of binds the next slot will be enough for the next iteration
if(curKey.bind.bind >= lastBind)
slotIt++;
}
}
}
}
}
}
}
// replay from the start as we may have corrupted state while fetching the above feedback.
m_pDevice->ReplayLog(0, eventId, eReplay_Full);
}
void D3D12Replay::ClearFeedbackCache()
{
m_BindlessFeedback.Usage.clear();
}
@@ -140,6 +140,7 @@
<ClCompile Include="d3d12_serialise.cpp" />
<ClCompile Include="d3d12_shaderdebug.cpp" />
<ClCompile Include="d3d12_shader_cache.cpp" />
<ClCompile Include="d3d12_shader_feedback.cpp" />
<ClCompile Include="d3d12_state.cpp" />
<ClCompile Include="d3d12_stringise.cpp" />
<ClCompile Include="precompiled.cpp">
@@ -75,7 +75,9 @@
<ClInclude Include="..\dx\official\D3D11On12On7.h">
<Filter>official</Filter>
</ClInclude>
<ClInclude Include="d3d12_hooks.h" />
<ClInclude Include="d3d12_hooks.h">
<Filter>Hooks</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="d3d12_common.cpp">
@@ -204,5 +206,8 @@
<ClCompile Include="d3d12_sdk_select.cpp">
<Filter>Util</Filter>
</ClCompile>
<ClCompile Include="d3d12_shader_feedback.cpp">
<Filter>Replay</Filter>
</ClCompile>
</ItemGroup>
</Project>
@@ -128,13 +128,13 @@ Operation oper(OpcodeType o, const rdcarray<Operand> &operands);
struct ResourceDecl
{
TextureType type;
CompType compType;
uint32_t sampleCount;
TextureType type = TextureType::Buffer;
CompType compType = CompType::Float;
uint32_t sampleCount = 0;
bool structured = false;
uint32_t stride;
bool hasCounter, globallyCoherant, rov;
uint32_t stride = 0;
bool hasCounter = false, globallyCoherant = false, rov = false;
bool raw = false;
};
+1 -1
View File
@@ -1623,7 +1623,7 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
&state.graphics.descSets, &state.compute.descSets,
};
const DynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
const VKDynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
m_VulkanPipelineState.shaderMessages = usage.messages;
+2 -2
View File
@@ -209,7 +209,7 @@ struct VulkanPostVSData
}
};
struct DynamicShaderFeedback
struct VKDynamicShaderFeedback
{
bool compute = false, valid = false;
rdcarray<BindpointIndex> used;
@@ -744,7 +744,7 @@ private:
GPUBuffer FeedbackBuffer;
std::map<uint32_t, DynamicShaderFeedback> Usage;
std::map<uint32_t, VKDynamicShaderFeedback> Usage;
} m_BindlessFeedback;
ShaderDebugData m_ShaderDebugData;
@@ -1310,7 +1310,7 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId)
// create it here so we won't re-run any code if the event is re-selected. We'll mark it as valid
// if it actually has any data in it later.
DynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
VKDynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
bool useBufferAddress = (m_pDriver->GetExtensions(NULL).ext_KHR_buffer_device_address ||
m_pDriver->GetExtensions(NULL).ext_EXT_buffer_device_address) &&
+16 -12
View File
@@ -441,7 +441,7 @@ void DoSerialise(SerialiserType &ser, TextureSwizzle4 &el)
SERIALISE_MEMBER(blue);
SERIALISE_MEMBER(alpha);
SIZE_CHECK(16);
SIZE_CHECK(4);
}
template <typename SerialiserType>
@@ -1374,11 +1374,14 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::RootSignatureRange &el)
SERIALISE_MEMBER(type);
SERIALISE_MEMBER(visibility);
SERIALISE_MEMBER(registerSpace);
SERIALISE_MEMBER(dynamicallyUsedCount);
SERIALISE_MEMBER(firstUsedIndex);
SERIALISE_MEMBER(lastUsedIndex);
SERIALISE_MEMBER(constantBuffers);
SERIALISE_MEMBER(samplers);
SERIALISE_MEMBER(views);
SIZE_CHECK(96);
SIZE_CHECK(104);
}
template <typename SerialiserType>
@@ -1391,6 +1394,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::View &el)
SERIALISE_MEMBER(viewFormat);
SERIALISE_MEMBER(swizzle);
SERIALISE_MEMBER(dynamicallyUsed);
SERIALISE_MEMBER(bufferFlags);
SERIALISE_MEMBER(bufferStructCount);
SERIALISE_MEMBER(elementByteSize);
@@ -1407,7 +1411,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::View &el)
SERIALISE_MEMBER(minLODClamp);
SIZE_CHECK(112);
SIZE_CHECK(96);
}
template <typename SerialiserType>
@@ -1547,7 +1551,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::OM &el)
SERIALISE_MEMBER(multiSampleCount);
SERIALISE_MEMBER(multiSampleQuality);
SIZE_CHECK(280);
SIZE_CHECK(264);
}
template <typename SerialiserType>
@@ -1591,7 +1595,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el)
SERIALISE_MEMBER(resourceStates);
SIZE_CHECK(1432);
SIZE_CHECK(1416);
}
#pragma endregion D3D12 pipeline state
@@ -1678,7 +1682,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::Texture &el)
SERIALISE_MEMBER(depthReadChannel);
SERIALISE_MEMBER(completeStatus);
SIZE_CHECK(64);
SIZE_CHECK(56);
}
template <typename SerialiserType>
@@ -1811,7 +1815,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::Attachment &el)
SERIALISE_MEMBER(mipLevel);
SERIALISE_MEMBER(swizzle);
SIZE_CHECK(40);
SIZE_CHECK(24);
}
template <typename SerialiserType>
@@ -1824,7 +1828,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::FBO &el)
SERIALISE_MEMBER(drawBuffers);
SERIALISE_MEMBER(readBuffer);
SIZE_CHECK(144);
SIZE_CHECK(112);
}
template <typename SerialiserType>
@@ -1845,7 +1849,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::FrameBuffer &el)
SERIALISE_MEMBER(readFBO);
SERIALISE_MEMBER(blendState);
SIZE_CHECK(336);
SIZE_CHECK(272);
}
template <typename SerialiserType>
@@ -1894,7 +1898,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::State &el)
SERIALISE_MEMBER(hints);
SIZE_CHECK(2024);
SIZE_CHECK(1960);
}
#pragma endregion OpenGL pipeline state
@@ -1942,7 +1946,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::BindingElement &el)
SERIALISE_MEMBER(chromaFilter);
SERIALISE_MEMBER(forceExplicitReconstruction);
SIZE_CHECK(200);
SIZE_CHECK(184);
};
template <typename SerialiserType>
@@ -2223,7 +2227,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::Attachment &el)
SERIALISE_MEMBER(numMips);
SERIALISE_MEMBER(numSlices);
SIZE_CHECK(56);
SIZE_CHECK(48);
}
template <typename SerialiserType>