Files
renderdoc/renderdoc/driver/d3d11/d3d11_context.cpp
T
baldurk 0b187934de Track bytes written for hidden SO counters. Closes #2662
* The D3D11 spec is unclear but checking sources including D3D11On12 it looks
  like the defined behaviour for SO counters is to calculate the number of bytes
  written and divide that by the VB stride on draw.
* Old captures can't be updated to work with this because the stride is unknown,
  but new captures will work correctly as well as any data that is stream'd out
  mid-capture.
2022-07-22 16:10:59 +01:00

1927 lines
58 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2022 Baldur Karlsson
* Copyright (c) 2014 Crytek
*
* 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 "d3d11_context.h"
#include <algorithm>
#include "strings/string_utils.h"
#include "d3d11_device.h"
#include "d3d11_manager.h"
#include "d3d11_renderstate.h"
#include "d3d11_replay.h"
#include "d3d11_resources.h"
WRAPPED_POOL_INST(WrappedID3D11DeviceContext);
WRAPPED_POOL_INST(WrappedID3D11CommandList);
INT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::BeginEvent(LPCWSTR Name)
{
return m_Context->PushMarker(0, Name);
}
INT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::EndEvent()
{
return m_Context->PopMarker();
}
void STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::SetMarker(LPCWSTR Name)
{
return m_Context->SetMarker(0, Name);
}
ULONG STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::AddRef()
{
return m_Context->AddRef();
}
ULONG STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::Release()
{
return m_Context->Release();
}
HRESULT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::QueryInterface(REFIID riid,
void **ppvObject)
{
return m_Context->QueryInterface(riid, ppvObject);
}
extern uint32_t NullCBOffsets[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
extern uint32_t NullCBCounts[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *realDevice,
ID3D11DeviceContext *context)
: m_pDevice(realDevice),
m_pRealContext(context),
m_ScratchSerialiser(new StreamWriter(1024), Ownership::Stream)
{
RenderDoc::Inst().RegisterMemoryRegion(this, sizeof(WrappedID3D11DeviceContext));
for(int i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++)
{
NullCBOffsets[i] = 0;
NullCBCounts[i] = 4096;
}
D3D11_FEATURE_DATA_D3D11_OPTIONS features;
RDCEraseEl(features);
HRESULT hr = S_OK;
if(m_pRealContext)
{
hr = m_pDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &features, sizeof(features));
m_Type = m_pRealContext->GetType();
}
else
{
m_Type = D3D11_DEVICE_CONTEXT_IMMEDIATE;
}
m_SetCBuffer1 = false;
if(SUCCEEDED(hr))
m_SetCBuffer1 = features.ConstantBufferOffsetting == TRUE;
m_pRealContext1 = NULL;
m_pRealContext2 = NULL;
m_pRealContext3 = NULL;
m_pRealContext4 = NULL;
if(m_pRealContext)
{
m_pRealContext->QueryInterface(__uuidof(ID3D11DeviceContext1), (void **)&m_pRealContext1);
m_pRealContext->QueryInterface(__uuidof(ID3D11DeviceContext2), (void **)&m_pRealContext2);
m_pRealContext->QueryInterface(__uuidof(ID3D11DeviceContext3), (void **)&m_pRealContext3);
m_pRealContext->QueryInterface(__uuidof(ID3D11DeviceContext4), (void **)&m_pRealContext4);
}
m_WrappedVideo.m_pContext = this;
if(m_pRealContext)
{
m_pRealContext->QueryInterface(__uuidof(ID3D11VideoContext), (void **)&m_WrappedVideo.m_pReal);
m_pRealContext->QueryInterface(__uuidof(ID3D11VideoContext1), (void **)&m_WrappedVideo.m_pReal1);
m_pRealContext->QueryInterface(__uuidof(ID3D11VideoContext2), (void **)&m_WrappedVideo.m_pReal2);
m_pRealContext->QueryInterface(__uuidof(ID3D11VideoContext3), (void **)&m_WrappedVideo.m_pReal3);
}
m_UserAnnotation.SetContext(this);
m_NeedUpdateSubWorkaround = false;
if(m_pRealContext)
{
D3D11_FEATURE_DATA_THREADING caps = {FALSE, FALSE};
hr = m_pDevice->CheckFeatureSupport(D3D11_FEATURE_THREADING, &caps, sizeof(caps));
if(SUCCEEDED(hr) && !caps.DriverCommandLists)
m_NeedUpdateSubWorkaround = true;
}
if(RenderDoc::Inst().IsReplayApp())
{
m_State = CaptureState::LoadingReplaying;
}
else
{
m_State = CaptureState::BackgroundCapturing;
}
// create a temporary and grab its resource ID
m_ResourceID = ResourceIDGen::GetNewUniqueID();
m_ContextRecord = NULL;
if(!RenderDoc::Inst().IsReplayApp())
{
m_ContextRecord = m_pDevice->GetResourceManager()->AddResourceRecord(m_ResourceID);
m_ContextRecord->DataInSerialiser = false;
m_ContextRecord->InternalResource = true;
m_ContextRecord->Length = 0;
m_ContextRecord->NumSubResources = 0;
m_ContextRecord->SubResources = NULL;
}
m_ScratchSerialiser.SetUserData(GetResourceManager());
m_ScratchSerialiser.SetVersion(D3D11InitParams::CurrentVersion);
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
m_EmptyCommandList = true;
m_ActionStack.push_back(&m_ParentAction);
m_CurEventID = 0;
m_CurActionID = 1;
m_MarkerIndentLevel = 0;
m_CurrentPipelineState = new D3D11RenderState(D3D11RenderState::Empty);
m_DeferredSavedState = NULL;
m_DoStateVerify = IsCaptureMode(m_State);
// take a reference on the device, as with all ID3D11DeviceChild objects.
m_pDevice->AddRef();
// start with 1 ext ref and 0 int refs
m_ExtRef = 1;
m_IntRef = 0;
if(!context || GetType() == D3D11_DEVICE_CONTEXT_IMMEDIATE)
{
m_CurrentPipelineState->SetImmediatePipeline(m_pDevice);
m_MarkedActive = false;
}
else
{
m_CurrentPipelineState->SetDevice(m_pDevice);
// we haven't actually marked active, but this makes the check much easier - just look at this
// bool flag rather than "if immediate and not flagged"
m_MarkedActive = true;
if(IsCaptureMode(m_State) && RenderDoc::Inst().GetCaptureOptions().captureAllCmdLists)
m_State = CaptureState::ActiveCapturing;
}
ReplayFakeContext(ResourceId());
}
WrappedID3D11DeviceContext::~WrappedID3D11DeviceContext()
{
if(m_ContextRecord)
m_ContextRecord->Delete(m_pDevice->GetResourceManager());
if(m_pRealContext && GetType() != D3D11_DEVICE_CONTEXT_IMMEDIATE)
m_pDevice->RemoveDeferredContext(this);
SAFE_DELETE(m_FrameReader);
SAFE_RELEASE(m_WrappedVideo.m_pReal);
SAFE_RELEASE(m_WrappedVideo.m_pReal1);
SAFE_RELEASE(m_WrappedVideo.m_pReal2);
SAFE_RELEASE(m_WrappedVideo.m_pReal3);
SAFE_RELEASE(m_pRealContext1);
SAFE_RELEASE(m_pRealContext2);
SAFE_RELEASE(m_pRealContext3);
SAFE_RELEASE(m_pRealContext4);
SAFE_DELETE(m_DeferredSavedState);
SAFE_DELETE(m_CurrentPipelineState);
SAFE_RELEASE(m_pRealContext);
m_pDevice = NULL;
RenderDoc::Inst().UnregisterMemoryRegion(this);
}
void WrappedID3D11DeviceContext::GetDevice(ID3D11Device **ppDevice)
{
if(ppDevice)
{
*ppDevice = (ID3D11Device *)m_pDevice;
(*ppDevice)->AddRef();
}
}
D3D11ResourceManager *WrappedID3D11DeviceContext::GetResourceManager()
{
return m_pDevice->GetResourceManager();
}
rdcstr WrappedID3D11DeviceContext::GetChunkName(uint32_t idx)
{
if((SystemChunk)idx < SystemChunk::FirstDriverChunk)
return ToStr((SystemChunk)idx);
return ToStr((D3D11Chunk)idx);
}
struct HiddenCounter
{
ResourceId id;
uint64_t counterValue;
uint32_t stride;
};
DECLARE_REFLECTION_STRUCT(HiddenCounter);
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, HiddenCounter &el)
{
SERIALISE_MEMBER(id);
SERIALISE_MEMBER(counterValue);
if(ser.VersionAtLeast(0x13))
{
SERIALISE_MEMBER(stride);
}
else
{
el.stride = 0;
}
}
template <typename SerialiserType>
bool WrappedID3D11DeviceContext::Serialise_BeginCaptureFrame(SerialiserType &ser)
{
D3D11RenderState state(D3D11RenderState::Empty);
if(ser.IsWriting())
{
state.CopyState(*m_CurrentPipelineState);
state.MarkReferenced(this, true);
}
SERIALISE_ELEMENT(state);
// stream-out hidden counters need to be saved, in case their results are used
// for a DrawAuto() somewhere. Each buffer used as a stream-out target has a hidden
// counter saved with it that stores the number of primitives written, which is then
// used for a DrawAuto(). If the stream-out happens in frame we don't need to worry,
// but if it references a buffer from before we need to have that counter available
// on replay to 'fake' the DrawAuto() just as a Draw() with known values
rdcarray<HiddenCounter> HiddenStreamOutCounters;
if(ser.IsWriting())
{
// this may break API guarantees, but we need to fetch the hidden counters
// so we need to restart any queries for currently set SO targets.
// Potentially to be more correct we could defer fetching the results of queries
// that are still running until they get detached (as they must be detached
// before being used for any DrawAuto calls - if we're in CAPFRAME we could
// serialise the data then. If they're never detached, we don't need the results)
bool restart[4] = {false};
for(UINT b = 0; b < 4; b++)
{
ID3D11Buffer *buf = m_CurrentPipelineState->SO.Buffers[b];
if(buf)
{
StreamOutData &so = m_pDevice->GetSOHiddenCounterForBuffer(GetIDForDeviceChild(buf));
if(so.running)
{
m_pRealContext->End(so.query);
so.running = false;
}
restart[b] = true;
}
}
D3D11_QUERY_DATA_SO_STATISTICS numPrims;
// readback all known counters
for(auto it = m_pDevice->GetSOHiddenCounters().begin();
it != m_pDevice->GetSOHiddenCounters().end(); ++it)
{
RDCEraseEl(numPrims);
HRESULT hr = S_FALSE;
do
{
hr = m_pRealContext->GetData(it->second.query, &numPrims,
sizeof(D3D11_QUERY_DATA_SO_STATISTICS), 0);
} while(hr == S_FALSE);
if(hr != S_OK)
{
numPrims.NumPrimitivesWritten = 0;
RDCERR("Couldn't retrieve hidden buffer counter for streamout on buffer %s",
ToStr(it->first).c_str());
}
HiddenCounter h;
h.id = it->first;
h.counterValue = (uint64_t)numPrims.NumPrimitivesWritten;
h.stride = it->second.stride;
HiddenStreamOutCounters.push_back(h);
}
// restart any counters we were forced to stop
for(UINT b = 0; b < 4; b++)
{
ID3D11Buffer *buf = m_CurrentPipelineState->SO.Buffers[b];
if(buf && restart[b])
{
StreamOutData &so = m_pDevice->GetSOHiddenCounterForBuffer(GetIDForDeviceChild(buf));
// release any previous query as the hidden counter is overwritten
SAFE_RELEASE(so.query);
D3D11_QUERY queryTypes[] = {
D3D11_QUERY_SO_STATISTICS_STREAM0, D3D11_QUERY_SO_STATISTICS_STREAM1,
D3D11_QUERY_SO_STATISTICS_STREAM2, D3D11_QUERY_SO_STATISTICS_STREAM3,
};
D3D11_QUERY_DESC qdesc;
qdesc.MiscFlags = 0;
qdesc.Query = queryTypes[b];
m_pDevice->GetReal()->CreateQuery(&qdesc, &so.query);
m_pRealContext->Begin(so.query);
so.running = true;
// stride doesn't change as the shader hasn't changed
}
}
}
SERIALISE_ELEMENT(HiddenStreamOutCounters);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
{
m_DoStateVerify = false;
{
m_CurrentPipelineState->CopyState(state);
m_CurrentPipelineState->SetDevice(m_pDevice);
state.ApplyState(this);
}
m_DoStateVerify = true;
VerifyState();
}
// read in the known stream-out counters at the start of the frame.
// any stream-out that happens in the captured frame will be replayed
// and those counters will override this value when it comes to a
// DrawAuto()
for(const HiddenCounter &c : HiddenStreamOutCounters)
{
if(m_pDevice->GetResourceManager()->HasLiveResource(c.id))
{
StreamOutData &so =
m_pDevice->GetSOHiddenCounterForBuffer(m_pDevice->GetResourceManager()->GetLiveID(c.id));
so.numPrims = c.counterValue;
so.stride = c.stride;
}
}
}
return true;
}
void WrappedID3D11DeviceContext::MarkResourceReferenced(ResourceId id, FrameRefType refType)
{
if(GetType() == D3D11_DEVICE_CONTEXT_IMMEDIATE)
{
m_pDevice->GetResourceManager()->MarkResourceFrameReferenced(id, refType);
}
else
{
bool newRef = m_ContextRecord->MarkResourceFrameReferenced(id, refType);
// we need to keep this resource alive so that we can insert its record on capture
// if this command list gets executed.
if(newRef)
{
D3D11ResourceRecord *record = m_pDevice->GetResourceManager()->GetResourceRecord(id);
if(record)
{
record->AddRef();
m_DeferredReferences.insert(id);
}
}
}
}
void WrappedID3D11DeviceContext::MarkDirtyResource(ResourceId id)
{
if(GetType() == D3D11_DEVICE_CONTEXT_IMMEDIATE)
{
m_pDevice->GetResourceManager()->MarkDirtyResource(id);
}
else
{
m_DeferredDirty.insert(id);
}
}
void WrappedID3D11DeviceContext::VerifyState()
{
#if 0
if(m_DoStateVerify)
{
D3D11RenderState state(this);
}
#endif
}
void WrappedID3D11DeviceContext::BeginCaptureFrame()
{
WriteSerialiser &ser = m_ScratchSerialiser;
SCOPED_SERIALISE_CHUNK(SystemChunk::CaptureBegin);
Serialise_BeginCaptureFrame(ser);
{
SCOPED_LOCK(m_AnnotLock);
m_AnnotationQueue.clear();
}
m_ContextRecord->AddChunk(scope.Get(), 1);
}
void WrappedID3D11DeviceContext::AttemptCapture()
{
m_State = CaptureState::ActiveCapturing;
m_FailureReason = CaptureSucceeded;
// deferred contexts are initially NOT successful unless empty. That's because we don't have the
// serialised
// contents of whatever is in them up until now (could be anything).
// Only after they have been through a Finish() and then in CAPFRAME mode are they considered
// successful.
if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED)
{
RDCDEBUG("Deferred Context %s Attempting capture - initially %s, %s",
ToStr(GetResourceID()).c_str(), m_SuccessfulCapture ? "successful" : "unsuccessful",
m_EmptyCommandList ? "empty" : "non-empty");
m_SuccessfulCapture |= m_EmptyCommandList;
if(m_SuccessfulCapture)
m_FailureReason = CaptureSucceeded;
else
m_FailureReason = CaptureFailed_UncappedCmdlist;
RDCDEBUG("Deferred Context %s Attempting capture - now %s", ToStr(GetResourceID()).c_str(),
m_SuccessfulCapture ? "successful" : "unsuccessful");
}
else
{
RDCDEBUG("Immediate Context %s Attempting capture", ToStr(GetResourceID()).c_str());
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
m_ContextRecord->LockChunks();
while(m_ContextRecord->HasChunks())
{
Chunk *chunk = m_ContextRecord->GetLastChunk();
chunk->Delete();
m_ContextRecord->PopChunk();
}
m_ContextRecord->UnlockChunks();
m_ContextRecord->FreeParents(m_pDevice->GetResourceManager());
}
}
void WrappedID3D11DeviceContext::FinishCapture()
{
if(GetType() != D3D11_DEVICE_CONTEXT_DEFERRED ||
!RenderDoc::Inst().GetCaptureOptions().captureAllCmdLists)
{
m_State = CaptureState::BackgroundCapturing;
m_SuccessfulCapture = false;
m_FailureReason = CaptureSucceeded;
}
}
void WrappedID3D11DeviceContext::EndCaptureFrame()
{
WriteSerialiser &ser = m_ScratchSerialiser;
ser.SetActionChunk();
SCOPED_SERIALISE_CHUNK(SystemChunk::CaptureEnd);
SERIALISE_ELEMENT(m_ResourceID).Named("Context"_lit).TypedAs("ID3D11DeviceContext *"_lit);
m_ContextRecord->AddChunk(scope.Get());
}
template <typename SerialiserType>
bool WrappedID3D11DeviceContext::Serialise_Present(SerialiserType &ser, UINT SyncInterval, UINT Flags)
{
// we don't do anything with these parameters, they're just here to store
// them for user benefits
SERIALISE_ELEMENT(SyncInterval);
SERIALISE_ELEMENT(Flags);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading() && IsLoading(m_State))
{
AddEvent();
ActionDescription action;
action.copyDestination = m_pDevice->GetBackbufferResourceID();
action.customName = StringFormat::Fmt("Present(%s)", ToStr(action.copyDestination).c_str());
action.flags |= ActionFlags::Present;
AddAction(action);
}
return true;
}
void WrappedID3D11DeviceContext::Present(UINT SyncInterval, UINT Flags)
{
SERIALISE_TIME_CALL();
if(IsActiveCapturing(m_State))
{
USE_SCRATCH_SERIALISER();
GET_SERIALISER.SetActionChunk();
SCOPED_SERIALISE_CHUNK(D3D11Chunk::SwapchainPresent);
SERIALISE_ELEMENT(m_ResourceID).Named("Context"_lit).TypedAs("ID3D11DeviceContext *"_lit);
Serialise_Present(ser, SyncInterval, Flags);
m_ContextRecord->AddChunk(scope.Get());
}
}
bool WrappedID3D11DeviceContext::ShadowStorageInUse(D3D11ResourceRecord *record)
{
ResourceId id = record->GetResourceID();
for(auto mapit = m_OpenMaps.begin(); mapit != m_OpenMaps.end(); ++mapit)
if(mapit->first.resource == id)
return true;
return false;
}
void WrappedID3D11DeviceContext::CleanupCapture()
{
if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED)
{
m_SuccessfulCapture |= m_EmptyCommandList;
if(m_SuccessfulCapture)
m_FailureReason = CaptureSucceeded;
else
m_FailureReason = CaptureFailed_UncappedCmdlist;
for(auto it = m_MapResourceRecordAllocs.begin(); it != m_MapResourceRecordAllocs.end(); ++it)
{
D3D11ResourceRecord *record = m_pDevice->GetResourceManager()->GetResourceRecord(it->first);
if(record)
{
if(record->NumSubResources > 0)
record = record->SubResources[0];
record->FreeContextID(it->second);
}
}
m_MapResourceRecordAllocs.clear();
if(RenderDoc::Inst().GetCaptureOptions().captureAllCmdLists)
return;
}
else
{
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
}
m_ContextRecord->LockChunks();
while(m_ContextRecord->HasChunks())
{
Chunk *chunk = m_ContextRecord->GetLastChunk();
chunk->Delete();
m_ContextRecord->PopChunk();
}
m_ContextRecord->UnlockChunks();
m_ContextRecord->FreeParents(m_pDevice->GetResourceManager());
}
void WrappedID3D11DeviceContext::BeginFrame()
{
{
SCOPED_LOCK(m_AnnotLock);
m_AnnotationQueue.clear();
}
}
void WrappedID3D11DeviceContext::EndFrame()
{
DrainAnnotationQueue();
}
bool WrappedID3D11DeviceContext::IsFL11_1()
{
return m_pDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_1;
}
bool WrappedID3D11DeviceContext::ProcessChunk(ReadSerialiser &ser, D3D11Chunk chunk)
{
SERIALISE_ELEMENT(m_CurContextId)
.Named("Context"_lit)
.TypedAs("ID3D11DeviceContext *"_lit)
.Unimportant();
SERIALISE_CHECK_READ_ERRORS();
m_AddedAction = false;
bool ret = false;
switch(chunk)
{
case D3D11Chunk::IASetInputLayout: ret = Serialise_IASetInputLayout(ser, 0x0); break;
case D3D11Chunk::IASetVertexBuffers:
ret = Serialise_IASetVertexBuffers(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::IASetIndexBuffer:
ret = Serialise_IASetIndexBuffer(ser, 0, DXGI_FORMAT_UNKNOWN, 0);
break;
case D3D11Chunk::IASetPrimitiveTopology:
ret = Serialise_IASetPrimitiveTopology(ser, D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED);
break;
case D3D11Chunk::VSSetConstantBuffers:
ret = Serialise_VSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::VSSetShaderResources:
ret = Serialise_VSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::VSSetSamplers: ret = Serialise_VSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::VSSetShader: ret = Serialise_VSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::HSSetConstantBuffers:
ret = Serialise_HSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::HSSetShaderResources:
ret = Serialise_HSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::HSSetSamplers: ret = Serialise_HSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::HSSetShader: ret = Serialise_HSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::DSSetConstantBuffers:
ret = Serialise_DSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::DSSetShaderResources:
ret = Serialise_DSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::DSSetSamplers: ret = Serialise_DSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::DSSetShader: ret = Serialise_DSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::GSSetConstantBuffers:
ret = Serialise_GSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::GSSetShaderResources:
ret = Serialise_GSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::GSSetSamplers: ret = Serialise_GSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::GSSetShader: ret = Serialise_GSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::SOSetTargets: ret = Serialise_SOSetTargets(ser, 0, 0x0, 0x0); break;
case D3D11Chunk::PSSetConstantBuffers:
ret = Serialise_PSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::PSSetShaderResources:
ret = Serialise_PSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::PSSetSamplers: ret = Serialise_PSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::PSSetShader: ret = Serialise_PSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::CSSetConstantBuffers:
ret = Serialise_CSSetConstantBuffers(ser, 0, 0, 0x0);
break;
case D3D11Chunk::CSSetShaderResources:
ret = Serialise_CSSetShaderResources(ser, 0, 0, 0x0);
break;
case D3D11Chunk::CSSetUnorderedAccessViews:
ret = Serialise_CSSetUnorderedAccessViews(ser, 0, 0, 0x0, 0x0);
break;
case D3D11Chunk::CSSetSamplers: ret = Serialise_CSSetSamplers(ser, 0, 0, 0x0); break;
case D3D11Chunk::CSSetShader: ret = Serialise_CSSetShader(ser, 0x0, 0x0, 0); break;
case D3D11Chunk::RSSetViewports: ret = Serialise_RSSetViewports(ser, 0, 0x0); break;
case D3D11Chunk::RSSetScissorRects: ret = Serialise_RSSetScissorRects(ser, 0, 0x0); break;
case D3D11Chunk::RSSetState: ret = Serialise_RSSetState(ser, 0x0); break;
case D3D11Chunk::OMSetRenderTargets:
ret = Serialise_OMSetRenderTargets(ser, 0, 0x0, 0x0);
break;
case D3D11Chunk::OMSetRenderTargetsAndUnorderedAccessViews:
ret = Serialise_OMSetRenderTargetsAndUnorderedAccessViews(ser, 0, 0x0, 0x0, 0, 0, 0x0, 0x0);
break;
case D3D11Chunk::OMSetBlendState:
ret = Serialise_OMSetBlendState(ser, 0x0, (FLOAT *)0x0, 0);
break;
case D3D11Chunk::OMSetDepthStencilState:
ret = Serialise_OMSetDepthStencilState(ser, 0x0, 0);
break;
case D3D11Chunk::DrawIndexedInstanced:
ret = Serialise_DrawIndexedInstanced(ser, 0, 0, 0, 0, 0);
break;
case D3D11Chunk::DrawInstanced: ret = Serialise_DrawInstanced(ser, 0, 0, 0, 0); break;
case D3D11Chunk::DrawIndexed: ret = Serialise_DrawIndexed(ser, 0, 0, 0); break;
case D3D11Chunk::Draw: ret = Serialise_Draw(ser, 0, 0); break;
case D3D11Chunk::DrawAuto: ret = Serialise_DrawAuto(ser); break;
case D3D11Chunk::DrawIndexedInstancedIndirect:
ret = Serialise_DrawIndexedInstancedIndirect(ser, 0x0, 0);
break;
case D3D11Chunk::DrawInstancedIndirect:
ret = Serialise_DrawInstancedIndirect(ser, 0x0, 0);
break;
case D3D11Chunk::Map: ret = Serialise_Map(ser, 0, 0, (D3D11_MAP)0, 0, 0); break;
case D3D11Chunk::Unmap: ret = Serialise_Unmap(ser, 0, 0); break;
case D3D11Chunk::CopySubresourceRegion:
ret = Serialise_CopySubresourceRegion(ser, 0x0, 0, 0, 0, 0, 0x0, 0, 0x0);
break;
case D3D11Chunk::CopyResource: ret = Serialise_CopyResource(ser, 0x0, 0x0); break;
case D3D11Chunk::UpdateSubresource:
ret = Serialise_UpdateSubresource(ser, 0x0, 0, 0x0, 0x0, 0, 0);
break;
case D3D11Chunk::CopyStructureCount:
ret = Serialise_CopyStructureCount(ser, 0x0, 0, 0x0);
break;
case D3D11Chunk::ResolveSubresource:
ret = Serialise_ResolveSubresource(ser, 0x0, 0, 0x0, 0, DXGI_FORMAT_UNKNOWN);
break;
case D3D11Chunk::GenerateMips: ret = Serialise_GenerateMips(ser, 0x0); break;
case D3D11Chunk::ClearDepthStencilView:
ret = Serialise_ClearDepthStencilView(ser, 0x0, 0, 0.0f, 0);
break;
case D3D11Chunk::ClearRenderTargetView:
ret = Serialise_ClearRenderTargetView(ser, 0x0, (FLOAT *)0x0);
break;
case D3D11Chunk::ClearUnorderedAccessViewUint:
ret = Serialise_ClearUnorderedAccessViewUint(ser, 0x0, (UINT *)0x0);
break;
case D3D11Chunk::ClearUnorderedAccessViewFloat:
ret = Serialise_ClearUnorderedAccessViewFloat(ser, 0x0, (FLOAT *)0x0);
break;
case D3D11Chunk::ClearState: ret = Serialise_ClearState(ser); break;
case D3D11Chunk::ExecuteCommandList: ret = Serialise_ExecuteCommandList(ser, 0x0, 0); break;
case D3D11Chunk::Dispatch: ret = Serialise_Dispatch(ser, 0, 0, 0); break;
case D3D11Chunk::DispatchIndirect: ret = Serialise_DispatchIndirect(ser, 0x0, 0); break;
case D3D11Chunk::FinishCommandList: ret = Serialise_FinishCommandList(ser, 0, 0x0); break;
case D3D11Chunk::Flush: ret = Serialise_Flush(ser); break;
case D3D11Chunk::SetPredication: ret = Serialise_SetPredication(ser, 0x0, 0x0); break;
case D3D11Chunk::SetResourceMinLOD: ret = Serialise_SetResourceMinLOD(ser, 0x0, 0); break;
case D3D11Chunk::Begin: ret = Serialise_Begin(ser, 0x0); break;
case D3D11Chunk::End: ret = Serialise_End(ser, 0x0); break;
case D3D11Chunk::CopySubresourceRegion1:
ret = Serialise_CopySubresourceRegion1(ser, 0x0, 0, 0, 0, 0, 0x0, 0, 0x0, 0);
break;
case D3D11Chunk::UpdateSubresource1:
ret = Serialise_UpdateSubresource1(ser, 0x0, 0, 0x0, 0x0, 0, 0, 0);
break;
case D3D11Chunk::ClearView: ret = Serialise_ClearView(ser, 0x0, 0x0, 0x0, 0); break;
case D3D11Chunk::VSSetConstantBuffers1:
ret = Serialise_VSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::HSSetConstantBuffers1:
ret = Serialise_HSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::DSSetConstantBuffers1:
ret = Serialise_DSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::GSSetConstantBuffers1:
ret = Serialise_GSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::PSSetConstantBuffers1:
ret = Serialise_PSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::CSSetConstantBuffers1:
ret = Serialise_CSSetConstantBuffers1(ser, 0, 0, 0x0, 0x0, 0x0);
break;
case D3D11Chunk::PushMarker: ret = Serialise_PushMarker(ser, 0, L""); break;
case D3D11Chunk::SetMarker: ret = Serialise_SetMarker(ser, 0, L""); break;
case D3D11Chunk::PopMarker: ret = Serialise_PopMarker(ser); break;
case D3D11Chunk::DiscardResource: ret = Serialise_DiscardResource(ser, NULL); break;
case D3D11Chunk::DiscardView: ret = Serialise_DiscardView(ser, NULL); break;
case D3D11Chunk::DiscardView1: ret = Serialise_DiscardView1(ser, NULL, NULL, 0); break;
case D3D11Chunk::PostExecuteCommandList:
ret = Serialise_PostExecuteCommandList(ser, NULL, FALSE);
break;
case D3D11Chunk::PostFinishCommandListSet:
ret = Serialise_PostFinishCommandListSet(ser, NULL);
break;
case D3D11Chunk::SwapDeviceContextState:
ret = Serialise_SwapDeviceContextState(ser, NULL, NULL);
break;
case D3D11Chunk::SwapchainPresent:
ret = Serialise_Present(ser, 0, 0);
break;
// in order to get a warning if we miss a case, we explicitly handle the device creation chunks
// here. If we actually encounter one it's an error (we shouldn't see these inside the captured
// frame itself)
case D3D11Chunk::DeviceInitialisation:
case D3D11Chunk::SetResourceName:
case D3D11Chunk::CreateSwapBuffer:
case D3D11Chunk::CreateTexture1D:
case D3D11Chunk::CreateTexture2D:
case D3D11Chunk::CreateTexture2D1:
case D3D11Chunk::CreateTexture3D:
case D3D11Chunk::CreateTexture3D1:
case D3D11Chunk::CreateBuffer:
case D3D11Chunk::CreateVertexShader:
case D3D11Chunk::CreateHullShader:
case D3D11Chunk::CreateDomainShader:
case D3D11Chunk::CreateGeometryShader:
case D3D11Chunk::CreateGeometryShaderWithStreamOutput:
case D3D11Chunk::CreatePixelShader:
case D3D11Chunk::CreateComputeShader:
case D3D11Chunk::GetClassInstance:
case D3D11Chunk::CreateClassInstance:
case D3D11Chunk::CreateClassLinkage:
case D3D11Chunk::CreateShaderResourceView:
case D3D11Chunk::CreateShaderResourceView1:
case D3D11Chunk::CreateRenderTargetView:
case D3D11Chunk::CreateRenderTargetView1:
case D3D11Chunk::CreateDepthStencilView:
case D3D11Chunk::CreateUnorderedAccessView:
case D3D11Chunk::CreateUnorderedAccessView1:
case D3D11Chunk::CreateInputLayout:
case D3D11Chunk::CreateBlendState:
case D3D11Chunk::CreateBlendState1:
case D3D11Chunk::CreateDepthStencilState:
case D3D11Chunk::CreateRasterizerState:
case D3D11Chunk::CreateRasterizerState1:
case D3D11Chunk::CreateRasterizerState2:
case D3D11Chunk::CreateSamplerState:
case D3D11Chunk::CreateQuery:
case D3D11Chunk::CreateQuery1:
case D3D11Chunk::CreatePredicate:
case D3D11Chunk::CreateCounter:
case D3D11Chunk::CreateDeferredContext:
case D3D11Chunk::SetExceptionMode:
case D3D11Chunk::ExternalDXGIResource:
case D3D11Chunk::OpenSharedResource:
case D3D11Chunk::OpenSharedResource1:
case D3D11Chunk::OpenSharedResourceByName:
case D3D11Chunk::SetShaderDebugPath:
case D3D11Chunk::SetShaderExtUAV:
RDCERR("Unexpected chunk while processing frame: %s", ToStr(chunk).c_str());
return false;
// no explicit default so that we have compiler warnings if a chunk isn't explicitly handled.
case D3D11Chunk::Max: break;
}
{
SystemChunk system = (SystemChunk)chunk;
if(system == SystemChunk::CaptureEnd)
{
if(IsLoading(m_State) && m_LastChunk != D3D11Chunk::SwapchainPresent)
{
AddEvent();
ActionDescription action;
action.customName = "End of Capture";
action.flags |= ActionFlags::Present;
action.copyDestination = m_pDevice->GetBackbufferResourceID();
AddAction(action);
}
ret = true;
}
else if(!ret)
{
RDCERR("Unrecognised Chunk type %d", chunk);
return false;
}
}
if(IsLoading(m_State) && m_CurEventID > 0)
{
if(chunk == D3D11Chunk::SetMarker)
{
// no push/pop necessary
}
else if(chunk == D3D11Chunk::PushMarker)
{
// push down the action stack to the latest action
m_ActionStack.push_back(&m_ActionStack.back()->children.back());
}
else if(chunk == D3D11Chunk::PopMarker)
{
// refuse to pop off further than the root action (mismatched begin/end events e.g.)
if(m_ActionStack.size() > 1)
m_ActionStack.pop_back();
}
if(!m_AddedAction)
AddEvent();
}
m_AddedAction = false;
return ret;
}
void WrappedID3D11DeviceContext::AddUsage(const ActionDescription &a)
{
const D3D11RenderState *pipe = m_CurrentPipelineState;
uint32_t e = a.eventId;
ActionFlags ActionMask = ActionFlags::Drawcall | ActionFlags::Dispatch | ActionFlags::CmdList;
if(!(a.flags & ActionMask))
return;
const bool isDispatch = bool(a.flags & ActionFlags::Dispatch);
//////////////////////////////
// IA
if(a.flags & ActionFlags::Indexed && pipe->IA.IndexBuffer != NULL)
m_ResourceUses[GetIDForDeviceChild(pipe->IA.IndexBuffer)].push_back(
EventUsage(e, ResourceUsage::IndexBuffer));
for(int i = 0; i < D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT; i++)
if(pipe->IA.Used_VB(m_pDevice, i))
m_ResourceUses[GetIDForDeviceChild(pipe->IA.VBs[i])].push_back(
EventUsage(e, ResourceUsage::VertexBuffer));
//////////////////////////////
// Shaders
const D3D11RenderState::Shader *shArr[6] = {
&pipe->VS, &pipe->HS, &pipe->DS, &pipe->GS, &pipe->PS, &pipe->CS,
};
int firstShader = 0, numShaders = 5;
if(isDispatch)
{
firstShader = 5;
numShaders = 1;
}
for(int s = firstShader; s < firstShader + numShaders; s++)
{
const D3D11RenderState::Shader &sh = *shArr[s];
for(int i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++)
if(sh.Used_CB(i))
m_ResourceUses[GetIDForDeviceChild(sh.ConstantBuffers[i])].push_back(
EventUsage(e, CBUsage(s)));
for(int i = 0; i < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; i++)
{
if(sh.Used_SRV(i))
{
WrappedID3D11ShaderResourceView1 *view = (WrappedID3D11ShaderResourceView1 *)sh.SRVs[i];
m_ResourceUses[view->GetResourceResID()].push_back(
EventUsage(e, ResUsage(s), view->GetResourceID()));
}
}
if(s == 5)
{
for(int i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++)
{
if(pipe->CS.Used_UAV(i) && pipe->CSUAVs[i])
{
WrappedID3D11UnorderedAccessView1 *view =
(WrappedID3D11UnorderedAccessView1 *)pipe->CSUAVs[i];
m_ResourceUses[view->GetResourceResID()].push_back(
EventUsage(e, ResourceUsage::CS_RWResource, view->GetResourceID()));
}
}
}
}
// don't record usage for rasterization pipeline on dispatch calls
if(isDispatch)
return;
//////////////////////////////
// SO
for(int i = 0; i < D3D11_SO_BUFFER_SLOT_COUNT; i++)
if(pipe->SO.Buffers[i]) // assuming for now that any SO target bound is used.
m_ResourceUses[GetIDForDeviceChild(pipe->SO.Buffers[i])].push_back(
EventUsage(e, ResourceUsage::StreamOut));
//////////////////////////////
// OM
for(int i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++)
{
if(pipe->PS.Used_UAV(i) && pipe->OM.UAVs[i])
{
WrappedID3D11UnorderedAccessView1 *view = (WrappedID3D11UnorderedAccessView1 *)pipe->OM.UAVs[i];
m_ResourceUses[view->GetResourceResID()].push_back(
EventUsage(e, ResourceUsage::PS_RWResource, view->GetResourceID()));
}
}
if(pipe->OM.DepthView) // assuming for now that any DSV bound is used.
{
WrappedID3D11DepthStencilView *view = (WrappedID3D11DepthStencilView *)pipe->OM.DepthView;
m_ResourceUses[view->GetResourceResID()].push_back(
EventUsage(e, ResourceUsage::DepthStencilTarget, view->GetResourceID()));
}
for(int i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
if(pipe->OM.RenderTargets[i]) // assuming for now that any RTV bound is used.
{
WrappedID3D11RenderTargetView1 *view =
(WrappedID3D11RenderTargetView1 *)pipe->OM.RenderTargets[i];
m_ResourceUses[view->GetResourceResID()].push_back(
EventUsage(e, ResourceUsage::ColorTarget, view->GetResourceID()));
}
}
}
void WrappedID3D11DeviceContext::AddAction(const ActionDescription &a)
{
if(m_CurEventID == 0)
return;
ActionDescription action = a;
m_AddedAction = true;
action.eventId = m_CurEventID;
action.actionId = m_CurActionID;
for(int i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
action.outputs[i] = ResourceId();
if(m_CurrentPipelineState->OM.RenderTargets[i])
action.outputs[i] = m_pDevice->GetResourceManager()->GetOriginalID(
((WrappedID3D11RenderTargetView1 *)m_CurrentPipelineState->OM.RenderTargets[i])
->GetResourceResID());
}
{
action.depthOut = ResourceId();
if(m_CurrentPipelineState->OM.DepthView)
action.depthOut = m_pDevice->GetResourceManager()->GetOriginalID(
((WrappedID3D11DepthStencilView *)m_CurrentPipelineState->OM.DepthView)->GetResourceResID());
}
// markers don't increment action ID
ActionFlags MarkerMask = ActionFlags::SetMarker | ActionFlags::PushMarker | ActionFlags::PopMarker;
if(!(action.flags & MarkerMask))
m_CurActionID++;
action.events.swap(m_CurEvents);
AddUsage(action);
// should have at least the root action here, push this action
// onto the back's children list.
if(!m_ActionStack.empty())
m_ActionStack.back()->children.push_back(action);
else
RDCERR("Somehow lost action stack!");
}
void WrappedID3D11DeviceContext::AddEvent()
{
if(m_CurEventID == 0)
return;
APIEvent apievent;
apievent.fileOffset = m_CurChunkOffset;
apievent.eventId = m_CurEventID;
apievent.chunkIndex = uint32_t(m_StructuredFile->chunks.size() - 1);
// if we're using replay-time debug messages, fetch them now since we can do better to correlate
// to events on replay
if(m_pDevice->GetReplayOptions().apiValidation)
{
rdcarray<DebugMessage> messages = m_pDevice->GetDebugMessages();
for(size_t i = 0; i < messages.size(); i++)
{
messages[i].eventId = apievent.eventId;
m_pDevice->AddDebugMessage(messages[i]);
}
}
m_CurEvents.push_back(apievent);
if(IsLoading(m_State))
{
m_Events.resize(apievent.eventId + 1);
m_Events[apievent.eventId] = apievent;
}
}
const APIEvent &WrappedID3D11DeviceContext::GetEvent(uint32_t eventId) const
{
// start at where the requested eventId would be
size_t idx = eventId;
// find the next valid event (some may be skipped)
while(idx < m_Events.size() - 1 && m_Events[idx].eventId == 0)
idx++;
return m_Events[RDCMIN(idx, m_Events.size() - 1)];
}
void WrappedID3D11DeviceContext::ReplayFakeContext(ResourceId id)
{
m_FakeContext = id;
}
RDResult WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t startEventID,
uint32_t endEventID, bool partial)
{
m_State = readType;
if(!m_FrameReader)
{
RETURN_ERROR_RESULT(ResultCode::InvalidParameter,
"Can't replay context capture without frame reader");
}
m_FrameReader->SetOffset(0);
ReadSerialiser ser(m_FrameReader, Ownership::Nothing);
ser.SetStringDatabase(&m_StringDB);
ser.SetUserData(GetResourceManager());
ser.SetVersion(m_pDevice->GetLogVersion());
if(IsLoading(m_State) || IsStructuredExporting(m_State))
{
ser.ConfigureStructuredExport(&GetChunkName, IsStructuredExporting(m_State),
m_pDevice->GetTimeBase(), m_pDevice->GetTimeFrequency());
ser.GetStructuredFile().Swap(*m_pDevice->GetStructuredFile());
m_StructuredFile = &ser.GetStructuredFile();
}
else
{
m_StructuredFile = m_pDevice->GetStructuredFile();
}
m_DoStateVerify = true;
SystemChunk header = ser.ReadChunk<SystemChunk>();
RDCASSERTEQUAL(header, SystemChunk::CaptureBegin);
if(partial)
ser.SkipCurrentChunk();
else
Serialise_BeginCaptureFrame(ser);
ser.EndChunk();
m_CurEvents.clear();
if(IsActiveReplaying(m_State))
{
if(!m_Events.empty())
{
APIEvent ev = GetEvent(startEventID);
m_CurEventID = ev.eventId;
ser.GetReader()->SetOffset(ev.fileOffset);
}
ClearMaps();
for(size_t i = 0; i < m_pDevice->GetNumDeferredContexts(); i++)
{
WrappedID3D11DeviceContext *defcontext = m_pDevice->GetDeferredContext(i);
defcontext->ClearMaps();
}
}
else
{
m_CurEventID = 1;
}
uint64_t startOffset = ser.GetReader()->GetOffset();
for(;;)
{
if(IsActiveReplaying(m_State) && m_CurEventID > endEventID)
{
// set event ID correctly as we haven't actually replayed the next one.
m_CurEventID = endEventID;
// we can just break out if we've done all the events desired.
break;
}
m_CurChunkOffset = ser.GetReader()->GetOffset();
D3D11Chunk chunktype = ser.ReadChunk<D3D11Chunk>();
if(ser.IsErrored())
return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message);
m_ChunkMetadata = ser.ChunkMetadata();
bool success = ProcessChunk(ser, chunktype);
ser.EndChunk();
if(ser.IsErrored())
return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message);
// if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay
// failure.
if(!success)
{
rdcstr extra;
if(m_pDevice->GetInfoQueue())
{
extra += "\n";
for(UINT64 i = 0;
i < m_pDevice->GetInfoQueue()->GetNumStoredMessagesAllowedByRetrievalFilter(); i++)
{
SIZE_T len = 0;
m_pDevice->GetInfoQueue()->GetMessage(i, NULL, &len);
char *msgbuf = new char[len];
D3D11_MESSAGE *message = (D3D11_MESSAGE *)msgbuf;
m_pDevice->GetInfoQueue()->GetMessage(i, message, &len);
extra += "\n";
extra += message->pDescription;
delete[] msgbuf;
}
}
else
{
extra +=
"\n\nMore debugging information may be available by enabling API validation on "
"replay";
}
if(m_pDevice->HasFatalError())
{
RDResult result = m_pDevice->FatalErrorCheck();
result.message = rdcstr(result.message) + extra;
return result;
}
m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra;
return m_FailedReplayResult;
}
RenderDoc::Inst().SetProgress(
LoadProgress::FrameEventsRead,
float(m_CurChunkOffset - startOffset) / float(ser.GetReader()->GetSize()));
if((SystemChunk)chunktype == SystemChunk::CaptureEnd || ser.GetReader()->AtEnd())
break;
m_LastChunk = chunktype;
m_CurEventID++;
}
if(IsLoading(m_State))
{
m_pDevice->GetReplay()->WriteFrameRecord().actionList = m_ParentAction.children;
m_pDevice->GetReplay()->WriteFrameRecord().frameInfo.debugMessages =
m_pDevice->GetDebugMessages();
for(auto it = WrappedID3D11Buffer::m_BufferList.begin();
it != WrappedID3D11Buffer::m_BufferList.end(); ++it)
m_ResourceUses[it->first];
for(auto it = WrappedID3D11Texture1D::m_TextureList.begin();
it != WrappedID3D11Texture1D::m_TextureList.end(); ++it)
m_ResourceUses[it->first];
for(auto it = WrappedID3D11Texture2D1::m_TextureList.begin();
it != WrappedID3D11Texture2D1::m_TextureList.end(); ++it)
m_ResourceUses[it->first];
for(auto it = WrappedID3D11Texture3D1::m_TextureList.begin();
it != WrappedID3D11Texture3D1::m_TextureList.end(); ++it)
m_ResourceUses[it->first];
// it's easier to remove duplicate usages here than check it as we go.
// this means if textures are bound in multiple places in the same action
// we don't have duplicate uses
for(auto it = m_ResourceUses.begin(); it != m_ResourceUses.end(); ++it)
{
rdcarray<EventUsage> &v = it->second;
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()) - v.begin(), ~0U);
}
}
// swap the structure back now that we've accumulated the frame as well.
if(IsLoading(m_State) || IsStructuredExporting(m_State))
ser.GetStructuredFile().Swap(*m_pDevice->GetStructuredFile());
m_StructuredFile = NULL;
m_DoStateVerify = false;
return ResultCode::Succeeded;
}
void WrappedID3D11DeviceContext::ClearMaps()
{
auto it = m_OpenMaps.begin();
for(; it != m_OpenMaps.end(); ++it)
{
RDCASSERT(m_pDevice->GetResourceManager()->HasLiveResource(it->first.resource));
ID3D11Resource *res =
(ID3D11Resource *)m_pDevice->GetResourceManager()->GetLiveResource(it->first.resource);
m_pRealContext->Unmap(UnwrapResource(res), it->first.subresource);
}
m_OpenMaps.clear();
}
void WrappedID3D11DeviceContext::IntAddRef()
{
Atomic::Inc32(&m_IntRef);
}
void WrappedID3D11DeviceContext::IntRelease()
{
Atomic::Dec32(&m_IntRef);
ASSERT_REFCOUNT(m_IntRef);
// don't defer destruction of device contexts, delete them immediately.
if(m_IntRef + m_ExtRef == 0)
delete this;
}
ULONG STDMETHODCALLTYPE WrappedID3D11DeviceContext::AddRef()
{
// if we're about to create a new external reference on this object, add back our reference on
// the device
if(m_ExtRef == 0)
m_pDevice->AddRef();
Atomic::Inc32(&m_ExtRef);
return (ULONG)m_ExtRef;
}
ULONG STDMETHODCALLTYPE WrappedID3D11DeviceContext::Release()
{
Atomic::Dec32(&m_ExtRef);
ASSERT_REFCOUNT(m_ExtRef);
WrappedID3D11Device *dev = m_pDevice;
int32_t intRef = m_IntRef;
int32_t extRef = m_ExtRef;
// handle our own death first, so that if we're about to release the last external reference on
// the device below that we don't then double delete. The immediate context can never die like
// this because the device holds an internal reference on it.
if(intRef + extRef == 0)
{
delete this;
}
// if we just released the last external reference on this object, release our reference on the
// device.
if(extRef == 0)
dev->Release();
return (ULONG)extRef;
}
HRESULT STDMETHODCALLTYPE WrappedID3D11DeviceContext::QueryInterface(REFIID riid, void **ppvObject)
{
if(riid == __uuidof(IUnknown))
{
*ppvObject = (IUnknown *)(ID3D11DeviceContext *)this;
AddRef();
return S_OK;
}
else if(riid == __uuidof(ID3D11DeviceContext))
{
*ppvObject = (ID3D11DeviceContext *)this;
AddRef();
return S_OK;
}
else if(riid == __uuidof(ID3D11DeviceChild))
{
*ppvObject = (ID3D11DeviceChild *)this;
AddRef();
return S_OK;
}
else if(riid == __uuidof(ID3D11DeviceContext1))
{
if(m_pRealContext1)
{
*ppvObject = (ID3D11DeviceContext1 *)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
else if(riid == __uuidof(ID3D11DeviceContext2))
{
if(m_pRealContext2)
{
*ppvObject = (ID3D11DeviceContext2 *)this;
AddRef();
RDCWARN(
"Trying to get ID3D11DeviceContext2. DX11.2 tiled resources are not supported at this "
"time.");
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
else if(riid == __uuidof(ID3D11DeviceContext3))
{
if(m_pRealContext3)
{
*ppvObject = (ID3D11DeviceContext3 *)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
else if(riid == __uuidof(ID3D11DeviceContext4))
{
if(m_pRealContext4)
{
*ppvObject = (ID3D11DeviceContext4 *)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
else if(riid == __uuidof(ID3D11Multithread))
{
// forward to the device as the lock is shared amongst all things
return m_pDevice->QueryInterface(riid, ppvObject);
}
else if(riid == __uuidof(ID3DUserDefinedAnnotation))
{
*ppvObject = (ID3DUserDefinedAnnotation *)&m_UserAnnotation;
AddRef();
return S_OK;
}
else if(riid == __uuidof(ID3D11InfoQueue))
{
// forward to device
return m_pDevice->QueryInterface(riid, ppvObject);
}
else if(riid == __uuidof(ID3D11VideoContext) || riid == __uuidof(ID3D11VideoContext1) ||
riid == __uuidof(ID3D11VideoContext2) || riid == __uuidof(ID3D11VideoContext3))
{
return m_WrappedVideo.QueryInterface(riid, ppvObject);
}
return RefCountDXGIObject::WrapQueryInterface(m_pRealContext, "ID3D11DeviceContext", riid,
ppvObject);
}
#pragma region Record Statistics
void WrappedID3D11DeviceContext::RecordIndexBindStats(ID3D11Buffer *Buffer)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
IndexBindStats &indices = stats.indices;
indices.calls += 1;
indices.sets += (Buffer != NULL);
indices.nulls += (Buffer == NULL);
}
void WrappedID3D11DeviceContext::RecordVertexBindStats(UINT NumBuffers, ID3D11Buffer *const Buffers[])
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
VertexBindStats &vertices = stats.vertices;
vertices.calls += 1;
RDCASSERT(NumBuffers < vertices.bindslots.size());
vertices.bindslots[NumBuffers] += 1;
for(UINT i = 0; i < NumBuffers; i++)
{
if(Buffers[i])
vertices.sets += 1;
else
vertices.nulls += 1;
}
}
void WrappedID3D11DeviceContext::RecordLayoutBindStats(ID3D11InputLayout *Layout)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
LayoutBindStats &layouts = stats.layouts;
layouts.calls += 1;
layouts.sets += (Layout != NULL);
layouts.nulls += (Layout == NULL);
}
void WrappedID3D11DeviceContext::RecordConstantStats(ShaderStage stage, UINT NumBuffers,
ID3D11Buffer *const Buffers[])
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RDCASSERT(size_t(stage) < stats.constants.size());
ConstantBindStats &constants = stats.constants[uint32_t(stage)];
constants.calls += 1;
RDCASSERT(NumBuffers < constants.bindslots.size());
constants.bindslots[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 = BucketForRecord<ConstantBindStats>::Get(bufferSize);
RDCASSERT(bucket < constants.sizes.size());
constants.sizes[bucket] += 1;
}
else
{
constants.nulls += 1;
}
}
}
void WrappedID3D11DeviceContext::RecordResourceStats(ShaderStage stage, UINT NumResources,
ID3D11ShaderResourceView *const Resources[])
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RDCASSERT(size_t(stage) < stats.resources.size());
ResourceBindStats &resources = stats.resources[(uint32_t)stage];
resources.calls += 1;
RDCASSERT(NumResources < resources.bindslots.size());
resources.bindslots[NumResources] += 1;
const TextureType mapping[] = {
TextureType::Unknown, TextureType::Buffer, TextureType::Texture1D,
TextureType::Texture1DArray, TextureType::Texture2D, TextureType::Texture2DArray,
TextureType::Texture2DMS, TextureType::Texture2DMSArray, TextureType::Texture3D,
TextureType::TextureCube, TextureType::TextureCubeArray, TextureType::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));
TextureType type = mapping[desc.ViewDimension];
// #mivance surprisingly this is not asserted in operator[] for
// rdcarray so I'm being paranoid
RDCASSERT((int)type < (int)resources.types.size());
resources.types[(int)type] += 1;
}
else
{
resources.nulls += 1;
}
}
}
void WrappedID3D11DeviceContext::RecordSamplerStats(ShaderStage stage, UINT NumSamplers,
ID3D11SamplerState *const Samplers[])
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RDCASSERT(size_t(stage) < stats.samplers.size());
SamplerBindStats &samplers = stats.samplers[uint32_t(stage)];
samplers.calls += 1;
RDCASSERT(NumSamplers < samplers.bindslots.size());
samplers.bindslots[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)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
ResourceUpdateStats &updates = stats.updates;
if(res == NULL)
return;
updates.calls += 1;
updates.clients += (Server == false);
updates.servers += (Server == true);
const TextureType mapping[] = {
TextureType::Unknown, // D3D11_RESOURCE_DIMENSION_UNKNOWN = 0,
TextureType::Buffer, // D3D11_RESOURCE_DIMENSION_BUFFER = 1,
TextureType::Texture1D, // D3D11_RESOURCE_DIMENSION_TEXTURE1D = 2,
TextureType::Texture2D, // D3D11_RESOURCE_DIMENSION_TEXTURE2D = 3,
TextureType::Texture3D, // D3D11_RESOURCE_DIMENSION_TEXTURE3D = 4
};
D3D11_RESOURCE_DIMENSION dim;
res->GetType(&dim);
RDCASSERT(dim < ARRAY_COUNT(mapping));
TextureType type = mapping[dim];
RDCASSERT((int)type < (int)updates.types.size());
updates.types[(int)type] += 1;
// #mivance it might be nice to query the buffer to differentiate
// between bindings for constant buffers
if(Size > 0)
{
size_t bucket = BucketForRecord<ResourceUpdateStats>::Get(Size);
updates.sizes[bucket] += 1;
}
else
{
updates.sizes[0] += 1;
}
}
void WrappedID3D11DeviceContext::RecordDrawStats(bool instanced, bool indirect, UINT InstanceCount)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
DrawcallStats &draws = stats.draws;
draws.calls += 1;
draws.instanced += (uint32_t)instanced;
draws.indirect += (uint32_t)indirect;
if(instanced)
{
size_t bucket = BucketForRecord<DrawcallStats>::Get(InstanceCount);
RDCASSERT(bucket < draws.counts.size());
draws.counts[bucket] += 1;
}
}
void WrappedID3D11DeviceContext::RecordDispatchStats(bool indirect)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
DispatchStats &dispatches = stats.dispatches;
dispatches.calls += 1;
dispatches.indirect += (uint32_t)indirect;
}
void WrappedID3D11DeviceContext::RecordShaderStats(ShaderStage stage, ID3D11DeviceChild *Current,
ID3D11DeviceChild *Shader)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RDCASSERT(size_t(stage) <= stats.shaders.size());
ShaderChangeStats &shaders = stats.shaders[uint32_t(stage)];
shaders.calls += 1;
shaders.sets += (Shader != NULL);
shaders.nulls += (Shader == NULL);
shaders.redundants += (Current == Shader);
}
void WrappedID3D11DeviceContext::RecordBlendStats(ID3D11BlendState *Blend,
const FLOAT BlendFactor[4], UINT SampleMask)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
BlendStats &blends = stats.blends;
blends.calls += 1;
blends.sets += (Blend != NULL);
blends.nulls += (Blend == NULL);
const D3D11RenderState::OutputMerger *Current = &m_CurrentPipelineState->OM;
bool same = (Current->BlendState == Blend) &&
(memcmp(Current->BlendFactor, BlendFactor, sizeof(Current->BlendFactor)) == 0) &&
(Current->SampleMask == SampleMask);
blends.redundants += (uint32_t)same;
}
void WrappedID3D11DeviceContext::RecordDepthStencilStats(ID3D11DepthStencilState *DepthStencil,
UINT StencilRef)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
DepthStencilStats &depths = stats.depths;
depths.calls += 1;
depths.sets += (DepthStencil != NULL);
depths.nulls += (DepthStencil == NULL);
const D3D11RenderState::OutputMerger *Current = &m_CurrentPipelineState->OM;
bool same = (Current->DepthStencilState == DepthStencil) && (Current->StencRef == StencilRef);
depths.redundants += (uint32_t)same;
}
void WrappedID3D11DeviceContext::RecordRasterizationStats(ID3D11RasterizerState *Rasterizer)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RasterizationStats &rasters = stats.rasters;
rasters.calls += 1;
rasters.sets += (Rasterizer != NULL);
rasters.nulls += (Rasterizer == NULL);
const D3D11RenderState::Rasterizer *Current = &m_CurrentPipelineState->RS;
bool same = (Current->State == Rasterizer);
rasters.redundants += (uint32_t)same;
}
void WrappedID3D11DeviceContext::RecordViewportStats(UINT NumViewports,
const D3D11_VIEWPORT *viewports)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RasterizationStats &rasters = stats.rasters;
rasters.calls += 1;
rasters.sets += 1;
// #mivance fairly sure setting 0 viewports/null viewports is illegal?
const D3D11RenderState::Rasterizer *Current = &m_CurrentPipelineState->RS;
bool same = (Current->NumViews == NumViewports);
for(UINT index = 0; index < NumViewports; index++)
{
same = (same && (Current->Viewports[index] == viewports[index]));
}
rasters.redundants += (uint32_t)same;
RDCASSERT(NumViewports < rasters.viewports.size());
rasters.viewports[NumViewports] += 1;
}
void WrappedID3D11DeviceContext::RecordScissorStats(UINT NumRects, const D3D11_RECT *rects)
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
RasterizationStats &rasters = stats.rasters;
rasters.calls += 1;
rasters.sets += 1;
// #mivance see above
const D3D11RenderState::Rasterizer *Current = &m_CurrentPipelineState->RS;
bool same = (Current->NumScissors == NumRects);
for(UINT index = 0; index < NumRects; index++)
{
same = (same && (Current->Scissors[index] == rects[index]));
}
rasters.redundants += (uint32_t)same;
RDCASSERT(NumRects < rasters.rects.size());
rasters.rects[NumRects] += 1;
}
void WrappedID3D11DeviceContext::RecordOutputMergerStats(UINT NumRTVs, ID3D11RenderTargetView *RTVs[],
ID3D11DepthStencilView *DSV,
UINT UAVStartSlot, UINT NumUAVs,
ID3D11UnorderedAccessView *UAVs[])
{
FrameStatistics &stats = m_pDevice->GetFrameStats();
OutputTargetStats &outputs = stats.outputs;
outputs.calls += 1;
// #mivance is an elaborate redundancy here even useful?
// const D3D11RenderState::outmerger* Current = &m_CurrentPipelineState->OM;
if(NumRTVs != D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL)
{
if(RTVs != NULL)
{
for(UINT index = 0; index < NumRTVs; index++)
{
outputs.sets += (RTVs[index] != NULL);
outputs.nulls += (RTVs[index] == NULL);
}
}
else
{
outputs.nulls += NumRTVs;
}
}
outputs.sets += (DSV != NULL);
outputs.nulls += (DSV == NULL);
if(NumUAVs != D3D11_KEEP_UNORDERED_ACCESS_VIEWS)
{
if(UAVs != NULL)
{
for(UINT index = 0; index < NumUAVs; index++)
{
outputs.sets += (UAVs[index] != NULL);
outputs.nulls += (UAVs[index] == NULL);
}
}
else
{
outputs.nulls += NumUAVs;
}
}
UINT NumSlots = 0;
if(NumRTVs != D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL)
NumSlots += NumRTVs;
if(NumUAVs != D3D11_KEEP_UNORDERED_ACCESS_VIEWS)
NumSlots += NumUAVs;
RDCASSERT(NumSlots < outputs.bindslots.size());
outputs.bindslots[NumSlots] += 1;
}
#pragma endregion