diff --git a/examples/dxview/CMakeLists.txt b/examples/dxview/CMakeLists.txt new file mode 100644 index 0000000..ea29203 --- /dev/null +++ b/examples/dxview/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.5) +project(dxview) + +find_package(glfw3 CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) + +add_executable(dxview + src/Viewer.h + src/Viewer.cc + src/dxview.cc +) + +target_include_directories(dxview +PRIVATE + ../../ +) + +target_compile_definitions(dxview +PRIVATE + DXVIEW_SWAP_CHAIN_BUFFER_COUNT=3 + DXVIEW_RES_DIR=L"${PROJECT_SOURCE_DIR}/res" +) + +target_link_libraries(dxview +PRIVATE + dxgi + d3dcompiler + d3d12 + glfw + spdlog::spdlog +) + +set_target_properties(dxview +PROPERTIES + CXX_STANDARD 17 +) \ No newline at end of file diff --git a/examples/dxview/res/gray.hlsl b/examples/dxview/res/gray.hlsl new file mode 100644 index 0000000..17f0a64 --- /dev/null +++ b/examples/dxview/res/gray.hlsl @@ -0,0 +1,3 @@ +float4 main() : SV_Target { + return float4(0.5, 0.5, 0.5, 1.0); +} \ No newline at end of file diff --git a/examples/dxview/res/lighting.hlsl b/examples/dxview/res/lighting.hlsl new file mode 100644 index 0000000..dc20850 --- /dev/null +++ b/examples/dxview/res/lighting.hlsl @@ -0,0 +1,52 @@ +struct RS2PS { + float4 position : SV_POSITION; + +#ifdef HAS_TEXCOORD_0 + float2 texcoord_0: TEXCOORD_0; +#endif +}; + +struct TextureInfo { + uint textureIndex; + uint samplerIndex; +}; + +struct PBRMetallicRoughness { + float4 baseColorFactor; + TextureInfo baseColorTexture; + float metallicFactor; + float roughnessFactor; + TextureInfo metallicRoughnessTexture; +}; + +cbuffer Material : register(b2) { + PBRMetallicRoughness pbrMetallicRoughness; +}; + +Texture2D textures[5] : register(t0); +SamplerState samplerState[5] : register(s0); + +float4 getBaseColor(float2 uv) { + float4 baseColor = pbrMetallicRoughness.baseColorFactor; + +#ifdef HAS_TEXCOORD_0 + TextureInfo baseColorTexture = pbrMetallicRoughness.baseColorTexture; + if (baseColorTexture.textureIndex >= 0) { + baseColor *= textures[baseColorTexture.textureIndex].Sample(samplerState[baseColorTexture.samplerIndex], uv); + } +#endif + + return baseColor; +} + +float4 main(RS2PS input) : SV_Target { + float2 uv = float2(0.0, 0.0); + +#ifdef HAS_TEXCOORD_0 + uv = input.texcoord_0; +#endif + + float4 color = getBaseColor(uv); + + return color; +} diff --git a/examples/dxview/res/primitive.hlsl b/examples/dxview/res/primitive.hlsl new file mode 100644 index 0000000..c9ca457 --- /dev/null +++ b/examples/dxview/res/primitive.hlsl @@ -0,0 +1,46 @@ +struct IA2VS { + float3 position : POSITION; + +#ifdef HAS_NORMAL + float3 normal : NORMAL; +#endif + +#ifdef HAS_TANGENT + float4 tangent : TANGENT; +#endif + +#ifdef HAS_TEXCOORD_0 + float2 texcoord_0: TEXCOORD_0; +#endif +}; + +struct VS2RS { + float4 position : SV_POSITION; + +#ifdef HAS_TEXCOORD_0 + float2 texcoord_0: TEXCOORD_0; +#endif +}; + +cbuffer Camera : register(b0) { + float4x4 V; + float4x4 P; + float4x4 VP; +}; + +cbuffer Node : register(b1) { + float4x4 M; +}; + +VS2RS main(IA2VS input) { + VS2RS output; + output.position = mul(float4(input.position, 1.0), M); + output.position = mul(output.position, V); + output.position = mul(output.position, P); + +#ifdef HAS_TEXCOORD_0 + output.texcoord_0 = input.texcoord_0; +#endif + + return output; +} \ No newline at end of file diff --git a/examples/dxview/src/Viewer.cc b/examples/dxview/src/Viewer.cc new file mode 100644 index 0000000..a26f8f7 --- /dev/null +++ b/examples/dxview/src/Viewer.cc @@ -0,0 +1,1292 @@ +#include "Viewer.h" + +#include +#include +#include + +using namespace DirectX; + +namespace { + +constexpr bool isPow2(uint64_t value) { + return (value == 0) ? false : ((value & (value - 1)) == 0); +} + +template +constexpr T alignPow2(T value, uint64_t alignment) { + assert(isPow2(alignment)); + return ((value + static_cast(alignment) - 1) & + ~(static_cast(alignment) - 1)); +} + +} // namespace + +Viewer::Viewer(HWND window, tinygltf::Model* pModel) + : pModel_(pModel), directFenceValue_(0), copyFenceValue_(0) { + initDirectX(window); + buildRenderTargets(); + buildResources(); + + // TODO: + HRESULT hr = S_OK; + + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Width = + alignPow2(sizeof(PBRMetallicRoughness), + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, + IID_PPV_ARGS(&pCameraBuffer_)); + assert(SUCCEEDED(hr)); +} + +void Viewer::update(double deltaTime) { + if (pDirectFence_->GetCompletedValue() < directFenceValue_) { + auto event = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS); + pDirectFence_->SetEventOnCompletion(directFenceValue_, event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); + } + + // TODO: + void* pData; + pCameraBuffer_->Map(0, nullptr, &pData); + + auto* pCameraData = static_cast(pData); + + constexpr auto kRadius = 3.0; + static auto degree = 0.0; + degree += 10.0 * deltaTime; + auto radian = degree * XM_PI / 180.0; + + XMMATRIX P = + XMMatrixPerspectiveFovRH(90.0f * XM_PI / 180.0f, 1.0f, 0.01f, 100.0f); + XMStoreFloat4x4(&pCameraData->P, XMMatrixTranspose(P)); + + XMMATRIX V = XMMatrixLookAtRH( + XMVectorSet(static_cast(kRadius * cos(radian)), 0.0f, + static_cast(kRadius * sin(radian)), 1.0f), + XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)); + XMStoreFloat4x4(&pCameraData->V, XMMatrixTranspose(V)); + + XMMATRIX VP = XMMatrixMultiply(V, P); + XMStoreFloat4x4(&pCameraData->VP, XMMatrixTranspose(VP)); + pCameraBuffer_->Unmap(0, nullptr); +} + +void Viewer::render(double deltaTime) { + const auto kSwapChainBackBufferIndex = + pSwapChain_->GetCurrentBackBufferIndex(); + + auto pDirectCommandAllocator = + pDirectCommandAllocators_[kSwapChainBackBufferIndex].Get(); + + pDirectCommandAllocator->Reset(); + pDirectCommandList_->Reset(pDirectCommandAllocator, nullptr); + + D3D12_VIEWPORT viewport = {}; + viewport.Width = 512.0; + viewport.Height = 512.0; + viewport.MaxDepth = 1.0f; + + pDirectCommandList_->RSSetViewports(1, &viewport); + + D3D12_RECT scissorRect = {}; + scissorRect.right = 512; + scissorRect.bottom = 512; + + pDirectCommandList_->RSSetScissorRects(1, &scissorRect); + + auto& renderTargets = renderTargets_[kSwapChainBackBufferIndex]; + + { + auto& renderTarget = renderTargets[RENDER_PASS_TYPE_PRESENT]; + auto pTexture = renderTarget.pTexture.Get(); + auto rtvDescriptor = renderTarget.viewDescriptor; + + { + D3D12_RESOURCE_BARRIER resourceBarrier = {}; + resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + resourceBarrier.Transition.pResource = pTexture; + resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + resourceBarrier.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + + pDirectCommandList_->ResourceBarrier(1, &resourceBarrier); + } + + pDirectCommandList_->OMSetRenderTargets(1, &rtvDescriptor, false, nullptr); + pDirectCommandList_->ClearRenderTargetView(rtvDescriptor, Colors::Black, 0, + nullptr); + + auto& scene = pModel_->scenes[pModel_->defaultScene]; + for (auto nodeIndex : scene.nodes) { + drawNode(nodeIndex); + } + + { + D3D12_RESOURCE_BARRIER resourceBarrier = {}; + resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + resourceBarrier.Transition.pResource = pTexture; + resourceBarrier.Transition.StateBefore = + D3D12_RESOURCE_STATE_RENDER_TARGET; + resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + + pDirectCommandList_->ResourceBarrier(1, &resourceBarrier); + } + } + + pDirectCommandList_->Close(); + + ID3D12CommandList* pCommandLists[] = {pDirectCommandList_.Get()}; + pDirectCommandQueue_->ExecuteCommandLists(_countof(pCommandLists), + pCommandLists); + pDirectCommandQueue_->Signal(pDirectFence_.Get(), ++directFenceValue_); + pSwapChain_->Present(0, 0); +} + +void Viewer::initDirectX(HWND window) { + HRESULT hr = S_OK; + + { + UINT flags = 0; + +#if _DEBUG + ComPtr pDebug; + hr = D3D12GetDebugInterface(IID_PPV_ARGS(&pDebug)); + assert(SUCCEEDED(hr)); + + if (pDebug) { + flags |= DXGI_CREATE_FACTORY_DEBUG; + pDebug->EnableDebugLayer(); + } +#endif + + hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(&pFactory_)); + assert(SUCCEEDED(hr)); + } + + { + UINT i = 0; + while (DXGI_ERROR_NOT_FOUND != pFactory_->EnumAdapters1(i, &pAdapter_)) { + DXGI_ADAPTER_DESC1 adapterDesc; + pAdapter_->GetDesc1(&adapterDesc); + + if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { + continue; + } + + hr = D3D12CreateDevice(pAdapter_.Get(), D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&pDevice_)); + assert(SUCCEEDED(hr)); + + if (pDevice_) { + break; + } + + ++i; + } + } + + { + D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {}; + commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + + hr = pDevice_->CreateCommandQueue(&commandQueueDesc, + IID_PPV_ARGS(&pDirectCommandQueue_)); + assert(SUCCEEDED(hr)); + } + + { + hr = pDevice_->CreateFence(directFenceValue_, D3D12_FENCE_FLAG_NONE, + IID_PPV_ARGS(&pDirectFence_)); + assert(SUCCEEDED(hr)); + } + + { + D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {}; + commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY; + commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + + hr = pDevice_->CreateCommandQueue(&commandQueueDesc, + IID_PPV_ARGS(&pCopyCommandQueue_)); + assert(SUCCEEDED(hr)); + } + + { + hr = pDevice_->CreateFence(copyFenceValue_, D3D12_FENCE_FLAG_NONE, + IID_PPV_ARGS(&pCopyFence_)); + assert(SUCCEEDED(hr)); + } + + { + ComPtr pOutput; + hr = pAdapter_->EnumOutputs(0, &pOutput); + assert(SUCCEEDED(hr)); + + UINT modeCount = 1; + DXGI_MODE_DESC modeDesc; + hr = pOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &modeCount, + &modeDesc); + assert(SUCCEEDED(hr) || hr == DXGI_ERROR_MORE_DATA); + + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferDesc = modeDesc; + + // TODO: + swapChainDesc.BufferDesc.Width = 512; + swapChainDesc.BufferDesc.Height = 512; + + swapChainDesc.SampleDesc = {1, 0}; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = DXVIEW_SWAP_CHAIN_BUFFER_COUNT; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = true; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + ComPtr pSwapChain; + hr = pFactory_->CreateSwapChain(pDirectCommandQueue_.Get(), &swapChainDesc, + &pSwapChain); + assert(SUCCEEDED(hr)); + + hr = pSwapChain.As(&pSwapChain_); + assert(SUCCEEDED(hr)); + } + + for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) { + hr = pSwapChain_->GetBuffer(i, IID_PPV_ARGS(&pSwapChainBuffers_[i])); + assert(SUCCEEDED(hr)); + } + + for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) { + hr = pDevice_->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&pDirectCommandAllocators_[i])); + assert(SUCCEEDED(hr)); + } + + { + hr = pDevice_->CreateCommandList( + 0, D3D12_COMMAND_LIST_TYPE_DIRECT, pDirectCommandAllocators_[0].Get(), + nullptr, IID_PPV_ARGS(&pDirectCommandList_)); + assert(SUCCEEDED(hr)); + + hr = pDirectCommandList_->Close(); + assert(SUCCEEDED(hr)); + } + + { + hr = pDevice_->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&pCopyCommandAllocator_)); + assert(SUCCEEDED(hr)); + } + + { + hr = pDevice_->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, + pCopyCommandAllocator_.Get(), nullptr, + IID_PPV_ARGS(&pCopyCommandList_)); + assert(SUCCEEDED(hr)); + + hr = pCopyCommandList_->Close(); + assert(SUCCEEDED(hr)); + } + + for (auto i = 0; i != D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; ++i) { + auto descriptorHeapType = static_cast(i); + descriptorIncrementSize_[i] = + pDevice_->GetDescriptorHandleIncrementSize(descriptorHeapType); + } +} + +void Viewer::buildRenderTargets() { + HRESULT hr = S_OK; + + for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) { + auto& pRTVDescriptorHeap = pRTVDescriptorHeaps_[i]; + + { + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + descriptorHeapDesc.NumDescriptors = 1; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + + hr = pDevice_->CreateDescriptorHeap(&descriptorHeapDesc, + IID_PPV_ARGS(&pRTVDescriptorHeap)); + } + + if (SUCCEEDED(hr)) { + auto& renderTargets = renderTargets_[i]; + auto RTVDescriptor = + pRTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + for (auto j = 0; j != RENDER_PASS_TYPE_COUNT; ++j) { + RenderTarget renderTarget = {}; + + if (j != RENDER_PASS_TYPE_PRESENT) { + assert(false); + } else { + renderTarget.pTexture = pSwapChainBuffers_[i]; + } + + renderTarget.viewDescriptor = RTVDescriptor; + + pDevice_->CreateRenderTargetView(pSwapChainBuffers_[i].Get(), nullptr, + RTVDescriptor); + RTVDescriptor.ptr += + descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_RTV]; + + renderTargets.push_back(renderTarget); + } + } + } +} + +void Viewer::buildResources() { + HRESULT hr = S_OK; + + hr = pCopyCommandList_->Reset(pCopyCommandAllocator_.Get(), nullptr); + assert(SUCCEEDED(hr)); + + std::vector > stagingResources; + stagingResources.reserve(256); + + buildBuffers(&stagingResources); + buildImages(&stagingResources); + + hr = pCopyCommandList_->Close(); + assert(SUCCEEDED(hr)); + + ID3D12CommandList* pCommandLists[] = {pCopyCommandList_.Get()}; + pCopyCommandQueue_->ExecuteCommandLists(_countof(pCommandLists), + pCommandLists); + pCopyCommandQueue_->Signal(pCopyFence_.Get(), ++copyFenceValue_); + + buildSamplerDescs(); + buildMaterials(); + buildMeshes(); + buildNodes(); + + if (pCopyFence_->GetCompletedValue() < copyFenceValue_) { + auto event = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS); + pCopyFence_->SetEventOnCompletion(copyFenceValue_, event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); + } +} + +void Viewer::buildBuffers( + std::vector >* pStagingResources) { + HRESULT hr = S_OK; + + for (auto& gltfBuffer : pModel_->buffers) { + ComPtr pDstBuffer; + { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = gltfBuffer.data.size(); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pDstBuffer)); + assert(SUCCEEDED(hr)); + pBuffers_.push_back(pDstBuffer); + } + + ComPtr pSrcBuffer; + { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = gltfBuffer.data.size(); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, + IID_PPV_ARGS(&pSrcBuffer)); + assert(SUCCEEDED(hr)); + pStagingResources->push_back(pSrcBuffer); + + void* pData; + hr = pSrcBuffer->Map(0, nullptr, &pData); + assert(SUCCEEDED(hr)); + + memcpy(pData, &gltfBuffer.data[0], gltfBuffer.data.size()); + } + + pCopyCommandList_->CopyBufferRegion(pDstBuffer.Get(), 0, pSrcBuffer.Get(), + 0, gltfBuffer.data.size()); + } +} + +void Viewer::buildImages( + std::vector >* pStagingResources) { + HRESULT hr = S_OK; + + for (auto& gltfImage : pModel_->images) { + ComPtr pDstTexture; + { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resourceDesc.Alignment = 0; + resourceDesc.Width = gltfImage.width; + resourceDesc.Height = gltfImage.height; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pDstTexture)); + assert(SUCCEEDED(hr)); + pTextures_.push_back(pDstTexture); + } + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; + UINT rowCount; + UINT64 rowSize; + UINT64 size; + pDevice_->GetCopyableFootprints(&pDstTexture->GetDesc(), 0, 1, 0, + &footprint, &rowCount, &rowSize, &size); + + ComPtr pSrcBuffer; + { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = size; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, + IID_PPV_ARGS(&pSrcBuffer)); + assert(SUCCEEDED(hr)); + pStagingResources->push_back(pSrcBuffer); + + void* pData; + hr = pSrcBuffer->Map(0, nullptr, &pData); + assert(SUCCEEDED(hr)); + + for (auto i = 0; i != rowCount; ++i) { + memcpy(static_cast(pData) + rowSize * i, + &gltfImage.image[0] + gltfImage.width * gltfImage.component * i, + gltfImage.width * gltfImage.component); + } + } + + D3D12_TEXTURE_COPY_LOCATION dstCopyLocation = {}; + dstCopyLocation.pResource = pDstTexture.Get(); + dstCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstCopyLocation.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION srcCopyLocation = {}; + srcCopyLocation.pResource = pSrcBuffer.Get(); + srcCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcCopyLocation.PlacedFootprint = footprint; + + pCopyCommandList_->CopyTextureRegion(&dstCopyLocation, 0, 0, 0, + &srcCopyLocation, nullptr); + } +} + +void Viewer::buildSamplerDescs() { + for (auto& glTFSampler : pModel_->samplers) { + D3D12_SAMPLER_DESC samplerDesc = {}; + switch (glTFSampler.minFilter) { + case TINYGLTF_TEXTURE_FILTER_NEAREST: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + else + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + break; + case TINYGLTF_TEXTURE_FILTER_LINEAR: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; + else + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + else + samplerDesc.Filter = D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + break; + case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; + else + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR; + else + samplerDesc.Filter = D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR; + break; + case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: + if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) + samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + else + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + break; + default: + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + } + + auto toTextureAddressMode = [](int wrap) { + switch (wrap) { + case TINYGLTF_TEXTURE_WRAP_REPEAT: + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE: + return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT: + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; + default: + assert(false); + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + } + }; + + samplerDesc.AddressU = toTextureAddressMode(glTFSampler.wrapS); + samplerDesc.AddressV = toTextureAddressMode(glTFSampler.wrapT); + samplerDesc.AddressW = toTextureAddressMode(glTFSampler.wrapR); + samplerDesc.MaxLOD = 256; + + samplerDescs_.push_back(samplerDesc); + } +} + +void Viewer::buildMaterials() { + HRESULT hr = S_OK; + + // Build materials. + for (auto& glTFMaterial : pModel_->materials) { + Material material = {}; + + // Set a material name. + material.name = glTFMaterial.name; + + // Set a blend desc. + auto& blendDesc = material.blendDesc; + if (glTFMaterial.alphaMode == "BLEND") { + blendDesc.RenderTarget[0].BlendEnable = true; + blendDesc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; + blendDesc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + blendDesc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; + blendDesc.RenderTarget[0].LogicOp = D3D12_LOGIC_OP_NOOP; + } else if (glTFMaterial.alphaMode == "MASK") { + assert(false); + } + + blendDesc.RenderTarget[0].RenderTargetWriteMask = + D3D12_COLOR_WRITE_ENABLE_ALL; + + // Set a rasterizer desc. + auto& rasterizerDesc = material.rasterizerDesc; + rasterizerDesc.FillMode = D3D12_FILL_MODE_SOLID; + + if (glTFMaterial.doubleSided) { + rasterizerDesc.CullMode = D3D12_CULL_MODE_NONE; + } else { + rasterizerDesc.CullMode = D3D12_CULL_MODE_BACK; + } + + rasterizerDesc.FrontCounterClockwise = true; + rasterizerDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + rasterizerDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + rasterizerDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + rasterizerDesc.ForcedSampleCount = 0; + rasterizerDesc.ConservativeRaster = + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + auto& pBuffer = material.pBuffer; + auto& pBufferData = material.pBufferData; + { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Width = + alignPow2(sizeof(PBRMetallicRoughness), + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pBuffer)); + assert(SUCCEEDED(hr)); + + hr = pBuffer->Map(0, nullptr, &pBufferData); + assert(SUCCEEDED(hr)); + } + + auto& pSRVDescriptorHeap = material.pSRVDescriptorHeap; + { + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.NumDescriptors = 5; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + + hr = pDevice_->CreateDescriptorHeap(&descriptorHeapDesc, + IID_PPV_ARGS(&pSRVDescriptorHeap)); + assert(SUCCEEDED(hr)); + } + + auto& pSamplerDescriptorHeap = material.pSamplerDescriptorHeap; + { + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + descriptorHeapDesc.NumDescriptors = 5; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + + hr = pDevice_->CreateDescriptorHeap( + &descriptorHeapDesc, IID_PPV_ARGS(&pSamplerDescriptorHeap)); + assert(SUCCEEDED(hr)); + } + + auto& glTFPBRMetallicRoughness = glTFMaterial.pbrMetallicRoughness; + { + auto pPBRMetallicRoughness = + static_cast(pBufferData); + + auto& baseColorFactor = pPBRMetallicRoughness->baseColorFactor; + + baseColorFactor.x = + static_cast(glTFPBRMetallicRoughness.baseColorFactor[0]); + baseColorFactor.y = + static_cast(glTFPBRMetallicRoughness.baseColorFactor[1]); + baseColorFactor.z = + static_cast(glTFPBRMetallicRoughness.baseColorFactor[2]); + baseColorFactor.w = + static_cast(glTFPBRMetallicRoughness.baseColorFactor[3]); + + auto& glTFBaseColorTexture = glTFPBRMetallicRoughness.baseColorTexture; + auto& baseColorTexture = pPBRMetallicRoughness->baseColorTexture; + if (glTFBaseColorTexture.index >= 0) { + baseColorTexture.textureIndex = 0; + baseColorTexture.samplerIndex = 0; + } else { + baseColorTexture.textureIndex = -1; + baseColorTexture.samplerIndex = -1; + } + + pPBRMetallicRoughness->metallicFactor = + static_cast(glTFPBRMetallicRoughness.metallicFactor); + pPBRMetallicRoughness->roughnessFactor = + static_cast(glTFPBRMetallicRoughness.roughnessFactor); + + auto& glTFMetallicRoughnessTexture = + glTFPBRMetallicRoughness.metallicRoughnessTexture; + auto& metallicRoughnessTexture = + pPBRMetallicRoughness->metallicRoughnessTexture; + if (glTFMetallicRoughnessTexture.index >= 0) { + metallicRoughnessTexture.textureIndex = 1; + metallicRoughnessTexture.samplerIndex = 1; + } else { + metallicRoughnessTexture.textureIndex = -1; + metallicRoughnessTexture.samplerIndex = -1; + } + } + + auto srvDescriptor = + pSRVDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + auto samplerDescriptor = + pSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + auto& glTFBaseColorTexture = glTFPBRMetallicRoughness.baseColorTexture; + if (glTFBaseColorTexture.index >= 0) { + auto& glTFTexture = pModel_->textures[glTFBaseColorTexture.index]; + + auto pTexture = pTextures_[glTFTexture.source].Get(); + pDevice_->CreateShaderResourceView(pTexture, nullptr, srvDescriptor); + + auto& samplerDesc = samplerDescs_[glTFTexture.sampler]; + pDevice_->CreateSampler(&samplerDesc, samplerDescriptor); + } + + srvDescriptor.ptr += + descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]; + samplerDescriptor.ptr += + descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]; + + auto& glTFMetallicRoughnessTexture = + glTFMaterial.pbrMetallicRoughness.metallicRoughnessTexture; + if (glTFMetallicRoughnessTexture.index >= 0) { + auto& glTFTexture = pModel_->textures[glTFMetallicRoughnessTexture.index]; + auto pTexture = pTextures_[glTFTexture.source].Get(); + + pDevice_->CreateShaderResourceView(pTexture, nullptr, srvDescriptor); + + auto& samplerDesc = samplerDescs_[glTFTexture.sampler]; + pDevice_->CreateSampler(&samplerDesc, samplerDescriptor); + } + materials_.push_back(material); + } +} + +void Viewer::buildMeshes() { + HRESULT hr = S_OK; + + for (auto& glTFMesh : pModel_->meshes) { + Mesh mesh = {}; + mesh.name = glTFMesh.name; + + auto& primitives = mesh.primitives; + for (auto& glTFPrimitive : glTFMesh.primitives) { + Primitive primitive = {}; + + auto& attributes = primitive.attributes; + for (auto& [attributeName, accessorIndex] : glTFPrimitive.attributes) { + const auto& glTFAccessor = pModel_->accessors[accessorIndex]; + const auto& glTFBufferView = + pModel_->bufferViews[glTFAccessor.bufferView]; + const auto& glTFBuffer = pModel_->buffers[glTFBufferView.buffer]; + + Attribute attribute = {}; + attribute.name = attributeName; + + switch (glTFAccessor.type) { + case TINYGLTF_TYPE_VEC2: + attribute.format = DXGI_FORMAT_R32G32_FLOAT; + break; + case TINYGLTF_TYPE_VEC3: + attribute.format = DXGI_FORMAT_R32G32B32_FLOAT; + break; + case TINYGLTF_TYPE_VEC4: + attribute.format = DXGI_FORMAT_R32G32B32A32_FLOAT; + break; + } + + attribute.vertexBufferView.BufferLocation = + pBuffers_[glTFBufferView.buffer]->GetGPUVirtualAddress() + + glTFBufferView.byteOffset + glTFAccessor.byteOffset; + attribute.vertexBufferView.SizeInBytes = static_cast( + glTFBufferView.byteLength - glTFAccessor.byteOffset); + attribute.vertexBufferView.StrideInBytes = + glTFAccessor.ByteStride(glTFBufferView); + + attributes.emplace_back(attribute); + + if (attributeName == "POSITION") { + primitive.vertexCount = static_cast(glTFAccessor.count); + } + } + + auto& primitiveTopology = primitive.primitiveTopology; + switch (glTFPrimitive.mode) { + case TINYGLTF_MODE_POINTS: + primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; + break; + case TINYGLTF_MODE_LINE: + primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; + break; + case TINYGLTF_MODE_LINE_STRIP: + primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; + break; + case TINYGLTF_MODE_TRIANGLES: + primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + case TINYGLTF_MODE_TRIANGLE_STRIP: + primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + break; + default: + assert(false); + } + + if (glTFPrimitive.indices >= 0) { + const auto& glTFAccessor = pModel_->accessors[glTFPrimitive.indices]; + const auto& glTFBufferView = + pModel_->bufferViews[glTFAccessor.bufferView]; + const auto& glTFBuffer = pModel_->buffers[glTFBufferView.buffer]; + + auto& indexBufferView = primitive.indexBufferView; + indexBufferView.BufferLocation = + pBuffers_[glTFBufferView.buffer]->GetGPUVirtualAddress() + + glTFBufferView.byteOffset + glTFAccessor.byteOffset; + indexBufferView.SizeInBytes = static_cast( + glTFBufferView.byteLength - glTFAccessor.byteOffset); + + switch (glTFAccessor.componentType) { + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + indexBufferView.Format = DXGI_FORMAT_R8_UINT; + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + indexBufferView.Format = DXGI_FORMAT_R16_UINT; + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + indexBufferView.Format = DXGI_FORMAT_R32_UINT; + break; + } + + auto& indexCount = primitive.indexCount; + indexCount = static_cast(glTFAccessor.count); + } + + auto buildDefines = [](const std::vector& attributes) { + std::vector defines; + + for (auto& attribute : attributes) { + if (attribute.name == "NORMAL") + defines.push_back({"HAS_NORMAL", "1"}); + else if (attribute.name == "TANGENT") + defines.push_back({"HAS_TANGENT", "1"}); + else if (attribute.name == "TEXCOORD_0") + defines.push_back({"HAS_TEXCOORD_0", "1"}); + } + + defines.push_back({nullptr, nullptr}); + + return defines; + }; + + auto compileShaderFromFile = [](LPCWSTR pFilePath, + D3D_SHADER_MACRO* pDefines, + LPCSTR pTarget, ID3DBlob** ppShader) { + HRESULT hr = S_OK; + + { + UINT flags = 0; + +#if _DEBUG + flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + ComPtr pError; + hr = D3DCompileFromFile(pFilePath, pDefines, + D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", + pTarget, flags, 0, ppShader, &pError); + + if (pError) { + spdlog::error("{}", static_cast(pError->GetBufferPointer())); + } + assert(SUCCEEDED(hr)); + } + + return hr; + }; + + auto createRootSignature = + [this](D3D12_ROOT_SIGNATURE_DESC* pRootSignatureDesc, + ID3D12RootSignature** ppRootSignature) { + HRESULT hr = S_OK; + + ComPtr pSerializeRootSignature; + ComPtr pError; + + { + hr = D3D12SerializeRootSignature( + pRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, + pSerializeRootSignature.GetAddressOf(), + pError.GetAddressOf()); + + if (pError) { + spdlog::error("{}", + static_cast(pError->GetBufferPointer())); + } + assert(SUCCEEDED(hr)); + } + + if (SUCCEEDED(hr)) { + hr = pDevice_->CreateRootSignature( + 0, pSerializeRootSignature->GetBufferPointer(), + pSerializeRootSignature->GetBufferSize(), + IID_PPV_ARGS(ppRootSignature)); + assert(SUCCEEDED(hr)); + } + + return hr; + }; + + auto buildInputElementDescs = + [](const std::vector& attributes) { + std::vector inputElementDescs; + + for (auto& attribute : attributes) { + D3D12_INPUT_ELEMENT_DESC inputElementDesc = {}; + inputElementDesc.SemanticName = &attribute.name[0]; + inputElementDesc.Format = attribute.format; + + // TODO: Need to parse semantic name and index from attribute name. + if (attribute.name == "TEXCOORD_0") { + inputElementDesc.SemanticName = "TEXCOORD_"; + inputElementDesc.SemanticIndex = 0; + } + + inputElementDesc.InputSlot = + static_cast(inputElementDescs.size()); + inputElementDesc.AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT; + inputElementDesc.InputSlotClass = + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + + inputElementDescs.push_back(inputElementDesc); + } + + return inputElementDescs; + }; + + if (glTFPrimitive.material >= 0) { + primitive.pMaterial = &materials_[glTFPrimitive.material]; + + auto& pRootSignature = primitive.pRootSignature; + { + D3D12_DESCRIPTOR_RANGE SRVDescriptorRange = {}; + + SRVDescriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorRange.NumDescriptors = 5; + + D3D12_DESCRIPTOR_RANGE SamplerDescriptorRange = {}; + + SamplerDescriptorRange.RangeType = + D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + SamplerDescriptorRange.NumDescriptors = 5; + + D3D12_ROOT_PARAMETER rootParams[5] = { + {D3D12_ROOT_PARAMETER_TYPE_CBV, + {0, 0}, + D3D12_SHADER_VISIBILITY_VERTEX}, + {D3D12_ROOT_PARAMETER_TYPE_CBV, + {1, 0}, + D3D12_SHADER_VISIBILITY_VERTEX}, + {D3D12_ROOT_PARAMETER_TYPE_CBV, + {2, 0}, + D3D12_SHADER_VISIBILITY_PIXEL}, + {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, + {1, &SRVDescriptorRange}, + D3D12_SHADER_VISIBILITY_PIXEL}, + {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, + {1, &SamplerDescriptorRange}, + D3D12_SHADER_VISIBILITY_PIXEL}}; + + // TEMP + rootParams[0].Descriptor.RegisterSpace = 0; + rootParams[1].Descriptor.RegisterSpace = 0; + rootParams[2].Descriptor.RegisterSpace = 0; + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; + rootSignatureDesc.NumParameters = 5; + rootSignatureDesc.pParameters = &rootParams[0]; + rootSignatureDesc.Flags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + hr = createRootSignature(&rootSignatureDesc, &pRootSignature); + assert(SUCCEEDED(hr)); + } + + { + auto defines = buildDefines(attributes); + auto pDefinces = defines.empty() ? nullptr : &defines[0]; + + ComPtr pVS; + hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl", + pDefinces, "vs_5_1", &pVS); + assert(SUCCEEDED(hr)); + + ComPtr pPS; + hr = compileShaderFromFile(DXVIEW_RES_DIR "/lighting.hlsl", pDefinces, + "ps_5_1", &pPS); + + auto inputElementDescs = buildInputElementDescs(attributes); + + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc = {}; + pipelineStateDesc.pRootSignature = pRootSignature.Get(); + pipelineStateDesc.VS = {pVS->GetBufferPointer(), + pVS->GetBufferSize()}; + pipelineStateDesc.PS = {pPS->GetBufferPointer(), + pPS->GetBufferSize()}; + pipelineStateDesc.BlendState = primitive.pMaterial->blendDesc; + pipelineStateDesc.SampleMask = UINT_MAX; + pipelineStateDesc.RasterizerState = + primitive.pMaterial->rasterizerDesc; + pipelineStateDesc.InputLayout = { + inputElementDescs.data(), + static_cast(inputElementDescs.size())}; + + switch (primitive.primitiveTopology) { + case D3D_PRIMITIVE_TOPOLOGY_POINTLIST: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; + break; + case D3D_PRIMITIVE_TOPOLOGY_LINELIST: + case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; + break; + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + break; + default: + assert(false); + } + + pipelineStateDesc.NumRenderTargets = 1; + pipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + pipelineStateDesc.SampleDesc = {1, 0}; + + auto& pPipelineState = primitive.pPipelineState; + hr = pDevice_->CreateGraphicsPipelineState( + &pipelineStateDesc, IID_PPV_ARGS(&pPipelineState)); + assert(SUCCEEDED(hr)); + } + } else { + auto& pRootSignature = primitive.pRootSignature; + { + D3D12_ROOT_PARAMETER rootParams[2] = { + {D3D12_ROOT_PARAMETER_TYPE_CBV, + {0, 0}, + D3D12_SHADER_VISIBILITY_VERTEX}, + {D3D12_ROOT_PARAMETER_TYPE_CBV, + {1, 0}, + D3D12_SHADER_VISIBILITY_VERTEX}}; + + // TEMP + rootParams[0].Descriptor.RegisterSpace = 0; + rootParams[1].Descriptor.RegisterSpace = 0; + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; + rootSignatureDesc.NumParameters = _countof(rootParams); + rootSignatureDesc.pParameters = &rootParams[0]; + rootSignatureDesc.Flags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + hr = createRootSignature(&rootSignatureDesc, &pRootSignature); + assert(SUCCEEDED(hr)); + } + + { + auto defines = buildDefines(attributes); + auto pDefinces = defines.empty() ? nullptr : &defines[0]; + + ComPtr pVS; + hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl", + pDefinces, "vs_5_1", &pVS); + assert(SUCCEEDED(hr)); + + ComPtr pPS; + hr = compileShaderFromFile(DXVIEW_RES_DIR "/gray.hlsl", pDefinces, + "ps_5_1", &pPS); + + auto inputElementDescs = buildInputElementDescs(attributes); + + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc = {}; + pipelineStateDesc.pRootSignature = pRootSignature.Get(); + pipelineStateDesc.VS = {pVS->GetBufferPointer(), + pVS->GetBufferSize()}; + pipelineStateDesc.PS = {pPS->GetBufferPointer(), + pPS->GetBufferSize()}; + pipelineStateDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = + D3D12_COLOR_WRITE_ENABLE_ALL; + pipelineStateDesc.SampleMask = UINT_MAX; + pipelineStateDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + pipelineStateDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; + pipelineStateDesc.RasterizerState.FrontCounterClockwise = true; + pipelineStateDesc.InputLayout = { + inputElementDescs.data(), + static_cast(inputElementDescs.size())}; + + switch (primitive.primitiveTopology) { + case D3D_PRIMITIVE_TOPOLOGY_POINTLIST: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; + break; + case D3D_PRIMITIVE_TOPOLOGY_LINELIST: + case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; + break; + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: + pipelineStateDesc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + break; + default: + assert(false); + } + + pipelineStateDesc.NumRenderTargets = 1; + pipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + pipelineStateDesc.SampleDesc = {1, 0}; + + auto& pPipelineState = primitive.pPipelineState; + hr = pDevice_->CreateGraphicsPipelineState( + &pipelineStateDesc, IID_PPV_ARGS(&pPipelineState)); + assert(SUCCEEDED(hr)); + } + } + primitives.push_back(primitive); + } + meshes_.push_back(mesh); + } +} + +void Viewer::buildNodes() { + HRESULT hr = S_OK; + + for (auto& glTFNode : pModel_->nodes) { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Width = + alignPow2(sizeof(PBRMetallicRoughness), + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc = {1, 0}; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + ComPtr pBuffer; + hr = pDevice_->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pBuffer)); + assert(SUCCEEDED(hr)); + + void* pData; + hr = pBuffer->Map(0, nullptr, &pData); + assert(SUCCEEDED(hr)); + + if (glTFNode.matrix.empty()) { + XMStoreFloat4x4(static_cast(pData), + XMMatrixIdentity()); + } else { + float* pElement = static_cast(pData); + for (auto value : glTFNode.matrix) { + *pElement = static_cast(value); + ++pElement; + } + } + + pNodeBuffers_.push_back(pBuffer); + } +} + +void Viewer::drawNode(uint64_t nodeIndex) { + const auto& glTFNode = pModel_->nodes[nodeIndex]; + + if (glTFNode.mesh >= 0) { + const auto& mesh = meshes_[glTFNode.mesh]; + + for (auto& primitive : mesh.primitives) { + pDirectCommandList_->SetGraphicsRootSignature( + primitive.pRootSignature.Get()); + pDirectCommandList_->SetPipelineState(primitive.pPipelineState.Get()); + pDirectCommandList_->IASetPrimitiveTopology(primitive.primitiveTopology); + + for (auto i = 0; i != primitive.attributes.size(); ++i) { + pDirectCommandList_->IASetVertexBuffers( + i, 1, &primitive.attributes[i].vertexBufferView); + } + + ID3D12DescriptorHeap* pDescriptorHeaps[] = { + primitive.pMaterial->pSRVDescriptorHeap.Get(), + primitive.pMaterial->pSamplerDescriptorHeap.Get()}; + pDirectCommandList_->SetDescriptorHeaps(_countof(pDescriptorHeaps), + pDescriptorHeaps); + + pDirectCommandList_->SetGraphicsRootConstantBufferView( + 0, pCameraBuffer_->GetGPUVirtualAddress()); + pDirectCommandList_->SetGraphicsRootConstantBufferView( + 1, pNodeBuffers_[nodeIndex]->GetGPUVirtualAddress()); + pDirectCommandList_->SetGraphicsRootConstantBufferView( + 2, primitive.pMaterial->pBuffer->GetGPUVirtualAddress()); + pDirectCommandList_->SetGraphicsRootDescriptorTable( + 3, primitive.pMaterial->pSRVDescriptorHeap + ->GetGPUDescriptorHandleForHeapStart()); + pDirectCommandList_->SetGraphicsRootDescriptorTable( + 4, primitive.pMaterial->pSamplerDescriptorHeap + ->GetGPUDescriptorHandleForHeapStart()); + + if (primitive.indexCount) { + pDirectCommandList_->IASetIndexBuffer(&primitive.indexBufferView); + pDirectCommandList_->DrawIndexedInstanced(primitive.indexCount, 1, 0, 0, + 0); + } else { + pDirectCommandList_->DrawInstanced(primitive.vertexCount, 1, 0, 0); + } + } + } + + for (auto childNodeIndex : glTFNode.children) { + drawNode(childNodeIndex); + } +} \ No newline at end of file diff --git a/examples/dxview/src/Viewer.h b/examples/dxview/src/Viewer.h new file mode 100644 index 0000000..2b682d6 --- /dev/null +++ b/examples/dxview/src/Viewer.h @@ -0,0 +1,130 @@ +#ifndef DXVIEW_VIEWER_GUARD +#define DXVIEW_VIEWER_GUARD + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using Microsoft::WRL::ComPtr; + +enum RenderPassType { RENDER_PASS_TYPE_PRESENT = 0, RENDER_PASS_TYPE_COUNT }; + +struct RenderTarget { + ComPtr pTexture; + D3D12_CPU_DESCRIPTOR_HANDLE viewDescriptor; +}; + +struct TextureInfo { + int32_t textureIndex; + int32_t samplerIndex; +}; + +struct PBRMetallicRoughness { + DirectX::XMFLOAT4 baseColorFactor; + TextureInfo baseColorTexture; + float metallicFactor; + float roughnessFactor; + TextureInfo metallicRoughnessTexture; +}; + +struct Material { + std::string name; + D3D12_BLEND_DESC blendDesc; + D3D12_RASTERIZER_DESC rasterizerDesc; + ComPtr pBuffer; + void* pBufferData; + ComPtr pSRVDescriptorHeap; + ComPtr pSamplerDescriptorHeap; +}; + +struct Attribute { + std::string name; + DXGI_FORMAT format; + D3D12_VERTEX_BUFFER_VIEW vertexBufferView; +}; + +struct Primitive { + std::vector attributes; + uint32_t vertexCount; + D3D12_PRIMITIVE_TOPOLOGY primitiveTopology; + D3D12_INDEX_BUFFER_VIEW indexBufferView; + uint32_t indexCount; + Material* pMaterial; + ComPtr pRootSignature; + ComPtr pPipelineState; +}; + +struct Mesh { + std::string name; + std::vector primitives; +}; + +struct Node { + DirectX::XMFLOAT4X4 M; +}; + +struct Camera { + DirectX::XMFLOAT4X4 V; + DirectX::XMFLOAT4X4 P; + DirectX::XMFLOAT4X4 VP; +}; + +class Viewer { + public: + Viewer(HWND window, tinygltf::Model* pModel); + + void update(double deltaTime); + void render(double deltaTime); + + private: + void initDirectX(HWND window); + void buildRenderTargets(); + void buildResources(); + void buildBuffers(std::vector >* pStagingResources); + void buildImages(std::vector >* pStagingResources); + void buildSamplerDescs(); + void buildMaterials(); + void buildMeshes(); + void buildNodes(); + + void drawNode(uint64_t nodeIndex); + + private: + tinygltf::Model* pModel_; + ComPtr pFactory_; + ComPtr pAdapter_; + ComPtr pDevice_; + ComPtr pDirectCommandQueue_; + UINT64 directFenceValue_; + ComPtr pDirectFence_; + ComPtr pCopyCommandQueue_; + UINT64 copyFenceValue_; + ComPtr pCopyFence_; + ComPtr pSwapChain_; + ComPtr pSwapChainBuffers_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT]; + ComPtr + pDirectCommandAllocators_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT]; + ComPtr pDirectCommandList_; + ComPtr pCopyCommandAllocator_; + ComPtr pCopyCommandList_; + UINT descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES]; + ComPtr + pRTVDescriptorHeaps_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT]; + std::vector renderTargets_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT]; + std::vector > pBuffers_; + std::vector > pTextures_; + std::vector samplerDescs_; + std::vector materials_; + std::vector meshes_; + std::vector > pNodeBuffers_; + ComPtr pCameraBuffer_; +}; + +#endif diff --git a/examples/dxview/src/dxview.cc b/examples/dxview/src/dxview.cc new file mode 100644 index 0000000..fd0382b --- /dev/null +++ b/examples/dxview/src/dxview.cc @@ -0,0 +1,90 @@ +#define GLFW_EXPOSE_NATIVE_WIN32 +#define TINYGLTF_IMPLEMENTATION +#define STBI_MSC_SECURE_CRT +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include +#include +#include +#include + +#undef GLFW_EXPOSE_NATIVE_WIN32 +#undef TINYGLTF_IMPLEMENTATION +#undef STBI_MSC_SECURE_CRT +#undef STB_IMAGE_IMPLEMENTATION +#undef STB_IMAGE_WRITE_IMPLEMENTATION + +#include +#include + +#include "Viewer.h" + +static void onError(int error, const char* message) { + spdlog::error("[{}] {}", error, message); +} + +static void onRender(Viewer* pViewer, double deltaTime) { + pViewer->update(deltaTime); + pViewer->render(deltaTime); +} + +int main(int argc, char* argv[]) { + tinygltf::TinyGLTF context; + + tinygltf::Model model; + std::string error; + std::string warning; + if (!context.LoadASCIIFromFile(&model, &error, &warning, argv[1])) { + if (!error.empty()) { + spdlog::error("{}", error); + } + + if (!warning.empty()) { + spdlog::warn("{}", warning); + } + + exit(EXIT_FAILURE); + } + + GLFWwindow* pWindow; + + glfwSetErrorCallback(onError); + + if (!glfwInit()) { + spdlog::error("Fail to initialize GLFW!!!"); + exit(EXIT_FAILURE); + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + pWindow = glfwCreateWindow(512, 512, "dxview", nullptr, nullptr); + if (!pWindow) { + spdlog::error("Fail to create GLFWwindow!!!"); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + auto pViewer = std::make_unique(glfwGetWin32Window(pWindow), &model); + if (!pViewer) { + spdlog::error("Fail to create Viewer"); + glfwDestroyWindow(pWindow); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + auto prevTimeStamp = glfwGetTime(); + while (!glfwWindowShouldClose(pWindow)) { + auto currTimeStamp = glfwGetTime(); + + onRender(pViewer.get(), currTimeStamp - prevTimeStamp); + prevTimeStamp = currTimeStamp; + + glfwPollEvents(); + } + + glfwDestroyWindow(pWindow); + glfwTerminate(); + exit(EXIT_SUCCESS); +} \ No newline at end of file