mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-09 09:10:49 +00:00
Cache EvaluateAttribute* per-sample data for debugging. Closes #1025
This commit is contained in:
@@ -34,6 +34,40 @@
|
||||
#include "d3d11_debug.h"
|
||||
#include "d3d11_manager.h"
|
||||
#include "d3d11_shader_cache.h"
|
||||
// struct that saves pointers as we iterate through to where we ultimately
|
||||
// want to copy the data to
|
||||
struct DataOutput
|
||||
{
|
||||
DataOutput(int regster, int element, int numWords, ShaderBuiltin attr, bool inc)
|
||||
{
|
||||
reg = regster;
|
||||
elem = element;
|
||||
numwords = numWords;
|
||||
sysattribute = attr;
|
||||
included = inc;
|
||||
}
|
||||
|
||||
int reg;
|
||||
int elem;
|
||||
ShaderBuiltin sysattribute;
|
||||
|
||||
int numwords;
|
||||
|
||||
bool included;
|
||||
};
|
||||
|
||||
struct DebugHit
|
||||
{
|
||||
uint32_t numHits;
|
||||
float posx;
|
||||
float posy;
|
||||
float depth;
|
||||
uint32_t primitive;
|
||||
uint32_t isFrontFace;
|
||||
uint32_t sample;
|
||||
uint32_t coverage;
|
||||
uint32_t rawdata; // arbitrary, depending on shader
|
||||
};
|
||||
|
||||
// over this number of cycles and things get problematic
|
||||
#define SHADER_DEBUG_WARN_THRESHOLD 100000
|
||||
@@ -57,6 +91,34 @@ bool PromptDebugTimeout(DXBC::ProgramType prog, uint32_t cycleCounter)
|
||||
return false;
|
||||
}
|
||||
|
||||
// apply coarse/fine derivatives to select threads within a quad to ensure all values are correct
|
||||
static void ApplyDerivatives(ShaderDebug::GlobalState &global, ShaderDebugTrace traces[4],
|
||||
const DataOutput &initialValue, float *data, float signmul,
|
||||
int32_t quadIdxA, int32_t quadIdxB = -1)
|
||||
{
|
||||
for(int w = 0; w < initialValue.numwords; w++)
|
||||
{
|
||||
traces[quadIdxA].inputs[initialValue.reg].value.fv[initialValue.elem + w] += signmul * data[w];
|
||||
if(quadIdxB >= 0)
|
||||
traces[quadIdxB].inputs[initialValue.reg].value.fv[initialValue.elem + w] += signmul * data[w];
|
||||
}
|
||||
|
||||
// quick check to see if this register was evaluated
|
||||
if(global.sampleEvalRegisterMask & (1ULL << initialValue.reg))
|
||||
{
|
||||
// apply derivative to any cached sample evaluations on these quad indices
|
||||
for(auto it = global.sampleEvalCache.begin(); it != global.sampleEvalCache.end(); ++it)
|
||||
{
|
||||
if((it->first.quadIndex == quadIdxA || it->first.quadIndex == quadIdxB) &&
|
||||
initialValue.reg == it->first.inputRegisterIndex)
|
||||
{
|
||||
for(int w = 0; w < initialValue.numwords; w++)
|
||||
it->second.value.fv[initialValue.elem + w] += data[w];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderDebug::State D3D11DebugManager::CreateShaderDebugState(ShaderDebugTrace &trace, int quadIdx,
|
||||
DXBC::DXBCFile *dxbc, bytebuf *cbufData)
|
||||
{
|
||||
@@ -555,41 +617,6 @@ void D3D11DebugManager::CreateShaderGlobalState(ShaderDebug::GlobalState &global
|
||||
}
|
||||
}
|
||||
|
||||
// struct that saves pointers as we iterate through to where we ultimately
|
||||
// want to copy the data to
|
||||
struct DataOutput
|
||||
{
|
||||
DataOutput(int regster, int element, int numWords, ShaderBuiltin attr, bool inc)
|
||||
{
|
||||
reg = regster;
|
||||
elem = element;
|
||||
numwords = numWords;
|
||||
sysattribute = attr;
|
||||
included = inc;
|
||||
}
|
||||
|
||||
int reg;
|
||||
int elem;
|
||||
ShaderBuiltin sysattribute;
|
||||
|
||||
int numwords;
|
||||
|
||||
bool included;
|
||||
};
|
||||
|
||||
struct DebugHit
|
||||
{
|
||||
uint32_t numHits;
|
||||
float posx;
|
||||
float posy;
|
||||
float depth;
|
||||
uint32_t primitive;
|
||||
uint32_t isFrontFace;
|
||||
uint32_t sample;
|
||||
uint32_t coverage;
|
||||
uint32_t rawdata; // arbitrary, depending on shader
|
||||
};
|
||||
|
||||
ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
|
||||
uint32_t idx, uint32_t instOffset, uint32_t vertOffset)
|
||||
{
|
||||
@@ -1064,11 +1091,14 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
}
|
||||
|
||||
vector<string> floatInputs;
|
||||
vector<pair<string, pair<uint32_t, uint32_t> > >
|
||||
vector<pair<string, pair<uint32_t, uint32_t>>>
|
||||
arrays; // name, pair<start semantic index, end semantic index>
|
||||
std::vector<std::string> inputVarNames;
|
||||
|
||||
uint32_t nextreg = 0;
|
||||
|
||||
inputVarNames.resize(dxbc->m_InputSig.size());
|
||||
|
||||
for(size_t i = 0; i < dxbc->m_InputSig.size(); i++)
|
||||
{
|
||||
extractHlsl += " ";
|
||||
@@ -1085,7 +1115,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
included = false;
|
||||
}
|
||||
|
||||
bool arrayElem = false;
|
||||
int arrayIndex = -1;
|
||||
|
||||
for(size_t a = 0; a < arrays.size(); a++)
|
||||
{
|
||||
@@ -1095,7 +1125,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
{
|
||||
extractHlsl += "//";
|
||||
included = false;
|
||||
arrayElem = true;
|
||||
arrayIndex = dxbc->m_InputSig[i].semanticIndex - arrays[a].second.first;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1293,6 +1323,10 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
extractHlsl += "[" + ToStr(arrayLength) + "]";
|
||||
extractHlsl += " : " + name;
|
||||
|
||||
inputVarNames[i] = "input_" + name;
|
||||
if(arrayLength > 0)
|
||||
inputVarNames[i] += StringFormat::Fmt("[%d]", RDCMAX(0, arrayIndex));
|
||||
|
||||
if(included && dxbc->m_InputSig[i].compType == CompType::Float)
|
||||
{
|
||||
if(arrayLength == 0)
|
||||
@@ -1318,7 +1352,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
|
||||
// arrays get added all at once (because in the struct data, they are contiguous even if
|
||||
// in the input signature they're not).
|
||||
if(!arrayElem)
|
||||
if(arrayIndex < 0)
|
||||
{
|
||||
if(arrayLength == 0)
|
||||
{
|
||||
@@ -1350,19 +1384,161 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
if(rtView != NULL)
|
||||
uavslot = 1;
|
||||
|
||||
extractHlsl +=
|
||||
"struct PSInitialData { uint hit; float3 pos; uint prim; uint fface; uint sample; uint "
|
||||
"covge; float derivValid; PSInput IN; PSInput INddx; PSInput INddy; PSInput INddxfine; "
|
||||
"PSInput INddyfine; };\n\n";
|
||||
// get the multisample count
|
||||
uint32_t outputSampleCount = 1;
|
||||
|
||||
{
|
||||
ID3D11Resource *res = NULL;
|
||||
|
||||
if(depthView)
|
||||
depthView->GetResource(&res);
|
||||
else if(rtView)
|
||||
rtView->GetResource(&res);
|
||||
|
||||
if(res)
|
||||
{
|
||||
D3D11_RESOURCE_DIMENSION dim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
|
||||
res->GetType(&dim);
|
||||
|
||||
if(dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D)
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
((ID3D11Texture2D *)res)->GetDesc(&desc);
|
||||
|
||||
outputSampleCount = RDCMAX(1U, desc.SampleDesc.Count);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(res);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<GlobalState::SampleEvalCacheKey> evalSampleCacheData;
|
||||
|
||||
uint64_t sampleEvalRegisterMask = 0;
|
||||
|
||||
// if we're not rendering at MSAA, no need to fill the cache because evaluates will all return the
|
||||
// plain input anyway.
|
||||
if(outputSampleCount > 1)
|
||||
{
|
||||
// scan the instructions to see if it contains any evaluates.
|
||||
for(size_t i = 0; i < dxbc->GetNumInstructions(); i++)
|
||||
{
|
||||
const ASMOperation &op = dxbc->GetInstruction(i);
|
||||
|
||||
// skip any non-eval opcodes
|
||||
if(op.operation != OPCODE_EVAL_CENTROID && op.operation != OPCODE_EVAL_SAMPLE_INDEX &&
|
||||
op.operation != OPCODE_EVAL_SNAPPED)
|
||||
continue;
|
||||
|
||||
// the generation of this key must match what we'll generate in the corresponding lookup
|
||||
GlobalState::SampleEvalCacheKey key;
|
||||
|
||||
// all the eval opcodes have rDst, vIn as the first two operands
|
||||
key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index;
|
||||
|
||||
for(int c = 0; c < 4; c++)
|
||||
{
|
||||
if(op.operands[0].comps[c] == 0xff)
|
||||
break;
|
||||
|
||||
key.numComponents = c + 1;
|
||||
}
|
||||
|
||||
key.firstComponent = op.operands[1].comps[op.operands[0].comps[0]];
|
||||
|
||||
sampleEvalRegisterMask |= 1ULL << key.inputRegisterIndex;
|
||||
|
||||
if(op.operation == OPCODE_EVAL_CENTROID)
|
||||
{
|
||||
// nothing to do - default key is centroid, sample is -1 and offset x/y is 0
|
||||
evalSampleCacheData.insert(key);
|
||||
}
|
||||
else if(op.operation == OPCODE_EVAL_SAMPLE_INDEX)
|
||||
{
|
||||
if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64)
|
||||
{
|
||||
// hooray, only sampling a single index, just add this key
|
||||
key.sample = (int32_t)op.operands[2].values[0];
|
||||
|
||||
evalSampleCacheData.insert(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// parameter is a register and we don't know which sample will be needed, fetch them all.
|
||||
// In most cases this will be a loop over them all, so they'll all be needed anyway
|
||||
for(uint32_t c = 0; c < outputSampleCount; c++)
|
||||
{
|
||||
key.sample = (int32_t)c;
|
||||
evalSampleCacheData.insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(op.operation == OPCODE_EVAL_SNAPPED)
|
||||
{
|
||||
if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64)
|
||||
{
|
||||
// hooray, only sampling a single offset, just add this key
|
||||
key.offsetx = (int32_t)op.operands[2].values[0];
|
||||
key.offsety = (int32_t)op.operands[2].values[1];
|
||||
|
||||
evalSampleCacheData.insert(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pDevice->AddDebugMessage(
|
||||
MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning,
|
||||
"EvaluateAttributeSnapped called with dynamic parameter, caching all possible "
|
||||
"evaluations which could have performance impact.");
|
||||
|
||||
for(key.offsetx = -8; key.offsetx <= 7; key.offsetx++)
|
||||
for(key.offsety = -8; key.offsety <= 7; key.offsety++)
|
||||
evalSampleCacheData.insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extractHlsl += R"(
|
||||
struct PSInitialData
|
||||
{
|
||||
// metadata we need ourselves
|
||||
uint hit;
|
||||
float3 pos;
|
||||
uint prim;
|
||||
uint fface;
|
||||
uint sample;
|
||||
uint covge;
|
||||
float derivValid;
|
||||
|
||||
// input values
|
||||
PSInput IN;
|
||||
PSInput INddx;
|
||||
PSInput INddy;
|
||||
PSInput INddxfine;
|
||||
PSInput INddyfine;
|
||||
};
|
||||
|
||||
)";
|
||||
|
||||
extractHlsl +=
|
||||
"RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u" + ToStr(uavslot) + ");\n\n";
|
||||
extractHlsl +=
|
||||
"void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim : "
|
||||
"SV_PrimitiveID, uint sample : SV_SampleIndex, uint covge : SV_Coverage, bool fface : "
|
||||
"SV_IsFrontFace)\n{\n";
|
||||
|
||||
if(!evalSampleCacheData.empty())
|
||||
{
|
||||
// float4 is wasteful in some cases but it's easier than using ByteAddressBuffer and manual
|
||||
// packing
|
||||
extractHlsl += "RWBuffer<float4> PSEvalBuffer : register(u" + ToStr(uavslot + 1) + ");\n\n";
|
||||
}
|
||||
|
||||
extractHlsl += R"(
|
||||
void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim : SV_PrimitiveID,
|
||||
uint sample : SV_SampleIndex, uint covge : SV_Coverage,
|
||||
bool fface : SV_IsFrontFace)
|
||||
{
|
||||
)";
|
||||
extractHlsl += " uint idx = " + ToStr(overdrawLevels) + ";\n";
|
||||
extractHlsl += " if(abs(debug_pixelPos.x - " + ToStr(x) +
|
||||
".5) < 0.5f && abs(debug_pixelPos.y - " + ToStr(y) + ".5) < 0.5f)\n";
|
||||
extractHlsl += StringFormat::Fmt(
|
||||
" if(abs(debug_pixelPos.x - %u.5) < 0.5f && abs(debug_pixelPos.y - %u.5) < 0.5f)\n", x, y);
|
||||
extractHlsl += " InterlockedAdd(PSInitialBuffer[0].hit, 1, idx);\n\n";
|
||||
extractHlsl += " idx = min(idx, " + ToStr(overdrawLevels) + ");\n\n";
|
||||
extractHlsl += " PSInitialBuffer[idx].pos = debug_pixelPos.xyz;\n";
|
||||
@@ -1377,6 +1553,69 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
extractHlsl += " PSInitialBuffer[idx].INddxfine = (PSInput)0;\n";
|
||||
extractHlsl += " PSInitialBuffer[idx].INddyfine = (PSInput)0;\n";
|
||||
|
||||
if(!evalSampleCacheData.empty())
|
||||
{
|
||||
extractHlsl += StringFormat::Fmt(" uint evalIndex = idx * %zu;\n", evalSampleCacheData.size());
|
||||
|
||||
uint32_t evalIdx = 0;
|
||||
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
|
||||
{
|
||||
uint32_t keyMask = 0;
|
||||
|
||||
for(int32_t i = 0; i < key.numComponents; i++)
|
||||
keyMask |= (1 << (key.firstComponent + i));
|
||||
|
||||
// find the name of the variable matching the operand, in the case of merged input variables.
|
||||
std::string name, swizzle = "xyzw";
|
||||
for(size_t i = 0; i < dxbc->m_InputSig.size(); i++)
|
||||
{
|
||||
if(dxbc->m_InputSig[i].regIndex == (uint32_t)key.inputRegisterIndex &&
|
||||
dxbc->m_InputSig[i].systemValue == ShaderBuiltin::Undefined &&
|
||||
(dxbc->m_InputSig[i].regChannelMask & keyMask) == keyMask)
|
||||
{
|
||||
name = inputVarNames[i];
|
||||
|
||||
if(!name.empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
swizzle.resize(key.numComponents);
|
||||
|
||||
if(name.empty())
|
||||
{
|
||||
RDCERR("Couldn't find matching input variable for v%d [%d:%d]", key.inputRegisterIndex,
|
||||
key.firstComponent, key.numComponents);
|
||||
extractHlsl += StringFormat::Fmt(" PSEvalBuffer[evalIndex+%u] = 0;\n", evalIdx);
|
||||
evalIdx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
name = StringFormat::Fmt("IN.%s.%s", name.c_str(), swizzle.c_str());
|
||||
|
||||
// we must write all components, so just swizzle the values - they'll be ignored later.
|
||||
std::string expandSwizzle = swizzle;
|
||||
while(expandSwizzle.size() < 4)
|
||||
expandSwizzle.push_back('x');
|
||||
|
||||
if(key.sample >= 0)
|
||||
{
|
||||
extractHlsl += StringFormat::Fmt(
|
||||
" PSEvalBuffer[evalIndex+%u] = EvaluateAttributeAtSample(%s, %d).%s;\n", evalIdx,
|
||||
name.c_str(), key.sample, expandSwizzle.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't need to special-case EvaluateAttributeAtCentroid, since it's just a case with
|
||||
// 0,0
|
||||
extractHlsl += StringFormat::Fmt(
|
||||
" PSEvalBuffer[evalIndex+%u] = EvaluateAttributeSnapped(%s, int2(%d, %d)).%s;\n",
|
||||
evalIdx, name.c_str(), key.offsetx, key.offsety, expandSwizzle.c_str());
|
||||
}
|
||||
evalIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < floatInputs.size(); i++)
|
||||
{
|
||||
const string &name = floatInputs[i];
|
||||
@@ -1408,7 +1647,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
|
||||
bdesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
bdesc.StructureByteStride = structStride;
|
||||
bdesc.ByteWidth = bdesc.StructureByteStride * (overdrawLevels + 1);
|
||||
bdesc.ByteWidth = structStride * (overdrawLevels + 1);
|
||||
|
||||
ID3D11Buffer *initialBuf = NULL;
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &initialBuf);
|
||||
@@ -1419,14 +1658,31 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
return empty;
|
||||
}
|
||||
|
||||
ID3D11Buffer *evalBuf = NULL;
|
||||
if(!evalSampleCacheData.empty())
|
||||
{
|
||||
bdesc.StructureByteStride = 0;
|
||||
bdesc.MiscFlags = 0;
|
||||
bdesc.ByteWidth = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1));
|
||||
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &evalBuf);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str());
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
bdesc.BindFlags = 0;
|
||||
bdesc.MiscFlags = 0;
|
||||
bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
bdesc.Usage = D3D11_USAGE_STAGING;
|
||||
bdesc.StructureByteStride = 0;
|
||||
bdesc.ByteWidth = structStride * (overdrawLevels + 1);
|
||||
|
||||
ID3D11Buffer *stageBuf = NULL;
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &stageBuf);
|
||||
ID3D11Buffer *initialStageBuf = NULL;
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &initialStageBuf);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
@@ -1434,6 +1690,22 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
return empty;
|
||||
}
|
||||
|
||||
uint32_t evalStructStride = uint32_t(evalSampleCacheData.size() * sizeof(Vec4f));
|
||||
|
||||
ID3D11Buffer *evalStageBuf = NULL;
|
||||
if(evalBuf)
|
||||
{
|
||||
bdesc.ByteWidth = evalStructStride * (overdrawLevels + 1);
|
||||
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &evalStageBuf);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str());
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc;
|
||||
uavdesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
uavdesc.Buffer.FirstElement = 0;
|
||||
@@ -1450,12 +1722,30 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
return empty;
|
||||
}
|
||||
|
||||
ID3D11UnorderedAccessView *evalUAV = NULL;
|
||||
if(evalBuf)
|
||||
{
|
||||
uavdesc.Buffer.NumElements = (overdrawLevels + 1) * (uint32_t)evalSampleCacheData.size();
|
||||
uavdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
hr = m_pDevice->CreateUnorderedAccessView(evalBuf, &uavdesc, &evalUAV);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str());
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
UINT zero = 0;
|
||||
m_pImmediateContext->ClearUnorderedAccessViewUint(initialUAV, &zero);
|
||||
if(evalUAV)
|
||||
m_pImmediateContext->ClearUnorderedAccessViewUint(evalUAV, &zero);
|
||||
|
||||
ID3D11UnorderedAccessView *uavs[] = {initialUAV, evalUAV};
|
||||
|
||||
UINT count = (UINT)-1;
|
||||
m_pImmediateContext->OMSetRenderTargetsAndUnorderedAccessViews(uavslot, &rtView, depthView,
|
||||
uavslot, 1, &initialUAV, &count);
|
||||
uavslot, 2, uavs, &count);
|
||||
m_pImmediateContext->PSSetShader(extract, NULL, 0);
|
||||
|
||||
SAFE_RELEASE(rtView);
|
||||
@@ -1466,11 +1756,12 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
|
||||
m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw);
|
||||
|
||||
m_pImmediateContext->CopyResource(stageBuf, initialBuf);
|
||||
m_pImmediateContext->CopyResource(initialStageBuf, initialBuf);
|
||||
m_pImmediateContext->CopyResource(evalStageBuf, evalBuf);
|
||||
}
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
hr = m_pImmediateContext->Map(stageBuf, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
hr = m_pImmediateContext->Map(initialStageBuf, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
@@ -1478,14 +1769,36 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
return empty;
|
||||
}
|
||||
|
||||
byte *initialData = new byte[bdesc.ByteWidth];
|
||||
memcpy(initialData, mapped.pData, bdesc.ByteWidth);
|
||||
byte *initialData = new byte[structStride * (overdrawLevels + 1)];
|
||||
memcpy(initialData, mapped.pData, structStride * (overdrawLevels + 1));
|
||||
|
||||
m_pImmediateContext->Unmap(stageBuf, 0);
|
||||
m_pImmediateContext->Unmap(initialStageBuf, 0);
|
||||
|
||||
byte *evalData = NULL;
|
||||
|
||||
if(evalStageBuf)
|
||||
{
|
||||
hr = m_pImmediateContext->Map(evalStageBuf, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
RDCERR("Failed to map stage buff HRESULT: %s", ToStr(hr).c_str());
|
||||
return empty;
|
||||
}
|
||||
|
||||
evalData = new byte[evalStructStride * (overdrawLevels + 1)];
|
||||
memcpy(evalData, mapped.pData, evalStructStride * (overdrawLevels + 1));
|
||||
|
||||
m_pImmediateContext->Unmap(evalStageBuf, 0);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(initialUAV);
|
||||
SAFE_RELEASE(initialBuf);
|
||||
SAFE_RELEASE(stageBuf);
|
||||
SAFE_RELEASE(initialStageBuf);
|
||||
|
||||
SAFE_RELEASE(evalUAV);
|
||||
SAFE_RELEASE(evalBuf);
|
||||
SAFE_RELEASE(evalStageBuf);
|
||||
|
||||
SAFE_RELEASE(extract);
|
||||
|
||||
@@ -1497,6 +1810,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
{
|
||||
RDCLOG("No hit for this event");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
return empty;
|
||||
}
|
||||
|
||||
@@ -1534,6 +1848,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
}
|
||||
|
||||
DebugHit *winner = NULL;
|
||||
float *evalSampleCache = (float *)evalData;
|
||||
|
||||
if(sample == ~0U)
|
||||
sample = 0;
|
||||
@@ -1545,7 +1860,10 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
DebugHit *hit = (DebugHit *)(initialData + i * structStride);
|
||||
|
||||
if(hit->primitive == primitive && hit->sample == sample)
|
||||
{
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1560,6 +1878,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
depthFunc == D3D11_COMPARISON_NOT_EQUAL || depthFunc == D3D11_COMPARISON_EQUAL)
|
||||
{
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1569,7 +1888,10 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
(depthFunc == D3D11_COMPARISON_GREATER_EQUAL && hit->depth >= winner->depth))
|
||||
{
|
||||
if(hit->sample == sample)
|
||||
{
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1578,6 +1900,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
{
|
||||
RDCLOG("Couldn't find any pixels that passed depth test at target co-ordinates");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
return empty;
|
||||
}
|
||||
|
||||
@@ -1587,6 +1910,8 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
GetDebugManager()->CreateShaderGlobalState(global, dxbc, rs->OM.UAVStartSlot, rs->OM.UAVs,
|
||||
rs->PS.SRVs);
|
||||
|
||||
global.sampleEvalRegisterMask = sampleEvalRegisterMask;
|
||||
|
||||
{
|
||||
DebugHit *hit = winner;
|
||||
|
||||
@@ -1603,13 +1928,14 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
|
||||
uint32_t *data = &hit->rawdata;
|
||||
|
||||
float *ddx = (float *)data;
|
||||
float *pos_ddx = (float *)data;
|
||||
|
||||
// ddx(SV_Position.x) MUST be 1.0
|
||||
if(*ddx != 1.0f)
|
||||
if(*pos_ddx != 1.0f)
|
||||
{
|
||||
RDCERR("Derivatives invalid");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
return empty;
|
||||
}
|
||||
|
||||
@@ -1661,33 +1987,76 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
quad[i].SetHelper();
|
||||
}
|
||||
|
||||
// We make the assumption that the coarse derivatives are
|
||||
// generated from (0,0) in the quad, and fine derivatives
|
||||
// are generated from the destination index and its
|
||||
// neighbours in X and Y.
|
||||
// This isn't spec'd but we must assume something and this
|
||||
// will hopefully get us closest to reproducing actual
|
||||
// results.
|
||||
// fetch any inputs that were evaluated at sample granularity
|
||||
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
|
||||
{
|
||||
// start with the basic input value
|
||||
ShaderVariable var = traces[destIdx].inputs[key.inputRegisterIndex];
|
||||
|
||||
// copy over the value into the variable
|
||||
memcpy(var.value.fv, evalSampleCache, var.columns * sizeof(float));
|
||||
|
||||
// store in the global cache for each quad. We'll apply derivatives below to adjust for each
|
||||
GlobalState::SampleEvalCacheKey k = key;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
k.quadIndex = i;
|
||||
global.sampleEvalCache[k] = var;
|
||||
}
|
||||
|
||||
// advance past this data - always by float4 as that's the buffer st ride
|
||||
evalSampleCache += 4;
|
||||
}
|
||||
|
||||
// We make the assumption that the coarse derivatives are generated from (0,0) in the quad, and
|
||||
// fine derivatives are generated from the destination index and its neighbours in X and Y.
|
||||
// This isn't spec'd but we must assume something and this will hopefully get us closest to
|
||||
// reproducing actual results.
|
||||
//
|
||||
// For debugging, we need members of the quad to be able
|
||||
// to generate coarse and fine derivatives.
|
||||
// For debugging, we need members of the quad to be able to generate coarse and fine
|
||||
// derivatives.
|
||||
//
|
||||
// For (0,0) we only need the coarse derivatives to get
|
||||
// our neighbours (1,0) and (0,1) which will give us
|
||||
// coarse and fine derivatives being identical.
|
||||
// For (0,0) we only need the coarse derivatives to get our neighbours (1,0) and (0,1) which
|
||||
// will give us coarse and fine derivatives being identical.
|
||||
//
|
||||
// For the others we will need to use a combination of
|
||||
// coarse and fine derivatives to get the diagonal element
|
||||
// in the quad. E.g. for (1,1):
|
||||
// For the others we will need to use a combination of coarse and fine derivatives to get the
|
||||
// diagonal element in the quad. In the examples below, remember that the quad indices are:
|
||||
//
|
||||
// +---+---+
|
||||
// | 0 | 1 |
|
||||
// +---+---+
|
||||
// | 2 | 3 |
|
||||
// +---+---+
|
||||
//
|
||||
// And that we have definitions of the derivatives:
|
||||
//
|
||||
// ddx_coarse = (1,0) - (0,0)
|
||||
// ddy_coarse = (0,1) - (0,0)
|
||||
//
|
||||
// i.e. the same for all members of the quad
|
||||
//
|
||||
// ddx_fine = (x,y) - (1-x,y)
|
||||
// ddy_fine = (x,y) - (x,1-y)
|
||||
//
|
||||
// i.e. the difference to the neighbour of our desired invocation (the one we have the actual
|
||||
// inputs for, from gathering above).
|
||||
//
|
||||
// So e.g. if our thread is at (1,1) destIdx = 3
|
||||
//
|
||||
// (1,0) = (1,1) - ddx_fine
|
||||
// (0,1) = (1,1) - ddy_fine
|
||||
// (0,0) = (1,1) - ddy_fine - ddx_coarse
|
||||
//
|
||||
// This only works if coarse and fine are calculated as we
|
||||
// are assuming.
|
||||
// and ddy_coarse is unused. For (1,0) destIdx = 1:
|
||||
//
|
||||
// (1,1) = (1,0) + ddy_fine
|
||||
// (0,1) = (1,0) - ddx_coarse + ddy_coarse
|
||||
// (0,0) = (1,0) - ddx_coarse
|
||||
//
|
||||
// and ddx_fine is unused (it's identical to ddx_coarse anyway)
|
||||
|
||||
ddx = (float *)data;
|
||||
// this is the value of input[1] - input[0]
|
||||
float *ddx_coarse = (float *)data;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
@@ -1697,41 +2066,20 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 0)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[1].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddx[w];
|
||||
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddx[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddx_coarse, 1.0f, 1, 3);
|
||||
else if(destIdx == 1)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[0].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddx[w];
|
||||
traces[2].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddx[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddx_coarse, -1.0f, 0, 2);
|
||||
else if(destIdx == 2)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[1].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddx[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddx_coarse, 1.0f, 1);
|
||||
else if(destIdx == 3)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[0].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddx[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddx_coarse, -1.0f, 0);
|
||||
}
|
||||
|
||||
ddx += initialValues[i].numwords;
|
||||
ddx_coarse += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
float *ddy = ddx;
|
||||
// this is the value of input[2] - input[0]
|
||||
float *ddy_coarse = ddx_coarse;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
@@ -1741,34 +2089,17 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 0)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[2].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddy[w];
|
||||
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddy[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddy_coarse, 1.0f, 2, 3);
|
||||
else if(destIdx == 1)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[2].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddy[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddy_coarse, 1.0f, 2);
|
||||
else if(destIdx == 2)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[0].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddy[w];
|
||||
traces[1].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddy[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddy_coarse, -1.0f, 0, 1);
|
||||
}
|
||||
|
||||
ddy += initialValues[i].numwords;
|
||||
ddy_coarse += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
float *ddxfine = ddy;
|
||||
float *ddxfine = ddy_coarse;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
@@ -1778,19 +2109,9 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 2)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddxfine[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddxfine, 1.0f, 3);
|
||||
else if(destIdx == 3)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[2].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddxfine[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddxfine, -1.0f, 2);
|
||||
}
|
||||
|
||||
ddxfine += initialValues[i].numwords;
|
||||
@@ -1806,20 +2127,9 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 1)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] += ddyfine[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddyfine, 1.0f, 3);
|
||||
else if(destIdx == 3)
|
||||
{
|
||||
for(int w = 0; w < initialValues[i].numwords; w++)
|
||||
{
|
||||
traces[1].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddyfine[w];
|
||||
traces[0].inputs[initialValues[i].reg].value.fv[initialValues[i].elem + w] -= ddyfine[w];
|
||||
}
|
||||
}
|
||||
ApplyDerivatives(global, traces, initialValues[i], ddyfine, -1.0f, 0, 1);
|
||||
}
|
||||
|
||||
ddyfine += initialValues[i].numwords;
|
||||
@@ -1827,6 +2137,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
|
||||
vector<ShaderDebugState> states;
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ VarType State::OperationType(const OpcodeType &op) const
|
||||
case OPCODE_BUFINFO:
|
||||
case OPCODE_SAMPLE_INFO:
|
||||
case OPCODE_SAMPLE_POS:
|
||||
case OPCODE_EVAL_CENTROID:
|
||||
case OPCODE_EVAL_SAMPLE_INDEX:
|
||||
case OPCODE_EVAL_SNAPPED:
|
||||
case OPCODE_LOD:
|
||||
case OPCODE_LD:
|
||||
case OPCODE_LD_MS: return VarType::Float;
|
||||
@@ -2745,6 +2748,79 @@ State State::GetNext(GlobalState &global, State quad[4]) const
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_EVAL_CENTROID:
|
||||
case OPCODE_EVAL_SAMPLE_INDEX:
|
||||
case OPCODE_EVAL_SNAPPED:
|
||||
{
|
||||
// opcodes only seem to be supported for regular inputs
|
||||
RDCASSERT(op.operands[1].type == TYPE_INPUT);
|
||||
|
||||
GlobalState::SampleEvalCacheKey key;
|
||||
|
||||
key.quadIndex = quadIndex;
|
||||
|
||||
// if this is TYPE_INPUT we can look up the index directly
|
||||
key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index;
|
||||
|
||||
for(int c = 0; c < 4; c++)
|
||||
{
|
||||
if(op.operands[0].comps[c] == 0xff)
|
||||
break;
|
||||
|
||||
key.numComponents = c + 1;
|
||||
}
|
||||
|
||||
key.firstComponent = op.operands[1].comps[op.operands[0].comps[0]];
|
||||
|
||||
if(op.operation == OPCODE_EVAL_SAMPLE_INDEX)
|
||||
{
|
||||
key.sample = srcOpers[1].value.i.x;
|
||||
}
|
||||
else if(op.operation == OPCODE_EVAL_SNAPPED)
|
||||
{
|
||||
key.offsetx = RDCCLAMP(srcOpers[1].value.i.x, -8, 7);
|
||||
key.offsety = RDCCLAMP(srcOpers[1].value.i.y, -8, 7);
|
||||
}
|
||||
else if(op.operation == OPCODE_EVAL_CENTROID)
|
||||
{
|
||||
// OPCODE_EVAL_CENTROID is the default, -1 sample and 0,0 offset
|
||||
}
|
||||
|
||||
// look up this combination in the cache, if we get a hit then return that value.
|
||||
auto it = global.sampleEvalCache.find(key);
|
||||
if(it != global.sampleEvalCache.end())
|
||||
{
|
||||
// perform source operand swizzling
|
||||
ShaderVariable var = it->second;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(op.operands[1].comps[i] < 4)
|
||||
var.value.uv[i] = it->second.value.uv[op.operands[1].comps[i]];
|
||||
|
||||
s.SetDst(op.operands[0], op, var);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we got here, either the cache is empty (we're not rendering MSAA at all) so we should
|
||||
// just return the interpolant, or something went wrong and the item we want isn't cached so
|
||||
// the best we can do is return the interpolant.
|
||||
|
||||
if(!global.sampleEvalCache.empty())
|
||||
{
|
||||
device->AddDebugMessage(
|
||||
MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning,
|
||||
StringFormat::Fmt(
|
||||
"Shader debugging %d: %s\n"
|
||||
"No sample evaluate found in cache. Possible out-of-bounds sample index",
|
||||
s.nextInstruction - 1, op.str.c_str()));
|
||||
}
|
||||
|
||||
s.SetDst(op.operands[0], op, srcOpers[0]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_SAMPLE_INFO:
|
||||
case OPCODE_SAMPLE_POS:
|
||||
{
|
||||
|
||||
@@ -108,6 +108,43 @@ public:
|
||||
};
|
||||
|
||||
vector<groupsharedMem> groupshared;
|
||||
|
||||
struct SampleEvalCacheKey
|
||||
{
|
||||
int32_t quadIndex = -1; // index of this thread in the quad
|
||||
int32_t inputRegisterIndex = -1; // index of the input register
|
||||
int32_t firstComponent = 0; // the first component in the register
|
||||
int32_t numComponents = 0; // how many components in the register
|
||||
int32_t sample = -1; // -1 for offset-from-centroid lookups
|
||||
int32_t offsetx = 0, offsety = 0; // integer offset from centroid
|
||||
|
||||
bool operator<(const SampleEvalCacheKey &o) const
|
||||
{
|
||||
if(quadIndex != o.quadIndex)
|
||||
return quadIndex < o.quadIndex;
|
||||
|
||||
if(inputRegisterIndex != o.inputRegisterIndex)
|
||||
return inputRegisterIndex < o.inputRegisterIndex;
|
||||
|
||||
if(firstComponent != o.firstComponent)
|
||||
return firstComponent < o.firstComponent;
|
||||
|
||||
if(numComponents != o.numComponents)
|
||||
return numComponents < o.numComponents;
|
||||
|
||||
if(sample != o.sample)
|
||||
return sample < o.sample;
|
||||
|
||||
if(offsetx != o.offsetx)
|
||||
return offsetx < o.offsetx;
|
||||
|
||||
return offsety < o.offsety;
|
||||
}
|
||||
};
|
||||
|
||||
// a bitmask of which registers were fetched into the cache, for quick checking
|
||||
uint64_t sampleEvalRegisterMask = 0;
|
||||
std::map<SampleEvalCacheKey, ShaderVariable> sampleEvalCache;
|
||||
};
|
||||
|
||||
class State : public ShaderDebugState
|
||||
|
||||
Reference in New Issue
Block a user