| /* |
| * Copyright 2020 Google LLC |
| * |
| */ |
| |
| /* |
| * Copyright (c) 2020, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 2 Clause License and |
| * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| * was not distributed with this source code in the LICENSE file, you can |
| * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| * Media Patent License 1.0 was not distributed with this source code in the |
| * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| */ |
| |
| #include "pch.h" |
| #include "DXEngine.h" |
| #include "SamplePixelShader.h" |
| #include "SampleVertexShader.h" |
| #include "ps_cbuf.h" |
| |
| using namespace Windows::Graphics::Display; |
| |
| DXEngine::DXEngine(Windows::UI::Core::CoreWindow ^ window) : m_frameIndex(0), m_rtvDescriptorSize(0) { |
| DisplayInformation ^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); |
| m_window = window; |
| m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height); |
| m_dpi = currentDisplayInformation->LogicalDpi; |
| UpdateRenderTargetSize(); |
| m_viewport = CD3DX12_VIEWPORT(0.0f, 0.0f, static_cast<float>(m_width), static_cast<float>(m_height)); |
| m_scissorRect = CD3DX12_RECT(0, 0, static_cast<LONG>(m_width), static_cast<LONG>(m_height)), strides_[0] = m_width; |
| strides_[1] = strides_[2] = strides_[0] / 2; |
| upload_buffer_size_[0] = upload_buffer_size_[1] = upload_buffer_size_[2] = 0; |
| } |
| |
| // Converts a length in device-independent pixels (DIPs) to a length in physical pixels. |
| static inline float ConvertDipsToPixels(float dips, float dpi) { |
| static const float dipsPerInch = 96.0f; |
| return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer. |
| } |
| |
| void DXEngine::UpdateRenderTargetSize() { |
| m_effectiveDpi = m_dpi; |
| |
| // Calculate the necessary render target size in pixels. |
| m_width = max(lround(ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi)), 1); |
| m_height = max(lround(ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi)), 1); |
| } |
| |
| int DXEngine::OnInit() { |
| if (LoadPipeline() || LoadAssets()) return -1; |
| ready_ = 1; |
| return 0; |
| } |
| |
| // Load the rendering pipeline dependencies. |
| int DXEngine::LoadPipeline() { |
| UINT dxgiFactoryFlags = 0; |
| D2D1_FACTORY_OPTIONS factory_options = {D2D1_DEBUG_LEVEL_NONE}; |
| #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<ID3D12Debug> debugController; |
| if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { |
| debugController->EnableDebugLayer(); |
| |
| // Enable additional debug layers. |
| dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; |
| factory_options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; |
| } |
| } |
| #endif |
| |
| ComPtr<IDXGIFactory4> factory; |
| CHECK_HRESULT(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); |
| |
| if (m_useWarpDevice) { |
| ComPtr<IDXGIAdapter> warpAdapter; |
| CHECK_HRESULT(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter))); |
| |
| CHECK_HRESULT(D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device))); |
| } else { |
| ComPtr<IDXGIAdapter1> hardwareAdapter; |
| GetHardwareAdapter(factory.Get(), &hardwareAdapter); |
| |
| CHECK_HRESULT(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; |
| |
| CHECK_HRESULT(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue))); |
| |
| ComPtr<ID3D11Device> d3d11Device; |
| CHECK_HRESULT(D3D11On12CreateDevice(m_device.Get(), D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, |
| reinterpret_cast<IUnknown**>(m_commandQueue.GetAddressOf()), 1, 0, &d3d11Device, |
| &d11on12device_context_, nullptr)); |
| |
| // Query the 11On12 device from the 11 device. |
| CHECK_HRESULT(d3d11Device.As(&d11on12device_)); |
| |
| // 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; |
| swapChainDesc.Scaling = DXGI_SCALING_NONE; |
| |
| ComPtr<IDXGISwapChain1> swapChain; |
| CHECK_HRESULT(factory->CreateSwapChainForCoreWindow( |
| m_commandQueue.Get(), // Swap chain needs the queue so that it can force a flush on it. |
| reinterpret_cast<IUnknown*>(m_window.Get()), &swapChainDesc, nullptr, &swapChain)); |
| |
| D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE; |
| CHECK_HRESULT( |
| D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &factory_options, &d2d1factory_)); |
| ComPtr<IDXGIDevice> dxgiDevice; |
| CHECK_HRESULT(d11on12device_.As(&dxgiDevice)); |
| CHECK_HRESULT(d2d1factory_->CreateDevice(dxgiDevice.Get(), &d2d1device2_)); |
| CHECK_HRESULT(d2d1device2_->CreateDeviceContext(deviceOptions, &d2d1deviceContext_)); |
| CHECK_HRESULT(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &d2writeFactory_)); |
| |
| float dpiX; |
| float dpiY; |
| d2d1factory_->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); |
| |
| CHECK_HRESULT(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; |
| CHECK_HRESULT(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap))); |
| |
| // Describe and create a shader resource view (SRV) heap for the texture. |
| D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {}; |
| srvHeapDesc.NumDescriptors = 4; |
| srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; |
| srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; |
| CHECK_HRESULT(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap))); |
| |
| m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); |
| m_srvDescriptorSize = 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++) { |
| CHECK_HRESULT(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))); |
| m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle); |
| // Create a wrapped 11On12 resource of this back buffer. Since we are |
| // rendering all D3D12 content first and then all D2D content, we specify |
| // the In resource state as RENDER_TARGET - because D3D12 will have last |
| // used it in this state - and the Out resource state as PRESENT. When |
| // ReleaseWrappedResources() is called on the 11On12 device, the resource |
| // will be transitioned to the PRESENT state. |
| D3D11_RESOURCE_FLAGS d3d11Flags = {D3D11_BIND_RENDER_TARGET}; |
| CHECK_HRESULT(d11on12device_->CreateWrappedResource( |
| m_renderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, |
| IID_PPV_ARGS(&wrappedBackBuffers_[n]))); |
| |
| // Create a render target for D2D to draw directly to this back buffer. |
| ComPtr<IDXGISurface> surface; |
| CHECK_HRESULT(wrappedBackBuffers_[n].As(&surface)); |
| CHECK_HRESULT( |
| d2d1deviceContext_->CreateBitmapFromDxgiSurface(surface.Get(), &bitmapProperties, &d2d1renderTargets_[n])); |
| |
| rtvHandle.Offset(1, m_rtvDescriptorSize); |
| } |
| } |
| |
| CHECK_HRESULT(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator))); |
| |
| CHECK_HRESULT(d2d1deviceContext_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::LightGreen), &textBrush_)); |
| CHECK_HRESULT(d2writeFactory_->CreateTextFormat(L"Verdana", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 24, L"en-us", &textFormat_)); |
| CHECK_HRESULT(textFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); |
| CHECK_HRESULT(textFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR)); |
| |
| return S_OK; |
| } |
| |
| #define TEXT_TOP 20 |
| #define TEXT_LEFT 20 |
| void DXEngine::RenderUI(std::shared_ptr<OneRenderTask> task) { |
| D2D1_SIZE_F rtSize = d2d1renderTargets_[m_frameIndex]->GetSize(); |
| D2D1_RECT_F textRect = D2D1::RectF(TEXT_LEFT, TEXT_TOP, rtSize.width - TEXT_LEFT, 100); |
| // static const WCHAR text[] = L"11On12"; |
| |
| // Acquire our wrapped render target resource for the current back buffer. |
| d11on12device_->AcquireWrappedResources(wrappedBackBuffers_[m_frameIndex].GetAddressOf(), 1); |
| |
| // Render text directly to the back buffer. |
| d2d1deviceContext_->SetTarget(d2d1renderTargets_[m_frameIndex].Get()); |
| d2d1deviceContext_->BeginDraw(); |
| d2d1deviceContext_->SetTransform(D2D1::Matrix3x2F::Identity()); |
| d2d1deviceContext_->DrawText(task->screen_msg.c_str(), (UINT32)task->screen_msg.length(), textFormat_.Get(), |
| &textRect, textBrush_.Get()); |
| CHECK_HRESULT_0(d2d1deviceContext_->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. |
| d11on12device_->ReleaseWrappedResources(wrappedBackBuffers_[m_frameIndex].GetAddressOf(), 1); |
| |
| // Flush to submit the 11 command list to the shared command queue. |
| d11on12device_context_->Flush(); |
| } |
| |
| // Load the sample assets. |
| int DXEngine::LoadAssets() { |
| // Create the root signature. |
| { |
| HRESULT hr; |
| 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_NONE /*D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC*/); |
| ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3, 0, 0, |
| D3D12_DESCRIPTOR_RANGE_FLAG_NONE /*D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC*/); |
| |
| CD3DX12_ROOT_PARAMETER1 rootParameters[1]; |
| rootParameters[0].InitAsDescriptorTable(2, &ranges[0], 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_PIXEL; |
| D3D12_STATIC_SAMPLER_DESC sampler1 = sampler; |
| sampler1.ShaderRegister = 1; |
| |
| D3D12_STATIC_SAMPLER_DESC samplers[2] = {sampler, sampler1}; |
| |
| CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; |
| rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 2, samplers, |
| D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); |
| |
| ComPtr<ID3DBlob> signature; |
| ComPtr<ID3DBlob> error; |
| hr = D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error); |
| if (FAILED(hr)) { |
| void* ss = error->GetBufferPointer(); |
| wprintf(L"%s\n", (wchar_t*)ss); |
| } |
| CHECK_HRESULT(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), |
| IID_PPV_ARGS(&m_rootSignature))); |
| |
| // create CbPixShData buffer |
| const D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_UPLOAD; |
| const D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_GENERIC_READ; |
| D3D12_HEAP_PROPERTIES heapProp; |
| heapProp.Type = heapType; |
| heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; // |
| heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; |
| heapProp.CreationNodeMask = 0; |
| heapProp.VisibleNodeMask = 0; |
| |
| const D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE; |
| hr = m_device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(256, flags), |
| state, NULL, IID_PPV_ARGS(&hdr_buffer_)); |
| CHECK_HRESULT(hr); |
| void* ptr = 0; |
| hr = hdr_buffer_->Map(0, NULL, &ptr); |
| if (FAILED(hr)) return 0; |
| CbPixShData buffer = {}; |
| buffer.scale_factor = 1.0; |
| buffer.hdr_10_10_10_2 = 0; |
| memcpy(ptr, &buffer, sizeof(CbPixShData)); |
| hdr_buffer_->Unmap(0, 0); |
| } |
| |
| // Create the pipeline state, which includes compiling and loading shaders. |
| { |
| // 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}}; |
| |
| // 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(SampleVertexShader, sizeof(SampleVertexShader)); |
| psoDesc.PS = CD3DX12_SHADER_BYTECODE(SamplePixelShader, sizeof(SamplePixelShader)); |
| 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; |
| HRESULT hr = m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)); |
| CHECK_HRESULT(hr); |
| } |
| |
| // Create the command list. |
| CHECK_HRESULT(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), |
| m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList))); |
| SET_D3D12_NAME(m_commandList); |
| setVertexes(); |
| |
| // Close the command list and execute it to begin the initial GPU setup. |
| CHECK_HRESULT(m_commandList->Close()); |
| ID3D12CommandList* ppCommandLists[] = {m_commandList.Get()}; |
| m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); |
| |
| // Create synchronization objects and wait until assets have been uploaded to the GPU. |
| { |
| CHECK_HRESULT(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) { |
| CHECK_HRESULT(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(); |
| } |
| return S_OK; |
| } |
| |
| void DXEngine::setVertexes() { |
| fRect startRect = {-1.0f, 1.0f, 1.0f, -1.0f}; |
| float texture_right = 1.0; |
| Vertex triangleVertices[] = {{{startRect.left, startRect.top, 0.0f}, {0.0f, 0.0f}}, |
| {{startRect.right, startRect.top, 0.0f}, {texture_right, 0.0f}}, |
| {{startRect.left, startRect.bottom, 0.0f}, {0.0f, 1.0f}}, |
| {{startRect.right, startRect.bottom, 0.0f}, {texture_right, 1.0f}}}; |
| const UINT vertexesNumber = sizeof(triangleVertices) / sizeof(Vertex); |
| setVertexes(triangleVertices, vertexesNumber); |
| } |
| |
| void DXEngine::OnResize() { |
| std::unique_lock<std::mutex> lck(cs_render_); |
| if (hasTexture_) { |
| CHECK_HRESULT_0(m_commandAllocator->Reset()); |
| CHECK_HRESULT_0(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); |
| setVertexes(); |
| InternalRender(FALSE, 0); |
| } |
| } |
| |
| // Update frame-based values. |
| void DXEngine::OnUpdate() {} |
| |
| // Render the scene. |
| void DXEngine::OnRender(std::shared_ptr<OneRenderTask> task) { |
| if (ready_ && task && !task->shown) { |
| InternalRender(TRUE, task); |
| } |
| } |
| |
| void DXEngine::InternalRender(BOOL reset, std::shared_ptr<OneRenderTask> task) { |
| // Record all the commands we need to render the scene into the command list. |
| PopulateCommandList(reset, task); |
| |
| // Execute the command list. |
| ID3D12CommandList* ppCommandLists[] = {m_commandList.Get()}; |
| m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); |
| |
| RenderUI(task); |
| |
| // Present the frame. |
| CHECK_HRESULT_0(m_swapChain->Present(1, 0)); |
| |
| WaitForPreviousFrame(); |
| } |
| |
| void DXEngine::OnDestroy() { |
| // Ensure that the GPU is no longer referencing resources that are about to be |
| // cleaned up by the destructor. |
| WaitForPreviousFrame(); |
| ready_ = 0; |
| CloseHandle(m_fenceEvent); |
| } |
| |
| void DXEngine::PopulateCommandList(BOOL reset, std::shared_ptr<OneRenderTask> task) { |
| // 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. |
| if (reset) { |
| CHECK_HRESULT_0(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. |
| CHECK_HRESULT_0(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); |
| } |
| |
| CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, |
| m_rtvDescriptorSize); |
| m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); |
| m_commandList->ResourceBarrier( |
| 1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, |
| D3D12_RESOURCE_STATE_RENDER_TARGET)); |
| const float clearColor[] = {0.0f, 0.2f, 0.4f, 1.0f}; |
| m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); |
| |
| if (task) { |
| if (task->type != renderType_) { |
| void* ptr = 0; |
| HRESULT hr = hdr_buffer_->Map(0, NULL, &ptr); |
| CHECK_HRESULT_0(hr); |
| renderType_ = task->type; |
| CbPixShData* cbptr = (CbPixShData*)ptr; |
| cbptr->scale_factor = renderType_ == ft10bit ? 64.0f : 1.0f; |
| cbptr->hdr_10_10_10_2 = renderType_ == ft10x3 ? 1 : 0; |
| if (cbptr->hdr_10_10_10_2) { |
| D3D12_RESOURCE_DESC desc = task->textures[0]->GetDesc(); |
| cbptr->disp_w = lround(m_viewport.Width); |
| cbptr->disp_h = lround(m_viewport.Height); |
| cbptr->frame_w = task->render_width; |
| cbptr->frame_h = task->render_height; |
| cbptr->ptex_w = (int)desc.Width; |
| cbptr->ptex_h = desc.Height; |
| } |
| hdr_buffer_->Unmap(0, 0); |
| } |
| const D3D12_RESOURCE_BARRIER barriers[3] = { |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[0].Get(), D3D12_RESOURCE_STATE_COPY_DEST, |
| D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE), |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[1].Get(), D3D12_RESOURCE_STATE_COPY_DEST, |
| D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE), |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[2].Get(), D3D12_RESOURCE_STATE_COPY_DEST, |
| D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)}; |
| m_commandList->ResourceBarrier(3, barriers); |
| CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(m_srvHeap->GetCPUDescriptorHandleForHeapStart()); |
| |
| m_commandList->SetGraphicsRootSignature(m_rootSignature.Get()); |
| |
| D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc{}; |
| D3D12_RESOURCE_DESC cbdesk = hdr_buffer_->GetDesc(); |
| cbvDesc.BufferLocation = hdr_buffer_->GetGPUVirtualAddress(); |
| cbvDesc.SizeInBytes = (UINT)cbdesk.Width; |
| m_device->CreateConstantBufferView(&cbvDesc, srvHandle); |
| srvHandle.Offset(1, m_srvDescriptorSize); |
| |
| for (int i = 0; i < 3; i++) { |
| D3D12_RESOURCE_DESC textureDesc = task->textures[i]->GetDesc(); |
| 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(task->textures[i].Get(), &srvDesc, srvHandle); |
| srvHandle.Offset(1, m_srvDescriptorSize); |
| } |
| // Set necessary state. |
| |
| ID3D12DescriptorHeap* ppHeaps[] = {m_srvHeap.Get()}; |
| m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); |
| |
| m_commandList->SetGraphicsRootDescriptorTable(0, m_srvHeap->GetGPUDescriptorHandleForHeapStart()); |
| m_commandList->RSSetViewports(1, &m_viewport); |
| m_commandList->RSSetScissorRects(1, &m_scissorRect); |
| |
| // Record commands. |
| m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP /*D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST*/); |
| m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView); |
| m_commandList->DrawInstanced(4, 1, 0, 0); |
| { |
| const D3D12_RESOURCE_BARRIER barriers[3] = { |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[0].Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, |
| D3D12_RESOURCE_STATE_COPY_DEST), |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[1].Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, |
| D3D12_RESOURCE_STATE_COPY_DEST), |
| CD3DX12_RESOURCE_BARRIER::Transition(task->textures[2].Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, |
| D3D12_RESOURCE_STATE_COPY_DEST)}; |
| m_commandList->ResourceBarrier(3, barriers); |
| } |
| } |
| |
| CHECK_HRESULT_0(m_commandList->Close()); |
| } |
| |
| void DXEngine::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; |
| CHECK_HRESULT_0(m_commandQueue->Signal(m_fence.Get(), fence)); |
| m_fenceValue++; |
| |
| // Wait until the previous frame is finished. |
| if (m_fence->GetCompletedValue() < fence) { |
| CHECK_HRESULT_0(m_fence->SetEventOnCompletion(fence, m_fenceEvent)); |
| WaitForSingleObject(m_fenceEvent, INFINITE); |
| } |
| |
| m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); |
| } |
| |
| // 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 DXEngine::GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter) { |
| ComPtr<IDXGIAdapter1> adapter; |
| *ppAdapter = nullptr; |
| |
| for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != 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 if 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(); |
| } |
| |
| void DXEngine::setVertexes(Vertex* vertexes, UINT size) { |
| UINT vertexBufferSize = sizeof(Vertex) * size; |
| CHECK_HRESULT_0(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(&vertexUploadBuffer_))); |
| SET_D3D12_NAME(vertexUploadBuffer_); |
| |
| // 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. |
| CHECK_HRESULT_0(vertexUploadBuffer_->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin))); |
| memcpy(pVertexDataBegin, vertexes, vertexBufferSize); |
| vertexUploadBuffer_->Unmap(0, nullptr); |
| |
| CHECK_HRESULT_0( |
| m_device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, |
| &CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize), |
| D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_vertexBuffer))); |
| SET_D3D12_NAME(m_vertexBuffer); |
| if (m_commandList) { |
| m_commandList->CopyBufferRegion(m_vertexBuffer.Get(), 0, vertexUploadBuffer_.Get(), 0, vertexBufferSize); |
| const D3D12_RESOURCE_BARRIER barriers[1] = {CD3DX12_RESOURCE_BARRIER::Transition( |
| m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, |
| D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER /*D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE*/)}; |
| m_commandList->ResourceBarrier(1, barriers); |
| } |
| |
| // Initialize the vertex buffer view. |
| m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); |
| m_vertexBufferView.StrideInBytes = sizeof(Vertex); |
| m_vertexBufferView.SizeInBytes = vertexBufferSize; |
| } |