mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
* Initial commit * Update macros * Add GPU Markers * Fix builds without microprofile * minor updates * clang format * Update profiler.cpp * clang format * Name Main Thread * Update profiler_macros.h * Fix end flip * Update fiddle_context_gl.cpp * clang format * Update rive_build_config.lua * Update rive_build_config.lua * forked microprofile so I can use a tag * Update render_context_d3d_impl.cpp * clang Co-authored-by: John White <aliasbinman@gmail.com>
715 lines
25 KiB
C++
715 lines
25 KiB
C++
#include "fiddle_context.hpp"
|
|
#include "rive/profiler/profiler_macros.h"
|
|
|
|
#if !defined(_WIN32) || defined(RIVE_UNREAL)
|
|
|
|
std::unique_ptr<FiddleContext> FiddleContext::MakeD3D12PLS(
|
|
FiddleContextOptions fiddleOptions)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
#else
|
|
|
|
#include "rive/renderer/rive_renderer.hpp"
|
|
#include "rive/renderer/d3d/d3d_utils.hpp"
|
|
#include "rive/renderer/d3d12/render_context_d3d12_impl.hpp"
|
|
#include <dxgi1_6.h>
|
|
|
|
#define GLFW_INCLUDE_NONE
|
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
|
#include "GLFW/glfw3.h"
|
|
#include <GLFW/glfw3native.h>
|
|
|
|
using namespace rive;
|
|
using namespace rive::gpu;
|
|
|
|
void SetIntelInformation(const DXGI_ADAPTER_DESC& desc,
|
|
D3DContextOptions& contextOptions)
|
|
{
|
|
contextOptions.isIntel = desc.VendorId == 0x163C ||
|
|
desc.VendorId == 0x8086 || desc.VendorId == 0x8087;
|
|
|
|
contextOptions.isIntelArc =
|
|
contextOptions.isIntel &&
|
|
std::wstring(desc.Description).find(L"Arc") != std::wstring::npos;
|
|
}
|
|
|
|
bool GetHardwareAdapter(IDXGIFactory1* pFactory,
|
|
const char* gpuNameFilter,
|
|
IDXGIAdapter1** ppAdapter)
|
|
{
|
|
using namespace Microsoft::WRL;
|
|
|
|
*ppAdapter = nullptr;
|
|
|
|
bool shouldUseNameFilter = false;
|
|
std::wstring gpuNameFilterW;
|
|
if (gpuNameFilter)
|
|
{
|
|
shouldUseNameFilter =
|
|
d3d_utils::GetWStringFromString(gpuNameFilter, gpuNameFilterW);
|
|
}
|
|
|
|
ComPtr<IDXGIAdapter1> adapter;
|
|
ComPtr<IDXGIFactory6> factory6;
|
|
if (SUCCEEDED(pFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
|
|
{
|
|
for (UINT adapterIndex = 0;
|
|
SUCCEEDED(factory6->EnumAdapterByGpuPreference(
|
|
adapterIndex,
|
|
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.
|
|
continue;
|
|
}
|
|
|
|
if (shouldUseNameFilter)
|
|
{
|
|
std::wstring gpuName(desc.Description,
|
|
wcslen(desc.Description));
|
|
if (gpuName.find(gpuNameFilterW) == std::wstring::npos)
|
|
{
|
|
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)))
|
|
{
|
|
*ppAdapter = adapter.Detach();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (shouldUseNameFilter)
|
|
{
|
|
std::wstring gpuName(desc.Description,
|
|
wcslen(desc.Description));
|
|
if (gpuName.find(gpuNameFilterW) == std::wstring::npos)
|
|
{
|
|
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)))
|
|
{
|
|
*ppAdapter = adapter.Detach();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
class FiddleContextD3D12PLS : public FiddleContext
|
|
{
|
|
public:
|
|
FiddleContextD3D12PLS(ComPtr<IDXGIFactory4> factory,
|
|
ComPtr<ID3D12Device> device,
|
|
bool isHeadless,
|
|
D3DContextOptions& contextOptions) :
|
|
m_isHeadless(isHeadless), m_factory(factory), m_device(device)
|
|
{
|
|
|
|
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
|
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
|
|
VERIFY_OK(m_device->CreateCommandQueue(&queueDesc,
|
|
IID_PPV_ARGS(&m_commandQueue)));
|
|
|
|
NAME_RAW_D3D12_OBJECT(m_commandQueue);
|
|
|
|
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
|
|
VERIFY_OK(
|
|
m_device->CreateCommandQueue(&queueDesc,
|
|
IID_PPV_ARGS(&m_copyCommandQueue)));
|
|
NAME_RAW_D3D12_OBJECT(m_copyCommandQueue);
|
|
|
|
#if defined(RIVE_MICROPROFILE)
|
|
MicroProfileGpuInitD3D12(m_device.Get(), m_commandQueue.Get());
|
|
#endif
|
|
|
|
for (auto i = 0; i < FrameCount; ++i)
|
|
{
|
|
VERIFY_OK(m_device->CreateCommandAllocator(
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
IID_PPV_ARGS(&m_allocators[i])));
|
|
|
|
NAME_RAW_D3D12_OBJECT(m_allocators[i]);
|
|
|
|
VERIFY_OK(m_device->CreateCommandAllocator(
|
|
D3D12_COMMAND_LIST_TYPE_COPY,
|
|
IID_PPV_ARGS(&m_copyAllocators[i])));
|
|
NAME_RAW_D3D12_OBJECT(m_copyAllocators[i]);
|
|
}
|
|
|
|
VERIFY_OK(m_device->CreateCommandList(0,
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
m_allocators[0].Get(),
|
|
NULL,
|
|
IID_PPV_ARGS(&m_commandList)));
|
|
NAME_RAW_D3D12_OBJECT(m_commandList);
|
|
|
|
VERIFY_OK(
|
|
m_device->CreateCommandList(0,
|
|
D3D12_COMMAND_LIST_TYPE_COPY,
|
|
m_copyAllocators[0].Get(),
|
|
NULL,
|
|
IID_PPV_ARGS(&m_copyCommandList)));
|
|
NAME_RAW_D3D12_OBJECT(m_copyCommandList);
|
|
|
|
m_renderContext =
|
|
RenderContextD3D12Impl::MakeContext(m_device,
|
|
m_copyCommandList.Get(),
|
|
contextOptions);
|
|
VERIFY_OK(m_copyCommandList->Close());
|
|
VERIFY_OK(m_commandList->Close());
|
|
|
|
ID3D12CommandList* ppCommandLists[] = {m_copyCommandList.Get()};
|
|
m_copyCommandQueue->ExecuteCommandLists(1, ppCommandLists);
|
|
|
|
VERIFY_OK(m_device->CreateFence(m_currentFrame,
|
|
D3D12_FENCE_FLAG_NONE,
|
|
IID_PPV_ARGS(&m_fence)));
|
|
|
|
// NOTE: Originally the code was setting this to -1 and then waiting on
|
|
// a signal of 0, but this does not work in practice because some D3D12
|
|
// implementations only allow the signaled value to increase - so
|
|
// starting the fence at unsigned -1 meant that it can never be changed
|
|
// again.
|
|
VERIFY_OK(m_device->CreateFence(m_currentFrame,
|
|
D3D12_FENCE_FLAG_NONE,
|
|
IID_PPV_ARGS(&m_copyFence)));
|
|
|
|
// Increment m_currentFrame since the value we initialized the fences to
|
|
// cannot be waited on (it'll return immediately).
|
|
m_currentFrame++;
|
|
|
|
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
VERIFY_OK(HRESULT_FROM_WIN32(GetLastError()));
|
|
|
|
assert(m_fenceEvent);
|
|
|
|
// Signal the fence to ensure that we wait for creation to be complete.
|
|
// Start at m_currentFrame which should currently be set to 1.
|
|
VERIFY_OK(
|
|
m_copyCommandQueue->Signal(m_copyFence.Get(), m_currentFrame));
|
|
|
|
if (m_copyFence->GetCompletedValue() != m_currentFrame)
|
|
{
|
|
VERIFY_OK(m_copyFence->SetEventOnCompletion(m_currentFrame,
|
|
m_fenceEvent));
|
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
|
|
|
assert(m_copyFence->GetCompletedValue() == m_currentFrame);
|
|
}
|
|
|
|
// Increment the current frame one more to get past this wait.
|
|
m_currentFrame++;
|
|
}
|
|
|
|
float dpiScale(GLFWwindow*) const override { return 1; }
|
|
|
|
rive::Factory* factory() override { return m_renderContext.get(); }
|
|
|
|
rive::gpu::RenderContext* renderContextOrNull() override
|
|
{
|
|
return m_renderContext.get();
|
|
}
|
|
|
|
rive::gpu::RenderTarget* renderTargetOrNull() override
|
|
{
|
|
return m_renderTargets[m_frameIndex].get();
|
|
}
|
|
|
|
void onSizeChanged(GLFWwindow* window,
|
|
int width,
|
|
int height,
|
|
uint32_t sampleCount) override
|
|
{
|
|
// wait for all frames to finish
|
|
waitForLastFrame();
|
|
|
|
if (!m_isHeadless)
|
|
{
|
|
if (!m_swapChain)
|
|
{
|
|
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
|
swapChainDesc.BufferCount = FrameCount;
|
|
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;
|
|
swapChainDesc.Flags = 0;
|
|
|
|
ComPtr<IDXGISwapChain1> swapChain;
|
|
VERIFY_OK(m_factory->CreateSwapChainForHwnd(
|
|
m_commandQueue.Get(), // Swap chain needs the queue so that
|
|
// it can force a flush on it.
|
|
glfwGetWin32Window(window),
|
|
&swapChainDesc,
|
|
nullptr,
|
|
nullptr,
|
|
&swapChain));
|
|
|
|
VERIFY_OK(swapChain.As(&m_swapChain));
|
|
}
|
|
else
|
|
{
|
|
// we must release all references to resize swapchain
|
|
for (auto i = 0; i < FrameCount; ++i)
|
|
{
|
|
m_renderTargets[i]->releaseTexturesImmediately();
|
|
}
|
|
// invalidates all previous buffers
|
|
m_swapChain->ResizeBuffers(FrameCount,
|
|
width,
|
|
height,
|
|
DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
0);
|
|
}
|
|
|
|
for (auto i = 0; i < FrameCount; ++i)
|
|
{
|
|
ComPtr<ID3D12Resource> backbuffer;
|
|
VERIFY_OK(m_swapChain->GetBuffer(i, IID_PPV_ARGS(&backbuffer)));
|
|
auto renderContextImpl =
|
|
m_renderContext->static_impl_cast<RenderContextD3D12Impl>();
|
|
m_renderTargets[i] =
|
|
renderContextImpl->makeRenderTarget(width, height);
|
|
m_renderTargets[i]->setTargetTexture(backbuffer);
|
|
}
|
|
}
|
|
|
|
if (m_isHeadless)
|
|
{
|
|
ComPtr<ID3D12Resource> backbuffer;
|
|
auto desc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
width,
|
|
height);
|
|
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
|
D3D12_CLEAR_VALUE clearValue{DXGI_FORMAT_R8G8B8A8_UNORM, {}};
|
|
auto heapProperties =
|
|
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
|
|
m_device->CreateCommittedResource(&heapProperties,
|
|
D3D12_HEAP_FLAG_NONE,
|
|
&desc,
|
|
D3D12_RESOURCE_STATE_PRESENT,
|
|
&clearValue,
|
|
IID_PPV_ARGS(&backbuffer));
|
|
auto renderContextImpl =
|
|
m_renderContext->static_impl_cast<RenderContextD3D12Impl>();
|
|
m_renderTargets[0] =
|
|
renderContextImpl->makeRenderTarget(width, height);
|
|
m_renderTargets[0]->setTargetTexture(backbuffer);
|
|
}
|
|
}
|
|
|
|
void toggleZoomWindow() override {}
|
|
|
|
std::unique_ptr<Renderer> makeRenderer(int width, int height) override
|
|
{
|
|
return std::make_unique<RiveRenderer>(m_renderContext.get());
|
|
}
|
|
|
|
void begin(const rive::gpu::RenderContext::FrameDescriptor& frameDescriptor)
|
|
override
|
|
{
|
|
m_renderContext->beginFrame(frameDescriptor);
|
|
}
|
|
|
|
UINT64 getFrameIndex() const
|
|
{
|
|
// when headless we only ever use one frames worth of textures for
|
|
// rendering
|
|
if (m_isHeadless)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return m_swapChain->GetCurrentBackBufferIndex();
|
|
}
|
|
}
|
|
|
|
UINT64 waitForNextSafeFrame()
|
|
{
|
|
// wait in case this swapchain still hasnt finished
|
|
auto safeFrame = m_fence->GetCompletedValue();
|
|
if (safeFrame < m_previousFrames[m_frameIndex])
|
|
{
|
|
VERIFY_OK(
|
|
m_fence->SetEventOnCompletion(m_previousFrames[m_frameIndex],
|
|
m_fenceEvent));
|
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
|
safeFrame = m_previousFrames[m_frameIndex];
|
|
}
|
|
|
|
auto copySafeFrame = m_copyFence->GetCompletedValue();
|
|
if (copySafeFrame < m_previousFrames[m_frameIndex])
|
|
{
|
|
VERIFY_OK(m_copyFence->SetEventOnCompletion(
|
|
m_previousFrames[m_frameIndex],
|
|
m_fenceEvent));
|
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
|
copySafeFrame = m_previousFrames[m_frameIndex];
|
|
}
|
|
|
|
return std::min(copySafeFrame, safeFrame);
|
|
}
|
|
|
|
void waitForLastFrame()
|
|
{
|
|
auto frame = m_currentFrame++;
|
|
m_previousFrames[m_frameIndex] = frame;
|
|
VERIFY_OK(m_copyCommandQueue->Signal(m_copyFence.Get(), frame));
|
|
VERIFY_OK(m_commandQueue->Signal(m_fence.Get(), frame));
|
|
|
|
VERIFY_OK(m_fence->SetEventOnCompletion(frame, m_fenceEvent));
|
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
|
|
|
VERIFY_OK(m_copyFence->SetEventOnCompletion(frame, m_fenceEvent));
|
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
|
}
|
|
|
|
void moveToNextFrame()
|
|
{
|
|
VERIFY_OK(m_commandList->Close());
|
|
VERIFY_OK(m_copyCommandList->Close());
|
|
|
|
ID3D12CommandList* ppCopyCommandLists[] = {m_copyCommandList.Get()};
|
|
|
|
ID3D12CommandList* ppCommandLists[] = {m_commandList.Get()};
|
|
|
|
m_previousFrames[m_frameIndex] = m_currentFrame++;
|
|
|
|
m_copyCommandQueue->ExecuteCommandLists(_countof(ppCopyCommandLists),
|
|
ppCopyCommandLists);
|
|
|
|
VERIFY_OK(m_copyCommandQueue->Signal(m_copyFence.Get(),
|
|
m_previousFrames[m_frameIndex]));
|
|
|
|
// tell the direct command que to wait for the copy command que to
|
|
// finish
|
|
m_commandQueue->Wait(m_copyFence.Get(), m_previousFrames[m_frameIndex]);
|
|
|
|
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists),
|
|
ppCommandLists);
|
|
|
|
VERIFY_OK(m_commandQueue->Signal(m_fence.Get(),
|
|
m_previousFrames[m_frameIndex]));
|
|
|
|
RIVE_PROF_GPUSUBMIT(0);
|
|
RIVE_PROF_ENDFRAME()
|
|
}
|
|
|
|
void flushPLSContext(RenderTarget* offscreenRenderTarget) final
|
|
{
|
|
m_frameIndex = getFrameIndex();
|
|
|
|
auto safeFrame = waitForNextSafeFrame();
|
|
|
|
VERIFY_OK(m_allocators[m_frameIndex]->Reset());
|
|
|
|
VERIFY_OK(m_copyAllocators[m_frameIndex]->Reset());
|
|
|
|
VERIFY_OK(m_commandList->Reset(m_allocators[m_frameIndex].Get(), NULL));
|
|
|
|
VERIFY_OK(m_copyCommandList->Reset(m_copyAllocators[m_frameIndex].Get(),
|
|
NULL));
|
|
#if defined(RIVE_MICROPROFILE)
|
|
MicroProfileGpuSetContext(m_commandList.Get());
|
|
#endif
|
|
RenderContextD3D12Impl::CommandLists cmdLists = {
|
|
m_copyCommandList.Get(),
|
|
m_commandList.Get()};
|
|
|
|
m_renderContext->flush({
|
|
.renderTarget = offscreenRenderTarget != nullptr
|
|
? offscreenRenderTarget
|
|
: m_renderTargets[m_frameIndex].get(),
|
|
.externalCommandBuffer = &cmdLists,
|
|
.currentFrameNumber = m_currentFrame,
|
|
.safeFrameNumber = safeFrame,
|
|
});
|
|
|
|
moveToNextFrame();
|
|
}
|
|
|
|
void readBackPixels(std::vector<uint8_t>* pixelData)
|
|
{
|
|
waitForLastFrame();
|
|
|
|
VERIFY_OK(m_allocators[m_frameIndex]->Reset());
|
|
VERIFY_OK(m_copyAllocators[m_frameIndex]->Reset());
|
|
VERIFY_OK(m_commandList->Reset(m_allocators[m_frameIndex].Get(), NULL));
|
|
VERIFY_OK(m_copyCommandList->Reset(m_copyAllocators[m_frameIndex].Get(),
|
|
NULL));
|
|
|
|
ComPtr<ID3D12Resource> readbackBuffer;
|
|
auto w = m_renderTargets[m_frameIndex]->width();
|
|
auto h = m_renderTargets[m_frameIndex]->height();
|
|
size_t outputBufferSize = w * h * 4;
|
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
|
|
|
auto targetTexture =
|
|
m_renderTargets[m_frameIndex]->targetTexture()->resource();
|
|
|
|
D3D12_HEAP_PROPERTIES readbackHeapProperties{
|
|
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK)};
|
|
|
|
D3D12_RESOURCE_DESC readbackBufferDesc{
|
|
CD3DX12_RESOURCE_DESC::Buffer(outputBufferSize)};
|
|
|
|
VERIFY_OK(
|
|
m_device->CreateCommittedResource(&readbackHeapProperties,
|
|
D3D12_HEAP_FLAG_NONE,
|
|
&readbackBufferDesc,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
nullptr,
|
|
IID_PPV_ARGS(&readbackBuffer)));
|
|
|
|
{
|
|
D3D12_RESOURCE_BARRIER outputBufferResourceBarrier{
|
|
CD3DX12_RESOURCE_BARRIER::Transition(
|
|
targetTexture,
|
|
D3D12_RESOURCE_STATE_PRESENT,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE)};
|
|
m_commandList->ResourceBarrier(1, &outputBufferResourceBarrier);
|
|
}
|
|
|
|
UINT numRows;
|
|
UINT64 rowSizeInBtes;
|
|
UINT64 totalBytes;
|
|
auto desc = targetTexture->GetDesc();
|
|
m_device->GetCopyableFootprints(&desc,
|
|
0,
|
|
1,
|
|
0,
|
|
&footprint,
|
|
&numRows,
|
|
&rowSizeInBtes,
|
|
&totalBytes);
|
|
footprint.Footprint.RowPitch = w * 4;
|
|
|
|
const CD3DX12_TEXTURE_COPY_LOCATION dst(readbackBuffer.Get(),
|
|
footprint);
|
|
const CD3DX12_TEXTURE_COPY_LOCATION src(targetTexture, 0);
|
|
|
|
m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
|
|
|
|
footprint.Footprint.RowPitch = static_cast<UINT>(rowSizeInBtes);
|
|
|
|
{
|
|
D3D12_RESOURCE_BARRIER outputBufferResourceBarrier{
|
|
CD3DX12_RESOURCE_BARRIER::Transition(
|
|
targetTexture,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
D3D12_RESOURCE_STATE_PRESENT)};
|
|
m_commandList->ResourceBarrier(1, &outputBufferResourceBarrier);
|
|
}
|
|
|
|
moveToNextFrame();
|
|
|
|
waitForLastFrame();
|
|
|
|
assert(pixelData);
|
|
pixelData->resize(outputBufferSize);
|
|
|
|
uint8_t* mappedData;
|
|
D3D12_RANGE range{0, outputBufferSize};
|
|
VERIFY_OK(readbackBuffer->Map(0,
|
|
&range,
|
|
reinterpret_cast<void**>(&mappedData)));
|
|
|
|
for (uint32_t y = 0; y < h; ++y)
|
|
{
|
|
auto row = reinterpret_cast<const char*>(mappedData) +
|
|
footprint.Footprint.RowPitch * y;
|
|
memcpy(pixelData->data() + (h - y - 1) * w * 4, row, w * 4);
|
|
}
|
|
|
|
D3D12_RANGE emptyRange{0, 0};
|
|
readbackBuffer->Unmap(0, &emptyRange);
|
|
}
|
|
|
|
void end(GLFWwindow*, std::vector<uint8_t>* pixelData = nullptr) override
|
|
{
|
|
flushPLSContext(nullptr);
|
|
|
|
if (pixelData != nullptr)
|
|
{
|
|
auto w = m_renderTargets[m_frameIndex]->width();
|
|
auto h = m_renderTargets[m_frameIndex]->height();
|
|
pixelData->resize(w * h * 4);
|
|
readBackPixels(pixelData);
|
|
}
|
|
|
|
if (!m_isHeadless)
|
|
m_swapChain->Present(0, 0);
|
|
|
|
RIVE_PROF_GPUFLIP();
|
|
}
|
|
|
|
private:
|
|
static constexpr SIZE_T FrameCount = 2;
|
|
|
|
ComPtr<ID3D12CommandAllocator> m_allocators[FrameCount];
|
|
ComPtr<ID3D12CommandAllocator> m_copyAllocators[FrameCount];
|
|
rcp<RenderTargetD3D12> m_renderTargets[FrameCount];
|
|
ComPtr<ID3D12Fence> m_fence;
|
|
ComPtr<ID3D12Fence> m_copyFence;
|
|
HANDLE m_fenceEvent = NULL;
|
|
UINT64 m_previousFrames[FrameCount] = {0};
|
|
UINT64 m_currentFrame = 0;
|
|
UINT64 m_frameIndex = 0;
|
|
const bool m_isHeadless;
|
|
|
|
ComPtr<IDXGIFactory4> m_factory;
|
|
ComPtr<IDXGISwapChain3> m_swapChain;
|
|
ComPtr<ID3D12CommandQueue> m_commandQueue;
|
|
ComPtr<ID3D12CommandQueue> m_copyCommandQueue;
|
|
ComPtr<ID3D12GraphicsCommandList> m_commandList;
|
|
ComPtr<ID3D12GraphicsCommandList> m_copyCommandList;
|
|
ComPtr<ID3D12Device> m_device;
|
|
std::unique_ptr<RenderContext> m_renderContext;
|
|
};
|
|
|
|
std::unique_ptr<FiddleContext> FiddleContext::MakeD3D12PLS(
|
|
FiddleContextOptions fiddleOptions)
|
|
{
|
|
UINT dxgiFactoryFlags = 0;
|
|
|
|
#ifdef 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;
|
|
}
|
|
|
|
// Query the newer interface for GPU-based validation
|
|
ComPtr<ID3D12Debug1> debugController1;
|
|
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController1))))
|
|
{
|
|
debugController1->SetEnableGPUBasedValidation(TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ComPtr<IDXGIFactory4> factory;
|
|
VERIFY_OK(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
|
|
|
|
ComPtr<ID3D12Device> device;
|
|
|
|
D3DContextOptions contextOptions;
|
|
|
|
contextOptions.shaderCompilationMode = fiddleOptions.shaderCompilationMode;
|
|
|
|
if (fiddleOptions.d3d12UseWarpDevice)
|
|
{
|
|
ComPtr<IDXGIAdapter> warpAdapter;
|
|
DXGI_ADAPTER_DESC adapterDesc{};
|
|
VERIFY_OK(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));
|
|
warpAdapter->GetDesc(&adapterDesc);
|
|
SetIntelInformation(adapterDesc, contextOptions);
|
|
VERIFY_OK(D3D12CreateDevice(warpAdapter.Get(),
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
IID_PPV_ARGS(&device)));
|
|
printf("D3D12 device: %S\n", adapterDesc.Description);
|
|
}
|
|
else
|
|
{
|
|
ComPtr<IDXGIAdapter1> hardwareAdapter;
|
|
DXGI_ADAPTER_DESC adapterDesc{};
|
|
|
|
if (!GetHardwareAdapter(factory.Get(),
|
|
fiddleOptions.gpuNameFilter,
|
|
&hardwareAdapter))
|
|
{
|
|
std::cout << "Failed to get adapter with name filter \""
|
|
<< fiddleOptions.gpuNameFilter
|
|
<< "\" trying again with out one.\n";
|
|
// try again with now filter
|
|
if (!GetHardwareAdapter(factory.Get(), nullptr, &hardwareAdapter))
|
|
{
|
|
std::cerr << "Failed to get hardware adaptor for dx12.\n";
|
|
assert(false);
|
|
}
|
|
}
|
|
hardwareAdapter->GetDesc(&adapterDesc);
|
|
|
|
SetIntelInformation(adapterDesc, contextOptions);
|
|
|
|
VERIFY_OK(D3D12CreateDevice(hardwareAdapter.Get(),
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
IID_PPV_ARGS(&device)));
|
|
printf("D3D12 device: %S\n", adapterDesc.Description);
|
|
}
|
|
|
|
if (!device)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (fiddleOptions.disableRasterOrdering)
|
|
{
|
|
contextOptions.disableRasterizerOrderedViews = true;
|
|
// Also disable typed UAVs in atomic mode, to get more complete test
|
|
// coverage.
|
|
contextOptions.disableTypedUAVLoadStore = true;
|
|
}
|
|
|
|
return std::make_unique<FiddleContextD3D12PLS>(
|
|
std::move(factory),
|
|
std::move(device),
|
|
fiddleOptions.allowHeadlessRendering,
|
|
contextOptions);
|
|
}
|
|
|
|
#endif
|