// Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. // // NVIDIA CORPORATION and its licensors retain all intellectual property // and proprietary rights in and to this software, related documentation // and any modifications thereto. Any use, reproduction, disclosure or // distribution of this software and related documentation without an express // license agreement from NVIDIA CORPORATION is strictly prohibited. #include "D3D12RenderVs.h" #include #include #include "vertstripe_ps.h" #include "vertstripe_vs.h" #define THROW_IF_FAILED(a) ThrowIfFailedVs(a, __FUNCTION__, __LINE__) D3D12RenderVs::D3D12RenderVs(IDXGIAdapter* pAdapter) { // Create D3D device THROW_IF_FAILED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&m_device))); } void D3D12RenderVs::Init(UINT numSharedHandle, HANDLE* pSharedHandles, DXGI_FORMAT dxgiFormat) { OpenRenderTargets(numSharedHandle, pSharedHandles, dxgiFormat); LoadPipeline(); } void D3D12RenderVs::SetClearColor(float red, float green, float blue) { m_clearcolor[0] = red; m_clearcolor[1] = green; m_clearcolor[2] = blue; m_clearcolor[3] = 1.0f; // alpha use_static_color = true; } void D3D12RenderVs::Render() { // Reset command list memory to initial state THROW_IF_FAILED(m_commandAllocator->Reset()); // Reset command list to recording state THROW_IF_FAILED(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); // Set render target auto rtvHandle = m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); rtvHandle.ptr += m_frameIndex * m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); m_commandList->OMSetRenderTargets(1, &rtvHandle, TRUE, nullptr); // Set root signature, view, and rect m_commandList->SetGraphicsRootSignature(m_rootSignature.Get()); m_commandList->RSSetViewports(1, &m_viewport); m_commandList->RSSetScissorRects(1, &m_scissorRect); // Transition to render { D3D12_RESOURCE_BARRIER resourceBarrier{ }; resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resourceBarrier.Transition.pResource = m_renderTargets[m_frameIndex].Get(); resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; m_commandList->ResourceBarrier(1, &resourceBarrier); } // Plain white background const FLOAT clearColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); // Issue draw command m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView); for (int i = 0; i < 16; i++) { m_commandList->DrawInstanced(3, 1, 3*i, 0); } // Transition to present { D3D12_RESOURCE_BARRIER resourceBarrier{ }; resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resourceBarrier.Transition.pResource = m_renderTargets[m_frameIndex].Get(); resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; m_commandList->ResourceBarrier(1, &resourceBarrier); } // Finish populating commands THROW_IF_FAILED(m_commandList->Close()); // Execute the command list ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); // Wait for completion const auto fenceValue = m_fenceValue; THROW_IF_FAILED(m_commandQueue->Signal(m_fence.Get(), fenceValue)); THROW_IF_FAILED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent)); WaitForSingleObject(m_fenceEvent, INFINITE); InterlockedIncrement(&m_fenceValue); // Update frame index m_renderedFrameIndex = m_frameIndex; m_frameIndex = (m_frameIndex + 1) % m_numRenderTargets; } void D3D12RenderVs::OpenRenderTargets(UINT numSharedHandle, HANDLE* pSharedHandles, DXGI_FORMAT dxgiFormat) { THROW_IF_FAILED((numSharedHandle > MAX_RENDER_TARGETS) ? E_INVALIDARG : S_OK); // Open resources on render device for (UINT i = 0; i < numSharedHandle; ++i) { THROW_IF_FAILED(m_device->OpenSharedHandle(pSharedHandles[i], IID_PPV_ARGS(&m_renderTargets[i]))); m_numRenderTargets++; } // Create render target descriptor heap { D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc{ }; rtvHeapDesc.NumDescriptors = m_numRenderTargets; rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; THROW_IF_FAILED(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvDescriptorHeap))); } // Create render target views on heap { const auto rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); auto rtvHandle = m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc{ }; rtvDesc.Format = dxgiFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; rtvDesc.Texture2D.MipSlice = 0; rtvDesc.Texture2D.PlaneSlice = 0; for (UINT i = 0; i < m_numRenderTargets; ++i) { m_device->CreateRenderTargetView(m_renderTargets[i].Get(), &rtvDesc, rtvHandle); rtvHandle.ptr += rtvDescriptorSize; } m_renderTargetFormat = dxgiFormat; } } void D3D12RenderVs::LoadPipeline() { // Create root signature { D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc{ }; rootSignatureDesc.NumParameters = 0; rootSignatureDesc.pParameters = nullptr; rootSignatureDesc.NumStaticSamplers = 0; rootSignatureDesc.pStaticSamplers = nullptr; rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; ComPtr signature; THROW_IF_FAILED(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, nullptr)); THROW_IF_FAILED(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature))); } // Size the viewport { const auto rtDesc = m_renderTargets[0]->GetDesc(); m_viewport.TopLeftX = 0.0f; m_viewport.TopLeftY = 0.0f; m_viewport.Width = static_cast(rtDesc.Width); m_viewport.Height = static_cast(rtDesc.Height); m_viewport.MinDepth = 0.0f; m_viewport.MaxDepth = 1.0f; m_scissorRect.left = 0; m_scissorRect.top = 0; m_scissorRect.right = static_cast(rtDesc.Width); m_scissorRect.bottom = static_cast(rtDesc.Height); } // Create pipeline state object { const D3D12_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } }; D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc{ }; pipelineStateDesc.pRootSignature = m_rootSignature.Get(); pipelineStateDesc.VS = { g_vertstripe_vs, sizeof(g_vertstripe_vs) }; pipelineStateDesc.PS = { g_vertstripe_ps, sizeof(g_vertstripe_ps) }; pipelineStateDesc.BlendState.AlphaToCoverageEnable = FALSE; pipelineStateDesc.BlendState.IndependentBlendEnable = FALSE; for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) { pipelineStateDesc.BlendState.RenderTarget[i].BlendEnable = FALSE; pipelineStateDesc.BlendState.RenderTarget[i].LogicOpEnable = FALSE; pipelineStateDesc.BlendState.RenderTarget[i].SrcBlend = D3D12_BLEND_ONE; pipelineStateDesc.BlendState.RenderTarget[i].DestBlend = D3D12_BLEND_ZERO; pipelineStateDesc.BlendState.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD; pipelineStateDesc.BlendState.RenderTarget[i].SrcBlendAlpha = D3D12_BLEND_ONE; pipelineStateDesc.BlendState.RenderTarget[i].DestBlendAlpha = D3D12_BLEND_ZERO; pipelineStateDesc.BlendState.RenderTarget[i].BlendOpAlpha = D3D12_BLEND_OP_ADD; pipelineStateDesc.BlendState.RenderTarget[i].LogicOp = D3D12_LOGIC_OP_NOOP; pipelineStateDesc.BlendState.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; } pipelineStateDesc.SampleMask = UINT_MAX; pipelineStateDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; pipelineStateDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; pipelineStateDesc.RasterizerState.FrontCounterClockwise = FALSE; pipelineStateDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; pipelineStateDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; pipelineStateDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; pipelineStateDesc.RasterizerState.DepthClipEnable = TRUE; pipelineStateDesc.RasterizerState.MultisampleEnable = FALSE; pipelineStateDesc.RasterizerState.AntialiasedLineEnable = FALSE; pipelineStateDesc.RasterizerState.ForcedSampleCount = 0; pipelineStateDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; pipelineStateDesc.DepthStencilState.DepthEnable = FALSE; pipelineStateDesc.DepthStencilState.StencilEnable = FALSE; pipelineStateDesc.InputLayout = { layout, _countof(layout) }; pipelineStateDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; pipelineStateDesc.NumRenderTargets = 1; pipelineStateDesc.RTVFormats[0] = m_renderTargetFormat; pipelineStateDesc.SampleDesc.Count = 1; THROW_IF_FAILED(m_device->CreateGraphicsPipelineState(&pipelineStateDesc, IID_PPV_ARGS(&m_pipelineState))); } // Create vertex buffer // Makes 8 rectangles. 4 per eye. From upper left, clockwise: // orange, purple, yellow, blue // Center of the four rectangles is the center of each eye // Rectangles (upper left, lower right) corners: // Left eye: // Orange: (-.71, .26), (-.51, 0) // Purple: (-.49, .26), (-.29, 0) // Yellow: (-.49, -.056), (-.29, -.316) // Blue: (-.71, -.056), (-.51, -.316) // Right eye: // Orange: (.29, .26), (.49, 0) // Purple: (.51, .26), (.71, 0) // Yellow: (.51, -.056), (.71, -.316) // Blue: (.29, -.056), (.49, -.316) { const Vertex vertices[] { // Left orange {{-.71f+.055f, .26f, .5f}}, {{-.51f+.055f, .26f, .5f}}, {{-.51f+.055f, .0f, .5f}}, {{-.51f+.055f, .0f, .5f}}, {{-.71f+.055f, .0f, .5f}}, {{-.71f+.055f, .26f, .5f}}, // Left purple {{-.49f+.055f, .26f, .5f}}, {{-.29f+.055f, .26f, .5f}}, {{-.29f+.055f, .0f, .5f}}, {{-.29f+.055f, .0f, .5f}}, {{-.49f+.055f, .0f, .5f}}, {{-.49f+.055f, .26f, .5f}}, // Left yellow {{-.49f+.055f, -.056f, .5f}}, {{-.29f+.055f, -.056f, .5f}}, {{-.29f+.055f, -.316f, .5f}}, {{-.29f+.055f, -.316f, .5f}}, {{-.49f+.055f, -.316f, .5f}}, {{-.49f+.055f, -.056f, .5f}}, // Left blue {{-.71f+.055f, -.056f, .5f}}, {{-.51f+.055f, -.056f, .5f}}, {{-.51f+.055f, -.316f, .5f}}, {{-.51f+.055f, -.316f, .5f}}, {{-.71f+.055f, -.316f, .5f}}, {{-.71f+.055f, -.056f, .5f}}, // Right orange {{.29f-.055f, .26f, .5f}}, {{.49f-.055f, .26f, .5f}}, {{.49f-.055f, .0f, .5f}}, {{.49f-.055f, .0f, .5f}}, {{.29f-.055f, .0f, .5f}}, {{.29f-.055f, .26f, .5f}}, // Right purple {{.51f-.055f, .26f, .5f}}, {{.71f-.055f, .26f, .5f}}, {{.71f-.055f, .0f, .5f}}, {{.71f-.055f, .0f, .5f}}, {{.51f-.055f, .0f, .5f}}, {{.51f-.055f, .26f, .5f}}, // Right yellow {{.51f-.055f, -.056f, .5f}}, {{.71f-.055f, -.056f, .5f}}, {{.71f-.055f, -.316f, .5f}}, {{.71f-.055f, -.316f, .5f}}, {{.51f-.055f, -.316f, .5f}}, {{.51f-.055f, -.056f, .5f}}, // Right blue {{.29f-.055f, -.056f, .5f}}, {{.49f-.055f, -.056f, .5f}}, {{.49f-.055f, -.316f, .5f}}, {{.49f-.055f, -.316f, .5f}}, {{.29f-.055f, -.316f, .5f}}, {{.29f-.055f, -.056f, .5f}}, }; D3D12_HEAP_PROPERTIES heapProps{ }; heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC resourceDesc{ }; resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; resourceDesc.Alignment = 0; resourceDesc.Width = sizeof(vertices); resourceDesc.Height = 1; resourceDesc.DepthOrArraySize = 1; resourceDesc.MipLevels = 1; resourceDesc.Format = DXGI_FORMAT_UNKNOWN; resourceDesc.SampleDesc.Count = 1; resourceDesc.SampleDesc.Quality = 0; resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; THROW_IF_FAILED(m_device->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_vertexBuffer))); UINT8* pVertexDataBegin = nullptr; THROW_IF_FAILED(m_vertexBuffer->Map(0, nullptr, reinterpret_cast(&pVertexDataBegin))); memcpy(pVertexDataBegin, vertices, sizeof(vertices)); m_vertexBuffer->Unmap(0, nullptr); m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); m_vertexBufferView.StrideInBytes = sizeof(Vertex); m_vertexBufferView.SizeInBytes = sizeof(vertices); } // Create command allocator THROW_IF_FAILED(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator))); // Create command queue { D3D12_COMMAND_QUEUE_DESC commandQueueDesc{ }; commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; THROW_IF_FAILED(m_device->CreateCommandQueue(&commandQueueDesc, IID_PPV_ARGS(&m_commandQueue))); } // This is the first fence value we'll set, has to be != our initial value // below so we can wait on the first fence correctly m_fenceValue = 1; // Create command queue completion fence THROW_IF_FAILED(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence))); m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (m_fenceEvent == nullptr) { THROW_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); } // Create command list THROW_IF_FAILED(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_commandList))); THROW_IF_FAILED(m_commandList->Close()); } void D3D12RenderVs::ResetCommandList() { THROW_IF_FAILED(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); } bool D3D12RenderVs::WriteRenderTargetToBMP(const char* filename) { // Dummy variables to compile ComPtr commandList; // Open file first - if that fails, we can skip the remaining altogether FILE* pFile = nullptr; if (fopen_s(&pFile, filename, "wb") != 0) return false; // Create a D3D11on12 context ComPtr d3d11Device; ComPtr d3d11On12Device; ComPtr d3d11DeviceContext; if (S_OK != D3D11On12CreateDevice(m_device.Get(), D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, reinterpret_cast(m_commandQueue.GetAddressOf()), 1, 0, &d3d11Device, &d3d11DeviceContext, nullptr)) { std::fprintf(stderr, __FUNCTION__": Failed to create D3D11 device. Aborting frame dump."); return false; } d3d11Device.As(&d3d11On12Device); ComPtr wrappedRenderTarget; D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; d3d11On12Device->CreateWrappedResource(m_renderTargets[m_renderedFrameIndex].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE, IID_PPV_ARGS(&wrappedRenderTarget)); // Read from the render target (given via renderef frame index) D3D12_RESOURCE_DESC rtvDesc = m_renderTargets[m_renderedFrameIndex]->GetDesc(); D3D11_TEXTURE2D_DESC texDesc = { (UINT)rtvDesc.Width, (UINT)rtvDesc.Height, 1, 1, rtvDesc.Format, {1, 0}, D3D11_USAGE_STAGING, 0, D3D11_CPU_ACCESS_READ, 0, }; std::vector data(texDesc.Width * texDesc.Height * 4); // !!Hardcoded 4=32bit depth. If required get from colour format ComPtr pTextureStaging; d3d11Device->CreateTexture2D(&texDesc, nullptr, &pTextureStaging); d3d11On12Device->AcquireWrappedResources(wrappedRenderTarget.GetAddressOf(), 1); // Acquire D3D12 resource for D3D11 interop d3d11DeviceContext->CopyResource(pTextureStaging.Get(), wrappedRenderTarget.Get()); D3D11_MAPPED_SUBRESOURCE mapped = {}; d3d11DeviceContext->Map(pTextureStaging.Get(), 0, D3D11_MAP_READ, 0, &mapped); int rowSize = texDesc.Width * 4; for (unsigned int y = 0; y < texDesc.Height; ++y) // Read one row at a time and push into the vector { memcpy((void*)((byte*)&data[0] + y * rowSize), (void*)((byte*)mapped.pData + (y * mapped.RowPitch)), rowSize); } d3d11DeviceContext->Unmap(pTextureStaging.Get(), 0); d3d11On12Device->ReleaseWrappedResources(wrappedRenderTarget.GetAddressOf(), 1); // Release D3D12 resource std::vector fileBuffer; //compose the buffer, starting with the bitmap header and infoheader BITMAPFILEHEADER bmpFileHeader = // - BitmapFileHeader { 0x4d42, 0, 0, 0, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), }; BITMAPINFOHEADER bmpInfoHeader = // - BitmapInfoHeader { sizeof(BITMAPINFOHEADER), (LONG)texDesc.Width, -(LONG)texDesc.Height, 1,32, BI_RGB, }; fileBuffer.resize(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + texDesc.Width * texDesc.Height * 4); // replace with size from format memcpy(&fileBuffer[0], &bmpFileHeader, sizeof(bmpFileHeader)); memcpy(&fileBuffer[sizeof(bmpFileHeader)], &bmpInfoHeader, sizeof(bmpInfoHeader)); // Copying one pixel at a time to rearrange the planes from RGBA to BGRA for (unsigned int i = 0; i < texDesc.Width * texDesc.Height; ++i) { fileBuffer[sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + i * 4] = data[i * 4 + 1]; fileBuffer[sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + i * 4 + 1] = data[i * 4 + 2]; fileBuffer[sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + i * 4 + 2] = data[i * 4 + 3]; fileBuffer[sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + i * 4 + 3] = data[i * 4 + 0]; } // Now the buffer is ready, simply write to file if (fwrite(&fileBuffer[0], fileBuffer.size(), 1, pFile) < 1) { fclose(pFile); return false; } fclose(pFile); return true; }