From 589e8fc3d1e4fc1ed46ac5673d5fdb076bf7cb0e Mon Sep 17 00:00:00 2001 From: David Date: Sat, 20 Jun 2026 11:24:18 +0100 Subject: [PATCH] Added HelloScrolling project to HelloWorld samples. --- .../D3D12HelloWorld/src/D3D12HelloWorld.sln | 6 + .../HelloScrolling/D3D12HelloScrolling.cpp | 568 ++++++++++++++++++ .../src/HelloScrolling/D3D12HelloScrolling.h | 97 +++ .../D3D12HelloScrolling.vcxproj | 174 ++++++ .../D3D12HelloScrolling.vcxproj.filters | 59 ++ .../src/HelloScrolling/DXSample.cpp | 128 ++++ .../src/HelloScrolling/DXSample.h | 63 ++ .../src/HelloScrolling/DXSampleHelper.h | 253 ++++++++ .../src/HelloScrolling/Main.cpp | 20 + .../src/HelloScrolling/Win32Application.cpp | 119 ++++ .../src/HelloScrolling/Win32Application.h | 29 + .../src/HelloScrolling/packages.config | 5 + .../src/HelloScrolling/shaders.hlsl | 45 ++ .../src/HelloScrolling/stdafx.cpp | 12 + .../src/HelloScrolling/stdafx.h | 32 + 15 files changed, 1610 insertions(+) create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.cpp create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.h create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj.filters create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.cpp create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.h create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSampleHelper.h create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Main.cpp create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.cpp create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.h create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/packages.config create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/shaders.hlsl create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.cpp create mode 100644 Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.h diff --git a/Samples/Desktop/D3D12HelloWorld/src/D3D12HelloWorld.sln b/Samples/Desktop/D3D12HelloWorld/src/D3D12HelloWorld.sln index 6fc605793..6ed38ddf5 100644 --- a/Samples/Desktop/D3D12HelloWorld/src/D3D12HelloWorld.sln +++ b/Samples/Desktop/D3D12HelloWorld/src/D3D12HelloWorld.sln @@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12HelloConstBuffers", "H EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12HelloTexture", "HelloTexture\D3D12HelloTexture.vcxproj", "{16B13BB4-D82D-4AC1-8B40-C6BCCC99954B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12HelloScrolling", "HelloScrolling\D3D12HelloScrolling.vcxproj", "{EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12HelloFrameBuffering", "HelloFrameBuffering\D3D12HelloFrameBuffering.vcxproj", "{DF8A573D-FD94-45F8-AED2-0BEFC00B9591}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12HelloVAEncode", "HelloVAEncode\D3D12HelloVAEncode.vcxproj", "{940957E3-82D6-4CD8-B39F-6FA47763973F}" @@ -59,6 +61,10 @@ Global {16B13BB4-D82D-4AC1-8B40-C6BCCC99954B}.Debug|x64.Build.0 = Debug|x64 {16B13BB4-D82D-4AC1-8B40-C6BCCC99954B}.Release|x64.ActiveCfg = Release|x64 {16B13BB4-D82D-4AC1-8B40-C6BCCC99954B}.Release|x64.Build.0 = Release|x64 + {EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16}.Debug|x64.ActiveCfg = Debug|x64 + {EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16}.Debug|x64.Build.0 = Debug|x64 + {EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16}.Release|x64.ActiveCfg = Release|x64 + {EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16}.Release|x64.Build.0 = Release|x64 {DF8A573D-FD94-45F8-AED2-0BEFC00B9591}.Debug|x64.ActiveCfg = Debug|x64 {DF8A573D-FD94-45F8-AED2-0BEFC00B9591}.Debug|x64.Build.0 = Debug|x64 {DF8A573D-FD94-45F8-AED2-0BEFC00B9591}.Release|x64.ActiveCfg = Release|x64 diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.cpp b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.cpp new file mode 100644 index 000000000..d3320bebd --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.cpp @@ -0,0 +1,568 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "D3D12HelloScrolling.h" + +extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = 618; } +extern "C" { __declspec(dllexport) extern const char* D3D12SDKPath = u8".\\D3D12\\"; } + +D3D12HelloScrolling::D3D12HelloScrolling(UINT width, UINT height, std::wstring name) : + DXSample(width, height, name), + m_frameIndex(0), + m_pCbvDataBegin(nullptr), + m_viewport(0.0f, 0.0f, static_cast(width), static_cast(height)), + m_scissorRect(0, 0, static_cast(width), static_cast(height)), + m_rtvDescriptorSize(0), + m_constantBufferData{} +{ +} + +void D3D12HelloScrolling::OnInit() +{ + LoadPipeline(); + LoadAssets(); +} + +// Load the rendering pipeline dependencies. +void D3D12HelloScrolling::LoadPipeline() +{ + UINT dxgiFactoryFlags = 0; + +#if defined(_DEBUG) + // Enable the debug layer (requires the Graphics Tools "optional feature"). + // NOTE: Enabling the debug layer after device creation will invalidate the active device. + { + ComPtr debugController; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) + { + debugController->EnableDebugLayer(); + + // Enable additional debug layers. + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; + } + } +#endif + + ComPtr factory; + ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); + + if (m_useWarpDevice) + { + ComPtr warpAdapter; + ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter))); + + ThrowIfFailed(D3D12CreateDevice( + warpAdapter.Get(), + D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&m_device) + )); + } + else + { + ComPtr hardwareAdapter; + GetHardwareAdapter(factory.Get(), &hardwareAdapter); + + ThrowIfFailed(D3D12CreateDevice( + hardwareAdapter.Get(), + D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&m_device) + )); + } + + // Describe and create the command queue. + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + ThrowIfFailed(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue))); + + // Describe and create the swap chain. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.BufferCount = FrameCount; + swapChainDesc.Width = m_width; + swapChainDesc.Height = m_height; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.SampleDesc.Count = 1; + + ComPtr swapChain; + ThrowIfFailed(factory->CreateSwapChainForHwnd( + m_commandQueue.Get(), // Swap chain needs the queue so that it can force a flush on it. + Win32Application::GetHwnd(), + &swapChainDesc, + nullptr, + nullptr, + &swapChain + )); + + // This sample does not support fullscreen transitions. + ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER)); + + ThrowIfFailed(swapChain.As(&m_swapChain)); + m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // Create descriptor heaps. + { + // Describe and create a render target view (RTV) descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = FrameCount; + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + ThrowIfFailed(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap))); + + m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + // Describe and create a constant buffer view (CBV) and a shader resource view (SRV) for the texture heap. + // Flags indicate that this descriptor heap can be bound to the pipeline + // and that descriptors contained in it can be referenced by a root table. + D3D12_DESCRIPTOR_HEAP_DESC cbvsrvHeapDesc = {}; + cbvsrvHeapDesc.NumDescriptors = 2; + cbvsrvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + cbvsrvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + ThrowIfFailed(m_device->CreateDescriptorHeap(&cbvsrvHeapDesc, IID_PPV_ARGS(&m_cbvsrvHeap))); + + m_cbvsrvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + // Create frame resources. + { + CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); + + // Create a RTV for each frame. + for (UINT n = 0; n < FrameCount; n++) + { + ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))); + m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle); + rtvHandle.Offset(1, m_rtvDescriptorSize); + } + } + + ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator))); + ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_BUNDLE, IID_PPV_ARGS(&m_bundleAllocator))); +} + +// Load the sample assets. +void D3D12HelloScrolling::LoadAssets() +{ + // Create a root signature consisting of a descriptor table with a single CBV. + { + D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {}; + + // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this. + featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; + + if (FAILED(m_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData)))) + { + featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; + } + + CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); + + CD3DX12_ROOT_PARAMETER1 rootParameters[2]; + rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_VERTEX); + rootParameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL); + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ComPtr signature; + ComPtr error; + ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)); + ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature))); + } + + // Create the pipeline state, which includes compiling and loading shaders. + { + UINT8* pVertexShaderData = nullptr; + UINT8* pPixelShaderData = nullptr; + UINT vertexShaderDataLength = 0; + UINT pixelShaderDataLength = 0; + + ThrowIfFailed(ReadDataFromFile(GetAssetFullPath(L"shaders_VSMain.cso").c_str(), &pVertexShaderData, &vertexShaderDataLength)); + ThrowIfFailed(ReadDataFromFile(GetAssetFullPath(L"shaders_PSMain.cso").c_str(), &pPixelShaderData, &pixelShaderDataLength)); + + // Define the vertex input layout. + D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "INSTANCE_OFFSET", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 } + }; + + // Describe and create the graphics pipeline state object (PSO). + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; + psoDesc.pRootSignature = m_rootSignature.Get(); + psoDesc.VS = CD3DX12_SHADER_BYTECODE(pVertexShaderData, vertexShaderDataLength); + psoDesc.PS = CD3DX12_SHADER_BYTECODE(pPixelShaderData, pixelShaderDataLength); + psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + psoDesc.DepthStencilState.DepthEnable = FALSE; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.SampleMask = UINT_MAX; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + psoDesc.SampleDesc.Count = 1; + + ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState))); + } + + // Create the command list. + ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList))); + + // Create the vertex buffer. + { + // Define the geometry for a quad. + Vertex quadVertices[] = + { + { { -0.25f, 0.25f * m_aspectRatio, 0.0f }, { 0.5f, 0.0f } }, + { { 0.25f, 0.25f * m_aspectRatio, 0.0f }, { 1.0f, 0.0f } }, + { { -0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.5f, 1.0f } }, + { { 0.25f, -0.25f * m_aspectRatio, 0.0f }, { 1.0f, 1.0f } }, + }; + + const UINT vertexBufferSize = sizeof(quadVertices); + + // Note: using upload heaps to transfer static data like vert buffers is not + // recommended. Every time the GPU needs it, the upload heap will be marshalled + // over. Please read up on Default Heap usage. An upload heap is used here for + // code simplicity and because there are very few verts to actually transfer. + ThrowIfFailed(m_device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&m_vertexBuffer))); + + // Copy the triangle data to the vertex buffer. + UINT8* pVertexDataBegin; + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin))); + memcpy(pVertexDataBegin, quadVertices, sizeof(quadVertices)); + m_vertexBuffer->Unmap(0, nullptr); + + // Initialize the vertex buffer view. + m_vertexBufferView[0].BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); + m_vertexBufferView[0].StrideInBytes = sizeof(Vertex); + m_vertexBufferView[0].SizeInBytes = vertexBufferSize; + } + + // Create instance data (positions) + const UINT INSTANCE_COUNT = 2; + { + std::vector instances(INSTANCE_COUNT); + instances[0].offset = XMFLOAT3(1.0f, 0.0f, 0); + instances[1].offset = XMFLOAT3(-1.0f, 0.0f, 0); + + const UINT instanceBufferSize = sizeof(InstanceData) * INSTANCE_COUNT; + + ThrowIfFailed( m_device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(instanceBufferSize), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&m_instanceBuffer))); + + UINT8 *pInstanceDataBegin; + CD3DX12_RANGE readRange(0, 0); + ThrowIfFailed( m_instanceBuffer->Map(0, &readRange, reinterpret_cast(&pInstanceDataBegin))); + memcpy(pInstanceDataBegin, instances.data(), instanceBufferSize); + m_instanceBuffer->Unmap(0, nullptr); + + m_vertexBufferView[1].BufferLocation = m_instanceBuffer->GetGPUVirtualAddress(); + m_vertexBufferView[1].StrideInBytes = sizeof(InstanceData); + m_vertexBufferView[1].SizeInBytes = instanceBufferSize; + } + + // Create the constant buffer. + { + const UINT constantBufferSize = sizeof(SceneConstantBuffer); // CB size is required to be 256-byte aligned. + + ThrowIfFailed(m_device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(constantBufferSize), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&m_constantBuffer))); + + // Describe and create a constant buffer view. + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + cbvDesc.BufferLocation = m_constantBuffer->GetGPUVirtualAddress(); + cbvDesc.SizeInBytes = constantBufferSize; + m_device->CreateConstantBufferView(&cbvDesc, m_cbvsrvHeap->GetCPUDescriptorHandleForHeapStart()); + + // Map and initialize the constant buffer. We don't unmap this until the + // app closes. Keeping things mapped for the lifetime of the resource is okay. + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast(&m_pCbvDataBegin))); + memcpy(m_pCbvDataBegin, &m_constantBufferData, sizeof(m_constantBufferData)); + } + + // Note: ComPtr's are CPU objects but this resource needs to stay in scope until + // the command list that references it has finished executing on the GPU. + // We will flush the GPU at the end of this method to ensure the resource is not + // prematurely destroyed. + ComPtr textureUploadHeap; + + // Create the texture. + { + // Describe and create a Texture2D. + D3D12_RESOURCE_DESC textureDesc = {}; + textureDesc.MipLevels = 1; + textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + textureDesc.Width = TextureWidth; + textureDesc.Height = TextureHeight; + textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + textureDesc.DepthOrArraySize = 1; + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + + ThrowIfFailed( m_device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), + D3D12_HEAP_FLAG_NONE, + &textureDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(&m_texture))); + + const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_texture.Get(), 0, 1); + + // Create the GPU upload buffer. + ThrowIfFailed( m_device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&textureUploadHeap))); + + // Copy data to the intermediate upload heap and then schedule a copy + // from the upload heap to the Texture2D. + std::vector texture = GenerateTextureData(); + + D3D12_SUBRESOURCE_DATA textureData = {}; + textureData.pData = &texture[0]; + textureData.RowPitch = TextureWidth * TexturePixelSize; + textureData.SlicePitch = textureData.RowPitch * TextureHeight; + + UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureData); + m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); + + // Describe and create a SRV for the texture. + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Format = textureDesc.Format; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = 1; + m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, CD3DX12_CPU_DESCRIPTOR_HANDLE(m_cbvsrvHeap->GetCPUDescriptorHandleForHeapStart(), 1, m_cbvsrvDescriptorSize)); + } + + // Close the command list and execute it to begin the initial GPU setup. + ThrowIfFailed( m_commandList->Close( ) ); + ID3D12CommandList *ppCommandLists[] = { m_commandList.Get( ) }; + m_commandQueue->ExecuteCommandLists( _countof( ppCommandLists ), ppCommandLists ); + + // Create and record the bundle. + { + ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_BUNDLE, m_bundleAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_bundle))); + m_bundle->SetGraphicsRootSignature(m_rootSignature.Get()); + m_bundle->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_bundle->IASetVertexBuffers(0, 2, m_vertexBufferView); + m_bundle->DrawInstanced(4, INSTANCE_COUNT, 0, 0); + ThrowIfFailed(m_bundle->Close()); + } + + // Create synchronization objects and wait until assets have been uploaded to the GPU. + { + ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence))); + m_fenceValue = 1; + + // Create an event handle to use for frame synchronization. + m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (m_fenceEvent == nullptr) + { + ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); + } + + // Wait for the command list to execute; we are reusing the same command + // list in our main loop but for now, we just want to wait for setup to + // complete before continuing. + WaitForPreviousFrame(); + } +} + +// Generate a simple black and white checkerboard texture. +std::vector D3D12HelloScrolling::GenerateTextureData() { + const UINT rowPitch = TextureWidth * TexturePixelSize; + const UINT rowHalfPitch = rowPitch >> 1; + const UINT cellPitch = rowPitch >> 4; // The width of a cell in the checkboard texture. + const UINT cellHeight = TextureHeight >> 3; // The height of a cell in the checkerboard texture. + const UINT textureSize = rowPitch * TextureHeight; + + std::vector data(textureSize); + UINT8 *pData = &data[0]; + + for(UINT n = 0; n < textureSize; n += TexturePixelSize) { + UINT x = n % rowPitch; + UINT y = n / rowPitch; + UINT i = x / cellPitch; + UINT j = y / cellHeight; + + if(i%2 == j%2) { + if(x < rowHalfPitch) { + pData[n] = 0x00; // R + pData[n+1] = 0xff; // G + pData[n+2] = 0x00; // B + pData[n+3] = 0xff; // A + } else { + pData[n] = 0x00; // R + pData[n+1] = 0x00; // G + pData[n+2] = 0x00; // B + pData[n+3] = 0xff; // A + } + } else { + pData[n] = 0xff; // R + pData[n+1] = 0xff; // G + pData[n+2] = 0xff; // B + pData[n+3] = 0xff; // A + } + } + return data; +} + +// Update frame-based values. +void D3D12HelloScrolling::OnUpdate() +{ + const float translationSpeed = 0.005f; + const float offsetBounds = 1.75f; + + m_constantBufferData.offset.x += translationSpeed; + if (m_constantBufferData.offset.x > offsetBounds) + { + m_constantBufferData.offset.x = -0.25f; + m_constantBufferData.flag.x ^= 1U; + } + memcpy(m_pCbvDataBegin, &m_constantBufferData, sizeof(m_constantBufferData)); +} + +// Render the scene. +void D3D12HelloScrolling::OnRender() +{ + // Record all the commands we need to render the scene into the command list. + PopulateCommandList(); + + // Execute the command list. + ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; + m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); + + // Present the frame. + ThrowIfFailed(m_swapChain->Present(1, 0)); + + WaitForPreviousFrame(); +} + +void D3D12HelloScrolling::OnDestroy() +{ + // Ensure that the GPU is no longer referencing resources that are about to be + // cleaned up by the destructor. + WaitForPreviousFrame(); + + CloseHandle(m_fenceEvent); +} + +// Fill the command list with all the render commands and dependent state. +void D3D12HelloScrolling::PopulateCommandList() +{ + // Command list allocators can only be reset when the associated + // command lists have finished execution on the GPU; apps should use + // fences to determine GPU execution progress. + ThrowIfFailed(m_commandAllocator->Reset()); + + // However, when ExecuteCommandList() is called on a particular command + // list, that command list can then be reset at any time and must be before + // re-recording. + ThrowIfFailed(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); + + // Set necessary state. + m_commandList->SetGraphicsRootSignature(m_rootSignature.Get()); + + ID3D12DescriptorHeap* ppHeaps[] = { m_cbvsrvHeap.Get() }; + m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); + + m_commandList->SetGraphicsRootDescriptorTable(0, m_cbvsrvHeap->GetGPUDescriptorHandleForHeapStart()); + m_commandList->SetGraphicsRootDescriptorTable(1, CD3DX12_GPU_DESCRIPTOR_HANDLE(m_cbvsrvHeap->GetGPUDescriptorHandleForHeapStart( ), 1, m_cbvsrvDescriptorSize)); + m_commandList->RSSetViewports(1, &m_viewport); + m_commandList->RSSetScissorRects(1, &m_scissorRect); + + // Indicate that the back buffer will be used as a render target. + m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); + + CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize); + m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); + + // Record commands. + const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; + m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); + + // Execute the commands stored in the bundle. + m_commandList->ExecuteBundle(m_bundle.Get()); + + // Indicate that the back buffer will now be used to present. + m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); + + ThrowIfFailed(m_commandList->Close()); +} + +void D3D12HelloScrolling::WaitForPreviousFrame() +{ + // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE. + // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering + // sample illustrates how to use fences for efficient resource usage and to + // maximize GPU utilization. + + // Signal and increment the fence value. + const UINT64 fence = m_fenceValue; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence)); + m_fenceValue++; + + // Wait until the previous frame is finished. + if (m_fence->GetCompletedValue() < fence) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent)); + WaitForSingleObject(m_fenceEvent, INFINITE); + } + + m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.h b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.h new file mode 100644 index 000000000..2e7d06598 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.h @@ -0,0 +1,97 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "DXSample.h" + +using namespace DirectX; + +// Note that while ComPtr is used to manage the lifetime of resources on the CPU, +// it has no understanding of the lifetime of resources on the GPU. Apps must account +// for the GPU lifetime of resources to avoid destroying objects that may still be +// referenced by the GPU. +// An example of this can be found in the class method: OnDestroy(). +using Microsoft::WRL::ComPtr; + +class D3D12HelloScrolling : public DXSample +{ +public: + D3D12HelloScrolling(UINT width, UINT height, std::wstring name); + + virtual void OnInit(); + virtual void OnUpdate(); + virtual void OnRender(); + virtual void OnDestroy(); + +private: + static const UINT FrameCount = 2; + static const UINT TextureWidth = 512; + static const UINT TextureHeight = 256; + static const UINT TexturePixelSize = 4; // The number of bytes used to represent a pixel in the texture. + + struct Vertex + { + XMFLOAT3 position; + XMFLOAT2 uv; + }; + + struct InstanceData { + XMFLOAT3 offset; + }; + + struct SceneConstantBuffer + { + XMFLOAT4 offset; + XMUINT4 flag; + float padding[56]; // Padding so the constant buffer is 256-byte aligned. + }; + static_assert((sizeof(SceneConstantBuffer) % 256) == 0, "Constant Buffer size must be 256-byte aligned"); + + // Pipeline objects. + CD3DX12_VIEWPORT m_viewport; + CD3DX12_RECT m_scissorRect; + ComPtr m_swapChain; + ComPtr m_device; + ComPtr m_renderTargets[FrameCount]; + ComPtr m_commandAllocator; + ComPtr m_bundleAllocator; + ComPtr m_commandQueue; + ComPtr m_rootSignature; + ComPtr m_rtvHeap; + ComPtr m_cbvsrvHeap; + ComPtr m_pipelineState; + ComPtr m_commandList; + ComPtr m_bundle; + UINT m_rtvDescriptorSize; + UINT m_cbvsrvDescriptorSize; + + // App resources. + ComPtr m_vertexBuffer; + D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView[2]; + ComPtr m_instanceBuffer; + ComPtr m_texture; + ComPtr m_constantBuffer; + SceneConstantBuffer m_constantBufferData; + UINT8* m_pCbvDataBegin; + + // Synchronization objects. + UINT m_frameIndex; + HANDLE m_fenceEvent; + ComPtr m_fence; + UINT64 m_fenceValue; + + void LoadPipeline(); + void LoadAssets(); + std::vector GenerateTextureData( ); + void PopulateCommandList(); + void WaitForPreviousFrame(); +}; diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj new file mode 100644 index 000000000..7f3d44c27 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj @@ -0,0 +1,174 @@ + + + + + + + Debug + x64 + + + Release + x64 + + + + {EA04126E-0EC5-4AD5-A4CC-7A7FF1CCBB16} + Win32Proj + D3D12HelloScrolling + D3D12HelloScrolling + 10.0 + + + false + false + false + + + false + false + false + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + true + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + false + + + Windows + true + d3d12.lib;dxgi.lib;d3dcompiler.lib;dxguid.lib;%(AdditionalDependencies) + d3d12.dll + + + true + + + copy %(Identity) "$(OutDir)" > NUL + $(OutDir)\%(Identity) + true + + + + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + d3d12.lib;dxgi.lib;d3dcompiler.lib;dxguid.lib;%(AdditionalDependencies) + d3d12.dll + + + true + + + copy %(Identity) "$(OutDir)" > NUL + $(OutDir)\%(Identity) + true + + + + + + + + + + + + + + + + + + + Create + Create + + + + + Document + true + dxc.exe -nologo -Tvs_6_0 -E"VSMain" -Zi -Qembed_debug -Fo"$(OutDir)%(Filename)_VSMain.cso" "%(FullPath)" +dxc.exe -nologo -Tps_6_0 -E"PSMain" -Zi -Qembed_debug -Fo"$(OutDir)%(Filename)_PSMain.cso" "%(FullPath)" + dxc.exe -nologo -Tvs_6_0 -E"VSMain" -Zi -Qembed_debug -Fo"$(OutDir)%(Filename)_VSMain.cso" "%(FullPath)" +dxc.exe -nologo -Tps_6_0 -E"PSMain" -Zi -Qembed_debug -Fo"$(OutDir)%(Filename)_PSMain.cso" "%(FullPath)" + $(OutDir)%(Filename)_VSMain.cso;$(OutDir)%(Filename)_PSMain.cso + $(OutDir)%(Filename)_VSMain.cso;$(OutDir)%(Filename)_PSMain.cso + %(Identity) + %(Identity) + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj.filters b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj.filters new file mode 100644 index 000000000..00cb0d294 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/D3D12HelloScrolling.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {67bebf86-9539-4f7f-b9e9-a376519ec1f1} + + + {954a8f61-1c3e-419b-baca-c5e2b83c689a} + + + {09095d68-bcd7-45c4-b007-5c8ddb5d60a4} + + + {8b0cb0c9-e0d8-4107-91c8-11654fac88e7} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Assets\Shaders + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.cpp b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.cpp new file mode 100644 index 000000000..fe324176e --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.cpp @@ -0,0 +1,128 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "DXSample.h" + +using namespace Microsoft::WRL; + +DXSample::DXSample(UINT width, UINT height, std::wstring name) : + m_width(width), + m_height(height), + m_title(name), + m_useWarpDevice(false) +{ + WCHAR assetsPath[512]; + GetAssetsPath(assetsPath, _countof(assetsPath)); + m_assetsPath = assetsPath; + + m_aspectRatio = static_cast(width) / static_cast(height); +} + +DXSample::~DXSample() +{ +} + +// Helper function for resolving the full path of assets. +std::wstring DXSample::GetAssetFullPath(LPCWSTR assetName) +{ + return m_assetsPath + assetName; +} + +// Helper function for acquiring the first available hardware adapter that supports Direct3D 12. +// If no such adapter can be found, *ppAdapter will be set to nullptr. +_Use_decl_annotations_ +void DXSample::GetHardwareAdapter( + IDXGIFactory1* pFactory, + IDXGIAdapter1** ppAdapter, + bool requestHighPerformanceAdapter) +{ + *ppAdapter = nullptr; + + ComPtr adapter; + + ComPtr factory6; + if (SUCCEEDED(pFactory->QueryInterface(IID_PPV_ARGS(&factory6)))) + { + for ( + UINT adapterIndex = 0; + SUCCEEDED(factory6->EnumAdapterByGpuPreference( + adapterIndex, + requestHighPerformanceAdapter == true ? DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE : DXGI_GPU_PREFERENCE_UNSPECIFIED, + IID_PPV_ARGS(&adapter))); + ++adapterIndex) + { + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + // Don't select the Basic Render Driver adapter. + // If you want a software adapter, pass in "/warp" on the command line. + continue; + } + + // Check to see whether the adapter supports Direct3D 12, but don't create the + // actual device yet. + if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) + { + break; + } + } + } + + if(adapter.Get() == nullptr) + { + for (UINT adapterIndex = 0; SUCCEEDED(pFactory->EnumAdapters1(adapterIndex, &adapter)); ++adapterIndex) + { + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + // Don't select the Basic Render Driver adapter. + // If you want a software adapter, pass in "/warp" on the command line. + continue; + } + + // Check to see whether the adapter supports Direct3D 12, but don't create the + // actual device yet. + if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) + { + break; + } + } + } + + *ppAdapter = adapter.Detach(); +} + +// Helper function for setting the window's title text. +void DXSample::SetCustomWindowText(LPCWSTR text) +{ + std::wstring windowText = m_title + L": " + text; + SetWindowText(Win32Application::GetHwnd(), windowText.c_str()); +} + +// Helper function for parsing any supplied command line args. +_Use_decl_annotations_ +void DXSample::ParseCommandLineArgs(WCHAR* argv[], int argc) +{ + for (int i = 1; i < argc; ++i) + { + if (_wcsnicmp(argv[i], L"-warp", wcslen(argv[i])) == 0 || + _wcsnicmp(argv[i], L"/warp", wcslen(argv[i])) == 0) + { + m_useWarpDevice = true; + m_title = m_title + L" (WARP)"; + } + } +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.h b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.h new file mode 100644 index 000000000..9d54a26f1 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSample.h @@ -0,0 +1,63 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "DXSampleHelper.h" +#include "Win32Application.h" + +class DXSample +{ +public: + DXSample(UINT width, UINT height, std::wstring name); + virtual ~DXSample(); + + virtual void OnInit() = 0; + virtual void OnUpdate() = 0; + virtual void OnRender() = 0; + virtual void OnDestroy() = 0; + + // Samples override the event handlers to handle specific messages. + virtual void OnKeyDown(UINT8 /*key*/) {} + virtual void OnKeyUp(UINT8 /*key*/) {} + + // Accessors. + UINT GetWidth() const { return m_width; } + UINT GetHeight() const { return m_height; } + const WCHAR* GetTitle() const { return m_title.c_str(); } + + void ParseCommandLineArgs(_In_reads_(argc) WCHAR* argv[], int argc); + +protected: + std::wstring GetAssetFullPath(LPCWSTR assetName); + + void GetHardwareAdapter( + _In_ IDXGIFactory1* pFactory, + _Outptr_result_maybenull_ IDXGIAdapter1** ppAdapter, + bool requestHighPerformanceAdapter = false); + + void SetCustomWindowText(LPCWSTR text); + + // Viewport dimensions. + UINT m_width; + UINT m_height; + float m_aspectRatio; + + // Adapter info. + bool m_useWarpDevice; + +private: + // Root assets path. + std::wstring m_assetsPath; + + // Window title. + std::wstring m_title; +}; diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSampleHelper.h b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSampleHelper.h new file mode 100644 index 000000000..d2be28eb2 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/DXSampleHelper.h @@ -0,0 +1,253 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include + +// Note that while ComPtr is used to manage the lifetime of resources on the CPU, +// it has no understanding of the lifetime of resources on the GPU. Apps must account +// for the GPU lifetime of resources to avoid destroying objects that may still be +// referenced by the GPU. +using Microsoft::WRL::ComPtr; + +inline std::string HrToString(HRESULT hr) +{ + char s_str[64] = {}; + sprintf_s(s_str, "HRESULT of 0x%08X", static_cast(hr)); + return std::string(s_str); +} + +class HrException : public std::runtime_error +{ +public: + HrException(HRESULT hr) : std::runtime_error(HrToString(hr)), m_hr(hr) {} + HRESULT Error() const { return m_hr; } +private: + const HRESULT m_hr; +}; + +#define SAFE_RELEASE(p) if (p) (p)->Release() + +inline void ThrowIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + throw HrException(hr); + } +} + +inline void GetAssetsPath(_Out_writes_(pathSize) WCHAR* path, UINT pathSize) +{ + if (path == nullptr) + { + throw std::exception(); + } + + DWORD size = GetModuleFileName(nullptr, path, pathSize); + if (size == 0 || size == pathSize) + { + // Method failed or path was truncated. + throw std::exception(); + } + + WCHAR* lastSlash = wcsrchr(path, L'\\'); + if (lastSlash) + { + *(lastSlash + 1) = L'\0'; + } +} + +inline HRESULT ReadDataFromFile(LPCWSTR filename, byte** data, UINT* size) +{ + using namespace Microsoft::WRL; + +#if WINVER >= _WIN32_WINNT_WIN8 + CREATEFILE2_EXTENDED_PARAMETERS extendedParams = {}; + extendedParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + extendedParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extendedParams.dwFileFlags = FILE_FLAG_SEQUENTIAL_SCAN; + extendedParams.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extendedParams.lpSecurityAttributes = nullptr; + extendedParams.hTemplateFile = nullptr; + + Wrappers::FileHandle file(CreateFile2(filename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extendedParams)); +#else + Wrappers::FileHandle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, nullptr)); +#endif + if (file.Get() == INVALID_HANDLE_VALUE) + { + throw std::exception(); + } + + FILE_STANDARD_INFO fileInfo = {}; + if (!GetFileInformationByHandleEx(file.Get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + throw std::exception(); + } + + if (fileInfo.EndOfFile.HighPart != 0) + { + throw std::exception(); + } + + *data = reinterpret_cast(malloc(fileInfo.EndOfFile.LowPart)); + *size = fileInfo.EndOfFile.LowPart; + + if (!ReadFile(file.Get(), *data, fileInfo.EndOfFile.LowPart, nullptr, nullptr)) + { + throw std::exception(); + } + + return S_OK; +} + +inline HRESULT ReadDataFromDDSFile(LPCWSTR filename, byte** data, UINT* offset, UINT* size) +{ + if (FAILED(ReadDataFromFile(filename, data, size))) + { + return E_FAIL; + } + + // DDS files always start with the same magic number. + static const UINT DDS_MAGIC = 0x20534444; + UINT magicNumber = *reinterpret_cast(*data); + if (magicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + struct DDS_PIXELFORMAT + { + UINT size; + UINT flags; + UINT fourCC; + UINT rgbBitCount; + UINT rBitMask; + UINT gBitMask; + UINT bBitMask; + UINT aBitMask; + }; + + struct DDS_HEADER + { + UINT size; + UINT flags; + UINT height; + UINT width; + UINT pitchOrLinearSize; + UINT depth; + UINT mipMapCount; + UINT reserved1[11]; + DDS_PIXELFORMAT ddsPixelFormat; + UINT caps; + UINT caps2; + UINT caps3; + UINT caps4; + UINT reserved2; + }; + + auto ddsHeader = reinterpret_cast(*data + sizeof(UINT)); + if (ddsHeader->size != sizeof(DDS_HEADER) || ddsHeader->ddsPixelFormat.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + const ptrdiff_t ddsDataOffset = sizeof(UINT) + sizeof(DDS_HEADER); + *offset = ddsDataOffset; + *size = *size - ddsDataOffset; + + return S_OK; +} + +// Assign a name to the object to aid with debugging. +#if defined(_DEBUG) || defined(DBG) +inline void SetName(ID3D12Object* pObject, LPCWSTR name) +{ + pObject->SetName(name); +} +inline void SetNameIndexed(ID3D12Object* pObject, LPCWSTR name, UINT index) +{ + WCHAR fullName[50]; + if (swprintf_s(fullName, L"%s[%u]", name, index) > 0) + { + pObject->SetName(fullName); + } +} +#else +inline void SetName(ID3D12Object*, LPCWSTR) +{ +} +inline void SetNameIndexed(ID3D12Object*, LPCWSTR, UINT) +{ +} +#endif + +// Naming helper for ComPtr. +// Assigns the name of the variable as the name of the object. +// The indexed variant will include the index in the name of the object. +#define NAME_D3D12_OBJECT(x) SetName((x).Get(), L#x) +#define NAME_D3D12_OBJECT_INDEXED(x, n) SetNameIndexed((x)[n].Get(), L#x, n) + +inline UINT CalculateConstantBufferByteSize(UINT byteSize) +{ + // Constant buffer size is required to be aligned. + return (byteSize + (D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1)) & ~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1); +} + +#ifdef D3D_COMPILE_STANDARD_FILE_INCLUDE +inline Microsoft::WRL::ComPtr CompileShader( + const std::wstring& filename, + const D3D_SHADER_MACRO* defines, + const std::string& entrypoint, + const std::string& target) +{ + UINT compileFlags = 0; +#if defined(_DEBUG) || defined(DBG) + compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + HRESULT hr; + + Microsoft::WRL::ComPtr byteCode = nullptr; + Microsoft::WRL::ComPtr errors; + hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, + entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors); + + if (errors != nullptr) + { + OutputDebugStringA((char*)errors->GetBufferPointer()); + } + ThrowIfFailed(hr); + + return byteCode; +} +#endif + +// Resets all elements in a ComPtr array. +template +void ResetComPtrArray(T* comPtrArray) +{ + for (auto &i : *comPtrArray) + { + i.Reset(); + } +} + + +// Resets all elements in a unique_ptr array. +template +void ResetUniquePtrArray(T* uniquePtrArray) +{ + for (auto &i : *uniquePtrArray) + { + i.reset(); + } +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Main.cpp b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Main.cpp new file mode 100644 index 000000000..405c2e7f8 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Main.cpp @@ -0,0 +1,20 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "D3D12HelloScrolling.h" + +_Use_decl_annotations_ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) +{ + D3D12HelloScrolling sample(1280, 720, L"D3D12 Hello Scrolling"); + return Win32Application::Run(&sample, hInstance, nCmdShow); +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.cpp b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.cpp new file mode 100644 index 000000000..50ddf9127 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.cpp @@ -0,0 +1,119 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "Win32Application.h" + +HWND Win32Application::m_hwnd = nullptr; + +int Win32Application::Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow) +{ + // Parse the command line parameters + int argc; + LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); + pSample->ParseCommandLineArgs(argv, argc); + LocalFree(argv); + + // Initialize the window class. + WNDCLASSEX windowClass = { 0 }; + windowClass.cbSize = sizeof(WNDCLASSEX); + windowClass.style = CS_HREDRAW | CS_VREDRAW; + windowClass.lpfnWndProc = WindowProc; + windowClass.hInstance = hInstance; + windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); + windowClass.lpszClassName = L"DXSampleClass"; + RegisterClassEx(&windowClass); + + RECT windowRect = { 0, 0, static_cast(pSample->GetWidth()), static_cast(pSample->GetHeight()) }; + AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE); + + // Create the window and store a handle to it. + m_hwnd = CreateWindow( + windowClass.lpszClassName, + pSample->GetTitle(), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + nullptr, // We have no parent window. + nullptr, // We aren't using menus. + hInstance, + pSample); + + // Initialize the sample. OnInit is defined in each child-implementation of DXSample. + pSample->OnInit(); + + ShowWindow(m_hwnd, nCmdShow); + + // Main sample loop. + MSG msg = {}; + while (msg.message != WM_QUIT) + { + // Process any messages in the queue. + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + pSample->OnDestroy(); + + // Return this part of the WM_QUIT message to Windows. + return static_cast(msg.wParam); +} + +// Main message handler for the sample. +LRESULT CALLBACK Win32Application::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + DXSample* pSample = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + + switch (message) + { + case WM_CREATE: + { + // Save the DXSample* passed in to CreateWindow. + LPCREATESTRUCT pCreateStruct = reinterpret_cast(lParam); + SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); + } + return 0; + + case WM_KEYDOWN: + if (pSample) + { + pSample->OnKeyDown(static_cast(wParam)); + } + return 0; + + case WM_KEYUP: + if (pSample) + { + pSample->OnKeyUp(static_cast(wParam)); + } + return 0; + + case WM_PAINT: + if (pSample) + { + pSample->OnUpdate(); + pSample->OnRender(); + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + // Handle any messages the switch statement didn't. + return DefWindowProc(hWnd, message, wParam, lParam); +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.h b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.h new file mode 100644 index 000000000..812a8f866 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/Win32Application.h @@ -0,0 +1,29 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "DXSample.h" + +class DXSample; + +class Win32Application +{ +public: + static int Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow); + static HWND GetHwnd() { return m_hwnd; } + +protected: + static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +private: + static HWND m_hwnd; +}; diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/packages.config b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/packages.config new file mode 100644 index 000000000..a8b737451 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/shaders.hlsl b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/shaders.hlsl new file mode 100644 index 000000000..b082fe821 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/shaders.hlsl @@ -0,0 +1,45 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +cbuffer SceneConstantBuffer : register(b0) +{ + float4 offset; + uint4 flag; + float4 padding[14]; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float2 uv : TEXCOORD; +}; + +Texture2D g_texture : register(t0); +SamplerState g_sampler : register(s0); + +PSInput VSMain(float3 position : POSITION, float2 uv : TEXCOORD, float3 off : INSTANCE_OFFSET) +{ + PSInput result; + + float3 p = position + off; + result.position = float4(p, 1) + offset; + result.uv = uv; + if ((flag.x && off.x >= 0.f) + || (!flag.x && off.x < 0.f)) + result.uv.x -= 0.5f; + + return result; +} + +float4 PSMain(PSInput input) : SV_TARGET +{ + return g_texture.Sample(g_sampler, input.uv); +} diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.cpp b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.cpp new file mode 100644 index 000000000..a6533dc11 --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.cpp @@ -0,0 +1,12 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" diff --git a/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.h b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.h new file mode 100644 index 000000000..dad80255c --- /dev/null +++ b/Samples/Desktop/D3D12HelloWorld/src/HelloScrolling/stdafx.h @@ -0,0 +1,32 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently. + +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers. +#endif + +#include + +#include +#include +#include +#include +#include "include/d3dx12/d3dx12.h" + +#include +#include +#include