// 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 "D3D12RenderImage.h" #include #include #include "image_ps.h" #include "image_vs.h" #include #include "dxcapi.h" #include "d3dx12.h" #include "WICTextureLoader12.h" using namespace DirectX; #define THROW_IF_FAILED(a) ThrowIfFailedImg(a, __FUNCTION__, __LINE__) D3D12RenderImage::D3D12RenderImage(IDXGIAdapter* pAdapter, const wchar_t* image_filename) { // Create D3D device THROW_IF_FAILED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&m_device))); // Save filename for later load m_filename = std::wstring(image_filename); update_texture.store(false); } void D3D12RenderImage::Init(UINT numSharedHandle, HANDLE* pSharedHandles, DXGI_FORMAT dxgiFormat) { OpenRenderTargets(numSharedHandle, pSharedHandles, dxgiFormat); LoadPipeline(); } void D3D12RenderImage::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 D3D12RenderImage::Render() { ComPtr textureUploadHeap; // needs to stay in scope until command list is executed if we are uploading a texture // 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()); // Set texture descriptor table 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); // Update texture if (update_texture.load()) { update_texture.store(false); UpdateImageTexture(textureUploadHeap); } // 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[] = { 0.55f, 0.1f, 0.35f, 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); m_commandList->DrawInstanced(3, 1, 0, 0); m_commandList->DrawInstanced(3, 1, 3, 0); m_commandList->DrawInstanced(3, 1, 6, 0); m_commandList->DrawInstanced(3, 1, 9, 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 D3D12RenderImage::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))); } { // Describe and create a shader resource view (SRV) heap for the texture. D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {}; srvHeapDesc.NumDescriptors = 1; srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; THROW_IF_FAILED(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap))); } // 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 D3D12RenderImage::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))); */ 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[1]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); CD3DX12_ROOT_PARAMETER1 rootParameters[1]; rootParameters[0].InitAsDescriptorTable(1, &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; 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; THROW_IF_FAILED(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)); 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 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0} }; D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc{ }; pipelineStateDesc.pRootSignature = m_rootSignature.Get(); pipelineStateDesc.VS = { g_image_vs, sizeof(g_image_vs) }; pipelineStateDesc.PS = { g_image_ps, sizeof(g_image_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 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))); } // Create command allocator THROW_IF_FAILED(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator))); // Create the command list. THROW_IF_FAILED(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList))); { const Vertex vertices[] { { { -1.0f, 1.0f , 0.0f }, { 0.f, 0.f } }, { { 0.0f, 1.0f , 0.0f }, { 1.f, 0.f } }, { { 0.0f, -1.0f , 0.0f }, { 1.f, 1.f } }, { { 0.0f, -1.0f , 0.0f }, { 1.f, 1.f } }, { { -1.0, -1.0f , 0.0f }, { 0.f, 1.f } }, { { -1.0f, 1.0f , 0.0f }, { 0.f, 0.f } }, { { 0.0f, 1.0f , 0.0f }, { 0.f, 0.f } }, { { 1.0f, 1.0f , 0.0f }, { 1.f, 0.f } }, { { 1.0f, -1.0f , 0.0f }, { 1.f, 1.f } }, { { 1.0f, -1.0f , 0.0f }, { 1.f, 1.f } }, { { 0.0, -1.0f , 0.0f }, { 0.f, 1.f } }, { { 0.0f, 1.0f , 0.0f }, { 0.f, 0.f } }, }; const UINT vertexBufferSize = sizeof(vertices); // 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. THROW_IF_FAILED(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. THROW_IF_FAILED(m_vertexBuffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin))); memcpy(pVertexDataBegin, vertices, sizeof(vertices)); m_vertexBuffer->Unmap(0, nullptr); // Initialize the vertex buffer view. m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); m_vertexBufferView.StrideInBytes = sizeof(Vertex); m_vertexBufferView.SizeInBytes = vertexBufferSize; } // 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.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; //THROW_IF_FAILED(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. //THROW_IF_FAILED(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)); //ComPtr txtDataRes; std::unique_ptr decodedImage; D3D12_SUBRESOURCE_DATA textureDataBird = {}; DXGI_FORMAT pix_format; THROW_IF_FAILED(LoadWICTextureFromFile(m_device.Get(), m_filename.c_str(), m_texture.GetAddressOf(), decodedImage, textureDataBird, pix_format)); const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_texture.Get(), 0, 1); // Create the GPU upload buffer. THROW_IF_FAILED(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))); UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureDataBird); m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); //auto image_data = decodedImage.get(); // //auto bird_image = LoadTextureData(); //D3D12_SUBRESOURCE_DATA textureData = {}; //textureData.pData = bird_image->GetPixels(); //textureData.RowPitch = bird_image->GetMetadata().width * TexturePixelSize; //textureData.SlicePitch = textureData.RowPitch * bird_image->GetMetadata().height; //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.Format = pix_format; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, m_srvHeap->GetCPUDescriptorHandleForHeapStart()); } // Close the command list and execute it to begin the initial GPU setup. THROW_IF_FAILED(m_commandList->Close()); ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); // 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()); // 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 D3D12RenderImage::ResetCommandList() { THROW_IF_FAILED(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())); } bool D3D12RenderImage::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; } // Generate a simple black and white checkerboard texture. std::vector D3D12RenderImage::GenerateTextureData() { const UINT rowPitch = TextureWidth * TexturePixelSize; const UINT cellPitch = rowPitch >> 3; // The width of a cell in the checkboard texture. const UINT cellHeight = TextureWidth >> 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) { pData[n] = 0x30; // R pData[n + 1] = 0x00; // G pData[n + 2] = 0x30; // 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; } void D3D12RenderImage::ShowNewTexture(const wchar_t* image_filename) { m_filename = std::wstring(image_filename); update_texture.store(true); } void D3D12RenderImage::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; THROW_IF_FAILED(m_commandQueue->Signal(m_fence.Get(), fence)); m_fenceValue++; // Wait until the previous frame is finished. if (m_fence->GetCompletedValue() < fence) { THROW_IF_FAILED(m_fence->SetEventOnCompletion(fence, m_fenceEvent)); WaitForSingleObject(m_fenceEvent, INFINITE); } // m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); } void D3D12RenderImage::UpdateImageTexture(ComPtr& textureUploadHeap) { std::unique_ptr decodedImage; D3D12_SUBRESOURCE_DATA textureDataBird = {}; DXGI_FORMAT pix_format; THROW_IF_FAILED(LoadWICTextureFromFile(m_device.Get(), m_filename.c_str(), m_texture.ReleaseAndGetAddressOf(), decodedImage, textureDataBird, pix_format)); const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_texture.Get(), 0, 1); // Create the GPU upload buffer. THROW_IF_FAILED(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))); UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureDataBird); m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; //srvDesc.Format = textureDesc.Format; srvDesc.Format = pix_format; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, m_srvHeap->GetCPUDescriptorHandleForHeapStart()); }