mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
1501 lines
44 KiB
C++
1501 lines
44 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2018 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "data/resource.h"
|
|
#include "driver/d3d11/d3d11_renderstate.h"
|
|
#include "driver/d3d11/d3d11_resources.h"
|
|
#include "driver/shaders/dxbc/dxbc_debug.h"
|
|
#include "maths/camera.h"
|
|
#include "maths/formatpacking.h"
|
|
#include "maths/matrix.h"
|
|
#include "maths/vec.h"
|
|
#include "serialise/serialiser.h"
|
|
#include "strings/string_utils.h"
|
|
#include "d3d11_context.h"
|
|
#include "d3d11_debug.h"
|
|
#include "d3d11_manager.h"
|
|
|
|
#include "data/hlsl/debugcbuffers.h"
|
|
|
|
void D3D11DebugManager::FillCBufferVariables(const string &prefix, size_t &offset, bool flatten,
|
|
const vector<DXBC::CBufferVariable> &invars,
|
|
vector<ShaderVariable> &outvars, const bytebuf &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 = VarType::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 = VarType::Float;
|
|
|
|
std::vector<ShaderVariable> mems;
|
|
|
|
FillCBufferVariables("", vec, flatten, invars[v].type.members, mems, data);
|
|
|
|
vr.isStruct = true;
|
|
|
|
vr.members = mems;
|
|
|
|
varmembers.push_back(vr);
|
|
}
|
|
}
|
|
|
|
var.isStruct = false;
|
|
}
|
|
else
|
|
{
|
|
var.isStruct = true;
|
|
|
|
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;
|
|
}
|
|
|
|
if(invars[v].type.descriptor.varClass == CLASS_OBJECT ||
|
|
invars[v].type.descriptor.varClass == CLASS_STRUCT ||
|
|
invars[v].type.descriptor.varClass == CLASS_INTERFACE_CLASS ||
|
|
invars[v].type.descriptor.varClass == CLASS_INTERFACE_POINTER)
|
|
{
|
|
RDCWARN("Unexpected variable '%s' of class '%u' in cbuffer, skipping.",
|
|
invars[v].name.c_str(), invars[v].type.descriptor.type);
|
|
continue;
|
|
}
|
|
|
|
size_t elemByteSize = 4;
|
|
VarType type = VarType::Float;
|
|
switch(invars[v].type.descriptor.type)
|
|
{
|
|
case VARTYPE_MIN12INT:
|
|
case VARTYPE_MIN16INT:
|
|
case VARTYPE_INT: type = VarType::Int; break;
|
|
case VARTYPE_MIN8FLOAT:
|
|
case VARTYPE_MIN10FLOAT:
|
|
case VARTYPE_MIN16FLOAT:
|
|
case VARTYPE_FLOAT: type = VarType::Float; break;
|
|
case VARTYPE_BOOL:
|
|
case VARTYPE_UINT:
|
|
case VARTYPE_UINT8:
|
|
case VARTYPE_MIN16UINT: type = VarType::UInt; break;
|
|
case VARTYPE_DOUBLE:
|
|
elemByteSize = 8;
|
|
type = VarType::Double;
|
|
break;
|
|
default:
|
|
RDCERR("Unexpected type %d for variable '%s' in cbuffer", invars[v].type.descriptor.type,
|
|
invars[v].name.c_str());
|
|
}
|
|
|
|
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 * sizeof(Vec4f) + comp * sizeof(float);
|
|
|
|
if(!outvars[outIdx].name.empty())
|
|
{
|
|
RDCASSERT(flatten);
|
|
|
|
RDCASSERT(outvars[vec].rows == 1);
|
|
RDCASSERT(outvars[vec].columns == comp);
|
|
RDCASSERT(rows == 1);
|
|
|
|
std::string combinedName = outvars[outIdx].name;
|
|
combinedName += ", " + basename;
|
|
outvars[outIdx].name = combinedName;
|
|
outvars[outIdx].rows = 1;
|
|
outvars[outIdx].isStruct = false;
|
|
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].isStruct = false;
|
|
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 c = 0; c < cols; c++)
|
|
{
|
|
size_t srcoffs = 4 * elemByteSize * c;
|
|
size_t dstoffs = rows * elemByteSize * c;
|
|
memcpy((byte *)(tmp) + dstoffs, d + srcoffs,
|
|
RDCMIN(data.size() - dataOffset + srcoffs, elemByteSize * rows));
|
|
}
|
|
|
|
// 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;
|
|
|
|
std::vector<ShaderVariable> varmembers;
|
|
std::vector<ShaderVariable> *out = &outvars;
|
|
size_t rowCopy = 1;
|
|
|
|
uint32_t registers = rows;
|
|
uint32_t regLen = cols;
|
|
const char *regName = "row";
|
|
|
|
std::string base = outvars[outIdx].name;
|
|
|
|
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";
|
|
}
|
|
}
|
|
|
|
size_t rowDataOffset = vec * sizeof(Vec4f);
|
|
|
|
for(size_t r = 0; r < registers * elems; r++)
|
|
{
|
|
if(isArray && registers > 1)
|
|
StringFormat::snprintf(buf, 63, "[%d].%s%d", r / registers, regName, r % registers);
|
|
else if(registers > 1)
|
|
StringFormat::snprintf(buf, 63, ".%s%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].isStruct = false;
|
|
(*out)[outIdx + r].columns = regLen;
|
|
|
|
size_t totalSize = 0;
|
|
|
|
if(flatten)
|
|
{
|
|
totalSize = elemByteSize * regLen;
|
|
}
|
|
else
|
|
{
|
|
// in a matrix, each major element before the last takes up a full
|
|
// vec4 at least
|
|
size_t vecSize = elemByteSize * 4;
|
|
|
|
if(columnMajor)
|
|
totalSize = vecSize * (cols - 1) + elemByteSize * rowCopy;
|
|
else
|
|
totalSize = vecSize * (rowCopy - 1) + elemByteSize * cols;
|
|
}
|
|
|
|
if((rowDataOffset % sizeof(Vec4f) != 0) &&
|
|
(rowDataOffset / sizeof(Vec4f) != (rowDataOffset + totalSize) / sizeof(Vec4f)))
|
|
{
|
|
rowDataOffset = AlignUp(rowDataOffset, sizeof(Vec4f));
|
|
}
|
|
|
|
// arrays are also aligned to the nearest Vec4f for each element
|
|
if(!flatten && isArray)
|
|
{
|
|
rowDataOffset = AlignUp(rowDataOffset, sizeof(Vec4f));
|
|
}
|
|
|
|
if(rowDataOffset < data.size())
|
|
{
|
|
const byte *d = &data[rowDataOffset];
|
|
|
|
memcpy(&((*out)[outIdx + r].value.uv[0]), d,
|
|
RDCMIN(data.size() - rowDataOffset, totalSize));
|
|
|
|
if(!flatten && columnMajor)
|
|
{
|
|
ShaderVariable tmp = (*out)[outIdx + r];
|
|
|
|
size_t transposeRows = rowCopy > 1 ? 4 : 1;
|
|
|
|
// transpose
|
|
for(size_t ri = 0; ri < transposeRows; ri++)
|
|
for(size_t ci = 0; ci < cols; ci++)
|
|
(*out)[outIdx + r].value.uv[ri * cols + ci] = tmp.value.uv[ci * transposeRows + ri];
|
|
}
|
|
}
|
|
|
|
if(flatten)
|
|
{
|
|
rowDataOffset += sizeof(Vec4f);
|
|
}
|
|
else
|
|
{
|
|
if(columnMajor)
|
|
rowDataOffset += sizeof(Vec4f) * (cols - 1) + sizeof(float) * rowCopy;
|
|
else
|
|
rowDataOffset += sizeof(Vec4f) * (rowCopy - 1) + sizeof(float) * cols;
|
|
}
|
|
}
|
|
|
|
if(!flatten)
|
|
{
|
|
var.isStruct = false;
|
|
var.members = varmembers;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void D3D11DebugManager::FillCBufferVariables(const vector<DXBC::CBufferVariable> &invars,
|
|
vector<ShaderVariable> &outvars, bool flattenVec4s,
|
|
const bytebuf &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]);
|
|
}
|
|
|
|
uint32_t D3D11DebugManager::PickVertex(uint32_t eventId, const MeshDisplay &cfg, uint32_t x,
|
|
uint32_t y)
|
|
{
|
|
if(cfg.position.numIndices == 0)
|
|
return ~0U;
|
|
|
|
D3D11RenderStateTracker tracker(m_WrappedContext);
|
|
|
|
struct MeshPickData
|
|
{
|
|
Vec3f RayPos;
|
|
uint32_t PickIdx;
|
|
|
|
Vec3f RayDir;
|
|
uint32_t PickNumVerts;
|
|
|
|
Vec2f PickCoords;
|
|
Vec2f PickViewport;
|
|
|
|
uint32_t MeshMode;
|
|
uint32_t PickUnproject;
|
|
Vec2f Padding;
|
|
|
|
Matrix4f PickMVP;
|
|
|
|
} cbuf;
|
|
|
|
cbuf.PickCoords = Vec2f((float)x, (float)y);
|
|
cbuf.PickViewport = Vec2f((float)GetWidth(), (float)GetHeight());
|
|
cbuf.PickIdx = cfg.position.indexByteStride ? 1 : 0;
|
|
cbuf.PickNumVerts = cfg.position.numIndices;
|
|
cbuf.PickUnproject = cfg.position.unproject ? 1 : 0;
|
|
|
|
Matrix4f projMat =
|
|
Matrix4f::Perspective(90.0f, 0.1f, 100000.0f, float(GetWidth()) / float(GetHeight()));
|
|
|
|
Matrix4f camMat = cfg.cam ? ((Camera *)cfg.cam)->GetMatrix() : Matrix4f::Identity();
|
|
|
|
Matrix4f pickMVP = projMat.Mul(camMat);
|
|
|
|
Matrix4f pickMVPProj;
|
|
if(cfg.position.unproject)
|
|
{
|
|
// the derivation of the projection matrix might not be right (hell, it could be an
|
|
// orthographic projection). But it'll be close enough likely.
|
|
Matrix4f guessProj =
|
|
cfg.position.farPlane != FLT_MAX
|
|
? Matrix4f::Perspective(cfg.fov, cfg.position.nearPlane, cfg.position.farPlane, cfg.aspect)
|
|
: Matrix4f::ReversePerspective(cfg.fov, cfg.position.nearPlane, cfg.aspect);
|
|
|
|
if(cfg.ortho)
|
|
guessProj = Matrix4f::Orthographic(cfg.position.nearPlane, cfg.position.farPlane);
|
|
|
|
pickMVPProj = projMat.Mul(camMat.Mul(guessProj.Inverse()));
|
|
}
|
|
|
|
Vec3f rayPos;
|
|
Vec3f rayDir;
|
|
// convert mouse pos to world space ray
|
|
{
|
|
Matrix4f inversePickMVP = pickMVP.Inverse();
|
|
|
|
float pickX = ((float)x) / ((float)GetWidth());
|
|
float pickXCanonical = RDCLERP(-1.0f, 1.0f, pickX);
|
|
|
|
float pickY = ((float)y) / ((float)GetHeight());
|
|
// flip the Y axis
|
|
float pickYCanonical = RDCLERP(1.0f, -1.0f, pickY);
|
|
|
|
Vec3f cameraToWorldNearPosition =
|
|
inversePickMVP.Transform(Vec3f(pickXCanonical, pickYCanonical, -1), 1);
|
|
|
|
Vec3f cameraToWorldFarPosition =
|
|
inversePickMVP.Transform(Vec3f(pickXCanonical, pickYCanonical, 1), 1);
|
|
|
|
Vec3f testDir = (cameraToWorldFarPosition - cameraToWorldNearPosition);
|
|
testDir.Normalise();
|
|
|
|
// Calculate the ray direction first in the regular way (above), so we can use the
|
|
// the output for testing if the ray we are picking is negative or not. This is similar
|
|
// to checking against the forward direction of the camera, but more robust
|
|
if(cfg.position.unproject)
|
|
{
|
|
Matrix4f inversePickMVPGuess = pickMVPProj.Inverse();
|
|
|
|
Vec3f nearPosProj = inversePickMVPGuess.Transform(Vec3f(pickXCanonical, pickYCanonical, -1), 1);
|
|
|
|
Vec3f farPosProj = inversePickMVPGuess.Transform(Vec3f(pickXCanonical, pickYCanonical, 1), 1);
|
|
|
|
rayDir = (farPosProj - nearPosProj);
|
|
rayDir.Normalise();
|
|
|
|
if(testDir.z < 0)
|
|
{
|
|
rayDir = -rayDir;
|
|
}
|
|
rayPos = nearPosProj;
|
|
}
|
|
else
|
|
{
|
|
rayDir = testDir;
|
|
rayPos = cameraToWorldNearPosition;
|
|
}
|
|
}
|
|
|
|
cbuf.RayPos = rayPos;
|
|
cbuf.RayDir = rayDir;
|
|
|
|
cbuf.PickMVP = cfg.position.unproject ? pickMVPProj : pickMVP;
|
|
|
|
bool isTriangleMesh = true;
|
|
switch(cfg.position.topology)
|
|
{
|
|
case Topology::TriangleList:
|
|
{
|
|
cbuf.MeshMode = MESH_TRIANGLE_LIST;
|
|
break;
|
|
}
|
|
case Topology::TriangleStrip:
|
|
{
|
|
cbuf.MeshMode = MESH_TRIANGLE_STRIP;
|
|
break;
|
|
}
|
|
case Topology::TriangleList_Adj:
|
|
{
|
|
cbuf.MeshMode = MESH_TRIANGLE_LIST_ADJ;
|
|
break;
|
|
}
|
|
case Topology::TriangleStrip_Adj:
|
|
{
|
|
cbuf.MeshMode = MESH_TRIANGLE_STRIP_ADJ;
|
|
break;
|
|
}
|
|
default: // points, lines, patchlists, unknown
|
|
{
|
|
cbuf.MeshMode = MESH_OTHER;
|
|
isTriangleMesh = false;
|
|
}
|
|
}
|
|
|
|
ID3D11Buffer *vb = NULL, *ib = NULL;
|
|
DXGI_FORMAT ifmt = cfg.position.indexByteStride == 4 ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
|
|
|
|
{
|
|
auto it = WrappedID3D11Buffer::m_BufferList.find(cfg.position.vertexResourceId);
|
|
|
|
if(it != WrappedID3D11Buffer::m_BufferList.end())
|
|
vb = it->second.m_Buffer;
|
|
|
|
it = WrappedID3D11Buffer::m_BufferList.find(cfg.position.indexResourceId);
|
|
|
|
if(it != WrappedID3D11Buffer::m_BufferList.end())
|
|
ib = it->second.m_Buffer;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// most IB/VBs will not be available as SRVs. So, we copy into our own buffers.
|
|
// In the case of VB we also tightly pack and unpack the data. IB can just be
|
|
// read as R16 or R32 via the SRV so it is just a straight copy
|
|
|
|
if(cfg.position.indexByteStride)
|
|
{
|
|
// resize up on demand
|
|
if(m_DebugRender.PickIBBuf == NULL ||
|
|
m_DebugRender.PickIBSize < cfg.position.numIndices * cfg.position.indexByteStride)
|
|
{
|
|
SAFE_RELEASE(m_DebugRender.PickIBBuf);
|
|
SAFE_RELEASE(m_DebugRender.PickIBSRV);
|
|
|
|
D3D11_BUFFER_DESC desc = {cfg.position.numIndices * cfg.position.indexByteStride,
|
|
D3D11_USAGE_DEFAULT,
|
|
D3D11_BIND_SHADER_RESOURCE,
|
|
0,
|
|
0,
|
|
0};
|
|
|
|
m_DebugRender.PickIBSize = cfg.position.numIndices * cfg.position.indexByteStride;
|
|
|
|
hr = m_pDevice->CreateBuffer(&desc, NULL, &m_DebugRender.PickIBBuf);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Failed to create PickIBBuf HRESULT: %s", ToStr(hr).c_str());
|
|
return ~0U;
|
|
}
|
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC sdesc;
|
|
sdesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
|
sdesc.Format = ifmt;
|
|
sdesc.Buffer.FirstElement = 0;
|
|
sdesc.Buffer.NumElements = cfg.position.numIndices;
|
|
|
|
hr = m_pDevice->CreateShaderResourceView(m_DebugRender.PickIBBuf, &sdesc,
|
|
&m_DebugRender.PickIBSRV);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
SAFE_RELEASE(m_DebugRender.PickIBBuf);
|
|
RDCERR("Failed to create PickIBSRV HRESULT: %s", ToStr(hr).c_str());
|
|
return ~0U;
|
|
}
|
|
}
|
|
|
|
// copy index data as-is, the view format will take care of the rest
|
|
|
|
RDCASSERT(cfg.position.indexByteOffset < 0xffffffff);
|
|
|
|
if(ib)
|
|
{
|
|
D3D11_BUFFER_DESC ibdesc;
|
|
ib->GetDesc(&ibdesc);
|
|
|
|
D3D11_BOX box;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
box.left = (uint32_t)cfg.position.indexByteOffset;
|
|
box.right = (uint32_t)cfg.position.indexByteOffset +
|
|
cfg.position.numIndices * cfg.position.indexByteStride;
|
|
box.top = 0;
|
|
box.bottom = 1;
|
|
|
|
box.right = RDCMIN(box.right, ibdesc.ByteWidth - (uint32_t)cfg.position.indexByteOffset);
|
|
|
|
m_pImmediateContext->CopySubresourceRegion(m_DebugRender.PickIBBuf, 0, 0, 0, 0, ib, 0, &box);
|
|
}
|
|
}
|
|
|
|
if(m_DebugRender.PickVBBuf == NULL ||
|
|
m_DebugRender.PickVBSize < cfg.position.numIndices * sizeof(Vec4f))
|
|
{
|
|
SAFE_RELEASE(m_DebugRender.PickVBBuf);
|
|
SAFE_RELEASE(m_DebugRender.PickVBSRV);
|
|
|
|
D3D11_BUFFER_DESC desc = {cfg.position.numIndices * sizeof(Vec4f),
|
|
D3D11_USAGE_DEFAULT,
|
|
D3D11_BIND_SHADER_RESOURCE,
|
|
0,
|
|
0,
|
|
0};
|
|
|
|
m_DebugRender.PickVBSize = cfg.position.numIndices * sizeof(Vec4f);
|
|
|
|
hr = m_pDevice->CreateBuffer(&desc, NULL, &m_DebugRender.PickVBBuf);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Failed to create PickVBBuf HRESULT: %s", ToStr(hr).c_str());
|
|
return ~0U;
|
|
}
|
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC sdesc;
|
|
sdesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
|
sdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
sdesc.Buffer.FirstElement = 0;
|
|
sdesc.Buffer.NumElements = cfg.position.numIndices;
|
|
|
|
hr = m_pDevice->CreateShaderResourceView(m_DebugRender.PickVBBuf, &sdesc,
|
|
&m_DebugRender.PickVBSRV);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
SAFE_RELEASE(m_DebugRender.PickVBBuf);
|
|
RDCERR("Failed to create PickVBSRV HRESULT: %s", ToStr(hr).c_str());
|
|
return ~0U;
|
|
}
|
|
}
|
|
|
|
// unpack and linearise the data
|
|
if(vb)
|
|
{
|
|
FloatVector *vbData = new FloatVector[cfg.position.numIndices];
|
|
|
|
bytebuf oldData;
|
|
GetBufferData(vb, cfg.position.vertexByteOffset, 0, oldData);
|
|
|
|
byte *data = &oldData[0];
|
|
byte *dataEnd = data + oldData.size();
|
|
|
|
bool valid;
|
|
|
|
uint32_t idxclamp = 0;
|
|
if(cfg.position.baseVertex < 0)
|
|
idxclamp = uint32_t(-cfg.position.baseVertex);
|
|
|
|
for(uint32_t i = 0; i < cfg.position.numIndices; i++)
|
|
{
|
|
uint32_t idx = i;
|
|
|
|
// apply baseVertex but clamp to 0 (don't allow index to become negative)
|
|
if(idx < idxclamp)
|
|
idx = 0;
|
|
else if(cfg.position.baseVertex < 0)
|
|
idx -= idxclamp;
|
|
else if(cfg.position.baseVertex > 0)
|
|
idx += cfg.position.baseVertex;
|
|
|
|
vbData[i] = HighlightCache::InterpretVertex(data, idx, cfg, dataEnd, valid);
|
|
}
|
|
|
|
D3D11_BOX box;
|
|
box.top = 0;
|
|
box.bottom = 1;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
box.left = 0;
|
|
box.right = cfg.position.numIndices * sizeof(Vec4f);
|
|
|
|
m_pImmediateContext->UpdateSubresource(m_DebugRender.PickVBBuf, 0, &box, vbData, sizeof(Vec4f),
|
|
sizeof(Vec4f));
|
|
|
|
delete[] vbData;
|
|
}
|
|
|
|
ID3D11ShaderResourceView *srvs[2] = {m_DebugRender.PickIBSRV, m_DebugRender.PickVBSRV};
|
|
|
|
ID3D11Buffer *buf = MakeCBuffer(&cbuf, sizeof(cbuf));
|
|
|
|
m_pImmediateContext->CSSetConstantBuffers(0, 1, &buf);
|
|
|
|
m_pImmediateContext->CSSetShaderResources(0, 2, srvs);
|
|
|
|
UINT reset = 0;
|
|
m_pImmediateContext->CSSetUnorderedAccessViews(0, 1, &m_DebugRender.PickResultUAV, &reset);
|
|
|
|
m_pImmediateContext->CSSetShader(m_DebugRender.MeshPickCS, NULL, 0);
|
|
|
|
m_pImmediateContext->Dispatch(cfg.position.numIndices / 1024 + 1, 1, 1);
|
|
|
|
m_pImmediateContext->CopyStructureCount(m_DebugRender.histogramBuff, 0,
|
|
m_DebugRender.PickResultUAV);
|
|
|
|
bytebuf results;
|
|
GetBufferData(m_DebugRender.histogramBuff, 0, 0, results);
|
|
|
|
uint32_t numResults = *(uint32_t *)&results[0];
|
|
|
|
if(numResults > 0)
|
|
{
|
|
if(isTriangleMesh)
|
|
{
|
|
struct PickResult
|
|
{
|
|
uint32_t vertid;
|
|
Vec3f intersectionPoint;
|
|
};
|
|
|
|
GetBufferData(m_DebugRender.PickResultBuf, 0, 0, results);
|
|
|
|
PickResult *pickResults = (PickResult *)&results[0];
|
|
|
|
PickResult *closest = pickResults;
|
|
|
|
// distance from raycast hit to nearest worldspace position of the mouse
|
|
float closestPickDistance = (closest->intersectionPoint - rayPos).Length();
|
|
|
|
// min with size of results buffer to protect against overflows
|
|
for(uint32_t i = 1; i < RDCMIN((uint32_t)DebugRenderData::maxMeshPicks, numResults); i++)
|
|
{
|
|
float pickDistance = (pickResults[i].intersectionPoint - rayPos).Length();
|
|
if(pickDistance < closestPickDistance)
|
|
{
|
|
closest = pickResults + i;
|
|
}
|
|
}
|
|
|
|
return closest->vertid;
|
|
}
|
|
else
|
|
{
|
|
struct PickResult
|
|
{
|
|
uint32_t vertid;
|
|
uint32_t idx;
|
|
float len;
|
|
float depth;
|
|
};
|
|
|
|
GetBufferData(m_DebugRender.PickResultBuf, 0, 0, results);
|
|
|
|
PickResult *pickResults = (PickResult *)&results[0];
|
|
|
|
PickResult *closest = pickResults;
|
|
|
|
// min with size of results buffer to protect against overflows
|
|
for(uint32_t i = 1; i < RDCMIN((uint32_t)DebugRenderData::maxMeshPicks, numResults); i++)
|
|
{
|
|
// We need to keep the picking order consistent in the face
|
|
// of random buffer appends, when multiple vertices have the
|
|
// identical position (e.g. if UVs or normals are different).
|
|
//
|
|
// We could do something to try and disambiguate, but it's
|
|
// never going to be intuitive, it's just going to flicker
|
|
// confusingly.
|
|
if(pickResults[i].len < closest->len ||
|
|
(pickResults[i].len == closest->len && pickResults[i].depth < closest->depth) ||
|
|
(pickResults[i].len == closest->len && pickResults[i].depth == closest->depth &&
|
|
pickResults[i].vertid < closest->vertid))
|
|
closest = pickResults + i;
|
|
}
|
|
|
|
return closest->vertid;
|
|
}
|
|
}
|
|
|
|
return ~0U;
|
|
}
|
|
|
|
void D3D11DebugManager::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace,
|
|
uint32_t mip, uint32_t sample, CompType typeHint, float pixel[4])
|
|
{
|
|
D3D11RenderStateTracker tracker(m_WrappedContext);
|
|
|
|
D3D11MarkerRegion marker("PickPixel");
|
|
|
|
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.hdrMultiplier = -1.0f;
|
|
texDisplay.linearDisplayAsGamma = true;
|
|
texDisplay.flipY = false;
|
|
texDisplay.mip = mip;
|
|
texDisplay.sampleIdx = sample;
|
|
texDisplay.customShaderId = ResourceId();
|
|
texDisplay.sliceFace = sliceFace;
|
|
texDisplay.rangeMin = 0.0f;
|
|
texDisplay.rangeMax = 1.0f;
|
|
texDisplay.scale = 1.0f;
|
|
texDisplay.resourceId = texture;
|
|
texDisplay.typeHint = typeHint;
|
|
texDisplay.rawOutput = true;
|
|
texDisplay.xOffset = -float(x);
|
|
texDisplay.yOffset = -float(y);
|
|
|
|
RenderTexture(texDisplay, false);
|
|
}
|
|
|
|
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 HRESULT: %s", ToStr(hr).c_str());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void D3D11DebugManager::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip,
|
|
const GetTextureDataParams ¶ms, bytebuf &data)
|
|
{
|
|
D3D11RenderStateTracker tracker(m_WrappedContext);
|
|
|
|
ID3D11Resource *dummyTex = NULL;
|
|
|
|
uint32_t subresource = 0;
|
|
uint32_t mips = 0;
|
|
|
|
size_t bytesize = 0;
|
|
|
|
if(WrappedID3D11Texture1D::m_TextureList.find(tex) != WrappedID3D11Texture1D::m_TextureList.end())
|
|
{
|
|
WrappedID3D11Texture1D *wrapTex =
|
|
(WrappedID3D11Texture1D *)WrappedID3D11Texture1D::m_TextureList[tex].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;
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
desc.Format =
|
|
IsSRGBFormat(desc.Format) ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.ArraySize = 1;
|
|
}
|
|
|
|
subresource = arrayIdx * mips + mip;
|
|
|
|
HRESULT hr = m_pDevice->CreateTexture1D(&desc, NULL, &d);
|
|
|
|
dummyTex = d;
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create staging texture to retrieve data. HRESULT: %s", ToStr(hr).c_str());
|
|
return;
|
|
}
|
|
|
|
bytesize = GetByteSize(desc.Width, 1, 1, desc.Format, mip);
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
subresource = mip;
|
|
|
|
desc.CPUAccessFlags = 0;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
|
|
|
|
ID3D11Texture1D *rtTex = NULL;
|
|
|
|
hr = m_pDevice->CreateTexture1D(&desc, NULL, &rtTex);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target texture to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
return;
|
|
}
|
|
|
|
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
|
|
rtvDesc.Format = desc.Format;
|
|
rtvDesc.Texture1D.MipSlice = mip;
|
|
|
|
ID3D11RenderTargetView *wrappedrtv = NULL;
|
|
hr = m_pDevice->CreateRenderTargetView(rtTex, &rtvDesc, &wrappedrtv);
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target rtv to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
SAFE_RELEASE(rtTex);
|
|
return;
|
|
}
|
|
|
|
ID3D11RenderTargetView *rtv = wrappedrtv;
|
|
|
|
m_pImmediateContext->OMSetRenderTargets(1, &rtv, NULL);
|
|
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
m_pImmediateContext->ClearRenderTargetView(rtv, color);
|
|
|
|
D3D11_VIEWPORT viewport = {0, 0, (float)(desc.Width >> mip), 1.0f, 0.0f, 1.0f};
|
|
|
|
int oldW = GetWidth(), oldH = GetHeight();
|
|
SetOutputDimensions(desc.Width, 1);
|
|
m_pImmediateContext->RSSetViewports(1, &viewport);
|
|
|
|
{
|
|
TextureDisplay texDisplay;
|
|
|
|
texDisplay.red = texDisplay.green = texDisplay.blue = texDisplay.alpha = true;
|
|
texDisplay.hdrMultiplier = -1.0f;
|
|
texDisplay.linearDisplayAsGamma = false;
|
|
texDisplay.overlay = DebugOverlay::NoOverlay;
|
|
texDisplay.flipY = false;
|
|
texDisplay.mip = mip;
|
|
texDisplay.sampleIdx = 0;
|
|
texDisplay.customShaderId = ResourceId();
|
|
texDisplay.sliceFace = arrayIdx;
|
|
texDisplay.rangeMin = params.blackPoint;
|
|
texDisplay.rangeMax = params.whitePoint;
|
|
texDisplay.scale = 1.0f;
|
|
texDisplay.resourceId = tex;
|
|
texDisplay.typeHint = params.typeHint;
|
|
texDisplay.rawOutput = false;
|
|
texDisplay.xOffset = 0;
|
|
texDisplay.yOffset = 0;
|
|
|
|
RenderTexture(texDisplay, false);
|
|
}
|
|
|
|
SetOutputDimensions(oldW, oldH);
|
|
|
|
m_pImmediateContext->CopyResource(d, rtTex);
|
|
SAFE_RELEASE(rtTex);
|
|
|
|
SAFE_RELEASE(wrappedrtv);
|
|
}
|
|
else
|
|
{
|
|
m_pImmediateContext->CopyResource(d, wrapTex);
|
|
}
|
|
}
|
|
else if(WrappedID3D11Texture2D1::m_TextureList.find(tex) !=
|
|
WrappedID3D11Texture2D1::m_TextureList.end())
|
|
{
|
|
WrappedID3D11Texture2D1 *wrapTex =
|
|
(WrappedID3D11Texture2D1 *)WrappedID3D11Texture2D1::m_TextureList[tex].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;
|
|
|
|
bool wasms = false;
|
|
|
|
if(desc.SampleDesc.Count > 1)
|
|
{
|
|
desc.ArraySize *= desc.SampleDesc.Count;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.SampleDesc.Quality = 0;
|
|
|
|
wasms = true;
|
|
}
|
|
|
|
ID3D11Texture2D *d = NULL;
|
|
|
|
mips = desc.MipLevels ? desc.MipLevels : CalcNumMips(desc.Width, desc.Height, 1);
|
|
|
|
if(mip >= mips || arrayIdx >= desc.ArraySize)
|
|
return;
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
desc.Format = (IsSRGBFormat(desc.Format) || wrapTex->m_RealDescriptor)
|
|
? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
|
: DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.ArraySize = 1;
|
|
}
|
|
|
|
subresource = arrayIdx * mips + mip;
|
|
|
|
HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &d);
|
|
|
|
dummyTex = d;
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create staging texture to retrieve data. HRESULT: %s", ToStr(hr).c_str());
|
|
return;
|
|
}
|
|
|
|
bytesize = GetByteSize(desc.Width, desc.Height, 1, desc.Format, mip);
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
subresource = mip;
|
|
|
|
desc.CPUAccessFlags = 0;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
|
|
|
|
ID3D11Texture2D *rtTex = NULL;
|
|
|
|
hr = m_pDevice->CreateTexture2D(&desc, NULL, &rtTex);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target texture to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
return;
|
|
}
|
|
|
|
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
|
rtvDesc.Format = desc.Format;
|
|
rtvDesc.Texture2D.MipSlice = mip;
|
|
|
|
ID3D11RenderTargetView *wrappedrtv = NULL;
|
|
hr = m_pDevice->CreateRenderTargetView(rtTex, &rtvDesc, &wrappedrtv);
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target rtv to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
SAFE_RELEASE(rtTex);
|
|
return;
|
|
}
|
|
|
|
ID3D11RenderTargetView *rtv = wrappedrtv;
|
|
|
|
m_pImmediateContext->OMSetRenderTargets(1, &rtv, NULL);
|
|
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
m_pImmediateContext->ClearRenderTargetView(rtv, color);
|
|
|
|
D3D11_VIEWPORT viewport = {0, 0, (float)(desc.Width >> mip), (float)(desc.Height >> mip),
|
|
0.0f, 1.0f};
|
|
|
|
int oldW = GetWidth(), oldH = GetHeight();
|
|
SetOutputDimensions(desc.Width, desc.Height);
|
|
m_pImmediateContext->RSSetViewports(1, &viewport);
|
|
|
|
{
|
|
TextureDisplay texDisplay;
|
|
|
|
texDisplay.red = texDisplay.green = texDisplay.blue = texDisplay.alpha = true;
|
|
texDisplay.hdrMultiplier = -1.0f;
|
|
texDisplay.linearDisplayAsGamma = false;
|
|
texDisplay.overlay = DebugOverlay::NoOverlay;
|
|
texDisplay.flipY = false;
|
|
texDisplay.mip = mip;
|
|
texDisplay.sampleIdx = params.resolve ? ~0U : arrayIdx;
|
|
texDisplay.customShaderId = ResourceId();
|
|
texDisplay.sliceFace = arrayIdx;
|
|
texDisplay.rangeMin = params.blackPoint;
|
|
texDisplay.rangeMax = params.whitePoint;
|
|
texDisplay.scale = 1.0f;
|
|
texDisplay.resourceId = tex;
|
|
texDisplay.typeHint = params.typeHint;
|
|
texDisplay.rawOutput = false;
|
|
texDisplay.xOffset = 0;
|
|
texDisplay.yOffset = 0;
|
|
|
|
RenderTexture(texDisplay, false);
|
|
}
|
|
|
|
SetOutputDimensions(oldW, oldH);
|
|
|
|
m_pImmediateContext->CopyResource(d, rtTex);
|
|
SAFE_RELEASE(rtTex);
|
|
|
|
SAFE_RELEASE(wrappedrtv);
|
|
}
|
|
else if(wasms && params.resolve)
|
|
{
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.CPUAccessFlags = 0;
|
|
|
|
ID3D11Texture2D *resolveTex = NULL;
|
|
|
|
hr = m_pDevice->CreateTexture2D(&desc, NULL, &resolveTex);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target texture to resolve texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
return;
|
|
}
|
|
|
|
m_pImmediateContext->ResolveSubresource(resolveTex, arrayIdx, wrapTex, arrayIdx, desc.Format);
|
|
m_pImmediateContext->CopyResource(d, resolveTex);
|
|
|
|
SAFE_RELEASE(resolveTex);
|
|
}
|
|
else if(wasms)
|
|
{
|
|
CopyTex2DMSToArray(UNWRAP(WrappedID3D11Texture2D1, d), wrapTex->GetReal());
|
|
}
|
|
else
|
|
{
|
|
m_pImmediateContext->CopyResource(d, wrapTex);
|
|
}
|
|
}
|
|
else if(WrappedID3D11Texture3D1::m_TextureList.find(tex) !=
|
|
WrappedID3D11Texture3D1::m_TextureList.end())
|
|
{
|
|
WrappedID3D11Texture3D1 *wrapTex =
|
|
(WrappedID3D11Texture3D1 *)WrappedID3D11Texture3D1::m_TextureList[tex].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;
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
desc.Format =
|
|
IsSRGBFormat(desc.Format) ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
}
|
|
|
|
subresource = mip;
|
|
|
|
HRESULT hr = m_pDevice->CreateTexture3D(&desc, NULL, &d);
|
|
|
|
dummyTex = d;
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create staging texture to retrieve data. HRESULT: %s", ToStr(hr).c_str());
|
|
return;
|
|
}
|
|
|
|
bytesize = GetByteSize(desc.Width, desc.Height, desc.Depth, desc.Format, mip);
|
|
|
|
if(params.remap != RemapTexture::NoRemap)
|
|
{
|
|
RDCASSERT(params.remap == RemapTexture::RGBA8);
|
|
|
|
subresource = mip;
|
|
|
|
desc.CPUAccessFlags = 0;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
|
|
|
|
ID3D11Texture3D *rtTex = NULL;
|
|
|
|
hr = m_pDevice->CreateTexture3D(&desc, NULL, &rtTex);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target texture to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
return;
|
|
}
|
|
|
|
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
|
|
rtvDesc.Format = desc.Format;
|
|
rtvDesc.Texture3D.MipSlice = mip;
|
|
rtvDesc.Texture3D.FirstWSlice = 0;
|
|
rtvDesc.Texture3D.WSize = 1;
|
|
ID3D11RenderTargetView *wrappedrtv = NULL;
|
|
ID3D11RenderTargetView *rtv = NULL;
|
|
|
|
D3D11_VIEWPORT viewport = {0, 0, (float)(desc.Width >> mip), (float)(desc.Height >> mip),
|
|
0.0f, 1.0f};
|
|
|
|
int oldW = GetWidth(), oldH = GetHeight();
|
|
|
|
for(UINT i = 0; i < (desc.Depth >> mip); i++)
|
|
{
|
|
rtvDesc.Texture3D.FirstWSlice = i;
|
|
hr = m_pDevice->CreateRenderTargetView(rtTex, &rtvDesc, &wrappedrtv);
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Couldn't create target rtv to downcast texture. HRESULT: %s", ToStr(hr).c_str());
|
|
SAFE_RELEASE(d);
|
|
SAFE_RELEASE(rtTex);
|
|
return;
|
|
}
|
|
|
|
rtv = wrappedrtv;
|
|
|
|
m_pImmediateContext->OMSetRenderTargets(1, &rtv, NULL);
|
|
float color[4] = {0.0f, 0.5f, 0.0f, 0.0f};
|
|
m_pImmediateContext->ClearRenderTargetView(rtv, color);
|
|
|
|
SetOutputDimensions(desc.Width, desc.Height);
|
|
m_pImmediateContext->RSSetViewports(1, &viewport);
|
|
|
|
TextureDisplay texDisplay;
|
|
|
|
texDisplay.red = texDisplay.green = texDisplay.blue = texDisplay.alpha = true;
|
|
texDisplay.hdrMultiplier = -1.0f;
|
|
texDisplay.linearDisplayAsGamma = false;
|
|
texDisplay.overlay = DebugOverlay::NoOverlay;
|
|
texDisplay.flipY = false;
|
|
texDisplay.mip = mip;
|
|
texDisplay.sampleIdx = 0;
|
|
texDisplay.customShaderId = ResourceId();
|
|
texDisplay.sliceFace = i << mip;
|
|
texDisplay.rangeMin = params.blackPoint;
|
|
texDisplay.rangeMax = params.whitePoint;
|
|
texDisplay.scale = 1.0f;
|
|
texDisplay.resourceId = tex;
|
|
texDisplay.typeHint = params.typeHint;
|
|
texDisplay.rawOutput = false;
|
|
texDisplay.xOffset = 0;
|
|
texDisplay.yOffset = 0;
|
|
|
|
RenderTexture(texDisplay, false);
|
|
|
|
SAFE_RELEASE(wrappedrtv);
|
|
}
|
|
|
|
SetOutputDimensions(oldW, oldH);
|
|
|
|
m_pImmediateContext->CopyResource(d, rtTex);
|
|
SAFE_RELEASE(rtTex);
|
|
}
|
|
else
|
|
{
|
|
m_pImmediateContext->CopyResource(d, wrapTex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Trying to get texture data for unknown ID %llu!", tex);
|
|
return;
|
|
}
|
|
|
|
MapIntercept intercept;
|
|
|
|
D3D11_MAPPED_SUBRESOURCE mapped = {0};
|
|
HRESULT hr = m_pImmediateContext->Map(dummyTex, subresource, D3D11_MAP_READ, 0, &mapped);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
data.resize(bytesize);
|
|
intercept.InitWrappedResource(dummyTex, subresource, data.data());
|
|
intercept.SetD3D(mapped);
|
|
intercept.CopyFromD3D();
|
|
|
|
// for 3D textures if we wanted a particular slice (arrayIdx > 0)
|
|
// copy it into the beginning.
|
|
if(intercept.numSlices > 1 && arrayIdx > 0 && (int)arrayIdx < intercept.numSlices)
|
|
{
|
|
byte *dst = data.data();
|
|
byte *src = data.data() + intercept.app.DepthPitch * arrayIdx;
|
|
|
|
for(int row = 0; row < intercept.numRows; row++)
|
|
{
|
|
memcpy(dst, src, intercept.app.RowPitch);
|
|
|
|
src += intercept.app.RowPitch;
|
|
dst += intercept.app.RowPitch;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Couldn't map staging texture to retrieve data. HRESULT: %s", ToStr(hr).c_str());
|
|
}
|
|
|
|
SAFE_RELEASE(dummyTex);
|
|
}
|
|
|
|
ResourceId D3D11DebugManager::ApplyCustomShader(ResourceId shader, ResourceId texid, uint32_t mip,
|
|
uint32_t arrayIdx, uint32_t sampleIdx,
|
|
CompType typeHint)
|
|
{
|
|
TextureShaderDetails details = GetShaderDetails(texid, typeHint, false);
|
|
|
|
CreateCustomShaderTex(details.texWidth, details.texHeight);
|
|
|
|
D3D11RenderStateTracker tracker(m_WrappedContext);
|
|
|
|
{
|
|
D3D11_RENDER_TARGET_VIEW_DESC desc;
|
|
|
|
desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
|
|
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
|
desc.Texture2D.MipSlice = mip;
|
|
|
|
WrappedID3D11Texture2D1 *wrapped = (WrappedID3D11Texture2D1 *)m_CustomShaderTex;
|
|
HRESULT hr = m_pDevice->CreateRenderTargetView(wrapped, &desc, &m_CustomShaderRTV);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Failed to create custom shader rtv HRESULT: %s", ToStr(hr).c_str());
|
|
return m_CustomShaderResourceId;
|
|
}
|
|
}
|
|
|
|
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)RDCMAX(1U, details.texWidth >> mip);
|
|
viewport.Height = (float)RDCMAX(1U, details.texHeight >> mip);
|
|
|
|
m_pImmediateContext->RSSetViewports(1, &viewport);
|
|
|
|
TextureDisplay disp;
|
|
disp.red = disp.green = disp.blue = disp.alpha = true;
|
|
disp.flipY = false;
|
|
disp.xOffset = 0.0f;
|
|
disp.yOffset = 0.0f;
|
|
disp.customShaderId = shader;
|
|
disp.resourceId = texid;
|
|
disp.typeHint = typeHint;
|
|
disp.backgroundColor = FloatVector(0, 0, 0, 1.0);
|
|
disp.hdrMultiplier = -1.0f;
|
|
disp.linearDisplayAsGamma = false;
|
|
disp.mip = mip;
|
|
disp.sampleIdx = sampleIdx;
|
|
disp.overlay = DebugOverlay::NoOverlay;
|
|
disp.rangeMin = 0.0f;
|
|
disp.rangeMax = 1.0f;
|
|
disp.rawOutput = false;
|
|
disp.scale = 1.0f;
|
|
disp.sliceFace = arrayIdx;
|
|
|
|
SetOutputDimensions(RDCMAX(1U, details.texWidth >> mip), RDCMAX(1U, details.texHeight >> mip));
|
|
|
|
RenderTexture(disp, true);
|
|
|
|
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 = CalcNumMips((int)w, (int)h, 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_pDevice->CreateTexture2D(&texdesc, NULL, &m_CustomShaderTex);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
RDCERR("Failed to create custom shader tex HRESULT: %s", ToStr(hr).c_str());
|
|
}
|
|
else
|
|
{
|
|
m_CustomShaderResourceId = GetIDForResource(m_CustomShaderTex);
|
|
}
|
|
}
|