Add Direct3D swapchain support

Change-Id: I4d4025fb842eb937785509bc7947f85f28a98ab8
Bug: skia:9935
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/288551
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2020-05-13 16:54:09 -04:00 committed by Skia Commit-Bot
parent 5b2ede3d0d
commit 682a2f4de6
12 changed files with 339 additions and 7 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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<GrManagedResource> 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<GrD3DTexture*>(tex);
} else {
GrRenderTarget* rt = proxies[i]->peekRenderTarget();
SkASSERT(rt);
resource = static_cast<GrD3DRenderTarget*>(rt);
}
resource->prepareForPresent(this);
}
}
}
bool GrD3DGpu::onSubmitToGpu(bool syncCpu) {
if (syncCpu) {
return this->submitDirectCommandList(SyncQueue::kForce);

View File

@ -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,

View File

@ -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) {

View File

@ -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.

View File

@ -60,6 +60,9 @@ public:
#endif
#ifdef SK_METAL
kMetal_BackendType,
#endif
#ifdef SK_DIRECT3D
kDirect3D_BackendType,
#endif
kRaster_BackendType,

View File

@ -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 <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%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<SkSurface> 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<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 = 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<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; // TODO: support MSAA
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();
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<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::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<WindowContext> MakeD3D12ForWin(HWND hwnd, const DisplayParams& params) {
std::unique_ptr<WindowContext> ctx(new D3D12WindowContext(hwnd, params));
if (!ctx->isValid()) {
return nullptr;
}
return ctx;
}
}
} //namespace sk_app

View File

@ -26,6 +26,10 @@ std::unique_ptr<WindowContext> MakeGLForWin(HWND, const DisplayParams&);
std::unique_ptr<WindowContext> MakeANGLEForWin(HWND, const DisplayParams&);
#ifdef SK_DIRECT3D
std::unique_ptr<WindowContext> MakeD3D12ForWin(HWND, const DisplayParams&);
#endif
#ifdef SK_DAWN
std::unique_ptr<WindowContext> MakeDawnD3D12ForWin(HWND, const DisplayParams&);
#endif

View File

@ -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();

View File

@ -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([=]() {