#include "Win32DxWindowInterface.h" #include "Win32DxInterface.h" #include "Win32Window.h" #include "Window.h" #include "Widget.h" #include #include #include #include #include #include #include #include #include Win32DxWindowInterface::Win32DxWindowInterface(Win32DxInterface* dxInterface) : mDxInterface(dxInterface) { } Win32DxWindowInterface::~Win32DxWindowInterface() { } void Win32DxWindowInterface::destroyWindow() { waitForPreviousFrame(); ::CloseHandle(mFenceEvent); } void Win32DxWindowInterface::onRender() { // Record all the commands we need to render the scene into the command list. populateCommandList(); // Execute the command list. ID3D12CommandList* ppCommandLists[] = { mCommandList.Get() }; mDxInterface->getCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); renderD2d(); // Present the frame. mSwapChain->Present(1, 0); waitForPreviousFrame(); } void Win32DxWindowInterface::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. mCommandAllocator->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. mCommandList->Reset(mCommandAllocator.Get(), mPipelineState.Get()); // Set necessary state. mCommandList->SetGraphicsRootSignature(mRootSignature.Get()); mCommandList->RSSetViewports(1, &mViewport); mCommandList->RSSetScissorRects(1, &mScissorRect); // Indicate that the back buffer will be used as a render target. auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(mRenderTargets[mFrameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); mCommandList->ResourceBarrier(1, &barrier); CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(mFrameIndex), mRtvDescriptorSize); mCommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); // Record commands. const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; mCommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); mCommandList->IASetVertexBuffers(0, 1, &mVertexBufferView); mCommandList->DrawInstanced(3, 1, 0, 0); // Indicate that the back buffer will now be used to present. //barrier = CD3DX12_RESOURCE_BARRIER::Transition(mRenderTargets[mFrameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); //mCommandList->ResourceBarrier(1, &barrier); mCommandList->Close(); } bool Win32DxWindowInterface::initialize(mt::Window* window) { if (mInitialized) { return true; } const auto width = window->getWidth(); const auto height = window->getHeight(); mViewport = CD3DX12_VIEWPORT(0.0f, 0.0f, width, height); mScissorRect = CD3DX12_RECT(0, 0, width, height); loadPipeline(window); loadAssets(); return true; } void Win32DxWindowInterface::loadPipeline(mt::Window* window) { DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.BufferCount = FrameCount; const auto width = window->getWidth(); const auto height = window->getHeight(); swapChainDesc.Width = width; swapChainDesc.Height = 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; const auto hwnd = dynamic_cast(window->getPlatformWindow())->getHandle(); Microsoft::WRL::ComPtr swapChain; mDxInterface->getFactory()->CreateSwapChainForHwnd( mDxInterface->getCommandQueue(), hwnd, &swapChainDesc, nullptr, nullptr, &swapChain ); mDxInterface->getFactory()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); swapChain.As(&mSwapChain); auto mFrameIndex = mSwapChain->GetCurrentBackBufferIndex(); // Query the desktop's dpi settings, which will be used to create // D2D's render targets. float dpiX{ 0.0 }; float dpiY{ 0.0 }; mDxInterface->getD2dFactory()->GetDesktopDpi(&dpiX, &dpiY); D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY ); // 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; mDxInterface->getDevice()->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&mRtvHeap)); mRtvDescriptorSize = mDxInterface->getDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); } // Create frame resources. { CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart()); // Create a RTV for each frame. for (UINT n = 0; n < FrameCount; n++) { mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])); mDxInterface->getDevice()->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, rtvHandle); D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; mDxInterface->get11On12Device()->CreateWrappedResource( mRenderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&mWrappedBackBuffers[n]) ); // Create a render target for D2D to draw directly to this back buffer. Microsoft::WRL::ComPtr surface; mWrappedBackBuffers[n].As(&surface); mDxInterface->getD2dContext()->CreateBitmapFromDxgiSurface( surface.Get(), &bitmapProperties, &mD2dRenderTargets[n] ); rtvHandle.Offset(1, mRtvDescriptorSize); } } mDxInterface->getDevice()->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)); } void Win32DxWindowInterface::loadAssets() { // Create an empty root signature. { CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); Microsoft::WRL::ComPtr signature; Microsoft::WRL::ComPtr error; D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); mDxInterface->getDevice()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)); } // Create the pipeline state, which includes compiling and loading shaders. { Microsoft::WRL::ComPtr vertexShader; Microsoft::WRL::ComPtr pixelShader; #if defined(_DEBUG) // Enable better shader debugging with the graphics debugging tools. // UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; UINT compileFlags = 0; #else UINT compileFlags = 0; #endif auto shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders.hlsl"; D3DCompileFromFile(shader_path.c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr); D3DCompileFromFile(shader_path.c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr); // 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 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } }; // Describe and create the graphics pipeline state object (PSO). D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; psoDesc.pRootSignature = mRootSignature.Get(); psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); 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; mDxInterface->getDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPipelineState)); } // Create the command list. mDxInterface->getDevice()->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), mPipelineState.Get(), IID_PPV_ARGS(&mCommandList)); // Create D2D/DWrite objects for rendering text. { mDxInterface->getD2dContext()->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &mTextBrush); mDxInterface->getDirectWriteFactory()->CreateTextFormat( L"Verdana", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 50, L"en-us", &mTextFormat ); mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); mTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); } // Command lists are created in the recording state, but there is nothing // to record yet. The main loop expects it to be closed, so close it now. mCommandList->Close(); // Create the vertex buffer. { // Define the geometry for a triangle. const float aspectRatio = 1.0; Vertex triangleVertices[] = { { { 0.0f, 0.25f * aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, { { 0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, { { -0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } }; const UINT vertexBufferSize = sizeof(triangleVertices); // 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. CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD); auto desc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize); mDxInterface->getDevice()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&mVertexBuffer)); // 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. mVertexBuffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin)); memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices)); mVertexBuffer->Unmap(0, nullptr); // Initialize the vertex buffer view. mVertexBufferView.BufferLocation = mVertexBuffer->GetGPUVirtualAddress(); mVertexBufferView.StrideInBytes = sizeof(Vertex); mVertexBufferView.SizeInBytes = vertexBufferSize; } // Create synchronization objects and wait until assets have been uploaded to the GPU. { mDxInterface->getDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)); mFenceValue = 1; // Create an event handle to use for frame synchronization. mFenceEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); if (mFenceEvent == 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(); } } void Win32DxWindowInterface::renderD2d() { D2D1_SIZE_F rtSize = mD2dRenderTargets[mFrameIndex]->GetSize(); D2D1_RECT_F textRect = D2D1::RectF(0, 0, rtSize.width, rtSize.height); static const WCHAR text[] = L"11On12"; // Acquire our wrapped render target resource for the current back buffer. mDxInterface->get11On12Device()->AcquireWrappedResources(mWrappedBackBuffers[mFrameIndex].GetAddressOf(), 1); // Render text directly to the back buffer. mDxInterface->getD2dContext()->SetTarget(mD2dRenderTargets[mFrameIndex].Get()); mDxInterface->getD2dContext()->BeginDraw(); mDxInterface->getD2dContext()->SetTransform(D2D1::Matrix3x2F::Identity()); mDxInterface->getD2dContext()->DrawText( text, _countof(text) - 1, mTextFormat.Get(), &textRect, mTextBrush.Get() ); mDxInterface->getD2dContext()->EndDraw(); // Release our wrapped render target resource. Releasing // transitions the back buffer resource to the state specified // as the OutState when the wrapped resource was created. mDxInterface->get11On12Device()->ReleaseWrappedResources(mWrappedBackBuffers[mFrameIndex].GetAddressOf(), 1); // Flush to submit the 11 command list to the shared command queue. mDxInterface->getD3d11DeviceContext()->Flush(); } void Win32DxWindowInterface::waitForPreviousFrame() { // Signal and increment the fence value. const UINT64 fence = mFenceValue; mDxInterface->getCommandQueue()->Signal(mFence.Get(), fence); mFenceValue++; // Wait until the previous frame is finished. if (mFence->GetCompletedValue() < fence) { mFence->SetEventOnCompletion(fence, mFenceEvent); ::WaitForSingleObject(mFenceEvent, INFINITE); } mFrameIndex = mSwapChain->GetCurrentBackBufferIndex(); }