diff --git a/BUILD.gn b/BUILD.gn index 38f7e6e0fe..aaa4e62497 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2193,6 +2193,10 @@ if (skia_enable_tools) { } } + if (skia_use_direct3d) { + sources += [ "tools/sk_app/win/D3D12WindowContext_win.cpp" ] + } + if (skia_use_dawn) { sources += [ "tools/sk_app/DawnWindowContext.cpp" ] if (is_linux) { diff --git a/src/gpu/GrBackendSurface.cpp b/src/gpu/GrBackendSurface.cpp index 8f07bb1ee2..72b9270ed6 100644 --- a/src/gpu/GrBackendSurface.cpp +++ b/src/gpu/GrBackendSurface.cpp @@ -926,6 +926,11 @@ void GrBackendRenderTarget::cleanup() { fVkInfo.cleanup(); } #endif +#ifdef SK_DIRECT3D + if (this->isValid() && GrBackendApi::kDirect3D == fBackend) { + fD3DInfo.cleanup(); + } +#endif } GrBackendRenderTarget::GrBackendRenderTarget(const GrBackendRenderTarget& that) : fIsValid(false) { diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp index 83af6b41c7..2502d08152 100644 --- a/src/gpu/GrDrawingManager.cpp +++ b/src/gpu/GrDrawingManager.cpp @@ -238,6 +238,7 @@ bool GrDrawingManager::flush(GrSurfaceProxy* proxies[], int numProxies, SkDEBUGCODE(this->validate()); if (kNone_GrFlushFlags == info.fFlags && !info.fNumSemaphores && !info.fFinishedProc && + access == SkSurface::BackendSurfaceAccess::kNoAccess && !externalRequests.hasRequests()) { bool canSkip = numProxies > 0; for (int i = 0; i < numProxies && canSkip; ++i) { diff --git a/src/gpu/d3d/GrD3DGpu.cpp b/src/gpu/d3d/GrD3DGpu.cpp index fb79434c9e..3cc41121dd 100644 --- a/src/gpu/d3d/GrD3DGpu.cpp +++ b/src/gpu/d3d/GrD3DGpu.cpp @@ -116,6 +116,7 @@ bool GrD3DGpu::submitDirectCommandList(SyncQueue sync) { } else if (result == GrD3DDirectCommandList::SubmitResult::kNoWork) { if (sync == SyncQueue::kForce) { this->waitForQueueCompletion(); + this->checkForFinishedCommandLists(); } return true; } @@ -1006,6 +1007,29 @@ void GrD3DGpu::addResourceBarriers(sk_sp resource, fCurrentDirectCommandList->resourceBarrier(std::move(resource), numBarriers, barriers); } +void GrD3DGpu::prepareSurfacesForBackendAccessAndExternalIO( + GrSurfaceProxy* proxies[], int numProxies, SkSurface::BackendSurfaceAccess access, + const GrPrepareForExternalIORequests& externalRequests) { + SkASSERT(numProxies >= 0); + SkASSERT(!numProxies || proxies); + + // prepare proxies by transitioning to PRESENT renderState + if (numProxies && access == SkSurface::BackendSurfaceAccess::kPresent) { + GrD3DTextureResource* resource; + for (int i = 0; i < numProxies; ++i) { + SkASSERT(proxies[i]->isInstantiated()); + if (GrTexture* tex = proxies[i]->peekTexture()) { + resource = static_cast(tex); + } else { + GrRenderTarget* rt = proxies[i]->peekRenderTarget(); + SkASSERT(rt); + resource = static_cast(rt); + } + resource->prepareForPresent(this); + } + } +} + bool GrD3DGpu::onSubmitToGpu(bool syncCpu) { if (syncCpu) { return this->submitDirectCommandList(SyncQueue::kForce); diff --git a/src/gpu/d3d/GrD3DGpu.h b/src/gpu/d3d/GrD3DGpu.h index 3c1c9738a1..3ffff983a2 100644 --- a/src/gpu/d3d/GrD3DGpu.h +++ b/src/gpu/d3d/GrD3DGpu.h @@ -190,6 +190,10 @@ private: finishedProc(finishedContext); } + void prepareSurfacesForBackendAccessAndExternalIO( + GrSurfaceProxy* proxies[], int numProxies, SkSurface::BackendSurfaceAccess access, + const GrPrepareForExternalIORequests& externalRequests) override; + bool onSubmitToGpu(bool syncCpu) override; GrBackendTexture onCreateBackendTexture(SkISize dimensions, diff --git a/src/gpu/d3d/GrD3DTextureResource.cpp b/src/gpu/d3d/GrD3DTextureResource.cpp index a62f2d8554..e54bf95e82 100644 --- a/src/gpu/d3d/GrD3DTextureResource.cpp +++ b/src/gpu/d3d/GrD3DTextureResource.cpp @@ -78,6 +78,10 @@ GrD3DTextureResource::~GrD3DTextureResource() { SkASSERT(!fResource); } +void GrD3DTextureResource::prepareForPresent(GrD3DGpu* gpu) { + this->setResourceState(gpu, D3D12_RESOURCE_STATE_PRESENT); +} + void GrD3DTextureResource::releaseResource(GrD3DGpu* gpu) { // TODO: do we need to migrate resource state if we change queues? if (fResource) { diff --git a/src/gpu/d3d/GrD3DTextureResource.h b/src/gpu/d3d/GrD3DTextureResource.h index bffb68988f..6f091ea406 100644 --- a/src/gpu/d3d/GrD3DTextureResource.h +++ b/src/gpu/d3d/GrD3DTextureResource.h @@ -55,6 +55,9 @@ public: void setResourceState(const GrD3DGpu* gpu, D3D12_RESOURCE_STATES newResourceState); + // Changes the layout to present + void prepareForPresent(GrD3DGpu* gpu); + unsigned int sampleQualityLevel() const { return fInfo.fSampleQualityLevel; } // This simply updates our tracking of the resourceState and does not actually do any gpu work. diff --git a/tools/sk_app/Window.h b/tools/sk_app/Window.h index 0648e970ce..f2d40360fc 100644 --- a/tools/sk_app/Window.h +++ b/tools/sk_app/Window.h @@ -60,6 +60,9 @@ public: #endif #ifdef SK_METAL kMetal_BackendType, +#endif +#ifdef SK_DIRECT3D + kDirect3D_BackendType, #endif kRaster_BackendType, diff --git a/tools/sk_app/win/D3D12WindowContext_win.cpp b/tools/sk_app/win/D3D12WindowContext_win.cpp new file mode 100644 index 0000000000..02cfe27b6e --- /dev/null +++ b/tools/sk_app/win/D3D12WindowContext_win.cpp @@ -0,0 +1,242 @@ +/* + * 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 "tools/sk_app/WindowContext.h" +#include "tools/sk_app/win/WindowContextFactory_win.h" + +#include "tools/gpu/d3d/D3DTestUtils.h" + +#include "include/core/SkSurface.h" +#include "include/gpu/d3d/GrD3DBackendContext.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%08x\n", result); \ + } \ + } while(false) + +using namespace Microsoft::WRL; + +namespace sk_app { + +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 getBackbufferSurface() override; + void swapBuffers() override; + + void resize(int width, int height) override; + void setDisplayParams(const DisplayParams& params) override; +private: + static constexpr int kNumFrames = 2; + + 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, 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 = GrContext::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 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; // TODO: support MSAA + + 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(); + + this->setupSurfaces(width, height); + + for (int i = 0; i < kNumFrames; ++i) { + fFenceValues[i] = 1; + } + 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, + D3D12_RESOURCE_STATE_PRESENT, + DXGI_FORMAT_R8G8B8A8_UNORM, + 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); + + // TODO: support MSAA + info.fResource = fBuffers[i]; + GrBackendRenderTarget backendRT(width, height, 1, info); + fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget( + 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 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::swapBuffers() { + SkSurface* surface = fSurfaces[fBufferIndex].get(); + + GrFlushInfo info; + surface->flush(SkSurface::BackendSurfaceAccess::kPresent, info); + + 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 + GrFlushInfo info; + info.fFlags = kSyncCpu_GrFlushFlag; + fContext->flush(info); + // 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(); +} + +namespace window_context_factory { + +std::unique_ptr MakeD3D12ForWin(HWND hwnd, const DisplayParams& params) { + std::unique_ptr ctx(new D3D12WindowContext(hwnd, params)); + if (!ctx->isValid()) { + return nullptr; + } + return ctx; +} + +} + +} //namespace sk_app diff --git a/tools/sk_app/win/WindowContextFactory_win.h b/tools/sk_app/win/WindowContextFactory_win.h index 33dd6d413b..c05a4f0acf 100644 --- a/tools/sk_app/win/WindowContextFactory_win.h +++ b/tools/sk_app/win/WindowContextFactory_win.h @@ -26,6 +26,10 @@ std::unique_ptr MakeGLForWin(HWND, const DisplayParams&); std::unique_ptr MakeANGLEForWin(HWND, const DisplayParams&); +#ifdef SK_DIRECT3D +std::unique_ptr MakeD3D12ForWin(HWND, const DisplayParams&); +#endif + #ifdef SK_DAWN std::unique_ptr MakeDawnD3D12ForWin(HWND, const DisplayParams&); #endif diff --git a/tools/sk_app/win/Window_win.cpp b/tools/sk_app/win/Window_win.cpp index 9afe0886b4..de001bfbb0 100644 --- a/tools/sk_app/win/Window_win.cpp +++ b/tools/sk_app/win/Window_win.cpp @@ -376,6 +376,12 @@ bool Window_win::attach(BackendType attachType) { fWindowContext = window_context_factory::MakeVulkanForWin(fHWnd, fRequestedDisplayParams); break; +#endif +#ifdef SK_DIRECT3D + case kDirect3D_BackendType: + fWindowContext = + window_context_factory::MakeD3D12ForWin(fHWnd, fRequestedDisplayParams); + break; #endif } this->onBackendCreated(); diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp index 6d41aad1ce..22ae76254e 100644 --- a/tools/viewer/Viewer.cpp +++ b/tools/viewer/Viewer.cpp @@ -83,15 +83,34 @@ Application* Application::Create(int argc, char** argv, void* platformData) { static DEFINE_string(slide, "", "Start on this sample."); static DEFINE_bool(list, false, "List samples?"); -#if defined(SK_VULKAN) -# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\"" -#elif defined(SK_METAL) && defined(SK_BUILD_FOR_MAC) -# define BACKENDS_STR "\"sw\", \"gl\", and \"mtl\"" -#elif defined(SK_DAWN) -# define BACKENDS_STR "\"sw\", \"gl\", and \"dawn\"" +#ifdef SK_GL +#define GL_BACKEND_STR ", \"gl\"" #else -# define BACKENDS_STR "\"sw\" and \"gl\"" +#define GL_BACKEND_STR #endif +#ifdef SK_VULKAN +#define VK_BACKEND_STR ", \"vk\"" +#else +#define VK_BACKEND_STR +#endif +#ifdef SK_METAL +#define MTL_BACKEND_STR ", \"mtl\"" +#else +#define MTL_BACKEND_STR +#endif +#ifdef SK_DIRECT3D +#define D3D_BACKEND_STR ", \"d3d\"" +#else +#define D3D_BACKEND_STR +#endif +#ifdef SK_DAWN +#define DAWN_BACKEND_STR ", \"dawn\"" +#else +#define DAWN_BACKEND_STR +#endif +#define BACKENDS_STR_EVALUATOR(sw, gl, vk, mtl, d3d, dawn) sw gl vk mtl d3d dawn +#define BACKENDS_STR BACKENDS_STR_EVALUATOR( \ + "\"sw\"", GL_BACKEND_STR, VK_BACKEND_STR, MTL_BACKEND_STR, D3D_BACKEND_STR, DAWN_BACKEND_STR) static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR "."); @@ -152,6 +171,9 @@ const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = { #endif #ifdef SK_METAL "Metal", +#endif +#ifdef SK_DIRECT3D + "Direct3D", #endif "Raster" }; @@ -177,6 +199,12 @@ static sk_app::Window::BackendType get_backend_type(const char* str) { return sk_app::Window::kMetal_BackendType; } else #endif +#ifdef SK_DIRECT3D + if (0 == strcmp(str, "d3d")) { + return sk_app::Window::kDirect3D_BackendType; + } else +#endif + if (0 == strcmp(str, "gl")) { return sk_app::Window::kNativeGL_BackendType; } else if (0 == strcmp(str, "sw")) { @@ -1683,6 +1711,10 @@ void Viewer::drawImGui() { #if defined(SK_METAL) ImGui::SameLine(); ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType); +#endif +#if defined(SK_DIRECT3D) + ImGui::SameLine(); + ImGui::RadioButton("Direct3D", &newBackend, sk_app::Window::kDirect3D_BackendType); #endif if (newBackend != fBackendType) { fDeferredActions.push_back([=]() {