/* * 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/ganesh/GrBackendSurface.h" #include "include/gpu/ganesh/GrDirectContext.h" #include "include/gpu/ganesh/GrTypes.h" #include "include/gpu/ganesh/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 #include #include #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, std::unique_ptr params); ~D3D12WindowContext() override; void initializeContext(); void destroyContext(); void setupSurfaces(int width, int height); bool isValid() override { return fDevice.get() != nullptr; } sk_sp getBackbufferSurface() override; void resize(int width, int height) override; void setDisplayParams(std::unique_ptr params) override; private: inline static constexpr int kNumFrames = 2; void onSwapBuffers() override; HWND fWindow; gr_cp fDevice; gr_cp fQueue; gr_cp fSwapChain; gr_cp fBuffers[kNumFrames]; sk_sp fSurfaces[kNumFrames]; // Synchronization objects. unsigned int fBufferIndex; HANDLE fFenceEvent; gr_cp fFence; uint64_t fFenceValues[kNumFrames]; }; D3D12WindowContext::D3D12WindowContext(HWND hwnd, std::unique_ptr params) : WindowContext(std::move(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->grContextOptions()); 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 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 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->msaaSampleCount(); 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->colorSpace(), &fDisplayParams->surfaceProps()); } else { GrBackendRenderTarget backendRT(width, height, info); fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, fDisplayParams->colorSpace(), &fDisplayParams->surfaceProps()); } } } 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 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(std::unique_ptr params) { this->destroyContext(); fDisplayParams = std::move(params); this->initializeContext(); } } // anonymous namespace namespace skwindow { std::unique_ptr MakeD3D12ForWin(HWND hwnd, std::unique_ptr params) { std::unique_ptr ctx(new D3D12WindowContext(hwnd, std::move(params))); if (!ctx->isValid()) { return nullptr; } return ctx; } } // namespace skwindow