diff options
Diffstat (limited to 'samples/d3d11/util.cpp')
| -rw-r--r-- | samples/d3d11/util.cpp | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/samples/d3d11/util.cpp b/samples/d3d11/util.cpp new file mode 100644 index 0000000..3681c00 --- /dev/null +++ b/samples/d3d11/util.cpp @@ -0,0 +1,1451 @@ +//---------------------------------------------------------------------------------- +// File: FaceWorks/samples/sample_d3d11/util.cpp +// SDK Version: v1.0 +// Email: [email protected] +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//---------------------------------------------------------------------------------- + +#include "util.h" +#include "shader.h" + +#include <algorithm> + +#include <DirectXMath.h> + +#include <DXUT/Core/DXUT.h> +#include <DXUT/Core/DXUTmisc.h> +#include <DXUT/Core/WICTextureLoader.h> +#include <DXUT/Core/DDSTextureLoader.h> + +#include <GFSDK_FaceWorks.h> + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#pragma warning(disable: 4351) // Before VS2015 the warning "New behavior: array elements will be default-initialized" is produced when an array is construct-initialized with (). +#endif + +using namespace std; +using namespace DirectX; + +static wstring StrVprintf(const wchar_t * fmt, va_list args) +{ + int cChAlloc = _vscwprintf(fmt, args) + 1; + wstring result = wstring(cChAlloc, L'\0'); + _vsnwprintf_s(&result[0], cChAlloc, _TRUNCATE, fmt, args); + return result; +} + +wstring StrPrintf(const wchar_t * fmt, ...) +{ + va_list args; + va_start(args, fmt); + return StrVprintf(fmt, args); +} + +#if defined(_DEBUG) +void DebugPrintf(const wchar_t * fmt, ...) +{ + va_list args; + va_start(args, fmt); + OutputDebugString(StrVprintf(fmt, args).c_str()); +} +#else +void DebugPrintf(const wchar_t * /*fmt*/, ...) {} +#endif + +HRESULT LoadFile(const wchar_t * strFilename, vector<char> * pData, bool bText) +{ + FILE * pFile = nullptr; + if (_wfopen_s(&pFile, strFilename, bText ? L"rt" : L"rb") != 0 || !pFile) + return E_FAIL; + assert(pFile); + + // Determine file size + fseek(pFile, 0, SEEK_END); + size_t size = ftell(pFile); + + // Read the whole file into memory + assert(pData); + pData->resize(bText ? size+1 : size); + rewind(pFile); + size_t sizeRead = fread(&(*pData)[0], sizeof(char), size, pFile); + + // Size can be smaller for text files, due to newline conversion + assert(sizeRead == size || (bText && sizeRead < size)); + fclose(pFile); + + // Automatically null-terminate text files so string functions can be used + if (bText) + { + pData->resize(sizeRead + 1); + (*pData)[sizeRead] = 0; + } + + return S_OK; +} + + + +const wchar_t * BaseFilename(const wchar_t * strFilename) +{ + if (const wchar_t * strBase = wcsrchr(strFilename, L'\\')) + return strBase + 1; + else + return strFilename; +} + +#if defined(_DEBUG) +void SetDebugName(ID3D11DeviceChild * pD3DObject, const char * strName) +{ + HRESULT hr = pD3DObject->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(strlen(strName)), strName); + assert(SUCCEEDED(hr)); +} + +void SetDebugName(ID3D11DeviceChild * pD3DObject, const wchar_t * strName) +{ + // D3D only supports narrow strings for debug names; convert to UTF-8 + int cCh = WideCharToMultiByte(CP_UTF8, 0, strName, -1, nullptr, 0, nullptr, nullptr); + string strUTF8(cCh, 0); + WideCharToMultiByte(CP_UTF8, 0, strName, -1, &strUTF8[0], cCh, nullptr, nullptr); + HRESULT hr = pD3DObject->SetPrivateData(WKPDID_D3DDebugObjectName, cCh, strUTF8.c_str()); + assert(SUCCEEDED(hr)); +} +#else +void SetDebugName(ID3D11DeviceChild * /*pD3DObject*/, const char * /*strName*/) {} +void SetDebugName(ID3D11DeviceChild * /*pD3DObject*/, const wchar_t * /*strName*/) {} +#endif + +// CMesh implementation + +CMesh::CMesh() +: m_verts(), + m_indices(), + m_pVtxBuffer(nullptr), + m_pIdxBuffer(nullptr), + m_vtxStride(0), + m_cIdx(0), + m_primtopo(D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED), + m_posMin(0.0f, 0.0f, 0.0f), + m_posMax(0.0f, 0.0f, 0.0f), + m_posCenter(0.0f, 0.0f, 0.0f), + m_diameter(0.0f), + m_uvScale(1.0f) +{ +} + +void CMesh::Draw(ID3D11DeviceContext * pCtx) +{ + UINT zero = 0; + pCtx->IASetVertexBuffers(0, 1, &m_pVtxBuffer, &m_vtxStride, &zero); + pCtx->IASetIndexBuffer(m_pIdxBuffer, DXGI_FORMAT_R32_UINT, 0); + pCtx->DrawIndexed(m_cIdx, 0, 0); +} + +void CMesh::Release() +{ + m_verts.clear(); + m_indices.clear(); + SAFE_RELEASE(m_pVtxBuffer); + SAFE_RELEASE(m_pIdxBuffer); +} + +HRESULT CreateFullscreenMesh(ID3D11Device * pDevice, CMesh * pMesh) +{ + HRESULT hr; + + // Positions are directly in clip space; normals aren't used. + + Vertex verts[] = + { + // pos normal uv + { XMFLOAT3(-1, -1, 0), XMFLOAT3(), XMFLOAT2(0, 1) }, + { XMFLOAT3( 3, -1, 0), XMFLOAT3(), XMFLOAT2(2, 1) }, + { XMFLOAT3(-1, 3, 0), XMFLOAT3(), XMFLOAT2(0, -1) }, + }; + + UINT indices[] = { 0, 1, 2 }; + + pMesh->m_verts.assign(&verts[0], &verts[dim(verts)]); + pMesh->m_indices.assign(&indices[0], &indices[dim(indices)]); + + D3D11_BUFFER_DESC vtxBufferDesc = + { + sizeof(Vertex) * dim(verts), + D3D11_USAGE_IMMUTABLE, + D3D11_BIND_VERTEX_BUFFER, + 0, // no cpu access + 0, // no misc flags + 0, // structured buffer stride + }; + D3D11_SUBRESOURCE_DATA vtxBufferData = { &verts[0], 0, 0 }; + + V_RETURN(pDevice->CreateBuffer(&vtxBufferDesc, &vtxBufferData, &pMesh->m_pVtxBuffer)); + + D3D11_BUFFER_DESC idxBufferDesc = + { + sizeof(UINT) * dim(indices), + D3D11_USAGE_IMMUTABLE, + D3D11_BIND_INDEX_BUFFER, + 0, // no cpu access + 0, // no misc flags + 0, // structured buffer stride + }; + D3D11_SUBRESOURCE_DATA idxBufferData = { &indices[0], 0, 0 }; + + V_RETURN(pDevice->CreateBuffer(&idxBufferDesc, &idxBufferData, &pMesh->m_pIdxBuffer)); + + pMesh->m_vtxStride = sizeof(Vertex); + pMesh->m_cIdx = UINT(dim(indices)); + pMesh->m_primtopo = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + + SetDebugName(pMesh->m_pVtxBuffer, "Fullscreen mesh VB"); + SetDebugName(pMesh->m_pIdxBuffer, "Fullscreen mesh IB"); + + return S_OK; +} + + + +// Mesh loading - helper functions + +HRESULT LoadObjMeshRaw( + const wchar_t * strFilename, + vector<Vertex> * pVerts, + vector<int> * pIndices, + XMFLOAT3 * pPosMin, + XMFLOAT3 * pPosMax) +{ + HRESULT hr; + + // Read the whole file into memory + vector<char> data; + V_RETURN(LoadFile(strFilename, &data, true)); + + vector<XMFLOAT3> positions; + vector<XMFLOAT3> normals; + vector<XMFLOAT2> uvs; + + struct OBJVertex { int iPos, iNormal, iUv; }; + vector<OBJVertex> OBJverts; + + struct OBJFace { int iVertStart, iVertEnd; }; + vector<OBJFace> OBJfaces; + + + XMVECTOR posMin = XMVectorReplicate(FLT_MAX); + XMVECTOR posMax = XMVectorReplicate(-FLT_MAX); + + // Parse the OBJ format line-by-line + char * pCtxLine = nullptr; + for (char * pLine = strtok_s(&data[0], "\n", &pCtxLine); + pLine; + pLine = strtok_s(nullptr, "\n", &pCtxLine)) + { + // Strip comments starting with # + if (char * pChzComment = strchr(pLine, '#')) + *pChzComment = 0; + + // Parse the line token-by-token + char * pCtxToken = nullptr; + char * pToken = strtok_s(pLine, " \t", &pCtxToken); + + // Ignore blank lines + if (!pToken) + continue; + + if (_stricmp(pToken, "v") == 0) + { + + XMFLOAT3 pos; + pos.x = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + pos.y = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + pos.z = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + positions.push_back(pos); + + XMVECTOR pos4 = XMLoadFloat3(&pos); + + posMin = XMVectorMin(posMin, pos4); + posMax = XMVectorMax(posMax, pos4); + } + else if (_stricmp(pToken, "vn") == 0) + { + // Add normal + XMFLOAT3 normal; + normal.x = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + normal.y = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + normal.z = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + normals.push_back(normal); + } + else if (_stricmp(pToken, "vt") == 0) + { + // Add UV, flipping V-axis since OBJ is stored in the opposite convention + XMFLOAT2 uv; + uv.x = float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + uv.y = 1.0f - float(atof(strtok_s(nullptr, " \t", &pCtxToken))); + uvs.push_back(uv); + } + else if (_stricmp(pToken, "f") == 0) + { + // Add face + OBJFace face; + face.iVertStart = int(OBJverts.size()); + + while (char * pToken2 = strtok_s(nullptr, " \t", &pCtxToken)) + { + // Parse vertex specification, with slashes separating position, UV, normal indices + // Note: assuming all three components are present, OBJ format allows some to be missing. + OBJVertex vert; + sscanf_s(pToken2, "%d/%d/%d", &vert.iPos, &vert.iUv, &vert.iNormal); + + // OBJ format uses 1-based indices; correct them + --vert.iPos; + --vert.iNormal; + --vert.iUv; + + OBJverts.push_back(vert); + } + + face.iVertEnd = int(OBJverts.size()); + OBJfaces.push_back(face); + } + else + { + // Unknown command; just ignore + } + } + + // Convert to vertex buffer and index buffer + + pVerts->reserve(OBJverts.size()); + + for (size_t iVert = 0, cVert = OBJverts.size(); iVert < cVert; ++iVert) + { + OBJVertex objv = OBJverts[iVert]; + Vertex v = {}; + v.m_pos = positions[objv.iPos]; + v.m_normal = normals[objv.iNormal]; + v.m_uv = uvs[objv.iUv]; + pVerts->push_back(v); + } + + for (size_t iFace = 0, cFace = OBJfaces.size(); iFace < cFace; ++iFace) + { + OBJFace face = OBJfaces[iFace]; + + int iVertBase = face.iVertStart; + + // Triangulate the face + for (int iVert = face.iVertStart + 2; iVert < face.iVertEnd; ++iVert) + { + pIndices->push_back(iVertBase); + pIndices->push_back(iVert - 1); + pIndices->push_back(iVert); + } + } + + if (pPosMin) XMStoreFloat3(pPosMin, posMin); + if (pPosMax) XMStoreFloat3(pPosMax, posMax); + + return S_OK; +} + +void DeduplicateVerts(CMesh * pMesh) +{ + struct VertexHasher + { + hash<float> fh; + size_t operator () (const Vertex & v) const + { + return fh(v.m_pos.x) ^ fh(v.m_pos.y) ^ fh(v.m_pos.z) ^ + fh(v.m_normal.x) ^ fh(v.m_normal.y) ^ fh(v.m_normal.z) ^ + fh(v.m_uv.x) ^ fh(v.m_uv.y) ^ + fh(v.m_curvature); + } + }; + + struct VertexEquator + { + bool operator () (const Vertex & u, const Vertex & v) const + { + return (u.m_pos.x == v.m_pos.x && + u.m_pos.y == v.m_pos.y && + u.m_pos.z == v.m_pos.z && + u.m_normal.x == v.m_normal.x && + u.m_normal.y == v.m_normal.y && + u.m_normal.z == v.m_normal.z && + u.m_uv.x == v.m_uv.x && + u.m_uv.y == v.m_uv.y && + u.m_curvature == v.m_curvature); + } + }; + + vector<Vertex> vertsDeduplicated; + vector<int> remappingTable; + unordered_map<Vertex, int, VertexHasher, VertexEquator> mapVertToIndex; + + vertsDeduplicated.reserve(pMesh->m_verts.size()); + remappingTable.reserve(pMesh->m_verts.size()); + + for (int i = 0, cVert = int(pMesh->m_verts.size()); i < cVert; ++i) + { + const Vertex & vert = pMesh->m_verts[i]; + unordered_map<Vertex, int, VertexHasher, VertexEquator>::iterator + iter = mapVertToIndex.find(vert); + if (iter == mapVertToIndex.end()) + { + // Found a new vertex that's not in the map yet. + int newIndex = int(vertsDeduplicated.size()); + vertsDeduplicated.push_back(vert); + remappingTable.push_back(newIndex); + mapVertToIndex[vert] = newIndex; + } + else + { + // It's already in the map; re-use the previous index + int newIndex = iter->second; + remappingTable.push_back(newIndex); + } + } + + assert(vertsDeduplicated.size() <= pMesh->m_verts.size()); + assert(remappingTable.size() == pMesh->m_verts.size()); + + vector<int> indicesRemapped; + indicesRemapped.reserve(pMesh->m_indices.size()); + + for (int i = 0, cIndex = int(pMesh->m_indices.size()); i < cIndex; ++i) + { + indicesRemapped.push_back(remappingTable[pMesh->m_indices[i]]); + } + + pMesh->m_verts.swap(vertsDeduplicated); + pMesh->m_indices.swap(indicesRemapped); +} + +// Custom allocator for FaceWorks - just logs the allocs and passes through to CRT + +void * MallocForFaceWorks(size_t bytes) +{ + void * p = ::operator new(bytes); + DebugPrintf(L"FaceWorks alloced %d bytes: %p\n", bytes, p); + return p; +} + +void FreeForFaceWorks(void * p) +{ + DebugPrintf(L"FaceWorks freed %p\n", p); + ::operator delete(p); +} + +void CalculateCurvature(CMesh * pMesh) +{ + // Calculate mesh curvature - also demonstrate using a custom allocator + + GFSDK_FaceWorks_ErrorBlob errorBlob = {}; + gfsdk_new_delete_t allocator = { &MallocForFaceWorks, &FreeForFaceWorks }; + GFSDK_FaceWorks_Result result = GFSDK_FaceWorks_CalculateMeshCurvature( + int(pMesh->m_verts.size()), + &pMesh->m_verts[0].m_pos, + sizeof(Vertex), + &pMesh->m_verts[0].m_normal, + sizeof(Vertex), + int(pMesh->m_indices.size()), + &pMesh->m_indices[0], + 2, // smoothing passes + &pMesh->m_verts[0].m_curvature, + sizeof(Vertex), + &errorBlob, + &allocator); + + if (result != GFSDK_FaceWorks_OK) + { +#if defined(_DEBUG) + wchar_t msg[512]; + _snwprintf_s(msg, dim(msg), _TRUNCATE, + L"GFSDK_FaceWorks_CalculateMeshCurvature() failed:\n%hs", errorBlob.m_msg); + DXUTTrace(__FILE__, __LINE__, E_FAIL, msg, true); +#endif + GFSDK_FaceWorks_FreeErrorBlob(&errorBlob); + return; + } + +#if defined(_DEBUG) && 0 + // Report min, max, mean curvature over mesh + float minCurvature = FLT_MAX; + float maxCurvature = -FLT_MAX; + float curvatureSum = 0.0f; + for (int i = 0, cVert = int(pMesh->m_verts.size()); i < cVert; ++i) + { + minCurvature = min(minCurvature, pMesh->m_verts[i].m_curvature); + maxCurvature = max(maxCurvature, pMesh->m_verts[i].m_curvature); + curvatureSum += pMesh->m_verts[i].m_curvature; + } + float meanCurvature = curvatureSum / float(pMesh->m_verts.size()); + DebugPrintf( + L"\tCurvature min = %0.2f cm^-1, max = %0.2f cm^-1, mean = %0.2f cm^-1\n", + minCurvature, maxCurvature, meanCurvature); +#endif // defined(_DEBUG) +} + +void CalculateUVScale(CMesh * pMesh) +{ + GFSDK_FaceWorks_ErrorBlob errorBlob = {}; + GFSDK_FaceWorks_Result result = GFSDK_FaceWorks_CalculateMeshUVScale( + int(pMesh->m_verts.size()), + &pMesh->m_verts[0].m_pos, + sizeof(Vertex), + &pMesh->m_verts[0].m_uv, + sizeof(Vertex), + int(pMesh->m_indices.size()), + &pMesh->m_indices[0], + &pMesh->m_uvScale, + &errorBlob); + if (result != GFSDK_FaceWorks_OK) + { +#if defined(_DEBUG) + wchar_t msg[512]; + _snwprintf_s(msg, dim(msg), _TRUNCATE, + L"GFSDK_FaceWorks_CalculateMeshUVScale() failed:\n%hs", errorBlob.m_msg); + DXUTTrace(__FILE__, __LINE__, E_FAIL, msg, true); +#endif + GFSDK_FaceWorks_FreeErrorBlob(&errorBlob); + return; + } + +#if 0 + DebugPrintf(L"\tUV scale %0.2f cm\n", pMesh->m_uvScale); +#endif +} + +void CalculateTangents(CMesh * pMesh) +{ + assert(pMesh->m_indices.size() % 3 == 0); + + // Clear tangents to zero + for (int i = 0, c = int(pMesh->m_verts.size()); i < c; ++i) + { + pMesh->m_verts[i].m_tangent = XMFLOAT3(0, 0, 0); + } + + // Generate a tangent for each triangle, based on triangle's UV mapping, + // and accumulate onto vertex + for (int i = 0, c = int(pMesh->m_indices.size()); i < c; i += 3) + { + int indices[3] = { pMesh->m_indices[i], pMesh->m_indices[i+1], pMesh->m_indices[i+2] }; + + // Gather positions for this triangle + XMVECTOR facePositions[3] = + { + XMLoadFloat3(&pMesh->m_verts[indices[0]].m_pos), + XMLoadFloat3(&pMesh->m_verts[indices[1]].m_pos), + XMLoadFloat3(&pMesh->m_verts[indices[2]].m_pos) + }; + + // Calculate edge and normal vectors + XMVECTOR edge0 = facePositions[1] - facePositions[0]; + XMVECTOR edge1 = facePositions[2] - facePositions[0]; + XMVECTOR normal = XMVector3Cross(edge0, edge1); + + // Calculate matrix from unit triangle to position space + XMMATRIX matUnitToPosition(edge0, edge1, normal, XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f)); + + // Gather UVs for this triangle + XMFLOAT2 faceUVs[3] = + { + pMesh->m_verts[indices[0]].m_uv, + pMesh->m_verts[indices[1]].m_uv, + pMesh->m_verts[indices[2]].m_uv, + }; + + +#if 0 + // This code is no longer used because it caused to invert a rank-deficient matrix + + // Calculate UV space edge vectors + // Calculate matrix from unit triangle to UV space + XMMATRIX matUnitToUV( + faceUVs[1].x - faceUVs[0].x, faceUVs[1].y - faceUVs[0].y, 0.0f, 0.0f, + faceUVs[2].x - faceUVs[0].x, faceUVs[2].y - faceUVs[0].y, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + // Calculate matrix from UV space to position space + XMMATRIX matUVToUnit = XMMatrixInverse(nullptr, matUnitToUV); + XMMATRIX matUVToPosition = XMMatrixMultiply(matUVToUnit, matUnitToPosition); + + // The x-axis of that matrix is the tangent vector + XMVECTOR tangent = XMVector3Normalize(matUVToPosition.r[0]); +#else + XMMATRIX matUVToUnitDenormalized( + faceUVs[2].y - faceUVs[0].y, faceUVs[0].y - faceUVs[1].y, 0.0f, 0.0f, + faceUVs[0].x - faceUVs[2].x, faceUVs[1].x - faceUVs[0].x, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + XMMATRIX matUVToPositionDenormalized = XMMatrixMultiply(matUVToUnitDenormalized, matUnitToPosition); + + XMVECTOR tangent = matUVToPositionDenormalized.r[0]; + if (XMVector3Equal(tangent, XMVectorZero())) + { + tangent = XMVector2Cross(matUVToPositionDenormalized.r[1], matUVToPositionDenormalized.r[2]); + if (XMVector3Equal(tangent, XMVectorZero())) + { + tangent = edge0; + } + } + + tangent = XMVector3Normalize(tangent); +#endif + // Accumulate onto vertices + XMStoreFloat3(&pMesh->m_verts[indices[0]].m_tangent, XMLoadFloat3(&pMesh->m_verts[indices[0]].m_tangent) + tangent); + XMStoreFloat3(&pMesh->m_verts[indices[1]].m_tangent, XMLoadFloat3(&pMesh->m_verts[indices[1]].m_tangent) + tangent); + XMStoreFloat3(&pMesh->m_verts[indices[2]].m_tangent, XMLoadFloat3(&pMesh->m_verts[indices[2]].m_tangent) + tangent); + } + + // Normalize summed tangents + for (int i = 0, c = int(pMesh->m_verts.size()); i < c; ++i) + { + XMStoreFloat3(&pMesh->m_verts[i].m_tangent, XMVector3Normalize(XMLoadFloat3(&pMesh->m_verts[i].m_tangent))); + } +} + +HRESULT LoadObjMesh( + const wchar_t * strFilename, + ID3D11Device * pDevice, + CMesh * pMesh) +{ + HRESULT hr; + + V_RETURN(LoadObjMeshRaw( + strFilename, &pMesh->m_verts, &pMesh->m_indices, + &pMesh->m_posMin, &pMesh->m_posMax)); + + DeduplicateVerts(pMesh); + + DebugPrintf( + L"Loaded %s, %d verts, %d indices\n", + strFilename, int(pMesh->m_verts.size()), int(pMesh->m_indices.size())); + + // !!!UNDONE: vertex cache optimization? + + XMVECTOR posMin = XMLoadFloat3(&pMesh->m_posMin); + XMVECTOR posMax = XMLoadFloat3(&pMesh->m_posMax); + XMStoreFloat3(&pMesh->m_posCenter, 0.5f * (posMin + posMax)); + pMesh->m_diameter = XMVectorGetX(XMVector3Length(posMax - posMin)); + + CalculateCurvature(pMesh); + CalculateUVScale(pMesh); + CalculateTangents(pMesh); + + D3D11_BUFFER_DESC vtxBufferDesc = + { + sizeof(Vertex) * UINT(pMesh->m_verts.size()), + D3D11_USAGE_IMMUTABLE, + D3D11_BIND_VERTEX_BUFFER, + 0, // no cpu access + 0, // no misc flags + 0, // structured buffer stride + }; + D3D11_SUBRESOURCE_DATA vtxBufferData = { &pMesh->m_verts[0], 0, 0 }; + + V_RETURN(pDevice->CreateBuffer(&vtxBufferDesc, &vtxBufferData, &pMesh->m_pVtxBuffer)); + + D3D11_BUFFER_DESC idxBufferDesc = + { + sizeof(int) * UINT(pMesh->m_indices.size()), + D3D11_USAGE_IMMUTABLE, + D3D11_BIND_INDEX_BUFFER, + 0, // no cpu access + 0, // no misc flags + 0, // structured buffer stride + }; + D3D11_SUBRESOURCE_DATA idxBufferData = { &pMesh->m_indices[0], 0, 0 }; + + V_RETURN(pDevice->CreateBuffer(&idxBufferDesc, &idxBufferData, &pMesh->m_pIdxBuffer)); + + pMesh->m_vtxStride = sizeof(Vertex); + pMesh->m_cIdx = UINT(pMesh->m_indices.size()); + pMesh->m_primtopo = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + + SetDebugName(pMesh->m_pVtxBuffer, StrPrintf(L"%s VB", BaseFilename(strFilename)).c_str()); + SetDebugName(pMesh->m_pIdxBuffer, StrPrintf(L"%s IB", BaseFilename(strFilename)).c_str()); + + return S_OK; +} + + +bool WCStringEndsWith(const wchar_t* hay, const wchar_t* needle) +{ + size_t hay_lenght = wcslen(hay); + size_t needle_length = wcslen(needle); + if (hay_lenght < needle_length) return false; + return wcscmp(hay + hay_lenght - needle_length, needle) == 0; +} + +HRESULT LoadTexture( + const wchar_t * strFilename, + ID3D11Device * pDevice, + ID3D11ShaderResourceView ** ppSrv, + int flags) +{ + return LoadTexture(strFilename, pDevice, nullptr, ppSrv, flags); +} + +HRESULT LoadTexture( + const wchar_t * strFilename, + ID3D11Device * pDevice, + ID3D11DeviceContext * pDeviceContext, + ID3D11ShaderResourceView ** ppSrv, + int flags) +{ + HRESULT hr; + + bool bMipmap = (flags & LT_Mipmap) != 0; + bool bHDR = (flags & LT_HDR) != 0; + bool bCubemap = (flags & LT_Cubemap) != 0; + bool bLinear = (flags & LT_Linear) != 0; + + // Load the texture, generating mipmaps if requested + + if (WCStringEndsWith(strFilename, L".dds")) + { + bMipmap = false; + V_RETURN(CreateDDSTextureFromFileEx( + pDevice, + bMipmap ? pDeviceContext : nullptr, + strFilename, + 4096, + bMipmap ? D3D11_USAGE_DEFAULT : D3D11_USAGE_IMMUTABLE, + (D3D11_BIND_SHADER_RESOURCE | (bMipmap ? (D3D11_BIND_RENDER_TARGET) : 0)), + 0, + (bCubemap ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0) | (bMipmap ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0), + !(bHDR || bLinear), + nullptr, + ppSrv)); + + } + else + { + assert((!bMipmap) || (pDeviceContext != nullptr)); + V_RETURN(CreateWICTextureFromFileEx( + pDevice, + bMipmap ? pDeviceContext : nullptr, + strFilename, + 4096, + bMipmap ? D3D11_USAGE_DEFAULT : D3D11_USAGE_IMMUTABLE, + (D3D11_BIND_SHADER_RESOURCE | (bMipmap ? (D3D11_BIND_RENDER_TARGET) : 0)), + 0, + (bCubemap ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0) | (bMipmap ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0), + !(bHDR || bLinear), + nullptr, + ppSrv)); + } + +#if defined(_DEBUG) + // Set the name of the texture + SetDebugName(*ppSrv, BaseFilename(strFilename)); + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + (*ppSrv)->GetDesc(&srvDesc); + + UINT cMipLevels = 1; + const wchar_t * strDimension = L"other"; + switch (srvDesc.ViewDimension) + { + case D3D11_SRV_DIMENSION_TEXTURE2D: + strDimension = L"2D"; + cMipLevels = srvDesc.Texture2D.MipLevels; + break; + + case D3D11_SRV_DIMENSION_TEXTURECUBE: + strDimension = L"cube"; + cMipLevels = srvDesc.TextureCube.MipLevels; + break; + + default: assert(false); break; + } + + DebugPrintf( + L"Loaded %s, format %s, %s, %d mip levels\n", + strFilename, + DXUTDXGIFormatToString(srvDesc.Format, false), + strDimension, + cMipLevels); +#endif + + return S_OK; +} + + + +// CMayaStyleCamera implementation + +CMayaStyleCamera::CMayaStyleCamera() +: CBaseCamera(), + m_fRadius(5.0f) +{ +} + +void CMayaStyleCamera::FrameMove(FLOAT /*fElapsedTime*/) +{ + if (IsKeyDown(m_aKeys[CAM_RESET])) + Reset(); + + // Get the mouse movement (if any) if the mouse button are down + GetInput(m_bEnablePositionMovement, m_nCurrentButtonMask != 0, false); + + // If left mouse button is down, update yaw and pitch based on mouse + if (m_nCurrentButtonMask & MOUSE_LEFT_BUTTON) + { + // Update the pitch & yaw angle based on mouse movement + float fYawDelta = -m_vMouseDelta.x * m_fRotationScaler; + float fPitchDelta = m_vMouseDelta.y * m_fRotationScaler; + + // Invert pitch if requested + if (m_bInvertPitch) + fPitchDelta = -fPitchDelta; + + m_fCameraPitchAngle += fPitchDelta; + m_fCameraYawAngle += fYawDelta; + + // Limit pitch to straight up or straight down + m_fCameraPitchAngle = max(-XM_PI / 2.0f, m_fCameraPitchAngle); + m_fCameraPitchAngle = min(+XM_PI / 2.0f, m_fCameraPitchAngle); + } + + // If mouse wheel is scrolled or right mouse button is down, update radius + if (m_nMouseWheelDelta != 0) + m_fRadius -= m_nMouseWheelDelta * m_fRadius * 0.1f / 120.0f; + if (m_nCurrentButtonMask & MOUSE_RIGHT_BUTTON) + m_fRadius += m_vMouseDelta.y * m_fRadius * 0.01f; + m_fRadius = max(m_fRadius, 1.0f); + m_nMouseWheelDelta = 0; + + // Make a rotation matrix based on the camera's yaw & pitch + XMMATRIX mCameraRot = XMMatrixRotationRollPitchYaw(m_fCameraPitchAngle, m_fCameraYawAngle, 0.0f); + + XMVECTOR vRight(mCameraRot.r[0]); + XMVECTOR vUp(mCameraRot.r[1]); + XMVECTOR vForward(mCameraRot.r[2]); + + XMVECTOR vLookAt = XMLoadFloat3(&m_vLookAt); + + // If middle mouse button is down, update look-at point + if (m_nCurrentButtonMask & MOUSE_MIDDLE_BUTTON) + { + vLookAt += vRight * (m_vMouseDelta.x * m_fRadius * 0.005f); + vLookAt += vUp * (m_vMouseDelta.y * m_fRadius * 0.005f); + XMStoreFloat3(&m_vLookAt, vLookAt); + } + + // Update the eye point based on a radius away from the lookAt position + XMVECTOR vEye = vLookAt - vForward * m_fRadius; + XMStoreFloat3(&m_vEye, vEye); + + // Update the view matrix + XMStoreFloat4x4(&m_mView, XMMatrixLookAtRH(vEye, vLookAt, vUp)); +} + +void CMayaStyleCamera::SetProjParams(FLOAT fFOV, FLOAT fAspect, FLOAT fNearPlane, FLOAT fFarPlane) +{ + // Set attributes for the projection matrix + m_fFOV = fFOV; + m_fAspect = fAspect; + m_fNearPlane = fNearPlane; + m_fFarPlane = fFarPlane; + + XMStoreFloat4x4(&m_mProj, XMMatrixPerspectiveFovRH(fFOV, fAspect, fNearPlane, fFarPlane)); +} + +void CMayaStyleCamera::SetViewParams(FXMVECTOR pvEyePt, FXMVECTOR pvLookatPt) +{ + CBaseCamera::SetViewParams(pvEyePt, pvLookatPt); + + XMVECTOR vecLook = pvLookatPt - pvEyePt; + m_fRadius = XMVectorGetX(XMVector3Length(vecLook)); +} + + + +// CFirstPersonCameraRH implementation + +void CFirstPersonCameraRH::FrameMove(FLOAT fElapsedTime) +{ + if (DXUTGetGlobalTimer()->IsStopped()) + { + if (DXUTGetFPS() == 0.0f) + fElapsedTime = 0; + else + fElapsedTime = 1.0f / DXUTGetFPS(); + } + + if (IsKeyDown(m_aKeys[CAM_RESET])) + Reset(); + + // Get keyboard/mouse/gamepad input + GetInput(m_bEnablePositionMovement, (m_nActiveButtonMask & m_nCurrentButtonMask) || m_bRotateWithoutButtonDown, + true); + + // Get amount of velocity based on the keyboard input and drag (if any) + UpdateVelocity(fElapsedTime); + + // Simple euler method to calculate position delta + XMVECTOR vPosDelta = XMVectorSet(-m_vVelocity.x, m_vVelocity.y, m_vVelocity.z, 0.0f)*fElapsedTime; + if (GetAsyncKeyState(VK_LSHIFT) != 0) { + vPosDelta *= 2.0f; + } + + // If rotating the camera + if ((m_nActiveButtonMask & m_nCurrentButtonMask) || + m_bRotateWithoutButtonDown || + m_vGamePadRightThumb.x != 0 || + m_vGamePadRightThumb.z != 0) + { + // Update the pitch & yaw angle based on mouse movement + float fYawDelta = -m_vRotVelocity.x; + float fPitchDelta = m_vRotVelocity.y; + + // Invert pitch if requested + if( m_bInvertPitch ) + fPitchDelta = -fPitchDelta; + + m_fCameraPitchAngle += fPitchDelta; + m_fCameraYawAngle += fYawDelta; + + // Limit pitch to straight up or straight down + m_fCameraPitchAngle = max(-XM_PI / 2.0f, m_fCameraPitchAngle); + m_fCameraPitchAngle = min(+XM_PI / 2.0f, m_fCameraPitchAngle); + } + + // Make a rotation matrix based on the camera's yaw & pitch + XMMATRIX mCameraRot = XMMatrixRotationRollPitchYaw(m_fCameraPitchAngle, m_fCameraYawAngle, 0.0f); + + // Transform vectors based on camera's rotation matrix + XMVECTOR vLocalUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); + XMVECTOR vLocalAhead = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + XMVECTOR vWorldUp = XMVector3TransformCoord(vLocalUp, mCameraRot); + XMVECTOR vWorldAhead = XMVector3TransformCoord(vLocalAhead, mCameraRot); + + // Transform the position delta by the camera's rotation + if (!m_bEnableYAxisMovement) + { + // If restricting Y movement, do not include pitch + // when transforming position delta vector. + mCameraRot = XMMatrixRotationRollPitchYaw(0.0f, m_fCameraYawAngle, 0.f); + } + XMVECTOR vPosDeltaWorld = XMVector3TransformCoord(vPosDelta, mCameraRot); + + // Move the eye position + XMVECTOR vEye = XMLoadFloat3(&m_vEye) + vPosDeltaWorld; + XMStoreFloat3(&m_vEye, vEye); + + if (m_bClipToBoundary) + ConstrainToBoundary(vEye); + + // Update the lookAt position based on the eye position + XMVECTOR vLookAt = vEye + vWorldAhead; + XMStoreFloat3(&m_vLookAt, vLookAt); + + // Update the view matrix + XMMATRIX mView = XMMatrixLookAtRH(vEye, vLookAt, vWorldUp); + XMStoreFloat4x4(&m_mView, mView); + XMStoreFloat4x4(&m_mCameraWorld, XMMatrixInverse(nullptr, mView)); +} + +void CFirstPersonCameraRH::SetProjParams(FLOAT fFOV, FLOAT fAspect, FLOAT fNearPlane, FLOAT fFarPlane) +{ + // Set attributes for the projection matrix + m_fFOV = fFOV; + m_fAspect = fAspect; + m_fNearPlane = fNearPlane; + m_fFarPlane = fFarPlane; + + XMStoreFloat4x4(&m_mProj, XMMatrixPerspectiveFovRH(fFOV, fAspect, fNearPlane, fFarPlane)); +} + + + +// CShadowMap implementation + +CShadowMap::CShadowMap() +: m_pDsv(nullptr), + m_pSrv(nullptr), + m_size(0), + m_vecLight(0.0f, 0.0f, 0.0f), + m_posMinScene(FLT_MAX, FLT_MAX, FLT_MAX), + m_posMaxScene(-FLT_MAX, -FLT_MAX, -FLT_MAX), + m_matWorldToClip(), + m_matWorldToUvzw(), + m_vecDiam() +{ +} + +HRESULT CShadowMap::Init(unsigned int size, ID3D11Device * pDevice) +{ + HRESULT hr; + + // Create 2D texture for shadow map + D3D11_TEXTURE2D_DESC texDesc = + { + size, size, // width, height + 1, 1, // mip levels, array size + DXGI_FORMAT_R32_TYPELESS, + { 1, 0 }, // multisample count, quality + D3D11_USAGE_DEFAULT, + D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL, + 0, // no cpu access + 0, // no misc flags + }; + ID3D11Texture2D * pTex = nullptr; + V_RETURN(pDevice->CreateTexture2D(&texDesc, nullptr, &pTex)); + + // Create depth-stencil view + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = + { + DXGI_FORMAT_D32_FLOAT, + D3D11_DSV_DIMENSION_TEXTURE2D, + }; + V_RETURN(pDevice->CreateDepthStencilView(pTex, &dsvDesc, &m_pDsv)); + + // Create shader resource view + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = + { + DXGI_FORMAT_R32_FLOAT, + D3D11_SRV_DIMENSION_TEXTURE2D, + }; + srvDesc.Texture2D.MipLevels = 1; + V_RETURN(pDevice->CreateShaderResourceView(pTex, &srvDesc, &m_pSrv)); + + // The texture isn't needed any more + SAFE_RELEASE(pTex); + + m_size = size; + + SetDebugName(m_pDsv, "Shadow map - DSV"); + SetDebugName(m_pSrv, "Shadow map - SRV"); + DebugPrintf(L"Created shadow map, format D32_FLOAT, %d x %d\n", size, size); + + return S_OK; +} + +void CShadowMap::UpdateMatrix() +{ + // Calculate view matrix based on light direction + + XMVECTOR posEye = XMVectorZero(); + XMVECTOR posLookAt = posEye - XMLoadFloat3(&m_vecLight); + XMVECTOR vecUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); + + // Handle light vector being straight up or down + if (fabsf(m_vecLight.x) < 1e-4f && fabsf(m_vecLight.z) < 1e-4f) { + vecUp = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f); + } + + XMMATRIX matView = XMMatrixLookAtRH(posEye, posLookAt, vecUp); + + // Transform scene AABB into view space and recalculate bounds + + XMVECTOR posMinView = XMVectorReplicate(FLT_MAX); + XMVECTOR posMaxView = XMVectorReplicate(-FLT_MAX); + + for (int i = 0; i < 8; ++i) + { + XMVECTOR posWorld = XMVectorSet( + (i & 1) ? m_posMaxScene.x : m_posMinScene.x, + (i & 2) ? m_posMaxScene.y : m_posMinScene.y, + (i & 4) ? m_posMaxScene.z : m_posMinScene.z, + 0.0f); + + XMVECTOR posView = XMVector3TransformCoord(posWorld, matView); + + posMinView = XMVectorMin(posMinView, posView); + posMaxView = XMVectorMax(posMaxView, posView); + } + + XMStoreFloat3(&m_vecDiam, posMaxView - posMinView); + +#if 0 + DebugPrintf( + L"Shadow map world-space diameter (cm): %0.2f %0.2f %0.2f\n", + m_vecDiam.x, m_vecDiam.y, m_vecDiam.z); +#endif + + // Calculate orthogonal projection matrix to fit the scene bounds + XMMATRIX matProj = XMMatrixOrthographicOffCenterRH( + XMVectorGetX(posMinView), XMVectorGetX(posMaxView), + XMVectorGetY(posMinView), XMVectorGetY(posMaxView), + -XMVectorGetZ(posMaxView), -XMVectorGetZ(posMinView)); + XMStoreFloat4x4(&m_matProj, matProj); + + XMMATRIX matWorldToClip = matView * matProj; + XMStoreFloat4x4(&m_matWorldToClip, matWorldToClip); + + // Calculate matrix that maps to [0, 1] UV space instead of [-1, 1] clip space + + XMMATRIX matClipToUvzw( + 0.5f, 0, 0, 0, + 0, -0.5f, 0, 0, + 0, 0, 1, 0, + 0.5f, 0.5f, 0, 1); + + XMMATRIX matWorldToUvzw = matWorldToClip * matClipToUvzw; + XMStoreFloat4x4(&m_matWorldToUvzw, matWorldToUvzw); + + // Calculate inverse transpose matrix for transforming normals + + XMMATRIX matWorldToUvzNormal = XMMatrixTranspose(XMMatrixInverse(nullptr, XMMATRIX( + matWorldToUvzw.r[0], + matWorldToUvzw.r[1], + matWorldToUvzw.r[2], + XMVectorSelect(matWorldToUvzw.r[3], g_XMZero, g_XMSelect1110) + ))); + XMStoreFloat4x4(&m_matWorldToUvzNormal, matWorldToUvzNormal); +} + +void CShadowMap::BindRenderTarget(ID3D11DeviceContext * pCtx) +{ + D3D11_VIEWPORT viewport = + { + 0.0f, 0.0f, // left, top + float(m_size), float(m_size), // width, height + 0.0f, 1.0f, // min/max depth + }; + + pCtx->RSSetViewports(1, &viewport); + pCtx->OMSetRenderTargets(0, nullptr, m_pDsv); +} + +XMFLOAT3 CShadowMap::CalcFilterUVZScale(float filterRadius) const +{ + // Expand the filter in the Z direction (this controls how far + // the filter can tilt before it starts contracting). + // Tuned empirically. + float zScale = 4.0f; + + return XMFLOAT3( + filterRadius / m_vecDiam.x, + filterRadius / m_vecDiam.y, + zScale * filterRadius / m_vecDiam.z); +} + + + +// CVarShadowMap implementation + +extern CMesh g_meshFullscreen; + +CVarShadowMap::CVarShadowMap() +: m_pRtv(nullptr), + m_pSrv(nullptr), + m_pRtvTemp(nullptr), + m_pSrvTemp(nullptr), + m_size(0), + m_blurRadius(1.0f) +{ +} + +HRESULT CVarShadowMap::Init(unsigned int size, ID3D11Device * pDevice) +{ + HRESULT hr; + + // Create 2D textures for VSM and temp VSM for blur + D3D11_TEXTURE2D_DESC texDesc = + { + size, size, // width, height + 1, 1, // mip levels, array size + DXGI_FORMAT_R32G32_FLOAT, + { 1, 0 }, // multisample count, quality + D3D11_USAGE_DEFAULT, + D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, + 0, // no cpu access + 0, // no misc flags + }; + ID3D11Texture2D * pTex = nullptr, * pTexTemp = nullptr; + V_RETURN(pDevice->CreateTexture2D(&texDesc, nullptr, &pTex)); + V_RETURN(pDevice->CreateTexture2D(&texDesc, nullptr, &pTexTemp)); + + // Create render target views + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = + { + DXGI_FORMAT_R32G32_FLOAT, + D3D11_RTV_DIMENSION_TEXTURE2D, + }; + V_RETURN(pDevice->CreateRenderTargetView(pTex, &rtvDesc, &m_pRtv)); + V_RETURN(pDevice->CreateRenderTargetView(pTexTemp, &rtvDesc, &m_pRtvTemp)); + + // Create shader resource views + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = + { + DXGI_FORMAT_R32G32_FLOAT, + D3D11_SRV_DIMENSION_TEXTURE2D, + }; + srvDesc.Texture2D.MipLevels = 1; + V_RETURN(pDevice->CreateShaderResourceView(pTex, &srvDesc, &m_pSrv)); + V_RETURN(pDevice->CreateShaderResourceView(pTexTemp, &srvDesc, &m_pSrvTemp)); + + // The textures aren't needed any more + SAFE_RELEASE(pTex); + SAFE_RELEASE(pTexTemp); + + m_size = size; + + SetDebugName(m_pRtv, "VSM - RTV"); + SetDebugName(m_pSrv, "VSM - SRV"); + SetDebugName(m_pRtvTemp, "VSM - temp RTV"); + SetDebugName(m_pSrvTemp, "VSM - temp SRV"); + DebugPrintf(L"Created VSM, format R32G32_FLOAT, %d x %d\n", size, size); + + return S_OK; +} + +void CVarShadowMap::UpdateFromShadowMap(const CShadowMap & shadow, ID3D11DeviceContext * pCtx) +{ + D3D11_VIEWPORT viewport = + { + 0.0f, 0.0f, // left, top + float(m_size), float(m_size), // width, height + 0.0f, 1.0f, // min/max depth + }; + + pCtx->RSSetViewports(1, &viewport); + pCtx->OMSetRenderTargets(1, &m_pRtv, nullptr); + + g_shdmgr.BindCreateVSM(pCtx, shadow.m_pSrv); + g_meshFullscreen.Draw(pCtx); +} + +void CVarShadowMap::GaussianBlur(ID3D11DeviceContext * pCtx) +{ + D3D11_VIEWPORT viewport = + { + 0.0f, 0.0f, // left, top + float(m_size), float(m_size), // width, height + 0.0f, 1.0f, // min/max depth + }; + + pCtx->RSSetViewports(1, &viewport); + pCtx->OMSetRenderTargets(1, &m_pRtvTemp, nullptr); + + g_shdmgr.BindGaussian(pCtx, m_pSrv, m_blurRadius, 0.0f); + g_meshFullscreen.Draw(pCtx); + + pCtx->OMSetRenderTargets(0, nullptr, nullptr); + g_shdmgr.BindGaussian(pCtx, m_pSrvTemp, 0.0f, m_blurRadius); + pCtx->OMSetRenderTargets(1, &m_pRtv, nullptr); + g_meshFullscreen.Draw(pCtx); +} + + + +// CGpuProfiler implementation + +CGpuProfiler g_gpuProfiler; + +CGpuProfiler::CGpuProfiler() +: m_iFrameQuery(0), + m_iFrameCollect(-1), + m_apQueryTsDisjoint(), + m_apQueryTs(), + m_fTsUsed(), + m_adT(), + m_gpuFrameTime(0.0f), + m_adTAvg(), + m_gpuFrameTimeAvg(0.0f), + m_adTTotal(), + m_gpuFrameTimeTotal(0.0f), + m_frameCount(0), + m_tBeginAvg(0) +{ +} + +HRESULT CGpuProfiler::Init(ID3D11Device * pDevice) +{ + HRESULT hr; + + // Create all the queries we'll need + + D3D11_QUERY_DESC queryDesc = { D3D11_QUERY_TIMESTAMP_DISJOINT, 0 }; + V_RETURN(pDevice->CreateQuery(&queryDesc, &m_apQueryTsDisjoint[0])); + V_RETURN(pDevice->CreateQuery(&queryDesc, &m_apQueryTsDisjoint[1])); + + queryDesc.Query = D3D11_QUERY_TIMESTAMP; + for (GTS gts = GTS_BeginFrame; gts < GTS_Max; gts = GTS(gts + 1)) + { + V_RETURN(pDevice->CreateQuery(&queryDesc, &m_apQueryTs[gts][0])); + V_RETURN(pDevice->CreateQuery(&queryDesc, &m_apQueryTs[gts][1])); + } + + timeBeginPeriod(1); + + return S_OK; +} + +void CGpuProfiler::Release() +{ + SAFE_RELEASE(m_apQueryTsDisjoint[0]); + SAFE_RELEASE(m_apQueryTsDisjoint[1]); + + for (GTS gts = GTS_BeginFrame; gts < GTS_Max; gts = GTS(gts + 1)) + { + SAFE_RELEASE(m_apQueryTs[gts][0]); + SAFE_RELEASE(m_apQueryTs[gts][1]); + } + + timeEndPeriod(1); +} + +void CGpuProfiler::BeginFrame(ID3D11DeviceContext * pCtx) +{ + pCtx->Begin(m_apQueryTsDisjoint[m_iFrameQuery]); + Timestamp(pCtx, GTS_BeginFrame); +} + +void CGpuProfiler::Timestamp(ID3D11DeviceContext * pCtx, GTS gts) +{ + pCtx->End(m_apQueryTs[gts][m_iFrameQuery]); + m_fTsUsed[gts][m_iFrameQuery] = true; +} + +void CGpuProfiler::EndFrame(ID3D11DeviceContext * pCtx) +{ + Timestamp(pCtx, GTS_EndFrame); + pCtx->End(m_apQueryTsDisjoint[m_iFrameQuery]); + + ++m_iFrameQuery &= 1; +} + +void CGpuProfiler::WaitForDataAndUpdate(ID3D11DeviceContext * pCtx) +{ + if (m_iFrameCollect < 0) + { + // Haven't run enough frames yet to have data + m_iFrameCollect = 0; + return; + } + + int iFrame = m_iFrameCollect; + ++m_iFrameCollect &= 1; + + if (!m_fTsUsed[GTS_BeginFrame][iFrame] || + !m_fTsUsed[GTS_EndFrame][iFrame]) + { + DebugPrintf(L"Couldn't analyze timestamp data because begin and end frame timestamps weren't recorded\n"); + return; + } + + // Wait for data + while (pCtx->GetData(m_apQueryTsDisjoint[iFrame], nullptr, 0, 0) == S_FALSE) + { + Sleep(1); + } + + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timestampDisjoint; + if (pCtx->GetData(m_apQueryTsDisjoint[iFrame], ×tampDisjoint, sizeof(timestampDisjoint), 0) != S_OK) + { + DebugPrintf(L"Couldn't retrieve timestamp disjoint query data\n"); + return; + } + + if (timestampDisjoint.Disjoint) + { + // Throw out this frame's data + DebugPrintf(L"Timestamps disjoint\n"); + return; + } + + // Wait for data + while (pCtx->GetData(m_apQueryTs[GTS_BeginFrame][iFrame], nullptr, 0, 0) == S_FALSE) + { + Sleep(1); + } + + UINT64 timestampBeginFrame; + if (pCtx->GetData(m_apQueryTs[GTS_BeginFrame][iFrame], ×tampBeginFrame, sizeof(UINT64), 0) != S_OK) + { + DebugPrintf(L"Couldn't retrieve timestamp query data for GTS %d\n", GTS_BeginFrame); + return; + } + + UINT64 timestampPrev = timestampBeginFrame; + UINT64 timestampEndFrame = 0; + for (GTS gts = GTS(GTS_BeginFrame + 1); gts < GTS_Max; gts = GTS(gts + 1)) + { + UINT64 timestamp; + if (m_fTsUsed[gts][iFrame]) + { + // Wait for data + while (pCtx->GetData(m_apQueryTs[gts][iFrame], nullptr, 0, 0) == S_FALSE) + { + Sleep(1); + } + + if (pCtx->GetData(m_apQueryTs[gts][iFrame], ×tamp, sizeof(UINT64), 0) != S_OK) + { + DebugPrintf(L"Couldn't retrieve timestamp query data for GTS %d\n", gts); + return; + } + } + else + { + // Timestamp wasn't used in the frame we're collecting; + // just pretend it took zero time + timestamp = timestampPrev; + } + + if (gts == GTS_EndFrame) + timestampEndFrame = timestamp; + + m_adT[gts] = float(timestamp - timestampPrev) / float(timestampDisjoint.Frequency); + timestampPrev = timestamp; + + m_adTTotal[gts] += m_adT[gts]; + + // Reset timestamp-used flags for new frame + m_fTsUsed[gts][iFrame] = false; + } + + m_gpuFrameTime = float(timestampEndFrame - timestampBeginFrame) / float(timestampDisjoint.Frequency); + m_gpuFrameTimeTotal += m_gpuFrameTime; + + ++m_frameCount; + DWORD curTime = timeGetTime(); + if (curTime > m_tBeginAvg + 500) + { + for (GTS gts = GTS_BeginFrame; gts < GTS_Max; gts = GTS(gts + 1)) + { + m_adTAvg[gts] = m_adTTotal[gts] / m_frameCount; + m_adTTotal[gts] = 0.0f; + } + + m_gpuFrameTimeAvg = m_gpuFrameTimeTotal / m_frameCount; + m_gpuFrameTimeTotal = 0.0f; + + m_frameCount = 0; + m_tBeginAvg = curTime; + } +} |