| /* |
| * Copyright 2020 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkSurface.h" |
| #include "include/gpu/GrBackendSurface.h" |
| #include "include/gpu/GrDirectContext.h" |
| #include "include/gpu/GrTypes.h" |
| #include "include/gpu/d3d/GrD3DBackendContext.h" |
| #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
| #include "tools/gpu/d3d/D3DTestUtils.h" |
| #include "tools/window/DisplayParams.h" |
| #include "tools/window/WindowContext.h" |
| #include "tools/window/win/WindowContextFactory_win.h" |
| |
| #include <d3d12.h> |
| #include <dxgi1_4.h> |
| #include <wrl/client.h> |
| |
| #define GR_D3D_CALL_ERRCHECK(X) \ |
| do { \ |
| HRESULT result = X; \ |
| SkASSERT(SUCCEEDED(result)); \ |
| if (!SUCCEEDED(result)) { \ |
| SkDebugf("Failed Direct3D call. Error: 0x%08lx\n", result); \ |
| } \ |
| } while (false) |
| |
| using namespace Microsoft::WRL; |
| |
| using skwindow::DisplayParams; |
| using skwindow::WindowContext; |
| |
| namespace { |
| |
| class D3D12WindowContext : public WindowContext { |
| public: |
| D3D12WindowContext(HWND hwnd, const DisplayParams& params); |
| ~D3D12WindowContext() override; |
| void initializeContext(); |
| void destroyContext(); |
| void setupSurfaces(int width, int height); |
| |
| bool isValid() override { |
| return fDevice.get() != nullptr; |
| } |
| |
| sk_sp<SkSurface> getBackbufferSurface() override; |
| |
| void resize(int width, int height) override; |
| void setDisplayParams(const DisplayParams& params) override; |
| private: |
| inline static constexpr int kNumFrames = 2; |
| |
| void onSwapBuffers() override; |
| |
| HWND fWindow; |
| gr_cp<ID3D12Device> fDevice; |
| gr_cp<ID3D12CommandQueue> fQueue; |
| gr_cp<IDXGISwapChain3> fSwapChain; |
| gr_cp<ID3D12Resource> fBuffers[kNumFrames]; |
| sk_sp<SkSurface> fSurfaces[kNumFrames]; |
| |
| // Synchronization objects. |
| unsigned int fBufferIndex; |
| HANDLE fFenceEvent; |
| gr_cp<ID3D12Fence> fFence; |
| uint64_t fFenceValues[kNumFrames]; |
| }; |
| |
| D3D12WindowContext::D3D12WindowContext(HWND hwnd, const DisplayParams& params) |
| : WindowContext(params) |
| , fWindow(hwnd) { |
| |
| this->initializeContext(); |
| } |
| |
| D3D12WindowContext::~D3D12WindowContext() { |
| this->destroyContext(); |
| } |
| |
| void D3D12WindowContext::initializeContext() { |
| GrD3DBackendContext backendContext; |
| sk_gpu_test::CreateD3DBackendContext(&backendContext); |
| fDevice = backendContext.fDevice; |
| fQueue = backendContext.fQueue; |
| |
| fContext = GrDirectContext::MakeDirect3D(backendContext, fDisplayParams.fGrContextOptions); |
| SkASSERT(fContext); |
| |
| // Make the swapchain |
| RECT windowRect; |
| GetWindowRect(fWindow, &windowRect); |
| unsigned int width = windowRect.right - windowRect.left; |
| unsigned int height = windowRect.bottom - windowRect.top; |
| |
| UINT dxgiFactoryFlags = 0; |
| SkDEBUGCODE(dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;) |
| |
| gr_cp<IDXGIFactory4> factory; |
| GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); |
| |
| DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; |
| swapChainDesc.BufferCount = kNumFrames; |
| 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; |
| |
| gr_cp<IDXGISwapChain1> swapChain; |
| GR_D3D_CALL_ERRCHECK(factory->CreateSwapChainForHwnd( |
| fQueue.get(), fWindow, &swapChainDesc, nullptr, nullptr, &swapChain)); |
| |
| // We don't support fullscreen transitions. |
| GR_D3D_CALL_ERRCHECK(factory->MakeWindowAssociation(fWindow, DXGI_MWA_NO_ALT_ENTER)); |
| |
| GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&fSwapChain))); |
| |
| fBufferIndex = fSwapChain->GetCurrentBackBufferIndex(); |
| |
| fSampleCount = fDisplayParams.fMSAASampleCount; |
| |
| this->setupSurfaces(width, height); |
| |
| for (int i = 0; i < kNumFrames; ++i) { |
| fFenceValues[i] = 10000; // use a high value to make it easier to track these in PIX |
| } |
| GR_D3D_CALL_ERRCHECK(fDevice->CreateFence(fFenceValues[fBufferIndex], D3D12_FENCE_FLAG_NONE, |
| IID_PPV_ARGS(&fFence))); |
| |
| fFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); |
| SkASSERT(fFenceEvent); |
| |
| fWidth = width; |
| fHeight = height; |
| } |
| |
| void D3D12WindowContext::setupSurfaces(int width, int height) { |
| // set up base resource info |
| GrD3DTextureResourceInfo info(nullptr, |
| nullptr, |
| D3D12_RESOURCE_STATE_PRESENT, |
| DXGI_FORMAT_R8G8B8A8_UNORM, |
| 1, |
| 1, |
| 0); |
| for (int i = 0; i < kNumFrames; ++i) { |
| GR_D3D_CALL_ERRCHECK(fSwapChain->GetBuffer(i, IID_PPV_ARGS(&fBuffers[i]))); |
| |
| SkASSERT(fBuffers[i]->GetDesc().Width == (UINT64)width && |
| fBuffers[i]->GetDesc().Height == (UINT64)height); |
| |
| info.fResource = fBuffers[i]; |
| if (fSampleCount > 1) { |
| GrBackendTexture backendTexture(width, height, info); |
| fSurfaces[i] = SkSurfaces::WrapBackendTexture(fContext.get(), |
| backendTexture, |
| kTopLeft_GrSurfaceOrigin, |
| fSampleCount, |
| kRGBA_8888_SkColorType, |
| fDisplayParams.fColorSpace, |
| &fDisplayParams.fSurfaceProps); |
| } else { |
| GrBackendRenderTarget backendRT(width, height, info); |
| fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(), |
| backendRT, |
| kTopLeft_GrSurfaceOrigin, |
| kRGBA_8888_SkColorType, |
| fDisplayParams.fColorSpace, |
| &fDisplayParams.fSurfaceProps); |
| } |
| } |
| } |
| |
| void D3D12WindowContext::destroyContext() { |
| CloseHandle(fFenceEvent); |
| fFence.reset(nullptr); |
| |
| for (int i = 0; i < kNumFrames; ++i) { |
| fSurfaces[i].reset(nullptr); |
| fBuffers[i].reset(nullptr); |
| } |
| |
| fSwapChain.reset(nullptr); |
| fQueue.reset(nullptr); |
| fDevice.reset(nullptr); |
| } |
| |
| sk_sp<SkSurface> D3D12WindowContext::getBackbufferSurface() { |
| // Update the frame index. |
| const UINT64 currentFenceValue = fFenceValues[fBufferIndex]; |
| fBufferIndex = fSwapChain->GetCurrentBackBufferIndex(); |
| |
| // If the last frame for this buffer index is not done, wait until it is ready. |
| if (fFence->GetCompletedValue() < fFenceValues[fBufferIndex]) { |
| GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[fBufferIndex], fFenceEvent)); |
| WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE); |
| } |
| |
| // Set the fence value for the next frame. |
| fFenceValues[fBufferIndex] = currentFenceValue + 1; |
| |
| return fSurfaces[fBufferIndex]; |
| } |
| |
| void D3D12WindowContext::onSwapBuffers() { |
| SkSurface* surface = fSurfaces[fBufferIndex].get(); |
| |
| GrFlushInfo info; |
| fContext->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, info); |
| fContext->submit(); |
| |
| GR_D3D_CALL_ERRCHECK(fSwapChain->Present(1, 0)); |
| |
| // Schedule a Signal command in the queue. |
| GR_D3D_CALL_ERRCHECK(fQueue->Signal(fFence.get(), fFenceValues[fBufferIndex])); |
| } |
| |
| void D3D12WindowContext::resize(int width, int height) { |
| // Clean up any outstanding resources in command lists |
| fContext->flush(); |
| fContext->submit(GrSyncCpu::kYes); |
| |
| // release the previous surface and backbuffer resources |
| for (int i = 0; i < kNumFrames; ++i) { |
| // Let present complete |
| if (fFence->GetCompletedValue() < fFenceValues[i]) { |
| GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[i], fFenceEvent)); |
| WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE); |
| } |
| fSurfaces[i].reset(nullptr); |
| fBuffers[i].reset(nullptr); |
| } |
| |
| GR_D3D_CALL_ERRCHECK(fSwapChain->ResizeBuffers(0, width, height, |
| DXGI_FORMAT_R8G8B8A8_UNORM, 0)); |
| |
| this->setupSurfaces(width, height); |
| |
| fWidth = width; |
| fHeight = height; |
| } |
| |
| void D3D12WindowContext::setDisplayParams(const DisplayParams& params) { |
| this->destroyContext(); |
| fDisplayParams = params; |
| this->initializeContext(); |
| } |
| |
| } // anonymous namespace |
| |
| namespace skwindow { |
| |
| std::unique_ptr<WindowContext> MakeD3D12ForWin(HWND hwnd, const DisplayParams& params) { |
| std::unique_ptr<WindowContext> ctx(new D3D12WindowContext(hwnd, params)); |
| if (!ctx->isValid()) { |
| return nullptr; |
| } |
| return ctx; |
| } |
| |
| } // namespace skwindow |