Files
renderdoc/renderdoc/driver/d3d11/d3d11_analyse.cpp
T

4709 lines
138 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* 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 "maths/vec.h"
#include "d3d11_manager.h"
#include "d3d11_context.h"
#include "d3d11_debug.h"
#include "shaders/dxbc_debug.h"
#include "maths/formatpacking.h"
#include "data/resource.h"
#include "serialise/serialiser.h"
#include "common/string_utils.h"
#include "driver/d3d11/d3d11_resources.h"
#include "driver/d3d11/d3d11_renderstate.h"
void D3D11DebugManager::FillCBufferVariables(const string &prefix, size_t &offset, bool flatten,
const vector<DXBC::CBufferVariable> &invars, vector<ShaderVariable> &outvars,
const vector<byte> &data)
{
using namespace DXBC;
using namespace ShaderDebug;
size_t o = offset;
for(size_t v=0; v < invars.size(); v++)
{
size_t vec = o + invars[v].descriptor.offset/16;
size_t comp = (invars[v].descriptor.offset - (invars[v].descriptor.offset&~0xf))/4;
size_t sz = RDCMAX(1U, invars[v].type.descriptor.bytesize/16);
offset = vec + sz;
string basename = prefix + invars[v].name;
uint32_t rows = invars[v].type.descriptor.rows;
uint32_t cols = invars[v].type.descriptor.cols;
uint32_t elems = RDCMAX(1U,invars[v].type.descriptor.elements);
if(!invars[v].type.members.empty())
{
char buf[64] = {0};
StringFormat::snprintf(buf, 63, "[%d]", elems);
ShaderVariable var;
var.name = basename;
var.rows = var.columns = 0;
var.type = eVar_Float;
std::vector<ShaderVariable> varmembers;
if(elems > 1)
{
for(uint32_t i=0; i < elems; i++)
{
StringFormat::snprintf(buf, 63, "[%d]", i);
if(flatten)
{
FillCBufferVariables(basename + buf + ".", vec, flatten, invars[v].type.members, outvars, data);
}
else
{
ShaderVariable vr;
vr.name = basename + buf;
vr.rows = vr.columns = 0;
vr.type = eVar_Float;
std::vector<ShaderVariable> mems;
FillCBufferVariables("", vec, flatten, invars[v].type.members, mems, data);
vr.members = mems;
varmembers.push_back(vr);
}
}
}
else
{
if(flatten)
FillCBufferVariables(basename + ".", vec, flatten, invars[v].type.members, outvars, data);
else
FillCBufferVariables("", vec, flatten, invars[v].type.members, varmembers, data);
}
if(!flatten)
{
var.members = varmembers;
outvars.push_back(var);
}
continue;
}
size_t elemByteSize = 4;
VarType type = eVar_Float;
switch(invars[v].type.descriptor.type)
{
case VARTYPE_INT:
type = eVar_Int;
break;
case VARTYPE_FLOAT:
type = eVar_Float;
break;
case VARTYPE_BOOL:
case VARTYPE_UINT:
case VARTYPE_UINT8:
type = eVar_UInt;
break;
case VARTYPE_DOUBLE:
elemByteSize = 8;
type = eVar_Double;
break;
default:
RDCFATAL("Unexpected type in constant buffer");
}
bool columnMajor = invars[v].type.descriptor.varClass == CLASS_MATRIX_COLUMNS;
size_t outIdx = vec;
if(!flatten)
{
outIdx = outvars.size();
outvars.resize(RDCMAX(outIdx+1, outvars.size()));
}
else
{
if(columnMajor)
outvars.resize(RDCMAX(outIdx+cols*elems, outvars.size()));
else
outvars.resize(RDCMAX(outIdx+rows*elems, outvars.size()));
}
size_t dataOffset = vec*16 + comp*4;
if(outvars[outIdx].name.count > 0)
{
RDCASSERT(flatten);
RDCASSERT(outvars[vec].rows == 1);
RDCASSERT(outvars[vec].columns == comp);
RDCASSERT(rows == 1);
string combinedName = outvars[outIdx].name.elems;
combinedName += ", " + basename;
outvars[outIdx].name = combinedName;
outvars[outIdx].rows = 1;
outvars[outIdx].columns += cols;
if(dataOffset < data.size())
{
const byte *d = &data[dataOffset];
memcpy(&outvars[outIdx].value.uv[comp], d, RDCMIN(data.size()-dataOffset, elemByteSize*cols));
}
}
else
{
outvars[outIdx].name = basename;
outvars[outIdx].rows = 1;
outvars[outIdx].type = type;
outvars[outIdx].columns = cols;
ShaderVariable &var = outvars[outIdx];
bool isArray = invars[v].type.descriptor.elements > 1;
if(rows*elems == 1)
{
if(dataOffset < data.size())
{
const byte *d = &data[dataOffset];
memcpy(&outvars[outIdx].value.uv[flatten ? comp : 0], d, RDCMIN(data.size()-dataOffset, elemByteSize*cols));
}
}
else if(!isArray && !flatten)
{
outvars[outIdx].rows = rows;
if(dataOffset < data.size())
{
const byte *d = &data[dataOffset];
RDCASSERT(rows <= 4 && rows*cols <= 16);
if(columnMajor)
{
uint32_t tmp[16] = {0};
// matrices always have 4 columns, for padding reasons (the same reason arrays
// put every element on a new vec4)
for(uint32_t r=0; r < rows; r++)
{
size_t srcoffs = 4*elemByteSize*r;
size_t dstoffs = cols*elemByteSize*r;
memcpy((byte *)(tmp) + dstoffs, d + srcoffs,
RDCMIN(data.size()-dataOffset + srcoffs, elemByteSize*cols));
}
// transpose
for(size_t r=0; r < rows; r++)
for(size_t c=0; c < cols; c++)
outvars[outIdx].value.uv[r*cols+c] = tmp[c*rows+r];
}
else // CLASS_MATRIX_ROWS or other data not to transpose.
{
// matrices always have 4 columns, for padding reasons (the same reason arrays
// put every element on a new vec4)
for(uint32_t r=0; r < rows; r++)
{
size_t srcoffs = 4*elemByteSize*r;
size_t dstoffs = cols*elemByteSize*r;
memcpy((byte *)(&outvars[outIdx].value.uv[0]) + dstoffs, d + srcoffs,
RDCMIN(data.size()-dataOffset + srcoffs, elemByteSize*cols));
}
}
}
}
else if(rows*elems > 1)
{
char buf[64] = {0};
var.name = outvars[outIdx].name;
vector<ShaderVariable> varmembers;
vector<ShaderVariable> *out = &outvars;
size_t rowCopy = 1;
uint32_t registers = rows;
uint32_t regLen = cols;
const char *regName = "row";
if(!flatten)
{
var.rows = 0;
var.columns = 0;
outIdx = 0;
out = &varmembers;
varmembers.resize(elems);
rowCopy = rows;
rows = 1;
registers = 1;
}
else
{
if(columnMajor)
{
registers = cols;
regLen = rows;
regName = "col";
}
}
string base = outvars[outIdx].name.elems;
for(size_t r=0; r < registers*elems; r++)
{
if(isArray && registers > 1)
StringFormat::snprintf(buf, 63, "[%d].%hs%d", r/registers, regName, r%registers);
else if(registers > 1)
StringFormat::snprintf(buf, 63, ".%hs%d", regName, r);
else
StringFormat::snprintf(buf, 63, "[%d]", r);
(*out)[outIdx+r].name = base + buf;
(*out)[outIdx+r].rows = (uint32_t)rowCopy;
(*out)[outIdx+r].type = type;
(*out)[outIdx+r].columns = regLen;
size_t dataOffset = (vec+r*rowCopy)*16;
if(dataOffset < data.size())
{
const byte *d = &data[dataOffset];
memcpy(&((*out)[outIdx+r].value.uv[0]), d, RDCMIN(data.size()-dataOffset, elemByteSize*rowCopy*regLen));
if(!flatten && columnMajor)
{
ShaderVariable tmp = (*out)[outIdx];
// transpose
for(size_t r=0; r < rows; r++)
for(size_t c=0; c < cols; c++)
(*out)[outIdx].value.uv[r*cols+c] = tmp.value.uv[c*rows+r];
}
}
}
if(!flatten)
var.members = varmembers;
}
}
}
}
void D3D11DebugManager::FillCBufferVariables(const vector<DXBC::CBufferVariable> &invars, vector<ShaderVariable> &outvars,
bool flattenVec4s, const vector<byte> &data)
{
size_t zero = 0;
vector<ShaderVariable> v;
FillCBufferVariables("", zero, flattenVec4s, invars, v, data);
outvars.reserve(v.size());
for(size_t i=0; i < v.size(); i++)
outvars.push_back(v[i]);
}
ShaderDebug::State D3D11DebugManager::CreateShaderDebugState(ShaderDebugTrace &trace, int quadIdx, DXBC::DXBCFile *dxbc, vector<byte> *cbufData)
{
using namespace DXBC;
using namespace ShaderDebug;
State initialState = State(quadIdx, &trace, dxbc, m_WrappedDevice);
// use pixel shader here to get inputs
int32_t maxReg = -1;
for(size_t i=0; i < dxbc->m_InputSig.size(); i++)
maxReg = RDCMAX(maxReg, (int32_t)dxbc->m_InputSig[i].regIndex);
bool inputCoverage = false;
for(size_t i=0; i < dxbc->m_Declarations.size(); i++)
{
if(dxbc->m_Declarations[i].declaration == OPCODE_DCL_INPUT &&
dxbc->m_Declarations[i].operand.type == TYPE_INPUT_COVERAGE_MASK)
{
inputCoverage = true;
break;
}
}
if(maxReg >= 0 || inputCoverage)
{
create_array(trace.inputs, maxReg+1 + (inputCoverage?1:0));
for(size_t i=0; i < dxbc->m_InputSig.size(); i++)
{
char buf[64] = {0};
SigParameter &sig = dxbc->m_InputSig[i];
StringFormat::snprintf(buf, 63, "v%d", sig.regIndex);
ShaderVariable v;
v.name = StringFormat::Fmt("%hs (%hs)", buf, sig.semanticIdxName.elems);
v.rows = 1;
v.columns =
sig.regChannelMask & 0x8 ? 4 :
sig.regChannelMask & 0x4 ? 3 :
sig.regChannelMask & 0x2 ? 2 :
sig.regChannelMask & 0x1 ? 1 :
0;
if(sig.compType == eCompType_UInt)
v.type = eVar_UInt;
else if(sig.compType == eCompType_SInt)
v.type = eVar_Int;
if(trace.inputs[sig.regIndex].columns == 0)
trace.inputs[sig.regIndex] = v;
else
trace.inputs[sig.regIndex].columns = RDCMAX(trace.inputs[sig.regIndex].columns, v.columns);
}
if(inputCoverage)
{
trace.inputs[maxReg+1] = ShaderVariable("vCoverage", 0U, 0U, 0U, 0U);
trace.inputs[maxReg+1].columns = 1;
}
}
uint32_t specialOutputs = 0;
maxReg = -1;
for(size_t i=0; i < dxbc->m_OutputSig.size(); i++)
{
if(dxbc->m_OutputSig[i].regIndex == ~0U)
specialOutputs++;
else
maxReg = RDCMAX(maxReg, (int32_t)dxbc->m_OutputSig[i].regIndex);
}
if(maxReg >= 0 || specialOutputs > 0)
{
create_array(initialState.outputs, maxReg+1 + specialOutputs);
for(size_t i=0; i < dxbc->m_OutputSig.size(); i++)
{
SigParameter &sig = dxbc->m_OutputSig[i];
if(sig.regIndex == ~0U)
continue;
char buf[64] = {0};
StringFormat::snprintf(buf, 63, "o%d", sig.regIndex);
ShaderVariable v;
v.name = StringFormat::Fmt("%hs (%hs)", buf, sig.semanticIdxName.elems);
v.rows = 1;
v.columns =
sig.regChannelMask & 0x8 ? 4 :
sig.regChannelMask & 0x4 ? 3 :
sig.regChannelMask & 0x2 ? 2 :
sig.regChannelMask & 0x1 ? 1 :
0;
if(initialState.outputs[sig.regIndex].columns == 0)
initialState.outputs[sig.regIndex] = v;
else
initialState.outputs[sig.regIndex].columns = RDCMAX(initialState.outputs[sig.regIndex].columns, v.columns);
}
int32_t outIdx = maxReg+1;
for(size_t i=0; i < dxbc->m_OutputSig.size(); i++)
{
SigParameter &sig = dxbc->m_OutputSig[i];
if(sig.regIndex != ~0U)
continue;
ShaderVariable v;
if(sig.systemValue == eAttr_OutputControlPointIndex) v.name = "vOutputControlPointID";
else if(sig.systemValue == eAttr_DepthOutput) v.name = "oDepth";
else if(sig.systemValue == eAttr_DepthOutputLessEqual) v.name = "oDepthLessEqual";
else if(sig.systemValue == eAttr_DepthOutputGreaterEqual) v.name = "oDepthGreaterEqual";
else if(sig.systemValue == eAttr_MSAACoverage) v.name = "oMask";
//if(sig.systemValue == TYPE_OUTPUT_CONTROL_POINT) str = "oOutputControlPoint";
else
{
RDCERR("Unhandled output: %hs (%d)", sig.semanticName, sig.systemValue);
continue;
}
v.rows = 1;
v.columns =
sig.regChannelMask & 0x8 ? 4 :
sig.regChannelMask & 0x4 ? 3 :
sig.regChannelMask & 0x2 ? 2 :
sig.regChannelMask & 0x1 ? 1 :
0;
initialState.outputs[outIdx++] = v;
}
}
create_array(trace.cbuffers, dxbc->m_CBuffers.size());
for(size_t i=0; i < dxbc->m_CBuffers.size(); i++)
{
if(dxbc->m_CBuffers[i].descriptor.type != CBuffer::Descriptor::TYPE_CBUFFER)
continue;
vector<ShaderVariable> vars;
FillCBufferVariables(dxbc->m_CBuffers[i].variables, vars, true, cbufData[i]);
trace.cbuffers[i] = vars;
for(int32_t c=0; c < trace.cbuffers[i].count; c++)
trace.cbuffers[i][c].name = StringFormat::Fmt("cb%u[%u] (%hs)", (uint32_t)i, (uint32_t)c, trace.cbuffers[i][c].name.elems);
}
initialState.Init();
return initialState;
}
void D3D11DebugManager::CreateShaderGlobalState(ShaderDebug::GlobalState &global, uint32_t UAVStartSlot, ID3D11UnorderedAccessView **UAVs, ID3D11ShaderResourceView **SRVs)
{
for(int i=0; UAVs != NULL && i+UAVStartSlot < D3D11_PS_CS_UAV_REGISTER_COUNT; i++)
{
int dsti = i+UAVStartSlot;
if(UAVs[i])
{
ID3D11Resource *res = NULL;
UAVs[i]->GetResource(&res);
global.uavs[dsti].hiddenCounter = GetStructCount(UAVs[i]);
D3D11_UNORDERED_ACCESS_VIEW_DESC udesc;
UAVs[i]->GetDesc(&udesc);
if(udesc.Format != DXGI_FORMAT_UNKNOWN)
{
ResourceFormat fmt = MakeResourceFormat(udesc.Format);
global.uavs[dsti].format.byteWidth = fmt.compByteWidth;
global.uavs[dsti].format.numComps = fmt.compCount;
global.uavs[dsti].format.fmt = fmt.compType;
if(udesc.Format == DXGI_FORMAT_R11G11B10_FLOAT)
global.uavs[dsti].format.byteWidth = 11;
if(udesc.Format == DXGI_FORMAT_R10G10B10A2_UINT || udesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
global.uavs[dsti].format.byteWidth = 10;
}
if(udesc.ViewDimension == D3D11_UAV_DIMENSION_BUFFER)
{
global.uavs[dsti].firstElement = udesc.Buffer.FirstElement;
global.uavs[dsti].numElements = udesc.Buffer.NumElements;
}
if(res)
{
if(WrappedID3D11Buffer::IsAlloc(res))
{
global.uavs[dsti].data = GetBufferData((ID3D11Buffer *)res, 0, 0);
}
else
{
RDCERR("UAVs of textures currently not supported in shader debugging");
}
}
SAFE_RELEASE(res);
}
}
for(int i=0; SRVs != NULL && i < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; i++)
{
if(SRVs[i])
{
ID3D11Resource *res = NULL;
SRVs[i]->GetResource(&res);
D3D11_SHADER_RESOURCE_VIEW_DESC sdesc;
SRVs[i]->GetDesc(&sdesc);
if(sdesc.Format != DXGI_FORMAT_UNKNOWN)
{
ResourceFormat fmt = MakeResourceFormat(sdesc.Format);
global.srvs[i].format.byteWidth = fmt.compByteWidth;
global.srvs[i].format.numComps = fmt.compCount;
global.srvs[i].format.fmt = fmt.compType;
if(sdesc.Format == DXGI_FORMAT_R11G11B10_FLOAT)
global.srvs[i].format.byteWidth = 11;
if(sdesc.Format == DXGI_FORMAT_R10G10B10A2_UINT || sdesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
global.srvs[i].format.byteWidth = 10;
}
if(sdesc.ViewDimension == D3D11_SRV_DIMENSION_BUFFER)
{
// I know this isn't what the docs say, but as best as I can tell
// this is how it's used.
global.srvs[i].firstElement = sdesc.Buffer.FirstElement;
global.srvs[i].numElements = sdesc.Buffer.NumElements;
}
else if(sdesc.ViewDimension == D3D11_SRV_DIMENSION_BUFFEREX)
{
global.srvs[i].firstElement = sdesc.BufferEx.FirstElement;
global.srvs[i].numElements = sdesc.BufferEx.NumElements;
}
if(res)
{
if(WrappedID3D11Buffer::IsAlloc(res))
{
global.srvs[i].data = GetBufferData((ID3D11Buffer *)res, 0, 0);
}
}
SAFE_RELEASE(res);
}
}
}
// 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) { reg = regster; elem = element; numwords = numWords; }
int reg;
int elem;
int numwords;
};
struct DebugHit
{
uint32_t numHits;
float posx; float posy;
float depth;
uint32_t primitive;
uint32_t coverage;
uint32_t rawdata; // arbitrary, depending on shader
};
ShaderDebugTrace D3D11DebugManager::DebugVertex(uint32_t frameID, uint32_t eventID, uint32_t vertid, uint32_t instid, uint32_t idx, uint32_t instOffset, uint32_t vertOffset)
{
using namespace DXBC;
using namespace ShaderDebug;
ShaderDebugTrace empty;
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_WithoutDraw);
ID3D11VertexShader *stateVS = NULL;
m_WrappedContext->VSGetShader(&stateVS, NULL, NULL);
WrappedID3D11Shader<ID3D11VertexShader> *vs = (WrappedID3D11Shader<ID3D11VertexShader> *)stateVS;
SAFE_RELEASE(stateVS);
if(!vs)
return empty;
DXBCFile *dxbc = vs->GetDXBC();
if(!dxbc)
return empty;
D3D11RenderState *rs = m_WrappedContext->GetCurrentPipelineState();
vector<D3D11_INPUT_ELEMENT_DESC> inputlayout = m_WrappedDevice->GetLayoutDesc(rs->IA.Layout);
set<UINT> vertexbuffers;
uint32_t trackingOffs[32] = {0};
// need special handling for other step rates
for(size_t i=0; i < inputlayout.size(); i++)
{
RDCASSERT(inputlayout[i].InstanceDataStepRate <= 1);
UINT slot = RDCCLAMP(inputlayout[i].InputSlot, 0U, UINT(D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT-1));
vertexbuffers.insert(slot);
if(inputlayout[i].AlignedByteOffset == ~0U)
{
inputlayout[i].AlignedByteOffset = trackingOffs[slot];
}
else
{
trackingOffs[slot] = inputlayout[i].AlignedByteOffset;
}
ResourceFormat fmt = MakeResourceFormat(inputlayout[i].Format);
trackingOffs[slot] += fmt.compByteWidth * fmt.compCount;
}
vector<byte> vertData[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
vector<byte> instData[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
for(auto it=vertexbuffers.begin(); it != vertexbuffers.end(); ++it)
{
UINT i = *it;
if(rs->IA.VBs[i])
{
vertData[i] = GetBufferData(rs->IA.VBs[i], rs->IA.Offsets[i] + rs->IA.Strides[i]*(vertOffset+idx), rs->IA.Strides[i]);
instData[i] = GetBufferData(rs->IA.VBs[i], rs->IA.Offsets[i] + rs->IA.Strides[i]*(instOffset+instid), rs->IA.Strides[i]);
}
}
vector<byte> cbufData[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
for(int i=0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++)
if(rs->VS.ConstantBuffers[i])
cbufData[i] = GetBufferData(rs->VS.ConstantBuffers[i], rs->VS.CBOffsets[i]*sizeof(Vec4f), 0);
ShaderDebugTrace ret;
GlobalState global;
CreateShaderGlobalState(global, 0, NULL, rs->VS.SRVs);
State initialState = CreateShaderDebugState(ret, -1, dxbc, cbufData);
for(int32_t i=0; i < ret.inputs.count; i++)
{
if(dxbc->m_InputSig[i].systemValue == eAttr_None ||
dxbc->m_InputSig[i].systemValue == eAttr_Position) // SV_Position seems to get promoted automatically, but it's invalid for vertex input
{
const D3D11_INPUT_ELEMENT_DESC *el = NULL;
string signame = strlower(string(dxbc->m_InputSig[i].semanticName.elems));
for(size_t l=0; l < inputlayout.size(); l++)
{
string layoutname = strlower(string(inputlayout[l].SemanticName));
if(signame == layoutname &&
dxbc->m_InputSig[i].semanticIndex == inputlayout[l].SemanticIndex)
{
el = &inputlayout[l];
break;
}
if(signame == layoutname + ToStr::Get(inputlayout[l].SemanticIndex))
{
el = &inputlayout[l];
break;
}
}
RDCASSERT(el);
if(!el)
continue;
byte *srcData = NULL;
size_t dataSize = 0;
if(el->InputSlotClass == D3D11_INPUT_PER_VERTEX_DATA)
{
if(vertData[el->InputSlot].size() >= el->AlignedByteOffset)
{
srcData = &vertData[el->InputSlot][el->AlignedByteOffset];
dataSize = vertData[el->InputSlot].size()-el->AlignedByteOffset;
}
}
else
{
if(instData[el->InputSlot].size() >= el->AlignedByteOffset)
{
srcData = &instData[el->InputSlot][el->AlignedByteOffset];
dataSize = instData[el->InputSlot].size()-el->AlignedByteOffset;
}
}
ResourceFormat fmt = MakeResourceFormat(el->Format);
// more data needed than is provided
if(dxbc->m_InputSig[i].compCount > fmt.compCount)
{
ret.inputs[i].value.u.w = 1;
if(fmt.compType == eCompType_Float)
ret.inputs[i].value.f.w = 1.0f;
}
// interpret special formats
if(fmt.special)
{
Vec3f *v3 = (Vec3f *)ret.inputs[i].value.fv;
Vec4f *v4 = (Vec4f *)ret.inputs[i].value.fv;
// only pull in all or nothing from these,
// if there's only e.g. 3 bytes remaining don't read and unpack some of
// a 4-byte special format
size_t packedsize = 4;
if (fmt.specialFormat == eSpecial_B8G8R8A8 || fmt.specialFormat == eSpecial_B5G5R5A1 ||
fmt.specialFormat == eSpecial_B5G6R5 || fmt.specialFormat == eSpecial_B4G4R4A4)
packedsize = 2;
if(srcData == NULL || packedsize > dataSize)
{
ret.inputs[i].value.u.x =
ret.inputs[i].value.u.y =
ret.inputs[i].value.u.z =
ret.inputs[i].value.u.w = 0;
}
else if (fmt.specialFormat == eSpecial_B8G8R8A8)
{
ret.inputs[i].value.f.x = float(srcData[2])/255.0f;
ret.inputs[i].value.f.y = float(srcData[1])/255.0f;
ret.inputs[i].value.f.z = float(srcData[0])/255.0f;
ret.inputs[i].value.f.w = float(srcData[3])/255.0f;
}
else if (fmt.specialFormat == eSpecial_B5G5R5A1)
{
uint16_t packed = ((uint16_t *)srcData)[0];
*v4 = ConvertFromB5G5R5A1(packed);
}
else if (fmt.specialFormat == eSpecial_B5G6R5)
{
uint16_t packed = ((uint16_t *)srcData)[0];
*v3 = ConvertFromB5G6R5(packed);
}
else if (fmt.specialFormat == eSpecial_B4G4R4A4)
{
uint16_t packed = ((uint16_t *)srcData)[0];
*v4 = ConvertFromB4G4R4A4(packed);
}
else if (fmt.specialFormat == eSpecial_R10G10B10A2)
{
uint32_t packed = ((uint32_t *)srcData)[0];
if (fmt.compType == eCompType_UInt)
{
ret.inputs[i].value.u.z = (packed >> 0) & 0x3ff;
ret.inputs[i].value.u.y = (packed >> 10) & 0x3ff;
ret.inputs[i].value.u.x = (packed >> 20) & 0x3ff;
ret.inputs[i].value.u.w = (packed >> 30) & 0x003;
}
else
{
*v4 = ConvertFromR10G10B10A2(packed);
}
}
else if (fmt.special && fmt.specialFormat == eSpecial_R11G11B10)
{
uint32_t packed = ((uint32_t *)srcData)[0];
*v3 = ConvertFromR11G11B10(packed);
}
}
else
{
for(uint32_t c=0; c < fmt.compCount; c++)
{
if(srcData == NULL || fmt.compByteWidth > dataSize)
{
ret.inputs[i].value.uv[c] = 0;
continue;
}
dataSize -= fmt.compByteWidth;
if(fmt.compByteWidth == 1)
{
byte *src = srcData+c*fmt.compByteWidth;
if(fmt.compType == eCompType_UInt)
ret.inputs[i].value.uv[c] = *src;
else if(fmt.compType == eCompType_SInt)
ret.inputs[i].value.iv[c] = *((int8_t *)src);
else if(fmt.compType == eCompType_UNorm)
ret.inputs[i].value.fv[c] = float(*src)/255.0f;
else if(fmt.compType == eCompType_SNorm)
{
signed char *schar = (signed char *)src;
// -128 is mapped to -1, then -127 to -127 are mapped to -1 to 1
if(*schar == -128)
ret.inputs[i].value.fv[c] = -1.0f;
else
ret.inputs[i].value.fv[c] = float(*schar)/127.0f;
}
else
RDCERR("Unexpected component type");
}
else if(fmt.compByteWidth == 2)
{
uint16_t *src = (uint16_t *)(srcData+c*fmt.compByteWidth);
if(fmt.compType == eCompType_Float)
ret.inputs[i].value.fv[c] = ConvertFromHalf(*src);
else if(fmt.compType == eCompType_UInt)
ret.inputs[i].value.uv[c] = *src;
else if(fmt.compType == eCompType_SInt)
ret.inputs[i].value.iv[c] = *((int16_t *)src);
else if(fmt.compType == eCompType_UNorm)
ret.inputs[i].value.fv[c] = float(*src)/float(UINT16_MAX);
else if(fmt.compType == eCompType_SNorm)
{
int16_t *sint = (int16_t *)src;
// -32768 is mapped to -1, then -32767 to -32767 are mapped to -1 to 1
if(*sint == -32768)
ret.inputs[i].value.fv[c] = -1.0f;
else
ret.inputs[i].value.fv[c] = float(*sint)/32767.0f;
}
else
RDCERR("Unexpected component type");
}
else if(fmt.compByteWidth == 4)
{
uint32_t *src = (uint32_t *)(srcData+c*fmt.compByteWidth);
if(fmt.compType == eCompType_Float ||
fmt.compType == eCompType_UInt ||
fmt.compType == eCompType_SInt)
memcpy(&ret.inputs[i].value.uv[c], src, 4);
else
RDCERR("Unexpected component type");
}
}
}
}
else if(dxbc->m_InputSig[i].systemValue == eAttr_VertexIndex)
{
if(dxbc->m_InputSig[i].compType == eCompType_Float)
ret.inputs[i].value.f.x =
ret.inputs[i].value.f.y =
ret.inputs[i].value.f.z =
ret.inputs[i].value.f.w = (float)vertid;
else
ret.inputs[i].value.u.x =
ret.inputs[i].value.u.y =
ret.inputs[i].value.u.z =
ret.inputs[i].value.u.w = vertid;
}
else if(dxbc->m_InputSig[i].systemValue == eAttr_InstanceIndex)
{
if(dxbc->m_InputSig[i].compType == eCompType_Float)
ret.inputs[i].value.f.x =
ret.inputs[i].value.f.y =
ret.inputs[i].value.f.z =
ret.inputs[i].value.f.w = (float)instid;
else
ret.inputs[i].value.u.x =
ret.inputs[i].value.u.y =
ret.inputs[i].value.u.z =
ret.inputs[i].value.u.w = instid;
}
else
{
RDCERR("Unhandled system value semantic on VS input");
}
}
State last;
vector<ShaderDebugState> states;
states.push_back((State)initialState);
while(true)
{
if(initialState.Finished())
break;
initialState = initialState.GetNext(global, NULL);
states.push_back((State)initialState);
}
ret.states = states;
return ret;
}
ShaderDebugTrace D3D11DebugManager::DebugPixel(uint32_t frameID, uint32_t eventID, uint32_t x, uint32_t y)
{
using namespace DXBC;
using namespace ShaderDebug;
ShaderDebugTrace empty;
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_WithoutDraw);
ID3D11PixelShader *statePS = NULL;
m_WrappedContext->PSGetShader(&statePS, NULL, NULL);
WrappedID3D11Shader<ID3D11PixelShader> *ps = (WrappedID3D11Shader<ID3D11PixelShader> *)statePS;
SAFE_RELEASE(statePS);
if(!ps)
return empty;
D3D11RenderState *rs = m_WrappedContext->GetCurrentPipelineState();
DXBCFile *dxbc = ps->GetDXBC();
if(!dxbc)
return empty;
vector<DataOutput> initialValues;
string extractHlsl = "struct PSInput\n{\n";
int structureStride = 0;
if(dxbc->m_InputSig.empty())
{
extractHlsl += "float4 input_dummy : SV_Position;\n";
initialValues.push_back(DataOutput(-1, 0, 4));
structureStride += 4;
}
vector<string> floatInputs;
for(size_t i=0; i < dxbc->m_InputSig.size(); i++)
{
extractHlsl += " ";
if(dxbc->m_InputSig[i].compType == eCompType_Float)
extractHlsl += "float";
else if(dxbc->m_InputSig[i].compType == eCompType_SInt)
extractHlsl += "int";
else if(dxbc->m_InputSig[i].compType == eCompType_UInt)
extractHlsl += "uint";
else
RDCERR("Unexpected input signature type: %d", dxbc->m_InputSig[i].compType);
int numCols =
(dxbc->m_InputSig[i].regChannelMask & 0x1 ? 1 : 0) +
(dxbc->m_InputSig[i].regChannelMask & 0x2 ? 1 : 0) +
(dxbc->m_InputSig[i].regChannelMask & 0x4 ? 1 : 0) +
(dxbc->m_InputSig[i].regChannelMask & 0x8 ? 1 : 0);
structureStride += 4*numCols;
string name = dxbc->m_InputSig[i].semanticIdxName.elems;
extractHlsl += ToStr::Get((uint32_t)numCols) + " input_" + name + " : " + name;
if(dxbc->m_InputSig[i].compType == eCompType_Float)
floatInputs.push_back("input_" + name);
extractHlsl += ";\n";
int firstElem =
dxbc->m_InputSig[i].regChannelMask & 0x1 ? 0 :
dxbc->m_InputSig[i].regChannelMask & 0x2 ? 1 :
dxbc->m_InputSig[i].regChannelMask & 0x4 ? 2 :
dxbc->m_InputSig[i].regChannelMask & 0x8 ? 3 :
-1;
initialValues.push_back(DataOutput(dxbc->m_InputSig[i].regIndex, firstElem, numCols));
}
extractHlsl += "};\n\n";
uint32_t overdrawLevels = 100; // maximum number of overdraw levels
extractHlsl += "struct PSInitialData { uint hit; float3 pos; uint prim; uint covge; PSInput IN; float derivValid; PSInput INddx; PSInput INddy; };\n\n";
extractHlsl += "RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u0);\n\n";
extractHlsl += "void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim : SV_PrimitiveID, uint covge : SV_Coverage)\n{\n";
extractHlsl += " uint idx = " + ToStr::Get(overdrawLevels) + ";\n";
extractHlsl += " if(abs(debug_pixelPos.x - " + ToStr::Get(x) + ".5) < 2 && abs(debug_pixelPos.y - " + ToStr::Get(y) + ".5) < 2)\n";
extractHlsl += " InterlockedAdd(PSInitialBuffer[0].hit, 1, idx);\n\n";
extractHlsl += " idx = min(idx, " + ToStr::Get(overdrawLevels) + ");\n\n";
extractHlsl += " PSInitialBuffer[idx].pos = debug_pixelPos.xyz;\n";
extractHlsl += " PSInitialBuffer[idx].prim = prim;\n";
extractHlsl += " PSInitialBuffer[idx].covge = covge;\n";
extractHlsl += " PSInitialBuffer[idx].IN = IN;\n";
extractHlsl += " PSInitialBuffer[idx].derivValid = ddx(debug_pixelPos.x);\n";
extractHlsl += " PSInitialBuffer[idx].INddx = (PSInput)0;\n";
extractHlsl += " PSInitialBuffer[idx].INddy = (PSInput)0;\n";
for(size_t i=0; i < floatInputs.size(); i++)
{
const string &name = floatInputs[i];
extractHlsl += " PSInitialBuffer[idx].INddx." + name + " = ddx(IN." + name + ");\n";
extractHlsl += " PSInitialBuffer[idx].INddy." + name + " = ddy(IN." + name + ");\n";
}
extractHlsl += "\n}";
ID3D11PixelShader *extract = MakePShader(extractHlsl.c_str(), "ExtractInputsPS", "ps_5_0");
uint32_t structStride = sizeof(uint32_t) // uint hit;
+ sizeof(float)*3 // float3 pos;
+ sizeof(uint32_t) // uint prim;
+ sizeof(uint32_t) // uint covge;
+ sizeof(float) // float derivValid;
+ structureStride*3; // PSInput IN, INddx, INddy;
HRESULT hr = S_OK;
D3D11_BUFFER_DESC bdesc;
bdesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
bdesc.CPUAccessFlags = 0;
bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
bdesc.Usage = D3D11_USAGE_DEFAULT;
bdesc.StructureByteStride = structStride;
bdesc.ByteWidth = bdesc.StructureByteStride * (overdrawLevels+1);
ID3D11Buffer *initialBuf = NULL;
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &initialBuf);
if(FAILED(hr))
{
RDCERR("Failed to create buffer %08x", hr);
return empty;
}
bdesc.BindFlags = 0;
bdesc.MiscFlags = 0;
bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
bdesc.Usage = D3D11_USAGE_STAGING;
bdesc.StructureByteStride = 0;
ID3D11Buffer *stageBuf = NULL;
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &stageBuf);
if(FAILED(hr))
{
RDCERR("Failed to create buffer %08x", hr);
return empty;
}
D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc;
uavdesc.Format = DXGI_FORMAT_UNKNOWN;
uavdesc.Buffer.FirstElement = 0;
uavdesc.Buffer.Flags = 0;
uavdesc.Buffer.NumElements = overdrawLevels+1;
uavdesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
ID3D11UnorderedAccessView *initialUAV = NULL;
hr = m_pDevice->CreateUnorderedAccessView(initialBuf, &uavdesc, &initialUAV);
if(FAILED(hr))
{
RDCERR("Failed to create buffer %08x", hr);
return empty;
}
UINT zero = 0;
m_pImmediateContext->ClearUnorderedAccessViewUint(initialUAV, &zero);
UINT count = (UINT)-1;
ID3D11DepthStencilView *depthView = NULL;
m_pImmediateContext->OMGetRenderTargets(0, NULL, &depthView);
m_pImmediateContext->OMSetRenderTargetsAndUnorderedAccessViews(0, NULL, depthView, 0, 1, &initialUAV, &count);
m_pImmediateContext->PSSetShader(extract, NULL, 0);
SAFE_RELEASE(depthView);
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw);
m_pImmediateContext->CopyResource(stageBuf, initialBuf);
D3D11_MAPPED_SUBRESOURCE mapped;
hr = m_pImmediateContext->Map(stageBuf, 0, D3D11_MAP_READ, 0, &mapped);
if(FAILED(hr))
{
RDCERR("Failed to map stage buff %08x", hr);
return empty;
}
byte *initialData = new byte[bdesc.ByteWidth];
memcpy(initialData, mapped.pData, bdesc.ByteWidth);
m_pImmediateContext->Unmap(stageBuf, 0);
SAFE_RELEASE(initialUAV);
SAFE_RELEASE(initialBuf);
SAFE_RELEASE(stageBuf);
SAFE_RELEASE(extract);
DebugHit *buf = (DebugHit *)initialData;
if(buf[0].numHits == 0)
{
RDCLOG("No hit for this event");
return empty;
}
// if we encounter multiple hits at our destination pixel co-ord (or any other) we
// really need to check depth state here, but that's difficult so skip it for now
// we can iterate over the hits and get the depth of each from the second element
// in each struct, but we also need the test depth AND need to be able to resolve
// the depth test in the same way for each fragment.
//
// For now, just take the first. Later need to modify buf to point at the data of
// the actual passing fragment.
// also with alpha blending on we'd need to be able to pick the right one anyway.
// so really here we just need to be able to get the depth result of each hit and
// let the user choose, since multiple might pass & apply.
// our debugging quad. Order is TL, TR, BL, BR
State quad[4];
// figure out the TL pixel's coords. Assume even top left (towards 0,0)
int xTL = x&(~1);
int yTL = y&(~1);
// get the index of our desired pixel
int destIdx = (x-xTL) + 2*(y-yTL);
vector<byte> cbufData[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
for(int i=0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++)
if(rs->PS.ConstantBuffers[i])
cbufData[i] = GetBufferData(rs->PS.ConstantBuffers[i], rs->PS.CBOffsets[i]*sizeof(Vec4f), 0);
D3D11_COMPARISON_FUNC depthFunc = D3D11_COMPARISON_LESS;
if(rs->OM.DepthStencilState)
{
D3D11_DEPTH_STENCIL_DESC desc;
rs->OM.DepthStencilState->GetDesc(&desc);
depthFunc = desc.DepthFunc;
}
DebugHit *winner = NULL;
for(size_t i=0; i < buf[0].numHits && i < overdrawLevels; i++)
{
DebugHit *hit = (DebugHit *)(initialData+i*structStride);
// only interested in destination pixel
if(hit->posx != (float)x + 0.5 || hit->posy != (float)y + 0.5)
continue;
if(winner == NULL || depthFunc == D3D11_COMPARISON_ALWAYS || depthFunc == D3D11_COMPARISON_NEVER ||
depthFunc == D3D11_COMPARISON_NOT_EQUAL || depthFunc == D3D11_COMPARISON_EQUAL)
{
winner = hit;
continue;
}
if(
(depthFunc == D3D11_COMPARISON_LESS && hit->depth < winner->depth) ||
(depthFunc == D3D11_COMPARISON_LESS_EQUAL && hit->depth <= winner->depth) ||
(depthFunc == D3D11_COMPARISON_GREATER && hit->depth > winner->depth) ||
(depthFunc == D3D11_COMPARISON_GREATER_EQUAL && hit->depth >= winner->depth)
)
{
winner = hit;
}
}
if(winner == NULL)
{
RDCLOG("Couldn't find any pixels that passed depth test at target co-ordinates");
return empty;
}
ShaderDebugTrace traces[4];
GlobalState global;
CreateShaderGlobalState(global, rs->OM.UAVStartSlot, rs->OM.UAVs, rs->PS.SRVs);
{
DebugHit *hit = winner;
State initialState = CreateShaderDebugState(traces[destIdx], destIdx, dxbc, cbufData);
rdctype::array<ShaderVariable> &ins = traces[destIdx].inputs;
if(ins.count > 0 && !strcmp(ins[ins.count-1].name.elems, "vCoverage"))
ins[ins.count-1].value.u.x = hit->coverage;
initialState.semantics.coverage = hit->coverage;
uint32_t *data = &hit->rawdata;
for(size_t i=0; i < initialValues.size(); i++)
{
int32_t *rawout = NULL;
if(initialValues[i].reg >= 0)
{
rawout = &traces[destIdx].inputs[initialValues[i].reg].value.iv[initialValues[i].elem];
memcpy(rawout, data, initialValues[i].numwords*4);
}
data += initialValues[i].numwords;
}
for(int i=0; i < 4; i++)
{
if(i != destIdx)
traces[i] = traces[destIdx];
quad[i] = initialState;
quad[i].SetTrace(&traces[i]);
}
float *ddx = (float *)data;
// ddx(SV_Position.x) MUST be 1.0
if(*ddx != 1.0f)
{
RDCERR("Derivatives invalid");
return empty;
}
ddx++;
for(size_t i=0; i < initialValues.size(); i++)
{
if(initialValues[i].reg >= 0)
{
// left
if(destIdx == 0 || 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];
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem+w] += ddx[w];
}
}
else
{
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];
}
}
}
ddx += initialValues[i].numwords;
}
float *ddy = ddx;
for(size_t i=0; i < initialValues.size(); i++)
{
if(initialValues[i].reg >= 0)
{
// top
if(destIdx == 0 || 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];
traces[3].inputs[initialValues[i].reg].value.fv[initialValues[i].elem+w] += ddy[w];
}
}
else
{
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];
}
}
}
ddy += initialValues[i].numwords;
}
}
vector<ShaderDebugState> states;
states.push_back((State)quad[destIdx]);
// simulate lockstep until all threads are finished
bool finished = true;
do
{
for(size_t i = 0; i < 4; i++)
{
if(!quad[i].Finished())
quad[i] = quad[i].GetNext(global, quad);
}
states.push_back((State)quad[destIdx]);
finished = quad[destIdx].Finished();
}
while(!finished);
traces[destIdx].states = states;
return traces[destIdx];
}
ShaderDebugTrace D3D11DebugManager::DebugThread(uint32_t frameID, uint32_t eventID, uint32_t groupid[3], uint32_t threadid[3])
{
using namespace DXBC;
using namespace ShaderDebug;
ShaderDebugTrace empty;
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_WithoutDraw);
ID3D11ComputeShader *stateCS = NULL;
m_WrappedContext->CSGetShader(&stateCS, NULL, NULL);
WrappedID3D11Shader<ID3D11ComputeShader> *cs = (WrappedID3D11Shader<ID3D11ComputeShader> *)stateCS;
SAFE_RELEASE(stateCS);
if(!cs)
return empty;
DXBCFile *dxbc = cs->GetDXBC();
if(!dxbc)
return empty;
D3D11RenderState *rs = m_WrappedContext->GetCurrentPipelineState();
vector<byte> cbufData[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
for(int i=0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++)
if(rs->CS.ConstantBuffers[i])
cbufData[i] = GetBufferData(rs->CS.ConstantBuffers[i], rs->CS.CBOffsets[i]*sizeof(Vec4f), 0);
ShaderDebugTrace ret;
GlobalState global;
CreateShaderGlobalState(global, 0, rs->CS.UAVs, rs->CS.SRVs);
State initialState = CreateShaderDebugState(ret, -1, dxbc, cbufData);
for(int i=0; i < 3; i++)
{
initialState.semantics.GroupID[i] = groupid[i];
initialState.semantics.ThreadID[i] = threadid[i];
}
vector<ShaderDebugState> states;
states.push_back((State)initialState);
while(true)
{
if(initialState.Finished())
break;
initialState = initialState.GetNext(global, NULL);
states.push_back((State)initialState);
}
ret.states = states;
return ret;
}
void D3D11DebugManager::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, uint32_t mip, float pixel[4])
{
m_pImmediateContext->OMSetRenderTargets(1, &m_DebugRender.PickPixelRT, NULL);
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
m_pImmediateContext->ClearRenderTargetView(m_DebugRender.PickPixelRT, color);
D3D11_VIEWPORT viewport;
RDCEraseEl(viewport);
int oldW = GetWidth(), oldH = GetHeight();
SetOutputDimensions(100, 100);
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = 100;
viewport.Height = 100;
m_pImmediateContext->RSSetViewports(1, &viewport);
{
TextureDisplay texDisplay;
texDisplay.Red = texDisplay.Green = texDisplay.Blue = texDisplay.Alpha = true;
texDisplay.HDRMul = -1.0f;
texDisplay.linearDisplayAsGamma = true;
texDisplay.FlipY = false;
texDisplay.mip = mip;
texDisplay.CustomShader = ResourceId();
texDisplay.sliceFace = sliceFace;
texDisplay.rangemin = 0.0f;
texDisplay.rangemax = 1.0f;
texDisplay.scale = 1.0f;
texDisplay.texid = texture;
texDisplay.rawoutput = true;
texDisplay.offx = -float(x);
texDisplay.offy = -float(y);
RenderTexture(texDisplay);
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = 0;
box.right = 1;
box.top = 0;
box.bottom = 1;
ID3D11Resource *res = NULL;
m_DebugRender.PickPixelRT->GetResource(&res);
m_pImmediateContext->CopySubresourceRegion(m_DebugRender.PickPixelStageTex, 0, 0, 0, 0, res, 0, &box);
SAFE_RELEASE(res);
D3D11_MAPPED_SUBRESOURCE mapped;
mapped.pData = NULL;
HRESULT hr = m_pImmediateContext->Map(m_DebugRender.PickPixelStageTex, 0, D3D11_MAP_READ, 0, &mapped);
if(FAILED(hr))
{
RDCERR("Failed to map stage buff %08x", hr);
}
float *pix = (float *)mapped.pData;
if(pix == NULL)
{
RDCERR("Failed to map pick-pixel staging texture.");
}
else
{
pixel[0] = pix[0];
pixel[1] = pix[1];
pixel[2] = pix[2];
pixel[3] = pix[3];
}
SetOutputDimensions(oldW, oldH);
m_pImmediateContext->Unmap(m_DebugRender.PickPixelStageTex, 0);
}
// from MSDN
struct DDS_PIXELFORMAT {
DWORD dwSize;
DWORD dwFlags;
DWORD dwFourCC;
DWORD dwRGBBitCount;
DWORD dwRBitMask;
DWORD dwGBitMask;
DWORD dwBBitMask;
DWORD dwABitMask;
};
struct DDS_HEADER {
DWORD dwSize;
DWORD dwFlags;
DWORD dwHeight;
DWORD dwWidth;
DWORD dwPitchOrLinearSize;
DWORD dwDepth;
DWORD dwMipMapCount;
DWORD dwReserved1[11];
DDS_PIXELFORMAT ddspf;
DWORD dwCaps;
DWORD dwCaps2;
DWORD dwCaps3;
DWORD dwCaps4;
DWORD dwReserved2;
};
struct DDS_HEADER_DXT10 {
DXGI_FORMAT dxgiFormat;
D3D10_RESOURCE_DIMENSION resourceDimension;
UINT miscFlag;
UINT arraySize;
UINT reserved;
};
#define DDSD_CAPS 0x1
#define DDSD_HEIGHT 0x2
#define DDSD_WIDTH 0x4
#define DDSD_PITCH 0x8
#define DDSD_PIXELFORMAT 0x1000
#define DDSD_MIPMAPCOUNT 0x20000
#define DDSD_LINEARSIZE 0x80000
#define DDSD_DEPTH 0x800000
#define DDSCAPS_COMPLEX 0x8
#define DDSCAPS_MIPMAP 0x400000
#define DDSCAPS_TEXTURE 0x1000
#define DDSCAPS2_CUBEMAP 0xff00 // d3d10+ requires all cubemap faces
#define DDSCAPS2_VOLUME 0x200000
#define DDPF_ALPHAPIXELS 0x1
#define DDPF_ALPHA 0x2
#define DDPF_FOURCC 0x4
#define DDPF_RGB 0x40
#define DDPF_YUV 0x200
#define DDPF_LUMINANCE 0x20000
#define DDPF_RGBA (DDPF_RGB|DDPF_ALPHAPIXELS)
bool D3D11DebugManager::SaveTexture(ResourceId id, uint32_t saveMip, wstring path)
{
if(WrappedID3D11Texture2D::m_TextureList.find(id) == WrappedID3D11Texture2D::m_TextureList.end())
return false;
WrappedID3D11Texture2D *wrapTex = (WrappedID3D11Texture2D *)WrappedID3D11Texture2D::m_TextureList[id].m_Texture;
if(path.find(L".dds") != wstring::npos)
{
D3D11_TEXTURE2D_DESC desc = {0};
wrapTex->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
ID3D11Texture2D *dummyTex = NULL;
HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &dummyTex);
if(FAILED(hr))
{
RDCERR("Couldn't create staging texture to save. %08x", hr);
return false;
}
m_pImmediateContext->CopyResource(dummyTex, wrapTex->GetReal());
DWORD magic = 0x20534444;
DDS_HEADER header;
DDS_HEADER_DXT10 headerDXT10;
RDCEraseEl(header);
RDCEraseEl(headerDXT10);
header.dwSize = sizeof(DDS_HEADER);
header.ddspf.dwSize = sizeof(DDS_PIXELFORMAT);
header.dwWidth = desc.Width;
header.dwHeight = desc.Height;
header.dwDepth = 0;
header.dwMipMapCount = desc.MipLevels;
header.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
if(desc.MipLevels > 1)
header.dwFlags |= DDSD_MIPMAPCOUNT;
if(IsDepthFormat(desc.Format))
header.dwFlags |= DDSD_DEPTH;
if(IsBlockFormat(desc.Format))
header.dwFlags |= DDSD_LINEARSIZE;
else
header.dwFlags |= DDSD_PITCH;
header.dwCaps = DDSCAPS_TEXTURE;
/*
// spec compliant, but seems to confuse DirectX Texture Tool :(
if(desc.MipLevels > 1)
header.dwCaps |= DDSCAPS_MIPMAP;
if(desc.MipLevels > 1 || desc.ArraySize > 1)
header.dwCaps |= DDSCAPS_COMPLEX;
*/
if(desc.ArraySize > 1)
header.dwCaps |= DDSCAPS_COMPLEX;
header.dwCaps2 = desc.ArraySize > 1 ? DDSCAPS2_VOLUME : 0;
headerDXT10.dxgiFormat = GetTypedFormat(desc.Format);
headerDXT10.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D;
headerDXT10.arraySize = desc.ArraySize;
if(desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE)
{
header.dwCaps2 = DDSCAPS2_CUBEMAP;
headerDXT10.arraySize /= 6;
}
if(IsBlockFormat(desc.Format))
{
int blockSize = GetFormatBPP(desc.Format) / 8;
header.dwPitchOrLinearSize = RDCMAX(1U, ((desc.Width+3)/4)) * blockSize;
}
else if(desc.Format == DXGI_FORMAT_R8G8_B8G8_UNORM ||
desc.Format == DXGI_FORMAT_G8R8_G8B8_UNORM)
{
header.dwPitchOrLinearSize = ((desc.Width+1) >> 1) * 4;
}
else
{
header.dwPitchOrLinearSize = (desc.Width * GetFormatBPP(desc.Format) + 7) / 8;
}
bool dx10Header = false;
// special case a couple of formats to write out non-DX10 style, for
// backwards compatibility
switch(desc.Format)
{
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_R8G8B8A8_UINT:
case DXGI_FORMAT_R8G8B8A8_SNORM:
case DXGI_FORMAT_R8G8B8A8_SINT:
{
header.ddspf.dwFlags = DDPF_RGBA;
header.ddspf.dwRGBBitCount = 32;
header.ddspf.dwRBitMask = 0x000000ff;
header.ddspf.dwGBitMask = 0x0000ff00;
header.ddspf.dwBBitMask = 0x00ff0000;
header.ddspf.dwABitMask = 0xff000000;
break;
}
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '1');
break;
}
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '3');
break;
}
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '5');
break;
}
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('B', 'C', '4', 'U');
break;
}
case DXGI_FORMAT_BC4_SNORM:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('B', 'C', '4', 'S');
break;
}
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('A', 'T', 'I', '2');
break;
}
case DXGI_FORMAT_BC5_SNORM:
{
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('B', 'C', '5', 'S');
break;
}
default:
{
// just write out DX10 header
header.ddspf.dwFlags = DDPF_FOURCC;
header.ddspf.dwFourCC = MAKE_FOURCC('D', 'X', '1', '0');
dx10Header = true;
break;
}
}
FILE *f = FileIO::fopen(path.c_str(), L"wb");
if(f)
{
FileIO::fwrite(&magic, sizeof(magic), 1, f);
FileIO::fwrite(&header, sizeof(header), 1, f);
if(dx10Header)
FileIO::fwrite(&headerDXT10, sizeof(headerDXT10), 1, f);
UINT i=0;
for(UINT slice=0; slice < RDCMAX(1U,desc.ArraySize); slice++)
{
for(UINT mip=0; mip < RDCMAX(1U,desc.MipLevels); mip++)
{
D3D11_MAPPED_SUBRESOURCE mapped;
hr = m_pImmediateContext->Map(dummyTex, i, D3D11_MAP_READ, 0, &mapped);
if(FAILED(hr))
{
RDCERR("Couldn't map subresource. %08x", hr);
FileIO::fclose(f);
return false;
}
byte *data = (byte *)mapped.pData;
UINT numRows = (desc.Height>>mip);
UINT pitch = (header.dwPitchOrLinearSize>>mip);
// pitch/rows are in blocks, not pixels, for block formats.
if(IsBlockFormat(desc.Format))
{
numRows = RDCMAX(1U, numRows/4);
// at least one block
pitch = RDCMAX(pitch, GetFormatBPP(desc.Format)/8);
}
for(UINT row=0; row < numRows; row++)
{
FileIO::fwrite(data, 1, pitch, f);
data += mapped.RowPitch;
}
m_pImmediateContext->Unmap(dummyTex, i);
i++;
}
}
}
FileIO::fclose(f);
SAFE_RELEASE(dummyTex);
return true;
}
RDCERR("Unknown file-type");
return false;
}
byte *D3D11DebugManager::GetTextureData(ResourceId id, uint32_t arrayIdx, uint32_t mip, size_t &dataSize)
{
ID3D11Resource *dummyTex = NULL;
uint32_t subresource = 0;
uint32_t mips = 0;
dataSize = 0;
size_t bytesize = 0;
if(WrappedID3D11Texture1D::m_TextureList.find(id) != WrappedID3D11Texture1D::m_TextureList.end())
{
WrappedID3D11Texture1D *wrapTex = (WrappedID3D11Texture1D *)WrappedID3D11Texture1D::m_TextureList[id].m_Texture;
D3D11_TEXTURE1D_DESC desc = {0};
wrapTex->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
ID3D11Texture1D *d = NULL;
mips = desc.MipLevels ? desc.MipLevels : CalcNumMips(desc.Width, 1, 1);
if(mip >= mips || arrayIdx >= desc.ArraySize) return NULL;
subresource = arrayIdx*mips + mip;
HRESULT hr = m_WrappedDevice->CreateTexture1D(&desc, NULL, &d);
dummyTex = d;
if(FAILED(hr))
{
RDCERR("Couldn't create staging texture to retrieve data. %08x", hr);
return NULL;
}
bytesize = GetByteSize(desc.Width, 1, 1, desc.Format, mip);
m_pImmediateContext->CopyResource(UNWRAP(WrappedID3D11Texture1D, d), wrapTex->GetReal());
}
else if(WrappedID3D11Texture2D::m_TextureList.find(id) != WrappedID3D11Texture2D::m_TextureList.end())
{
WrappedID3D11Texture2D *wrapTex = (WrappedID3D11Texture2D *)WrappedID3D11Texture2D::m_TextureList[id].m_Texture;
D3D11_TEXTURE2D_DESC desc = {0};
wrapTex->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
ID3D11Texture2D *d = NULL;
mips = desc.MipLevels ? desc.MipLevels : CalcNumMips(desc.Width, desc.Height, 1);
if(mip >= mips || arrayIdx >= desc.ArraySize) return NULL;
subresource = arrayIdx*mips + mip;
HRESULT hr = m_WrappedDevice->CreateTexture2D(&desc, NULL, &d);
dummyTex = d;
if(FAILED(hr))
{
RDCERR("Couldn't create staging texture to retrieve data. %08x", hr);
return NULL;
}
bytesize = GetByteSize(desc.Width, desc.Height, 1, desc.Format, mip);
m_pImmediateContext->CopyResource(UNWRAP(WrappedID3D11Texture2D, d), wrapTex->GetReal());
}
else if(WrappedID3D11Texture3D::m_TextureList.find(id) != WrappedID3D11Texture3D::m_TextureList.end())
{
WrappedID3D11Texture3D *wrapTex = (WrappedID3D11Texture3D *)WrappedID3D11Texture3D::m_TextureList[id].m_Texture;
D3D11_TEXTURE3D_DESC desc = {0};
wrapTex->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
ID3D11Texture3D *d = NULL;
mips = desc.MipLevels ? desc.MipLevels : CalcNumMips(desc.Width, desc.Height, desc.Depth);
if(mip >= mips) return NULL;
subresource = mip;
HRESULT hr = m_WrappedDevice->CreateTexture3D(&desc, NULL, &d);
dummyTex = d;
if(FAILED(hr))
{
RDCERR("Couldn't create staging texture to retrieve data. %08x", hr);
return NULL;
}
bytesize = GetByteSize(desc.Width, desc.Height, desc.Depth, desc.Format, mip);
m_pImmediateContext->CopyResource(UNWRAP(WrappedID3D11Texture3D, d), wrapTex->GetReal());
}
MapIntercept intercept;
D3D11_MAPPED_SUBRESOURCE mapped = {0};
HRESULT hr = m_pImmediateContext->Map(m_ResourceManager->UnwrapResource(dummyTex), subresource, D3D11_MAP_READ, 0, &mapped);
byte *ret = NULL;
if(SUCCEEDED(hr))
{
ret = new byte[bytesize];
dataSize = bytesize;
intercept.InitWrappedResource(dummyTex, subresource, ret);
intercept.SetD3D(mapped);
intercept.CopyFromD3D();
}
else
{
RDCERR("Couldn't map staging texture to retrieve data. %08x", hr);
}
SAFE_RELEASE(dummyTex);
return ret;
}
void D3D11DebugManager::FillTimers(uint32_t frameID, uint32_t &eventStart, rdctype::array<FetchDrawcall> &draws, vector<GPUTimer> &timers, int &reuseIdx)
{
const D3D11_QUERY_DESC qdesc = { D3D11_QUERY_TIMESTAMP, 0 };
if(draws.count == 0) return;
for(int32_t i=0; i < draws.count; i++)
{
FetchDrawcall &d = draws[i];
FillTimers(frameID, eventStart, d.children, timers, reuseIdx);
if(d.events.count == 0) continue;
GPUTimer *timer = NULL;
if(reuseIdx == -1)
{
timers.push_back(GPUTimer());
timer = &timers.back();
timer->drawcall = &d;
}
else
{
timer = &timers[reuseIdx++];
}
HRESULT hr = S_OK;
if(reuseIdx == -1)
{
hr = m_pDevice->CreateQuery(&qdesc, &timer->before);
RDCASSERT(SUCCEEDED(hr));
hr = m_pDevice->CreateQuery(&qdesc, &timer->after);
RDCASSERT(SUCCEEDED(hr));
}
m_WrappedDevice->ReplayLog(frameID, eventStart, d.eventID, eReplay_WithoutDraw);
m_pImmediateContext->Flush();
m_pImmediateContext->End(timer->before);
m_WrappedDevice->ReplayLog(frameID, eventStart, d.eventID, eReplay_OnlyDraw);
m_pImmediateContext->End(timer->after);
eventStart = d.eventID+1;
}
}
void D3D11DebugManager::TimeDrawcalls(rdctype::array<FetchDrawcall> &arr)
{
SCOPED_TIMER("Drawcall timing");
vector<GPUTimer> timers;
D3D11_QUERY_DESC disjointdesc = { D3D11_QUERY_TIMESTAMP_DISJOINT, 0 };
ID3D11Query *disjoint = NULL;
D3D11_QUERY_DESC qdesc = { D3D11_QUERY_TIMESTAMP, 0 };
ID3D11Query *start = NULL;
HRESULT hr = S_OK;
hr = m_pDevice->CreateQuery(&disjointdesc, &disjoint);
if(FAILED(hr))
{
RDCERR("Failed to create disjoint query %08x", hr);
return;
}
hr = m_pDevice->CreateQuery(&qdesc, &start);
if(FAILED(hr))
{
RDCERR("Failed to create start query %08x", hr);
return;
}
for(int loop=0; loop < 1; loop++)
{
{
m_pImmediateContext->Begin(disjoint);
m_pImmediateContext->End(start);
uint32_t ev = 0;
int reuse = loop == 0 ? -1 : 0;
FillTimers(0, ev, arr, timers, reuse);
m_pImmediateContext->End(disjoint);
}
{
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjointData;
do
{
hr = m_pImmediateContext->GetData(disjoint, &disjointData, sizeof(D3D11_QUERY_DATA_TIMESTAMP_DISJOINT), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
RDCASSERT(!disjointData.Disjoint);
double ticksToSecs = double(disjointData.Frequency);
UINT64 a=0;
m_pImmediateContext->GetData(start, &a, sizeof(UINT64), 0);
for(size_t i=0; i < timers.size(); i++)
{
hr = m_pImmediateContext->GetData(timers[i].before, &a, sizeof(UINT64), 0);
RDCASSERT(hr == S_OK);
UINT64 b=0;
hr = m_pImmediateContext->GetData(timers[i].after, &b, sizeof(UINT64), 0);
RDCASSERT(hr == S_OK);
timers[i].drawcall->duration = (double(b-a)/ticksToSecs);
a = b;
}
}
}
for(size_t i=0; i < timers.size(); i++)
{
SAFE_RELEASE(timers[i].before);
SAFE_RELEASE(timers[i].after);
}
SAFE_RELEASE(disjoint);
SAFE_RELEASE(start);
}
ResourceId D3D11DebugManager::ApplyCustomShader(ResourceId shader, ResourceId texid, uint32_t mip)
{
TextureShaderDetails details = GetShaderDetails(texid, false);
CreateCustomShaderTex(details.texWidth, details.texHeight);
m_pImmediateContext->OMSetRenderTargets(1, &m_CustomShaderRTV, NULL);
float clr[] = { 0.0f, 0.0f, 0.0f, 0.0f };
m_pImmediateContext->ClearRenderTargetView(m_CustomShaderRTV, clr);
D3D11_VIEWPORT viewport;
RDCEraseEl(viewport);
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = (float)details.texWidth;
viewport.Height = (float)details.texHeight;
m_pImmediateContext->RSSetViewports(1, &viewport);
TextureDisplay disp;
disp.Red = disp.Green = disp.Blue = disp.Alpha = true;
disp.FlipY = false;
disp.offx = 0.0f;
disp.offy = 0.0f;
disp.CustomShader = shader;
disp.texid = texid;
disp.lightBackgroundColour = disp.darkBackgroundColour = FloatVector(0,0,0,0);
disp.HDRMul = -1.0f;
disp.linearDisplayAsGamma = true;
disp.mip = mip;
disp.overlay = eTexOverlay_None;
disp.rangemin = 0.0f;
disp.rangemax = 1.0f;
disp.rawoutput = false;
disp.scale = 1.0f;
disp.sliceFace = 0;
SetOutputDimensions(details.texWidth, details.texHeight);
RenderTexture(disp);
return m_CustomShaderResourceId;
}
void D3D11DebugManager::CreateCustomShaderTex(uint32_t w, uint32_t h)
{
D3D11_TEXTURE2D_DESC texdesc;
texdesc.ArraySize = 1;
texdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
texdesc.CPUAccessFlags = 0;
texdesc.MipLevels = 1;
texdesc.MiscFlags = 0;
texdesc.SampleDesc.Count = 1;
texdesc.SampleDesc.Quality = 0;
texdesc.Usage = D3D11_USAGE_DEFAULT;
texdesc.Width = w;
texdesc.Height = h;
texdesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
if(m_CustomShaderTex)
{
D3D11_TEXTURE2D_DESC customTexDesc;
m_CustomShaderTex->GetDesc(&customTexDesc);
if(customTexDesc.Width == w && customTexDesc.Height == h)
return;
SAFE_RELEASE(m_CustomShaderRTV);
SAFE_RELEASE(m_CustomShaderTex);
}
HRESULT hr = m_WrappedDevice->CreateTexture2D(&texdesc, NULL, &m_CustomShaderTex);
if(FAILED(hr))
{
RDCERR("Failed to create custom shader tex %08x", hr);
}
else
{
WrappedID3D11Texture2D *wrapped = (WrappedID3D11Texture2D *)m_CustomShaderTex;
hr = m_pDevice->CreateRenderTargetView(wrapped->GetReal(), NULL, &m_CustomShaderRTV);
if(FAILED(hr))
RDCERR("Failed to create custom shader rtv %08x", hr);
m_CustomShaderResourceId = GetIDForResource(m_CustomShaderTex);
}
}
ResourceId D3D11DebugManager::RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID, const vector<uint32_t> &passEvents)
{
TextureShaderDetails details = GetShaderDetails(texid, false);
ResourceId id = texid;
D3D11_TEXTURE2D_DESC realTexDesc;
realTexDesc.BindFlags = D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE;
realTexDesc.Usage = D3D11_USAGE_DEFAULT;
realTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
realTexDesc.ArraySize = 1;
realTexDesc.MipLevels = 1;
realTexDesc.CPUAccessFlags = 0;
realTexDesc.MiscFlags = 0;
realTexDesc.SampleDesc.Count = 1;
realTexDesc.SampleDesc.Quality = 0;
realTexDesc.Width = details.texWidth;
realTexDesc.Height = details.texHeight;
if(details.texType == eTexType_2D)
{
realTexDesc.SampleDesc.Count = details.sampleCount;
realTexDesc.SampleDesc.Quality = details.sampleQuality;
}
D3D11RenderState old = *m_WrappedContext->GetCurrentPipelineState();
D3D11_TEXTURE2D_DESC customTexDesc;
RDCEraseEl(customTexDesc);
if(m_OverlayRenderTex)
m_OverlayRenderTex->GetDesc(&customTexDesc);
WrappedID3D11Texture2D *wrappedCustomRenderTex = (WrappedID3D11Texture2D *)m_OverlayRenderTex;
// need to recreate backing custom render tex
if(realTexDesc.Width != customTexDesc.Width ||
realTexDesc.Height != customTexDesc.Height ||
realTexDesc.Format != customTexDesc.Format ||
realTexDesc.SampleDesc.Count != customTexDesc.SampleDesc.Count ||
realTexDesc.SampleDesc.Quality != customTexDesc.SampleDesc.Quality)
{
SAFE_RELEASE(m_OverlayRenderTex);
m_OverlayResourceId = ResourceId();
ID3D11Texture2D *customRenderTex = NULL;
HRESULT hr = m_WrappedDevice->CreateTexture2D(&realTexDesc, NULL, &customRenderTex);
if(FAILED(hr))
{
RDCERR("Failed to create custom render tex %08x", hr);
return ResourceId();
}
wrappedCustomRenderTex = (WrappedID3D11Texture2D *)customRenderTex;
m_OverlayRenderTex = wrappedCustomRenderTex;
m_OverlayResourceId = wrappedCustomRenderTex->GetResourceID();
}
ID3D11Texture2D *preDrawDepth = NULL;
ID3D11Texture2D *renderDepth = NULL;
ID3D11DepthStencilView *dsView = NULL;
m_pImmediateContext->OMGetRenderTargets(0, NULL, &dsView);
D3D11_DEPTH_STENCIL_VIEW_DESC dsViewDesc;
RDCEraseEl(dsViewDesc);
if(dsView)
{
ID3D11Texture2D *realDepth = NULL;
dsView->GetResource((ID3D11Resource **)&realDepth);
dsView->GetDesc(&dsViewDesc);
SAFE_RELEASE(dsView);
D3D11_TEXTURE2D_DESC desc;
realDepth->GetDesc(&desc);
HRESULT hr = S_OK;
hr = m_pDevice->CreateTexture2D(&desc, NULL, &preDrawDepth);
if(FAILED(hr))
{
RDCERR("Failed to create preDrawDepth %08x", hr);
SAFE_RELEASE(realDepth);
return m_OverlayResourceId;
}
hr = m_pDevice->CreateTexture2D(&desc, NULL, &renderDepth);
if(FAILED(hr))
{
RDCERR("Failed to create renderDepth %08x", hr);
SAFE_RELEASE(realDepth);
return m_OverlayResourceId;
}
m_pImmediateContext->CopyResource(preDrawDepth, realDepth);
SAFE_RELEASE(realDepth);
}
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
rtDesc.Texture2D.MipSlice = 0;
if(realTexDesc.SampleDesc.Count > 1 ||
realTexDesc.SampleDesc.Quality > 0)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
}
ID3D11RenderTargetView *rtv = NULL;
HRESULT hr = m_pDevice->CreateRenderTargetView(wrappedCustomRenderTex->GetReal(), &rtDesc, &rtv);
if(FAILED(hr))
{
RDCERR("Failed to create custom render tex RTV %08x", hr);
return m_OverlayResourceId;
}
FLOAT black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
m_pImmediateContext->ClearRenderTargetView(rtv, black);
if(renderDepth)
{
m_pImmediateContext->CopyResource(renderDepth, preDrawDepth);
hr = m_pDevice->CreateDepthStencilView(renderDepth, &dsViewDesc, &dsView);
if(FAILED(hr))
{
RDCERR("Failed to create renderDepth DSV %08x", hr);
return m_OverlayResourceId;
}
}
m_pImmediateContext->OMSetRenderTargets(1, &rtv, dsView);
SAFE_RELEASE(dsView);
D3D11_DEPTH_STENCIL_DESC desc;
desc.BackFace.StencilFailOp = desc.BackFace.StencilPassOp = desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
desc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilPassOp = desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.DepthEnable = TRUE;
desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
desc.StencilEnable = FALSE;
desc.StencilReadMask = desc.StencilWriteMask = 0xff;
if(overlay == eTexOverlay_NaN ||
overlay == eTexOverlay_Clipping)
{
// just need the basic texture
}
else if(overlay == eTexOverlay_Drawcall)
{
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
desc.DepthEnable = FALSE;
desc.StencilEnable = FALSE;
ID3D11DepthStencilState *os = NULL;
hr = m_pDevice->CreateDepthStencilState(&desc, &os);
if(FAILED(hr))
{
RDCERR("Failed to create drawcall depth stencil state %08x", hr);
return m_OverlayResourceId;
}
m_pImmediateContext->OMSetDepthStencilState(os, 0);
m_pImmediateContext->OMSetBlendState(NULL, NULL, 0xffffffff);
ID3D11RasterizerState *rs = NULL;
{
D3D11_RASTERIZER_DESC desc;
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D11_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D11_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = FALSE;
desc.ScissorEnable = FALSE;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
hr = m_pDevice->CreateRasterizerState(&desc, &rs);
if(FAILED(hr))
{
RDCERR("Failed to create drawcall rast state %08x", hr);
return m_OverlayResourceId;
}
}
float clearColour[] = { 0.0f, 0.0f, 0.0f, 0.5f };
m_pImmediateContext->ClearRenderTargetView(rtv, clearColour);
float overlayConsts[] = { 0.8f, 0.1f, 0.8f, 1.0f };
ID3D11Buffer *buf = MakeCBuffer(overlayConsts, sizeof(overlayConsts));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->RSSetState(rs);
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw);
SAFE_RELEASE(os);
SAFE_RELEASE(rs);
}
else if(overlay == eTexOverlay_ViewportScissor)
{
m_pImmediateContext->VSSetShader(m_DebugRender.FullscreenVS, NULL, 0);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
desc.DepthEnable = FALSE;
desc.StencilEnable = FALSE;
ID3D11DepthStencilState *os = NULL;
hr = m_pDevice->CreateDepthStencilState(&desc, &os);
if(FAILED(hr))
{
RDCERR("Failed to create drawcall depth stencil state %08x", hr);
return m_OverlayResourceId;
}
m_pImmediateContext->OMSetDepthStencilState(os, 0);
m_pImmediateContext->OMSetBlendState(NULL, NULL, 0xffffffff);
ID3D11RasterizerState *rs = NULL;
ID3D11RasterizerState *rs2 = NULL;
{
D3D11_RASTERIZER_DESC desc;
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D11_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D11_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = FALSE;
desc.ScissorEnable = FALSE;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
hr = m_pDevice->CreateRasterizerState(&desc, &rs);
if(FAILED(hr))
{
RDCERR("Failed to create drawcall rast state %08x", hr);
return m_OverlayResourceId;
}
desc.ScissorEnable = TRUE;
hr = m_pDevice->CreateRasterizerState(&desc, &rs2);
if(FAILED(hr))
{
RDCERR("Failed to create drawcall rast state %08x", hr);
return m_OverlayResourceId;
}
}
float clearColour[] = { 0.0f, 0.0f, 0.0f, 0.0f };
m_pImmediateContext->ClearRenderTargetView(rtv, clearColour);
float overlayConsts[] = { 0.15f, 0.3f, 0.6f, 0.3f };
ID3D11Buffer *buf = MakeCBuffer(overlayConsts, sizeof(overlayConsts));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->RSSetState(rs);
m_pImmediateContext->Draw(3, 0);
float overlayConsts2[] = { 0.5f, 0.6f, 0.8f, 0.3f };
buf = MakeCBuffer(overlayConsts2, sizeof(overlayConsts2));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->RSSetState(rs2);
m_pImmediateContext->Draw(3, 0);
SAFE_RELEASE(os);
SAFE_RELEASE(rs);
SAFE_RELEASE(rs2);
}
else if(overlay == eTexOverlay_Wireframe)
{
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
desc.DepthEnable = FALSE;
ID3D11DepthStencilState *os = NULL;
hr = m_pDevice->CreateDepthStencilState(&desc, &os);
if(FAILED(hr))
{
RDCERR("Failed to create wireframe depth state %08x", hr);
return m_OverlayResourceId;
}
m_pImmediateContext->OMSetDepthStencilState(os, 0);
m_pImmediateContext->OMSetBlendState(NULL, NULL, 0xffffffff);
ID3D11RasterizerState *rs = NULL;
{
D3D11_RASTERIZER_DESC desc;
m_pImmediateContext->RSGetState(&rs);
if(rs)
{
rs->GetDesc(&desc);
}
else
{
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_BACK;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D11_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D11_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = TRUE;
desc.ScissorEnable = FALSE;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
}
SAFE_RELEASE(rs);
desc.FillMode = D3D11_FILL_WIREFRAME;
desc.CullMode = D3D11_CULL_NONE;
hr = m_pDevice->CreateRasterizerState(&desc, &rs);
if(FAILED(hr))
{
RDCERR("Failed to create wireframe rast state %08x", hr);
return m_OverlayResourceId;
}
}
float overlayConsts[] = { 200.0f/255.0f, 255.0f/255.0f, 0.0f/255.0f, 0.0f };
m_pImmediateContext->ClearRenderTargetView(rtv, overlayConsts);
overlayConsts[3] = 1.0f;
ID3D11Buffer *buf = MakeCBuffer(overlayConsts, sizeof(overlayConsts));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->RSSetState(rs);
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw);
SAFE_RELEASE(os);
SAFE_RELEASE(rs);
}
else if(overlay == eTexOverlay_QuadOverdrawPass || overlay == eTexOverlay_QuadOverdrawDraw)
{
SCOPED_TIMER("Quad Overdraw");
vector<uint32_t> events = passEvents;
if(overlay == eTexOverlay_QuadOverdrawDraw)
{
events.clear();
events.push_back(eventID);
}
if(!events.empty())
{
if(overlay == eTexOverlay_QuadOverdrawPass)
m_WrappedDevice->ReplayLog(frameID, 0, events[0], eReplay_WithoutDraw);
D3D11RenderState *state = m_WrappedContext->GetCurrentPipelineState();
uint32_t width = 1920>>1;
uint32_t height = 1080>>1;
{
ID3D11Resource *res = NULL;
if(state->OM.RenderTargets[0])
{
state->OM.RenderTargets[0]->GetResource(&res);
}
else if(state->OM.DepthView)
{
state->OM.DepthView->GetResource(&res);
}
else
{
RDCERR("Couldn't get size of existing targets");
return m_OverlayResourceId;
}
D3D11_RESOURCE_DIMENSION dim;
res->GetType(&dim);
if(dim == D3D11_RESOURCE_DIMENSION_TEXTURE1D)
{
D3D11_TEXTURE1D_DESC desc;
((ID3D11Texture1D *)res)->GetDesc(&desc);
width = desc.Width>>1;
height = 1;
}
else if(dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D)
{
D3D11_TEXTURE2D_DESC desc;
((ID3D11Texture2D *)res)->GetDesc(&desc);
width = desc.Width>>1;
height = desc.Height>>1;
}
else
{
RDCERR("Trying to show quad overdraw on invalid view");
return m_OverlayResourceId;
}
SAFE_RELEASE(res);
}
D3D11_TEXTURE2D_DESC uavTexDesc = {
width, height, 1U, 4U,
DXGI_FORMAT_R32_UINT,
{ 1, 0 },
D3D11_USAGE_DEFAULT,
D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE,
0,
0,
};
ID3D11Texture2D *overdrawTex = NULL;
ID3D11ShaderResourceView *overdrawSRV = NULL;
ID3D11UnorderedAccessView *overdrawUAV = NULL;
m_WrappedDevice->CreateTexture2D(&uavTexDesc, NULL, &overdrawTex);
m_WrappedDevice->CreateShaderResourceView(overdrawTex, NULL, &overdrawSRV);
m_WrappedDevice->CreateUnorderedAccessView(overdrawTex, NULL, &overdrawUAV);
UINT val = 0;
m_WrappedContext->ClearUnorderedAccessViewUint(overdrawUAV, &val);
for(size_t i=0; i < events.size(); i++)
{
D3D11RenderState oldstate = *m_WrappedContext->GetCurrentPipelineState();
D3D11_DEPTH_STENCIL_DESC dsdesc = {
/*DepthEnable =*/ TRUE,
/*DepthWriteMask =*/ D3D11_DEPTH_WRITE_MASK_ALL,
/*DepthFunc =*/ D3D11_COMPARISON_LESS,
/*StencilEnable =*/ FALSE,
/*StencilReadMask =*/ D3D11_DEFAULT_STENCIL_READ_MASK,
/*StencilWriteMask =*/ D3D11_DEFAULT_STENCIL_WRITE_MASK,
/*FrontFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS },
/*BackFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS },
};
ID3D11DepthStencilState *ds = NULL;
if(state->OM.DepthStencilState)
state->OM.DepthStencilState->GetDesc(&dsdesc);
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dsdesc.StencilWriteMask = 0;
m_WrappedDevice->CreateDepthStencilState(&dsdesc, &ds);
m_WrappedContext->OMSetDepthStencilState(ds, oldstate.OM.StencRef);
SAFE_RELEASE(ds);
UINT UAVcount = 0;
m_WrappedContext->OMSetRenderTargetsAndUnorderedAccessViews(0, NULL, oldstate.OM.DepthView, 0, 1, &overdrawUAV, &UAVcount);
m_pImmediateContext->PSSetShader(m_DebugRender.QuadOverdrawPS, NULL, 0);
m_WrappedDevice->ReplayLog(frameID, events[i], events[i], eReplay_OnlyDraw);
oldstate.ApplyState(m_WrappedContext);
if(overlay == eTexOverlay_QuadOverdrawPass)
{
m_WrappedDevice->ReplayLog(frameID, events[i], events[i], eReplay_OnlyDraw);
if(i+1 < events.size())
m_WrappedDevice->ReplayLog(frameID, events[i], events[i+1], eReplay_WithoutDraw);
}
}
// resolve pass
{
m_pImmediateContext->VSSetShader(m_DebugRender.FullscreenVS, NULL, 0);
m_pImmediateContext->PSSetShader(m_DebugRender.QOResolvePS, NULL, 0);
ID3D11Buffer *buf = MakeCBuffer((float *)&overdrawRamp[0].x, sizeof(Vec4f)*21);
m_pImmediateContext->PSSetConstantBuffers(0, 1, &buf);
m_pImmediateContext->OMSetRenderTargets(1, &rtv, NULL);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.NoDepthState, 0);
m_pImmediateContext->OMSetBlendState(NULL, NULL, 0xffffffff);
m_pImmediateContext->RSSetState(m_DebugRender.RastState);
float clearColour[] = { 0.0f, 0.0f, 0.0f, 0.0f };
m_pImmediateContext->ClearRenderTargetView(rtv, clearColour);
ID3D11ShaderResourceView *srv = ((WrappedID3D11ShaderResourceView *)overdrawSRV)->GetReal();
m_pImmediateContext->PSSetShaderResources(0, 1, &srv);
m_pImmediateContext->Draw(3, 0);
}
SAFE_RELEASE(overdrawTex);
SAFE_RELEASE(overdrawSRV);
SAFE_RELEASE(overdrawUAV);
if(overlay == eTexOverlay_QuadOverdrawPass)
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_WithoutDraw);
}
}
else if(preDrawDepth)
{
D3D11_DEPTH_STENCIL_DESC cur = {0};
UINT stencilRef = 0;
{
ID3D11DepthStencilState *os = NULL;
m_pImmediateContext->OMGetDepthStencilState(&os, &stencilRef);
if(os)
{
os->GetDesc(&cur);
SAFE_RELEASE(os);
}
else
{
cur.DepthFunc = D3D11_COMPARISON_LESS; // default depth func
cur.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
cur.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
cur.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
cur.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
cur.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
cur.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
cur.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
cur.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
}
}
if(overlay == eTexOverlay_DepthBoth ||
overlay == eTexOverlay_StencilBoth)
{
ID3D11DepthStencilState *os = NULL;
D3D11_DEPTH_STENCIL_DESC d = desc;
if(overlay == eTexOverlay_DepthBoth)
{
desc.DepthEnable = d.DepthEnable = TRUE;
desc.StencilEnable = d.StencilEnable = FALSE;
switch(cur.DepthFunc)
{
case D3D11_COMPARISON_ALWAYS:
d.DepthFunc = D3D11_COMPARISON_NEVER;
break;
case D3D11_COMPARISON_NEVER:
d.DepthFunc = D3D11_COMPARISON_ALWAYS;
break;
case D3D11_COMPARISON_EQUAL:
d.DepthFunc = D3D11_COMPARISON_NOT_EQUAL;
break;
case D3D11_COMPARISON_NOT_EQUAL:
d.DepthFunc = D3D11_COMPARISON_EQUAL;
break;
case D3D11_COMPARISON_LESS:
d.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL;
break;
case D3D11_COMPARISON_GREATER_EQUAL:
d.DepthFunc = D3D11_COMPARISON_LESS;
break;
case D3D11_COMPARISON_GREATER:
d.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
break;
case D3D11_COMPARISON_LESS_EQUAL:
d.DepthFunc = D3D11_COMPARISON_GREATER;
break;
}
}
else if(overlay == eTexOverlay_StencilBoth)
{
desc.DepthEnable = d.DepthEnable = FALSE;
desc.StencilEnable = d.StencilEnable = TRUE;
d.FrontFace = cur.FrontFace;
d.BackFace = cur.BackFace;
desc.StencilReadMask = d.StencilReadMask = cur.StencilReadMask;
desc.StencilWriteMask = d.StencilWriteMask = cur.StencilWriteMask;
switch(cur.FrontFace.StencilFunc)
{
case D3D11_COMPARISON_ALWAYS:
d.FrontFace.StencilFunc = D3D11_COMPARISON_NEVER;
break;
case D3D11_COMPARISON_NEVER:
d.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
break;
case D3D11_COMPARISON_EQUAL:
d.FrontFace.StencilFunc = D3D11_COMPARISON_NOT_EQUAL;
break;
case D3D11_COMPARISON_NOT_EQUAL:
d.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;
break;
case D3D11_COMPARISON_LESS:
d.FrontFace.StencilFunc = D3D11_COMPARISON_GREATER_EQUAL;
break;
case D3D11_COMPARISON_GREATER_EQUAL:
d.FrontFace.StencilFunc = D3D11_COMPARISON_LESS;
break;
case D3D11_COMPARISON_GREATER:
d.FrontFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
break;
case D3D11_COMPARISON_LESS_EQUAL:
d.FrontFace.StencilFunc = D3D11_COMPARISON_GREATER;
break;
}
switch(cur.BackFace.StencilFunc)
{
case D3D11_COMPARISON_ALWAYS:
d.BackFace.StencilFunc = D3D11_COMPARISON_NEVER;
break;
case D3D11_COMPARISON_NEVER:
d.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
break;
case D3D11_COMPARISON_EQUAL:
d.BackFace.StencilFunc = D3D11_COMPARISON_NOT_EQUAL;
break;
case D3D11_COMPARISON_NOT_EQUAL:
d.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL;
break;
case D3D11_COMPARISON_LESS:
d.BackFace.StencilFunc = D3D11_COMPARISON_GREATER_EQUAL;
break;
case D3D11_COMPARISON_GREATER_EQUAL:
d.BackFace.StencilFunc = D3D11_COMPARISON_LESS;
break;
case D3D11_COMPARISON_GREATER:
d.BackFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
break;
case D3D11_COMPARISON_LESS_EQUAL:
d.BackFace.StencilFunc = D3D11_COMPARISON_GREATER;
break;
}
}
SAFE_RELEASE(os);
hr = m_pDevice->CreateDepthStencilState(&d, &os);
if(FAILED(hr))
{
RDCERR("Failed to create depth/stencil overlay depth state %08x", hr);
return m_OverlayResourceId;
}
m_pImmediateContext->OMSetDepthStencilState(os, stencilRef);
m_pImmediateContext->OMSetBlendState(NULL, NULL, 0xffffffff);
float redConsts[] = { 255.0f/255.0f, 0.0f/255.0f, 0.0f/255.0f, 255.0f/255.0f };
ID3D11Buffer *buf = MakeCBuffer(redConsts, sizeof(redConsts));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw);
SAFE_RELEASE(os);
m_pImmediateContext->CopyResource(renderDepth, preDrawDepth);
d = desc;
if(overlay == eTexOverlay_DepthBoth)
{
d.DepthFunc = cur.DepthFunc;
}
else if(overlay == eTexOverlay_StencilBoth)
{
d.FrontFace = cur.FrontFace;
d.BackFace = cur.BackFace;
}
hr = m_pDevice->CreateDepthStencilState(&d, &os);
if(FAILED(hr))
{
RDCERR("Failed to create depth/stencil overlay depth state 2 %08x", hr);
return m_OverlayResourceId;
}
m_pImmediateContext->OMSetDepthStencilState(os, stencilRef);
float greenConsts[] = { 0.0f/255.0f, 255.0f/255.0f, 0.0f/255.0f, 255.0f/255.0f };
buf = MakeCBuffer(greenConsts, sizeof(greenConsts));
m_pImmediateContext->PSSetConstantBuffers(1, 1, &buf);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_WrappedDevice->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw);
SAFE_RELEASE(os);
}
}
SAFE_RELEASE(rtv);
SAFE_RELEASE(renderDepth);
SAFE_RELEASE(preDrawDepth);
old.ApplyState(m_WrappedContext);
return m_OverlayResourceId;
}
void D3D11DebugManager::PixelHistoryDepthCopySubresource(bool depthbound, ID3D11Texture2D *uavres, ID3D11UnorderedAccessView *uav, ID3D11Resource *depthres,
ID3D11ShaderResourceView **copyDepthSRV, ID3D11ShaderResourceView **copyStencilSRV,
ID3D11Buffer *srcxyCBuf, ID3D11Buffer *storexyCBuf, uint32_t x, uint32_t y)
{
if(depthbound && uavres && depthres)
m_pImmediateContext->CopySubresourceRegion(uavres, 0, 0, 0, 0, depthres, 0, NULL);
ID3D11ComputeShader *curCS = NULL;
ID3D11ClassInstance *curCSInst[D3D11_SHADER_MAX_INTERFACES] = { NULL };
UINT curCSNumInst = D3D11_SHADER_MAX_INTERFACES;
ID3D11Buffer *curCSCBuf[2] = {0};
ID3D11ShaderResourceView *curCSSRVs[2] = {0};
ID3D11UnorderedAccessView *curCSUAV = NULL;
UINT initCounts = ~0U;
m_pImmediateContext->CSGetShader(&curCS, curCSInst, &curCSNumInst);
m_pImmediateContext->CSGetConstantBuffers(0, 2, curCSCBuf);
m_pImmediateContext->CSGetShaderResources(0, 2, curCSSRVs);
m_pImmediateContext->CSGetUnorderedAccessViews(0, 1, &curCSUAV);
uint32_t storexyData[4] = { x, y, (copyStencilSRV != NULL), 0 };
D3D11_MAPPED_SUBRESOURCE mapped;
m_pImmediateContext->Map(storexyCBuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, storexyData, sizeof(storexyData));
m_pImmediateContext->Unmap(storexyCBuf, 0);
m_pImmediateContext->CSSetConstantBuffers(0, 1, &srcxyCBuf);
m_pImmediateContext->CSSetConstantBuffers(1, 1, &storexyCBuf);
m_pImmediateContext->CSSetUnorderedAccessViews(0, 1, &uav, &initCounts);
if(copyDepthSRV) m_pImmediateContext->CSSetShaderResources(0, 1, copyDepthSRV);
if(copyStencilSRV) m_pImmediateContext->CSSetShaderResources(1, 1, copyStencilSRV);
m_pImmediateContext->CSSetShader(depthbound ? m_DebugRender.PixelHistoryDepthCopyCS : m_DebugRender.PixelHistoryUnusedCS, NULL, 0);
m_pImmediateContext->Dispatch(1, 1, 1);
m_pImmediateContext->CSSetShader(curCS, curCSInst, curCSNumInst);
m_pImmediateContext->CSSetConstantBuffers(0, 2, curCSCBuf);
m_pImmediateContext->CSSetShaderResources(0, 2, curCSSRVs);
m_pImmediateContext->CSSetUnorderedAccessViews(0, 1, &curCSUAV, &initCounts);
SAFE_RELEASE(curCS);
for(UINT i=0; i < curCSNumInst; i++)
SAFE_RELEASE(curCSInst[i]);
SAFE_RELEASE(curCSCBuf[0]);
SAFE_RELEASE(curCSCBuf[1]);
SAFE_RELEASE(curCSSRVs[0]);
SAFE_RELEASE(curCSSRVs[1]);
SAFE_RELEASE(curCSUAV);
}
vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vector<uint32_t> events, ResourceId target, uint32_t x, uint32_t y)
{
vector<PixelModification> history;
if(events.empty())
return history;
TextureShaderDetails details = GetShaderDetails(target, true);
if(details.texFmt == DXGI_FORMAT_UNKNOWN)
return history;
SCOPED_TIMER("D3D11DebugManager::PixelHistory");
// needed for comparison with viewports
float xf = (float)x;
float yf = (float)y;
RDCDEBUG("Checking Pixel History on %llx (%u, %u) with %u possible events", target, x, y, (uint32_t)events.size());
// these occlusion queries are run with every test possible disabled
vector<ID3D11Query*> occl;
occl.reserve(events.size());
ID3D11Query *testQueries[6] = {0}; // one query for each test we do per-drawcall
uint32_t pixstoreStride = 3;
// reserve 3 pixels per draw (worst case all events). This is used for Pre value, Post value and
// # frag overdraw. It's reused later to retrieve per-fragment post values.
uint32_t pixstoreSlots = (uint32_t)(events.size() * pixstoreStride);
// define a texture that we can copy before/after results into
D3D11_TEXTURE2D_DESC pixstoreDesc = {
RDCMIN(2048U, AlignUp16(pixstoreSlots)),
RDCMAX(1U, pixstoreSlots / 2048),
1U,
1U,
details.texFmt,
{ 1, 0 },
D3D11_USAGE_STAGING,
0,
D3D11_CPU_ACCESS_READ,
0,
};
ID3D11Texture2D *pixstore = NULL;
m_pDevice->CreateTexture2D(&pixstoreDesc, NULL, &pixstore);
// This is used for shader output values.
pixstoreDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
ID3D11Texture2D *shadoutStore = NULL;
m_pDevice->CreateTexture2D(&pixstoreDesc, NULL, &shadoutStore);
// we use R32G32 so that we can bind this buffer as UAV and write to both depth and stencil components.
// the shader does the upcasting for us when we read from depth or stencil
pixstoreDesc.Format = DXGI_FORMAT_R32G32_FLOAT;
ID3D11Texture2D *pixstoreDepthReadback = NULL;
m_pDevice->CreateTexture2D(&pixstoreDesc, NULL, &pixstoreDepthReadback);
pixstoreDesc.Usage = D3D11_USAGE_DEFAULT;
pixstoreDesc.CPUAccessFlags = 0;
pixstoreDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
ID3D11Texture2D *pixstoreDepth = NULL;
m_pDevice->CreateTexture2D(&pixstoreDesc, NULL, &pixstoreDepth);
ID3D11UnorderedAccessView *pixstoreDepthUAV = NULL;
m_pDevice->CreateUnorderedAccessView(pixstoreDepth, NULL, &pixstoreDepthUAV);
// very wasteful, but we must leave the viewport as is to get correct rasterisation which means
// same dimensions of render target.
D3D11_TEXTURE2D_DESC shadoutDesc = {
details.texWidth,
details.texHeight,
1U,
1U,
DXGI_FORMAT_R32G32B32A32_FLOAT,
{ 1, 0 },
D3D11_USAGE_DEFAULT,
D3D11_BIND_RENDER_TARGET,
0,
0,
};
ID3D11Texture2D *shadOutput = NULL;
m_pDevice->CreateTexture2D(&shadoutDesc, NULL, &shadOutput);
ID3D11RenderTargetView *shadOutputRTV = NULL;
m_pDevice->CreateRenderTargetView(shadOutput, NULL, &shadOutputRTV);
shadoutDesc.Format = DXGI_FORMAT_R32G8X24_TYPELESS;
shadoutDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE;
ID3D11Texture2D *shaddepthOutput = NULL;
m_pDevice->CreateTexture2D(&shadoutDesc, NULL, &shaddepthOutput);
ID3D11DepthStencilView *shadOutputDSV = NULL;
{
D3D11_DEPTH_STENCIL_VIEW_DESC desc;
desc.Flags = 0;
desc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
desc.Texture2D.MipSlice = 0;
m_pDevice->CreateDepthStencilView(shaddepthOutput, &desc, &shadOutputDSV);
}
// depth texture to copy to, as CopySubresourceRegion can't copy single pixels out of a depth buffer,
// and we can't guarantee that the original depth texture is SRV-compatible to allow single-pixel copies
// via compute shader.
//
// Due to copies having to match formats between source and destination we don't create these textures up
// front but on demand, and resize up as necessary. We do a whole copy from this, then a CS copy via SRV to UAV
// to copy into the pixstore (which we do a final copy to for readback). The extra step is necessary as
// you can Copy to a staging texture but you can't use a CS, which we need for single-pixel depth (and stencil) copy.
D3D11_TEXTURE2D_DESC depthCopyD24S8Desc = {
details.texWidth,
details.texHeight,
1U,
1U,
DXGI_FORMAT_R24G8_TYPELESS,
{ 1, 0 },
D3D11_USAGE_DEFAULT,
D3D11_BIND_SHADER_RESOURCE,
0,
0,
};
ID3D11Texture2D *depthCopyD24S8 = NULL;
ID3D11ShaderResourceView *depthCopyD24S8_DepthSRV = NULL, *depthCopyD24S8_StencilSRV = NULL;
D3D11_TEXTURE2D_DESC depthCopyD32S8Desc = depthCopyD24S8Desc;
depthCopyD32S8Desc.Format = DXGI_FORMAT_R32G8X24_TYPELESS;
ID3D11Texture2D *depthCopyD32S8 = NULL;
ID3D11ShaderResourceView *depthCopyD32S8_DepthSRV = NULL, *depthCopyD32S8_StencilSRV = NULL;
D3D11_TEXTURE2D_DESC depthCopyD32Desc = depthCopyD32S8Desc;
depthCopyD32Desc.Format = DXGI_FORMAT_R32_TYPELESS;
ID3D11Texture2D *depthCopyD32 = NULL;
ID3D11ShaderResourceView *depthCopyD32_DepthSRV = NULL;
D3D11_TEXTURE2D_DESC depthCopyD16Desc = depthCopyD24S8Desc;
depthCopyD16Desc.Format = DXGI_FORMAT_R16_TYPELESS;
ID3D11Texture2D *depthCopyD16 = NULL;
ID3D11ShaderResourceView *depthCopyD16_DepthSRV = NULL;
D3D11_SHADER_RESOURCE_VIEW_DESC copyDepthSRVDesc, copyStencilSRVDesc;
copyDepthSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
copyDepthSRVDesc.Texture2D.MipLevels = 1;
copyDepthSRVDesc.Texture2D.MostDetailedMip = 0;
copyStencilSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
copyStencilSRVDesc.Texture2D.MipLevels = 1;
copyStencilSRVDesc.Texture2D.MostDetailedMip = 0;
ID3D11ShaderResourceView *shaddepthOutputDepthSRV = NULL, *shaddepthOutputStencilSRV = NULL;
copyDepthSRVDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
m_pDevice->CreateShaderResourceView(shaddepthOutput, &copyDepthSRVDesc, &shaddepthOutputDepthSRV);
copyDepthSRVDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
m_pDevice->CreateShaderResourceView(shaddepthOutput, &copyDepthSRVDesc, &shaddepthOutputStencilSRV);
uint32_t srcxyData[4] = { x, y, 0, 0 };
D3D11_SUBRESOURCE_DATA data = { srcxyData, sizeof(uint32_t)*4, sizeof(uint32_t)*4 };
ID3D11Buffer *srcxyCBuf = MakeCBuffer((float *)srcxyData, sizeof(srcxyData));
ID3D11Buffer *storexyCBuf = MakeCBuffer((float *)srcxyData, sizeof(srcxyData));
// so we do:
// per sample: orig depth --copy--> depthCopyXXX (created/upsized on demand) --CS pixel copy--> pixstoreDepth
// at end: pixstoreDepth --copy--> pixstoreDepthReadback
//
// First copy is needed since orig depth might not be SRV-able
// CS pixel copy is needed since it's the only way to copy only one pixel from depth texture
//
// final copy is needed to get data into a readback texture since we can't have CS writing to staging texture
ID3D11Resource *targetres = NULL;
if(WrappedID3D11Texture1D::m_TextureList.find(target) != WrappedID3D11Texture1D::m_TextureList.end())
targetres = ((WrappedID3D11Texture1D *)WrappedID3D11Texture1D::m_TextureList[target].m_Texture)->GetReal();
else if(WrappedID3D11Texture2D::m_TextureList.find(target) != WrappedID3D11Texture2D::m_TextureList.end())
targetres = ((WrappedID3D11Texture2D *)WrappedID3D11Texture2D::m_TextureList[target].m_Texture)->GetReal();
else if(WrappedID3D11Texture3D::m_TextureList.find(target) != WrappedID3D11Texture3D::m_TextureList.end())
targetres = ((WrappedID3D11Texture3D *)WrappedID3D11Texture3D::m_TextureList[target].m_Texture)->GetReal();
// while issuing the above queries we can check to see which tests are enabled so we don't
// bother checking if depth testing failed if the depth test was disabled
vector<uint32_t> flags(events.size());
enum {
TestEnabled_BackfaceCulling = 1<<0,
TestEnabled_DepthClip = 1<<1,
TestEnabled_Scissor = 1<<2,
TestEnabled_DepthTesting = 1<<3,
TestEnabled_StencilTesting = 1<<4,
// important to know if blending is enabled or not as we currently skip a bunch of stuff
// and only pay attention to the final passing fragment if blending is off
Blending_Enabled = 1<<5,
// additional flags we can trivially detect on the CPU for edge cases
TestMustFail_Scissor = 1<<6, // if the scissor is enabled, pixel lies outside all regions (could be only one)
TestMustPass_Scissor = 1<<7, // if the scissor is enabled, pixel lies inside all regions (could be only one)
TestMustFail_DepthTesting = 1<<8, // if the comparison func is NEVER
TestMustFail_StencilTesting = 1<<9, // if the comparison func is NEVER for both faces, or one face is backface culled and the other is NEVER
};
#if 1
BOOL occlData = 0;
const D3D11_QUERY_DESC occlDesc = { D3D11_QUERY_OCCLUSION_PREDICATE, 0 };
#else
UINT64 occlData = 0;
const D3D11_QUERY_DESC occlDesc = { D3D11_QUERY_OCCLUSION, 0 };
#endif
HRESULT hr = S_OK;
for(size_t i=0; i < events.size(); i++)
{
ID3D11Query *q = NULL;
m_pDevice->CreateQuery(&occlDesc, &q);
occl.push_back(q);
}
for(size_t i=0; i < ARRAY_COUNT(testQueries); i++)
m_pDevice->CreateQuery(&occlDesc, &testQueries[i]);
m_WrappedDevice->ReplayLog(frameID, 0, events[0], eReplay_WithoutDraw);
ID3D11RasterizerState *curRS = NULL;
ID3D11RasterizerState *newRS = NULL;
ID3D11DepthStencilState *newDS = NULL;
ID3D11PixelShader *curPS = NULL;
ID3D11ClassInstance *curInst[D3D11_SHADER_MAX_INTERFACES] = { NULL };
UINT curNumInst = 0;
UINT curNumViews = 0;
UINT curNumScissors = 0;
D3D11_VIEWPORT curViewports[16] = {0};
D3D11_RECT curScissors[16] = {0};
D3D11_RECT newScissors[16] = {0};
ID3D11BlendState *curBS = NULL;
float blendFactor[4] = {0};
UINT curSample = 0;
ID3D11DepthStencilState *curDS = NULL;
UINT stencilRef = 0;
D3D11_BOX srcbox = { x, y, 0, x+1, y+1, 1 };
////////////////////////////////////////////////////////////////////////
// Main loop over each event to determine if it rasterized to this pixel
for(size_t ev=0; ev < events.size(); ev++)
{
curNumInst = D3D11_SHADER_MAX_INTERFACES;
curNumScissors = curNumViews = 16;
m_pImmediateContext->RSGetState(&curRS);
m_pImmediateContext->OMGetBlendState(&curBS, blendFactor, &curSample);
m_pImmediateContext->OMGetDepthStencilState(&curDS, &stencilRef);
m_pImmediateContext->PSGetShader(&curPS, curInst, &curNumInst);
m_pImmediateContext->RSGetViewports(&curNumViews, curViewports);
m_pImmediateContext->RSGetScissorRects(&curNumScissors, curScissors);
// defaults (mostly)
// disable tests/clips and enable scissor as we need it to clip visibility to just our pixel
// TODO determine if a pixel would have been scissor clipped.
D3D11_RASTERIZER_DESC rd = {
/*FillMode =*/ D3D11_FILL_SOLID,
/*CullMode =*/ D3D11_CULL_NONE,
/*FrontCounterClockwise =*/ FALSE,
/*DepthBias =*/ D3D11_DEFAULT_DEPTH_BIAS,
/*DepthBiasClamp =*/ D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
/*SlopeScaledDepthBias =*/ D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
/*DepthClipEnable =*/ FALSE,
/*ScissorEnable =*/ TRUE,
/*MultisampleEnable =*/ FALSE,
/*AntialiasedLineEnable =*/ FALSE,
};
D3D11_RASTERIZER_DESC rsDesc;
RDCEraseEl(rsDesc);
if(curRS)
{
curRS->GetDesc(&rsDesc);
rd = rsDesc;
if(rd.CullMode != D3D11_CULL_NONE)
flags[ev] |= TestEnabled_BackfaceCulling;
if(rd.DepthClipEnable)
flags[ev] |= TestEnabled_DepthClip;
if(rd.ScissorEnable)
flags[ev] |= TestEnabled_Scissor;
rd.CullMode = D3D11_CULL_NONE;
rd.DepthClipEnable = FALSE;
rd.ScissorEnable = TRUE;
}
else
{
rsDesc.CullMode = D3D11_CULL_BACK;
rsDesc.ScissorEnable = FALSE;
// defaults
flags[ev] |= (TestEnabled_BackfaceCulling|TestEnabled_DepthClip);
}
if(curDS)
{
D3D11_DEPTH_STENCIL_DESC dsDesc;
curDS->GetDesc(&dsDesc);
if(dsDesc.DepthEnable)
{
if(dsDesc.DepthFunc != D3D11_COMPARISON_ALWAYS)
flags[ev] |= TestEnabled_DepthTesting;
if(dsDesc.DepthFunc == D3D11_COMPARISON_NEVER)
flags[ev] |= TestMustFail_DepthTesting;
}
if(dsDesc.StencilEnable)
{
if(dsDesc.FrontFace.StencilFunc != D3D11_COMPARISON_ALWAYS || dsDesc.BackFace.StencilFunc != D3D11_COMPARISON_ALWAYS)
flags[ev] |= TestEnabled_StencilTesting;
if(dsDesc.FrontFace.StencilFunc == D3D11_COMPARISON_NEVER && dsDesc.BackFace.StencilFunc == D3D11_COMPARISON_NEVER)
flags[ev] |= TestMustFail_StencilTesting;
if(dsDesc.FrontFace.StencilFunc == D3D11_COMPARISON_NEVER && rsDesc.CullMode == D3D11_CULL_BACK )
flags[ev] |= TestMustFail_StencilTesting;
if(rsDesc.CullMode == D3D11_CULL_FRONT && dsDesc.BackFace.StencilFunc == D3D11_COMPARISON_NEVER)
flags[ev] |= TestMustFail_StencilTesting;
}
}
else
{
// defaults
flags[ev] |= TestEnabled_DepthTesting;
}
if(rsDesc.ScissorEnable)
{
// see if we can find at least one scissor region this pixel could fall into
bool inRegion = false;
bool inAllRegions = true;
for(UINT i=0; i < curNumScissors && i < curNumViews; i++)
{
if(xf >= float(curScissors[i].left) &&
yf >= float(curScissors[i].top) &&
xf < float(curScissors[i].right) &&
yf < float(curScissors[i].bottom)
)
{
inRegion = true;
}
else
{
inAllRegions = false;
}
}
if(!inRegion)
flags[ev] |= TestMustFail_Scissor;
if(inAllRegions)
flags[ev] |= TestMustPass_Scissor;
}
if(curBS)
{
D3D11_BLEND_DESC desc;
curBS->GetDesc(&desc);
if(desc.IndependentBlendEnable)
{
for(int i=0; i < 8; i++)
{
if(desc.RenderTarget[i].BlendEnable)
{
flags[ev] |= Blending_Enabled;
break;
}
}
}
else
{
if(desc.RenderTarget[0].BlendEnable)
flags[ev] |= Blending_Enabled;
}
}
else
{
// no blending enabled by default
}
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_pImmediateContext->RSSetState(newRS);
SAFE_RELEASE(newRS);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.NopDepthState, stencilRef);
for(UINT i=0; i < curNumViews; i++)
{
// calculate scissor, relative to this viewport, that encloses only (x,y) pixel
// if (x,y) pixel isn't in viewport, make empty rect)
if(xf < curViewports[i].TopLeftX ||
yf < curViewports[i].TopLeftY ||
xf >= curViewports[i].TopLeftX + curViewports[i].Width ||
yf >= curViewports[i].TopLeftY + curViewports[i].Height)
{
newScissors[i].left = newScissors[i].top = newScissors[i].bottom = newScissors[i].right = 0;
}
else
{
newScissors[i].left = LONG(x);
newScissors[i].top = LONG(y);
newScissors[i].right = newScissors[i].left+1;
newScissors[i].bottom = newScissors[i].top+1;
}
}
// scissor every viewport
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
// figure out where this event lies in the pixstore texture
UINT storex = UINT(ev % (2048/pixstoreStride));
UINT storey = UINT(ev / (2048/pixstoreStride));
bool depthBound = false;
ID3D11Texture2D **copyTex = NULL;
ID3D11ShaderResourceView **copyDepthSRV = NULL;
ID3D11ShaderResourceView **copyStencilSRV = NULL;
ID3D11Resource *depthRes = NULL;
{
ID3D11DepthStencilView *dsv = NULL;
m_pImmediateContext->OMGetRenderTargets(0, NULL, &dsv);
if(dsv)
{
depthBound = true;
dsv->GetResource(&depthRes);
SAFE_RELEASE(dsv);
D3D11_RESOURCE_DIMENSION dim;
depthRes->GetType(&dim);
D3D11_TEXTURE2D_DESC desc2d;
RDCEraseEl(desc2d);
if(dim == D3D11_RESOURCE_DIMENSION_TEXTURE1D)
{
ID3D11Texture1D *tex = (ID3D11Texture1D *)depthRes;
D3D11_TEXTURE1D_DESC desc1d;
tex->GetDesc(&desc1d);
desc2d.Format = desc1d.Format;
desc2d.Width = desc1d.Width;
desc2d.Height = 1;
}
else if(dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D)
{
ID3D11Texture2D *tex = (ID3D11Texture2D *)depthRes;
tex->GetDesc(&desc2d);
}
else
{
RDCERR("Unexpected size of depth buffer");
}
D3D11_TEXTURE2D_DESC *copyDesc = NULL;
if(desc2d.Format == DXGI_FORMAT_R16_FLOAT ||
desc2d.Format == DXGI_FORMAT_R16_SINT ||
desc2d.Format == DXGI_FORMAT_R16_UINT ||
desc2d.Format == DXGI_FORMAT_R16_SNORM ||
desc2d.Format == DXGI_FORMAT_R16_UNORM ||
desc2d.Format == DXGI_FORMAT_R16_TYPELESS ||
desc2d.Format == DXGI_FORMAT_D16_UNORM)
{
copyDesc = &depthCopyD16Desc;
copyTex = &depthCopyD16;
copyDepthSRV = &depthCopyD16_DepthSRV;
copyStencilSRV = NULL;
copyDepthSRVDesc.Format = DXGI_FORMAT_R16_UNORM;
}
else if(desc2d.Format == DXGI_FORMAT_R24_UNORM_X8_TYPELESS ||
desc2d.Format == DXGI_FORMAT_R24G8_TYPELESS ||
desc2d.Format == DXGI_FORMAT_D24_UNORM_S8_UINT)
{
copyDesc = &depthCopyD24S8Desc;
copyTex = &depthCopyD24S8;
copyDepthSRV = &depthCopyD24S8_DepthSRV;
copyStencilSRV = &depthCopyD24S8_StencilSRV;
copyDepthSRVDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
copyStencilSRVDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
}
else if(desc2d.Format == DXGI_FORMAT_R32_FLOAT ||
desc2d.Format == DXGI_FORMAT_R32_SINT ||
desc2d.Format == DXGI_FORMAT_R32_UINT ||
desc2d.Format == DXGI_FORMAT_R32_TYPELESS ||
desc2d.Format == DXGI_FORMAT_D32_FLOAT)
{
copyDesc = &depthCopyD32Desc;
copyTex = &depthCopyD32;
copyDepthSRV = &depthCopyD32_DepthSRV;
copyStencilSRV = NULL;
copyDepthSRVDesc.Format = DXGI_FORMAT_R32_FLOAT;
}
else if(desc2d.Format == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS ||
desc2d.Format == DXGI_FORMAT_R32G8X24_TYPELESS ||
desc2d.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT)
{
copyDesc = &depthCopyD32S8Desc;
copyTex = &depthCopyD32S8;
copyDepthSRV = &depthCopyD32S8_DepthSRV;
copyStencilSRV = &depthCopyD32S8_StencilSRV;
copyDepthSRVDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
copyStencilSRVDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
}
if(*copyTex == NULL || desc2d.Width > copyDesc->Width || desc2d.Height > copyDesc->Height)
{
// recreate texture
SAFE_RELEASE(*copyTex);
SAFE_RELEASE(*copyDepthSRV);
if(copyStencilSRV) SAFE_RELEASE(*copyStencilSRV);
m_pDevice->CreateTexture2D(copyDesc, NULL, copyTex);
m_pDevice->CreateShaderResourceView(*copyTex, &copyDepthSRVDesc, copyDepthSRV);
if(copyStencilSRV) m_pDevice->CreateShaderResourceView(*copyTex, &copyStencilSRVDesc, copyStencilSRV);
}
}
}
m_pImmediateContext->CopySubresourceRegion(pixstore, 0, storex*pixstoreStride + 0, storey, 0, targetres, 0, &srcbox);
PixelHistoryDepthCopySubresource(depthBound, copyTex ? *copyTex : NULL, pixstoreDepthUAV, depthRes,
copyDepthSRV, copyStencilSRV, srcxyCBuf, storexyCBuf,
storex*pixstoreStride + 0, storey);
m_pImmediateContext->Begin(occl[ev]);
m_WrappedDevice->ReplayLog(frameID, 0, events[ev], eReplay_OnlyDraw);
m_pImmediateContext->End(occl[ev]);
m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst);
// determine how many fragments returned from the shader
{
D3D11_RASTERIZER_DESC rdsc = rsDesc;
rdsc.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rdsc, &newRS);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassIncrDepthState, stencilRef);
m_pImmediateContext->RSSetState(newRS);
SAFE_RELEASE(newRS);
ID3D11RenderTargetView* tmpViews[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0};
m_pImmediateContext->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, tmpViews, NULL);
uint32_t UAVStartSlot = 0;
for(int i=0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
if(tmpViews[i] != NULL)
{
UAVStartSlot = i+1;
SAFE_RELEASE(tmpViews[i]);
}
}
ID3D11RenderTargetView* prevRTVs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0};
ID3D11UnorderedAccessView* prevUAVs[D3D11_PS_CS_UAV_REGISTER_COUNT] = {0};
ID3D11DepthStencilView *prevDSV = NULL;
m_pImmediateContext->OMGetRenderTargetsAndUnorderedAccessViews(UAVStartSlot, prevRTVs, &prevDSV,
UAVStartSlot, D3D11_PS_CS_UAV_REGISTER_COUNT-UAVStartSlot, prevUAVs);
m_pImmediateContext->ClearDepthStencilView(shadOutputDSV, D3D11_CLEAR_STENCIL, 1.0f, 0);
m_pImmediateContext->OMSetRenderTargets(0, NULL, shadOutputDSV);
m_WrappedDevice->ReplayLog(frameID, 0, events[ev], eReplay_OnlyDraw);
UINT initCounts[D3D11_PS_CS_UAV_REGISTER_COUNT] = { ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, };
m_pImmediateContext->OMSetRenderTargetsAndUnorderedAccessViews(UAVStartSlot, prevRTVs, prevDSV, UAVStartSlot,
D3D11_PS_CS_UAV_REGISTER_COUNT-UAVStartSlot, prevUAVs, initCounts);
for(int i=0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
SAFE_RELEASE(prevRTVs[i]);
SAFE_RELEASE(prevUAVs[i]);
}
SAFE_RELEASE(prevDSV);
PixelHistoryDepthCopySubresource(true, NULL, pixstoreDepthUAV, shaddepthOutput,
&shaddepthOutputDepthSRV, &shaddepthOutputStencilSRV, srcxyCBuf, storexyCBuf,
storex*pixstoreStride + 2, storey);
}
// TODO, if drawcall has side-effects (i.e UAVs bound) replay from start of log here?
m_pImmediateContext->RSSetState(curRS);
m_pImmediateContext->RSSetScissorRects(curNumScissors, curScissors);
m_pImmediateContext->OMSetBlendState(curBS, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(curDS, stencilRef);
for(UINT i=0; i < curNumInst; i++)
SAFE_RELEASE(curInst[i]);
SAFE_RELEASE(curPS);
SAFE_RELEASE(curRS);
SAFE_RELEASE(curBS);
SAFE_RELEASE(curDS);
// replay only draw to get immediately post-modification values
m_WrappedDevice->ReplayLog(frameID, events[ev], events[ev], eReplay_OnlyDraw);
m_pImmediateContext->CopySubresourceRegion(pixstore, 0, storex*pixstoreStride + 1, storey, 0, targetres, 0, &srcbox);
PixelHistoryDepthCopySubresource(depthBound, copyTex ? *copyTex : NULL, pixstoreDepthUAV, depthRes,
copyDepthSRV, copyStencilSRV, srcxyCBuf, storexyCBuf,
storex*pixstoreStride + 1, storey);
if(ev < events.size()-1)
m_WrappedDevice->ReplayLog(frameID, events[ev]+1, events[ev+1], eReplay_WithoutDraw);
SAFE_RELEASE(depthRes);
}
////////////////////////////////////////////////////////////////////////
// Second loop over each event to determine if it the above query returned
// true and narrow down which tests (if any) it failed
for(size_t i=0; i < occl.size(); i++)
{
do
{
hr = m_pImmediateContext->GetData(occl[i], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
const FetchDrawcall *draw = m_WrappedDevice->GetDrawcall(frameID, events[i]);
bool clear = (draw->flags & eDraw_Clear);
if(occlData > 0 || clear)
{
PixelModification mod;
RDCEraseEl(mod);
uint32_t fragDupes = 1;
mod.eventID = events[i];
mod.preMod.col.value_u[0] = (uint32_t)i;
if((draw->flags & eDraw_Clear) == 0)
{
if(flags[i] & TestMustFail_DepthTesting)
mod.depthTestFailed = true;
if(flags[i] & TestMustFail_StencilTesting)
mod.stencilTestFailed = true;
if(flags[i] & TestMustFail_Scissor)
mod.scissorClipped = true;
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
curNumScissors = curNumViews = 16;
m_pImmediateContext->RSGetViewports(&curNumViews, curViewports);
m_pImmediateContext->RSGetScissorRects(&curNumScissors, curScissors);
m_pImmediateContext->RSGetState(&curRS);
m_pImmediateContext->OMGetDepthStencilState(&curDS, &stencilRef);
blendFactor[0] = blendFactor[1] = blendFactor[2] = blendFactor[3] = 1.0f;
curSample = ~0U;
D3D11_RASTERIZER_DESC rdesc = {
/*FillMode =*/ D3D11_FILL_SOLID,
/*CullMode =*/ D3D11_CULL_BACK,
/*FrontCounterClockwise =*/ FALSE,
/*DepthBias =*/ D3D11_DEFAULT_DEPTH_BIAS,
/*DepthBiasClamp =*/ D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
/*SlopeScaledDepthBias =*/ D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
/*DepthClipEnable =*/ TRUE,
/*ScissorEnable =*/ FALSE,
/*MultisampleEnable =*/ FALSE,
/*AntialiasedLineEnable =*/ FALSE,
};
if(curRS)
curRS->GetDesc(&rdesc);
SAFE_RELEASE(curRS);
D3D11_DEPTH_STENCIL_DESC dsdesc = {
/*DepthEnable =*/ TRUE,
/*DepthWriteMask =*/ D3D11_DEPTH_WRITE_MASK_ALL,
/*DepthFunc =*/ D3D11_COMPARISON_LESS,
/*StencilEnable =*/ FALSE,
/*StencilReadMask =*/ D3D11_DEFAULT_STENCIL_READ_MASK,
/*StencilWriteMask =*/ D3D11_DEFAULT_STENCIL_WRITE_MASK,
/*FrontFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS },
/*BackFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS },
};
if(curDS)
curDS->GetDesc(&dsdesc);
SAFE_RELEASE(curDS);
for(UINT v=0; v < curNumViews; v++)
{
// calculate scissor, relative to this viewport, that encloses only (x,y) pixel
// if (x,y) pixel isn't in viewport, make empty rect)
if(xf < curViewports[v].TopLeftX ||
yf < curViewports[v].TopLeftY ||
xf >= curViewports[v].TopLeftX + curViewports[v].Width ||
yf >= curViewports[v].TopLeftY + curViewports[v].Height)
{
newScissors[v].left = newScissors[v].top = newScissors[v].bottom = newScissors[v].right = 0;
}
else
{
newScissors[i].left = LONG(x);
newScissors[i].top = LONG(y);
newScissors[i].right = newScissors[i].left+1;
newScissors[i].bottom = newScissors[i].top+1;
}
}
// for each test we only disable pipeline rejection tests that fall *after* it.
// e.g. to get an idea if a pixel failed backface culling or not, we enable only backface
// culling and disable everything else (since it happens first).
// For depth testing, we leave all tests enabled up to then - as we only want to know which
// pixels were rejected by the depth test, not pixels that might have passed the depth test
// had they not been discarded earlier by backface culling or depth clipping.
// test shader discard
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassDepthState, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->Begin(testQueries[3]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[3]);
SAFE_RELEASE(newRS);
}
if(flags[i] & TestEnabled_BackfaceCulling)
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
rd.DepthClipEnable = FALSE;
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassDepthState, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->Begin(testQueries[0]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[0]);
SAFE_RELEASE(newRS);
}
if(flags[i] & TestEnabled_DepthClip)
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassDepthState, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->Begin(testQueries[1]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[1]);
SAFE_RELEASE(newRS);
}
// only check scissor if test is enabled and we don't know if it's pass or fail yet
if((flags[i] & (TestEnabled_Scissor|TestMustPass_Scissor|TestMustFail_Scissor)) == TestEnabled_Scissor)
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
// newScissors has scissor regions calculated to hit our target pixel on every viewport, but we must
// intersect that with the original scissors regions for correct testing behaviour.
// This amounts to making any scissor region that doesn't overlap with the target pixel empty.
//
// Note that in the case of only one scissor region we can trivially detect pass/fail of the test against
// our pixel on the CPU so we won't come in here (see check above against MustFail/MustPass). So we will
// only do this in the case where we have multiple scissor regions/viewports, some intersecting the pixel
// and some not. So we make the not intersecting scissor regions empty so our occlusion query tests to see
// if any pixels were written to the "passing" viewports
D3D11_RECT intersectScissors[16] = {0};
memcpy(intersectScissors, newScissors, sizeof(intersectScissors));
for(UINT s=0; s < curNumScissors; s++)
{
if(curScissors[i].left > newScissors[i].left ||
curScissors[i].right < newScissors[i].right ||
curScissors[i].top > newScissors[i].top ||
curScissors[i].bottom < newScissors[i].bottom)
{
// scissor region from the log doesn't touch our target pixel, make empty.
intersectScissors[i].left = intersectScissors[i].right = intersectScissors[i].top = intersectScissors[i].bottom = 0;
}
}
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassDepthState, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumScissors, intersectScissors);
m_pImmediateContext->Begin(testQueries[2]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[2]);
SAFE_RELEASE(newRS);
}
if(flags[i] & TestEnabled_DepthTesting)
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rd, &newRS);
D3D11_DEPTH_STENCIL_DESC dsd = dsdesc;
// make stencil trivially pass
dsd.StencilEnable = TRUE;
dsd.StencilReadMask = 0xff;
dsd.StencilWriteMask = 0xff;
dsd.FrontFace.StencilDepthFailOp = dsd.FrontFace.StencilFailOp = dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsd.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
dsd.BackFace.StencilDepthFailOp = dsd.BackFace.StencilFailOp = dsd.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsd.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
m_pDevice->CreateDepthStencilState(&dsd, &newDS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(newDS, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->Begin(testQueries[4]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[4]);
SAFE_RELEASE(newRS);
SAFE_RELEASE(newDS);
}
if(flags[i] & TestEnabled_StencilTesting)
{
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
rd.DepthClipEnable = FALSE;
rd.CullMode = D3D11_CULL_NONE;
m_pDevice->CreateRasterizerState(&rd, &newRS);
// leave depthstencil testing exactly as is, because a depth-fail means
// stencil isn't run
m_pDevice->CreateDepthStencilState(&dsdesc, &newDS);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw);
m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0);
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, curSample);
m_pImmediateContext->OMSetDepthStencilState(newDS, stencilRef);
m_pImmediateContext->RSSetState(newRS);
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->Begin(testQueries[5]);
m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw);
m_pImmediateContext->End(testQueries[5]);
SAFE_RELEASE(newRS);
SAFE_RELEASE(newDS);
}
// we check these in the order defined, as a positive from the backface cull test
// will invalidate tests later (as they will also be backface culled)
do
{
if(flags[i] & TestEnabled_BackfaceCulling)
{
do
{
hr = m_pImmediateContext->GetData(testQueries[0], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.backfaceCulled = (occlData == 0);
if(mod.backfaceCulled)
break;
}
if(flags[i] & TestEnabled_DepthClip)
{
do
{
hr = m_pImmediateContext->GetData(testQueries[1], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.depthClipped = (occlData == 0);
if(mod.depthClipped)
break;
}
if(!mod.backfaceCulled && (flags[i] & (TestEnabled_Scissor|TestMustPass_Scissor|TestMustFail_Scissor)) == TestEnabled_Scissor)
{
do
{
hr = m_pImmediateContext->GetData(testQueries[2], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.scissorClipped = (occlData == 0);
if(mod.scissorClipped)
break;
}
{
do
{
hr = m_pImmediateContext->GetData(testQueries[3], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.shaderDiscarded = (occlData == 0);
if(mod.shaderDiscarded)
break;
}
if(flags[i] & TestEnabled_DepthTesting)
{
do
{
hr = m_pImmediateContext->GetData(testQueries[4], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.depthTestFailed = (occlData == 0);
if(mod.depthTestFailed)
break;
}
if(flags[i] & TestEnabled_StencilTesting)
{
do
{
hr = m_pImmediateContext->GetData(testQueries[5], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
mod.stencilTestFailed = (occlData == 0);
if(mod.stencilTestFailed)
break;
}
} while (0);
}
history.push_back(mod);
RDCDEBUG("Event %u is visible", events[i]);
if(sizeof(occlData) == sizeof(UINT64)) // if we've changed from OCCLUSION_PREDICATE to OCCLUSION for debugging
RDCDEBUG(" %llu samples visible", occlData);
}
SAFE_RELEASE(occl[i]);
}
m_pImmediateContext->CopyResource(pixstoreDepthReadback, pixstoreDepth);
D3D11_MAPPED_SUBRESOURCE mapped = {0};
m_pImmediateContext->Map(pixstore, 0, D3D11_MAP_READ, 0, &mapped);
D3D11_MAPPED_SUBRESOURCE mappedDepth = {0};
m_pImmediateContext->Map(pixstoreDepthReadback, 0, D3D11_MAP_READ, 0, &mappedDepth);
byte *pixstoreDepthData = (byte *)mappedDepth.pData;
byte *pixstoreData = (byte *)mapped.pData;
////////////////////////////////////////////////////////////////////////////////////////
// Third loop over each modification event to read back the pre-draw colour + depth data
// as well as the # fragments to use in the next step
ResourceFormat fmt = MakeResourceFormat(GetTypedFormat(details.texFmt));
for(size_t h=0; h < history.size(); h++)
{
PixelModification &mod = history[h];
uint32_t pre = mod.preMod.col.value_u[0];
// figure out where this event lies in the pixstore texture
uint32_t storex = uint32_t(pre % (2048/pixstoreStride));
uint32_t storey = uint32_t(pre / (2048/pixstoreStride));
if((!fmt.special && fmt.compCount > 0 && fmt.compByteWidth > 0) || (fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8))
{
byte *rowdata = pixstoreData + mapped.RowPitch * storey;
for(int p=0; p < 2; p++)
{
byte *data = rowdata + fmt.compCount * fmt.compByteWidth * (storex * pixstoreStride + p);
ModificationValue *val = (p == 0 ? &mod.preMod : &mod.postMod);
if(fmt.compType == eCompType_SInt)
{
// need to get correct sign, but otherwise just copy
if(fmt.compByteWidth == 1)
{
int8_t *d = (int8_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
val->col.value_i[c] = d[c];
}
else if(fmt.compByteWidth == 2)
{
int16_t *d = (int16_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
val->col.value_i[c] = d[c];
}
else if(fmt.compByteWidth == 4)
{
int32_t *d = (int32_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
val->col.value_i[c] = d[c];
}
}
else
{
for(uint32_t c=0; c < fmt.compCount; c++)
memcpy(&val->col.value_u[c], data + fmt.compByteWidth * c, fmt.compByteWidth);
}
}
}
else
{
if(fmt.special && (fmt.specialFormat == eSpecial_R10G10B10A2 || fmt.specialFormat == eSpecial_R11G11B10))
{
byte *rowdata = pixstoreData + mapped.RowPitch * storey;
for(int p=0; p < 2; p++)
{
byte *data = rowdata + fmt.compCount * fmt.compByteWidth * (storex * pixstoreStride + p);
uint32_t *u = (uint32_t *)data;
ModificationValue *val = (p == 0 ? &mod.preMod : &mod.postMod);
Vec4f v;
if(fmt.specialFormat == eSpecial_R10G10B10A2)
v = ConvertFromR10G10B10A2(*u);
if(fmt.specialFormat == eSpecial_R11G11B10)
{
Vec3f v3 = ConvertFromR11G11B10(*u);
v = Vec4f(v3.x, v3.y, v3.z);
}
memcpy(&val->col.value_f[0], &v, sizeof(float)*4);
}
}
else
{
RDCWARN("need to fetch pixel values from special formats");
}
}
if(fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8)
{
std::swap(mod.preMod.col.value_u[0], mod.preMod.col.value_u[3]);
std::swap(mod.preMod.col.value_u[1], mod.preMod.col.value_u[2]);
std::swap(mod.postMod.col.value_u[0], mod.postMod.col.value_u[3]);
std::swap(mod.postMod.col.value_u[1], mod.postMod.col.value_u[2]);
}
{
byte *rowdata = pixstoreDepthData + mapped.RowPitch * storey;
float *data = (float *)(rowdata + 2 * sizeof(float) * (storex * pixstoreStride + 0));
mod.preMod.depth = data[0];
mod.preMod.stencil = int32_t(data[1]);
mod.postMod.depth = data[2];
mod.postMod.stencil = int32_t(data[3]);
// data[4] unused
mod.shaderOut.col.value_i[0] = int32_t(data[5]); // fragments writing to the pixel in this event
}
}
m_pImmediateContext->Unmap(pixstoreDepthReadback, 0);
m_pImmediateContext->Unmap(pixstore, 0);
/////////////////////////////////////////////////////////////////////////
// simple loop to expand out the history events by number of fragments,
// duplicatinug and setting fragIndex in each
for(size_t h=0; h < history.size(); )
{
int32_t frags = RDCMAX(1, history[h].shaderOut.col.value_i[0]);
PixelModification mod = history[h];
for(int32_t f=1; f < frags; f++)
history.insert(history.begin()+h+1, mod);
for(int32_t f=0; f < frags; f++)
history[h+f].fragIndex = f;
h += frags;
}
uint32_t prev = 0;
/////////////////////////////////////////////////////////////////////////
// loop for each fragment, for non-final fragments fetch the post-output
// buffer value, and for each fetch the shader output value
uint32_t postColSlot = 0;
uint32_t shadColSlot = 0;
uint32_t depthSlot = 0;
uint32_t rtIndex = 100000;
ID3D11RenderTargetView* RTVs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0};
ID3D11DepthStencilState *ds = NULL;
for(size_t h=0; h < history.size(); h++)
{
const FetchDrawcall *draw = m_WrappedDevice->GetDrawcall(frameID, history[h].eventID);
if(draw->flags & eDraw_Clear)
continue;
if(prev != history[h].eventID)
{
m_WrappedDevice->ReplayLog(frameID, 0, history[h].eventID, eReplay_WithoutDraw);
prev = history[h].eventID;
curNumScissors = curNumViews = 16;
m_pImmediateContext->RSGetViewports(&curNumViews, curViewports);
for(UINT v=0; v < curNumViews; v++)
{
// calculate scissor, relative to this viewport, that encloses only (x,y) pixel
// if (x,y) pixel isn't in viewport, make empty rect)
if(xf < curViewports[v].TopLeftX ||
yf < curViewports[v].TopLeftY ||
xf >= curViewports[v].TopLeftX + curViewports[v].Width ||
yf >= curViewports[v].TopLeftY + curViewports[v].Height)
{
newScissors[v].left = newScissors[v].top = newScissors[v].bottom = newScissors[v].right = 0;
}
else
{
newScissors[v].left = LONG(x);
newScissors[v].top = LONG(y);
newScissors[v].right = newScissors[v].left+1;
newScissors[v].bottom = newScissors[v].top+1;
}
}
m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors);
m_pImmediateContext->RSGetState(&curRS);
D3D11_RASTERIZER_DESC rdesc = {
/*FillMode =*/ D3D11_FILL_SOLID,
/*CullMode =*/ D3D11_CULL_BACK,
/*FrontCounterClockwise =*/ FALSE,
/*DepthBias =*/ D3D11_DEFAULT_DEPTH_BIAS,
/*DepthBiasClamp =*/ D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
/*SlopeScaledDepthBias =*/ D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
/*DepthClipEnable =*/ TRUE,
/*ScissorEnable =*/ FALSE,
/*MultisampleEnable =*/ FALSE,
/*AntialiasedLineEnable =*/ FALSE,
};
if(curRS)
curRS->GetDesc(&rdesc);
SAFE_RELEASE(curRS);
m_pImmediateContext->OMGetDepthStencilState(&curDS, &stencilRef);
// make a depth-stencil state object that writes to depth, uses same comparison
// as currently set, and tests stencil INCR_SAT / GREATER_EQUAL for fragment selection
D3D11_DEPTH_STENCIL_DESC dsdesc = {
/*DepthEnable =*/ TRUE,
/*DepthWriteMask =*/ D3D11_DEPTH_WRITE_MASK_ALL,
/*DepthFunc =*/ D3D11_COMPARISON_LESS,
/*StencilEnable =*/ TRUE,
/*StencilReadMask =*/ D3D11_DEFAULT_STENCIL_READ_MASK,
/*StencilWriteMask =*/ D3D11_DEFAULT_STENCIL_WRITE_MASK,
/*FrontFace =*/ { D3D11_STENCIL_OP_INCR_SAT, D3D11_STENCIL_OP_INCR_SAT, D3D11_STENCIL_OP_INCR_SAT, D3D11_COMPARISON_GREATER_EQUAL },
/*BackFace =*/ { D3D11_STENCIL_OP_INCR_SAT, D3D11_STENCIL_OP_INCR_SAT, D3D11_STENCIL_OP_INCR_SAT, D3D11_COMPARISON_GREATER_EQUAL },
};
if(curDS)
{
D3D11_DEPTH_STENCIL_DESC stateDesc;
curDS->GetDesc(&stateDesc);
dsdesc.DepthFunc = stateDesc.DepthFunc;
}
SAFE_RELEASE(curDS);
m_pDevice->CreateDepthStencilState(&dsdesc, &ds);
D3D11_RASTERIZER_DESC rd = rdesc;
rd.ScissorEnable = TRUE;
// leave depth clip mode as normal
// leave backface culling mode as normal
m_pDevice->CreateRasterizerState(&rd, &newRS);
m_pImmediateContext->RSSetState(newRS);
SAFE_RELEASE(newRS);
for(int i=0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
SAFE_RELEASE(RTVs[i]);
m_pImmediateContext->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, RTVs, NULL);
rtIndex = 100000;
for(uint32_t i=0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
if(RTVs[i])
{
if(rtIndex == 100000)
{
ID3D11Resource *res = NULL;
RTVs[i]->GetResource(&res);
if(res == targetres)
rtIndex = i;
SAFE_RELEASE(res);
}
// leave the target RTV in the array
if(rtIndex != i)
SAFE_RELEASE(RTVs[i]);
}
}
if(rtIndex == 100000)
{
rtIndex = 0;
RDCWARN("Couldn't find target RT bound at this event");
}
}
m_pImmediateContext->ClearDepthStencilView(shadOutputDSV, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, history[h].preMod.depth, 0);
m_pImmediateContext->OMSetDepthStencilState(ds, history[h].fragIndex);
// if we're not the last modification in our event, need to fetch post fragment value
if(h+1 < history.size() && history[h].eventID == history[h+1].eventID)
{
m_pImmediateContext->OMSetRenderTargets(rtIndex+1, RTVs, shadOutputDSV);
m_WrappedDevice->ReplayLog(frameID, 0, history[h].eventID, eReplay_OnlyDraw);
m_pImmediateContext->CopySubresourceRegion(pixstore, 0, postColSlot%2048, postColSlot/2048, 0, targetres, 0, &srcbox);
postColSlot++;
}
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.StencIncrEqDepthState, history[h].fragIndex);
m_pImmediateContext->ClearDepthStencilView(shadOutputDSV, D3D11_CLEAR_STENCIL, history[h].preMod.depth, 0);
// fetch shader output value & primitive ID
{
m_pImmediateContext->OMGetBlendState(&curBS, blendFactor, &curSample);
m_pImmediateContext->OMSetBlendState(NULL, blendFactor, ~0U);
// fetch shader output value
{
m_pImmediateContext->OMSetRenderTargets(1, &shadOutputRTV, shadOutputDSV);
m_WrappedDevice->ReplayLog(frameID, 0, history[h].eventID, eReplay_OnlyDraw);
m_pImmediateContext->CopySubresourceRegion(shadoutStore, 0, shadColSlot%2048, shadColSlot/2048, 0, shadOutput, 0, &srcbox);
shadColSlot++;
m_pImmediateContext->OMSetRenderTargets(0, NULL, NULL);
PixelHistoryDepthCopySubresource(true, NULL, pixstoreDepthUAV, shaddepthOutput,
&shaddepthOutputDepthSRV, &shaddepthOutputStencilSRV, srcxyCBuf, storexyCBuf,
depthSlot%2048, depthSlot/2048);
depthSlot++;
}
m_pImmediateContext->ClearDepthStencilView(shadOutputDSV, D3D11_CLEAR_STENCIL, history[h].preMod.depth, 0);
// fetch primitive ID
{
m_pImmediateContext->OMSetRenderTargets(1, &shadOutputRTV, shadOutputDSV);
m_pImmediateContext->PSGetShader(&curPS, curInst, &curNumInst);
m_pImmediateContext->PSSetShader(m_DebugRender.PrimitiveIDPS, NULL, 0);
m_WrappedDevice->ReplayLog(frameID, 0, history[h].eventID, eReplay_OnlyDraw);
m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst);
for(UINT i=0; i < curNumInst; i++)
SAFE_RELEASE(curInst[i]);
SAFE_RELEASE(curPS);
m_pImmediateContext->CopySubresourceRegion(shadoutStore, 0, shadColSlot%2048, shadColSlot/2048, 0, shadOutput, 0, &srcbox);
shadColSlot++;
}
m_pImmediateContext->OMSetBlendState(curBS, blendFactor, curSample);
SAFE_RELEASE(curBS);
}
}
SAFE_RELEASE(ds);
for(int i=0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
SAFE_RELEASE(RTVs[i]);
m_pImmediateContext->CopyResource(pixstoreDepthReadback, pixstoreDepth);
m_pImmediateContext->Map(pixstore, 0, D3D11_MAP_READ, 0, &mapped);
m_pImmediateContext->Map(pixstoreDepthReadback, 0, D3D11_MAP_READ, 0, &mappedDepth);
D3D11_MAPPED_SUBRESOURCE mappedShadout = {0};
m_pImmediateContext->Map(shadoutStore, 0, D3D11_MAP_READ, 0, &mappedShadout);
byte *shadoutStoreData = (byte *)mappedShadout.pData;
pixstoreData = (byte *)mapped.pData;
pixstoreDepthData = (byte *)mappedDepth.pData;
/////////////////////////////////////////////////////////////////////////
// final loop to fetch the values from above into the modification events
postColSlot = 0;
shadColSlot = 0;
depthSlot = 0;
for(size_t h=0; h < history.size(); h++)
{
const FetchDrawcall *draw = m_WrappedDevice->GetDrawcall(frameID, history[h].eventID);
if(draw->flags & eDraw_Clear)
continue;
// if we're not the last modification in our event, need to fetch post fragment value
if(h+1 < history.size() && history[h].eventID == history[h+1].eventID)
{
// colour
{
if((!fmt.special && fmt.compCount > 0 && fmt.compByteWidth > 0) || (fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8))
{
byte *rowdata = pixstoreData + mapped.RowPitch * (postColSlot/2048);
byte *data = rowdata + fmt.compCount * fmt.compByteWidth * (postColSlot%2048);
if(fmt.compType == eCompType_SInt)
{
// need to get correct sign, but otherwise just copy
if(fmt.compByteWidth == 1)
{
int8_t *d = (int8_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
history[h].postMod.col.value_i[c] = d[c];
}
else if(fmt.compByteWidth == 2)
{
int16_t *d = (int16_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
history[h].postMod.col.value_i[c] = d[c];
}
else if(fmt.compByteWidth == 4)
{
int32_t *d = (int32_t*)data;
for(uint32_t c=0; c < fmt.compCount; c++)
history[h].postMod.col.value_i[c] = d[c];
}
}
else
{
for(uint32_t c=0; c < fmt.compCount; c++)
memcpy(&history[h].postMod.col.value_u[c], data + fmt.compByteWidth * c, fmt.compByteWidth);
}
}
else
{
if(fmt.special && (fmt.specialFormat == eSpecial_R10G10B10A2 || fmt.specialFormat == eSpecial_R11G11B10))
{
byte *rowdata = pixstoreData + mapped.RowPitch * (postColSlot/2048);
byte *data = rowdata + fmt.compCount * fmt.compByteWidth * (postColSlot%2048);
uint32_t *u = (uint32_t *)data;
Vec4f v;
if(fmt.specialFormat == eSpecial_R10G10B10A2)
v = ConvertFromR10G10B10A2(*u);
if(fmt.specialFormat == eSpecial_R11G11B10)
{
Vec3f v3 = ConvertFromR11G11B10(*u);
v = Vec4f(v3.x, v3.y, v3.z);
}
memcpy(&history[h].postMod.col.value_f[0], &v, sizeof(float)*4);
}
else
{
RDCWARN("need to fetch pixel values from special formats");
}
}
if(fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8)
{
std::swap(history[h].preMod.col.value_u[0], history[h].preMod.col.value_u[3]);
std::swap(history[h].preMod.col.value_u[1], history[h].preMod.col.value_u[2]);
std::swap(history[h].postMod.col.value_u[0], history[h].postMod.col.value_u[3]);
std::swap(history[h].postMod.col.value_u[1], history[h].postMod.col.value_u[2]);
}
}
// we don't retrieve the correct-precision depth value post-fragment. This is only possible for
// D24 and D32 - D16 doesn't have attached stencil, so we wouldn't be able to get correct depth
// AND identify each fragment. Instead we just mark this as no data, and the shader output depth
// should be sufficient.
if(history[h].preMod.depth >= 0.0f)
history[h].postMod.depth = -2.0f;
else
history[h].postMod.depth = -1.0f;
// we can't retrieve stencil value after each fragment, as we use stencil to identify the fragment
if(history[h].preMod.stencil >= 0)
history[h].postMod.stencil = -2;
else
history[h].postMod.stencil = -1;
// in each case we only mark as "unknown" when the depth/stencil isn't already known to be unbound
postColSlot++;
}
// if we're not the first modification in our event, set our preMod to the previous postMod
if(h > 0 && history[h].eventID == history[h-1].eventID)
{
history[h].preMod = history[h-1].postMod;
}
// fetch shader output value
{
// colour
{
// shader output is always 4 32bit components, so we can copy straight
byte *rowdata = shadoutStoreData + mapped.RowPitch * (shadColSlot/2048);
byte *data = rowdata + 4 * sizeof(float) * (shadColSlot%2048);
memcpy(&history[h].shaderOut.col.value_u[0], data, 4*sizeof(float));
}
// depth
{
byte *rowdata = pixstoreDepthData + mapped.RowPitch * (depthSlot/2048);
float *data = (float *)(rowdata + 2 * sizeof(float) * (depthSlot%2048));
history[h].shaderOut.depth = data[0];
if(history[h].postMod.stencil == -1)
history[h].shaderOut.stencil = -1;
else
history[h].shaderOut.stencil = -2; // can't retrieve this as we use stencil to identify each fragment
}
shadColSlot++;
depthSlot++;
}
// fetch primitive ID
{
// shader output is always 4 32bit components, so we can copy straight
byte *rowdata = shadoutStoreData + mapped.RowPitch * (shadColSlot/2048);
byte *data = rowdata + 4 * sizeof(float) * (shadColSlot%2048);
memcpy(&history[h].primitiveID, data, sizeof(uint32_t));
shadColSlot++;
}
}
m_pImmediateContext->Unmap(shadoutStore, 0);
m_pImmediateContext->Unmap(pixstore, 0);
m_pImmediateContext->Unmap(pixstoreDepthReadback, 0);
// interpret float/unorm values
if(!fmt.special && fmt.compType != eCompType_UInt && fmt.compType != eCompType_SInt)
{
for(size_t h=0; h < history.size(); h++)
{
PixelModification &mod = history[h];
if(fmt.compType == eCompType_Float && fmt.compByteWidth == 2)
{
for(uint32_t c=0; c < fmt.compCount; c++)
{
mod.preMod.col.value_f[c] = ConvertFromHalf(uint16_t(mod.preMod.col.value_u[c]));
mod.postMod.col.value_f[c] = ConvertFromHalf(uint16_t(mod.postMod.col.value_u[c]));
}
}
else if(fmt.compType == eCompType_UNorm)
{
// only 32bit unorm format is depth, handled separately
float maxVal = fmt.compByteWidth == 2 ? 65535.0f : 255.0f;
RDCASSERT(fmt.compByteWidth < 4);
for(uint32_t c=0; c < fmt.compCount; c++)
{
mod.preMod.col.value_f[c] = float(mod.preMod.col.value_u[c])/maxVal;
mod.postMod.col.value_f[c] = float(mod.postMod.col.value_u[c])/maxVal;
}
}
else if(fmt.compType == eCompType_UNorm_SRGB)
{
RDCASSERT(fmt.compByteWidth == 1);
for(uint32_t c=0; c < RDCMIN(fmt.compCount, 3U); c++)
{
mod.preMod.col.value_f[c] = ConvertFromSRGB8(mod.preMod.col.value_u[c]&0xff);
mod.postMod.col.value_f[c] = ConvertFromSRGB8(mod.postMod.col.value_u[c]&0xff);
}
// alpha is not SRGB'd
if(fmt.compCount == 4)
{
mod.preMod.col.value_f[3] = float(mod.preMod.col.value_u[3]&0xff)/255.0f;
mod.postMod.col.value_f[3] = float(mod.postMod.col.value_u[3]&0xff)/255.0f;
}
}
else if(fmt.compType == eCompType_SNorm && fmt.compByteWidth == 2)
{
for(uint32_t c=0; c < fmt.compCount; c++)
{
mod.preMod.col.value_f[c] = float(mod.preMod.col.value_u[c]);
mod.postMod.col.value_f[c] = float(mod.postMod.col.value_u[c]);
}
}
else if(fmt.compType == eCompType_SNorm && fmt.compByteWidth == 1)
{
for(uint32_t c=0; c < fmt.compCount; c++)
{
int8_t *d = (int8_t *)&mod.preMod.col.value_u[c];
if(*d == -128)
mod.preMod.col.value_f[c] = -1.0f;
else
mod.preMod.col.value_f[c] = float(*d)/127.0f;
d = (int8_t *)&mod.postMod.col.value_u[c];
if(*d == -128)
mod.postMod.col.value_f[c] = -1.0f;
else
mod.postMod.col.value_f[c] = float(*d)/127.0f;
}
}
else if(fmt.compType == eCompType_SNorm && fmt.compByteWidth == 2)
{
for(uint32_t c=0; c < fmt.compCount; c++)
{
int16_t *d = (int16_t *)&mod.preMod.col.value_u[c];
if(*d == -32768)
mod.preMod.col.value_f[c] = -1.0f;
else
mod.preMod.col.value_f[c] = float(*d)/32767.0f;
d = (int16_t *)&mod.postMod.col.value_u[c];
if(*d == -32768)
mod.postMod.col.value_f[c] = -1.0f;
else
mod.postMod.col.value_f[c] = float(*d)/32767.0f;
}
}
}
}
for(size_t h=0; h < history.size(); h++)
{
PixelModification &hs = history[h];
RDCLOG("\nHistory %u @ frag %u from prim %u in %u (depth culled %u):\n" \
"pre {%f,%f,%f,%f} {%f,%d}\n" \
"+ shad {%f,%f,%f,%f} {%f,%d}\n" \
"-> post {%f,%f,%f,%f} {%f,%d}",
uint32_t(h), hs.fragIndex, hs.primitiveID, hs.eventID, hs.depthTestFailed,
hs.preMod.col.value_f[0], hs.preMod.col.value_f[1], hs.preMod.col.value_f[2], hs.preMod.col.value_f[3],
hs.preMod.depth, hs.preMod.stencil,
hs.shaderOut.col.value_f[0], hs.shaderOut.col.value_f[1], hs.shaderOut.col.value_f[2], hs.shaderOut.col.value_f[3],
hs.shaderOut.depth, hs.shaderOut.stencil,
hs.postMod.col.value_f[0], hs.postMod.col.value_f[1], hs.postMod.col.value_f[2], hs.postMod.col.value_f[3],
hs.postMod.depth, hs.postMod.stencil);
}
for(size_t i=0; i < ARRAY_COUNT(testQueries); i++)
SAFE_RELEASE(testQueries[i]);
SAFE_RELEASE(pixstore);
SAFE_RELEASE(shadoutStore);
SAFE_RELEASE(shadOutput);
SAFE_RELEASE(shadOutputRTV);
SAFE_RELEASE(shaddepthOutput);
SAFE_RELEASE(shadOutputDSV);
SAFE_RELEASE(shaddepthOutputDepthSRV);
SAFE_RELEASE(shaddepthOutputStencilSRV);
SAFE_RELEASE(pixstoreDepthReadback);
SAFE_RELEASE(pixstoreDepth);
SAFE_RELEASE(pixstoreDepthUAV);
SAFE_RELEASE(depthCopyD24S8);
SAFE_RELEASE(depthCopyD24S8_DepthSRV);
SAFE_RELEASE(depthCopyD24S8_StencilSRV);
SAFE_RELEASE(depthCopyD32S8);
SAFE_RELEASE(depthCopyD32S8_DepthSRV);
SAFE_RELEASE(depthCopyD32S8_StencilSRV);
SAFE_RELEASE(depthCopyD32);
SAFE_RELEASE(depthCopyD32_DepthSRV);
SAFE_RELEASE(depthCopyD16);
SAFE_RELEASE(depthCopyD16_DepthSRV);
return history;
}