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:
parent
5b2ede3d0d
commit
682a2f4de6
4
BUILD.gn
4
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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -60,6 +60,9 @@ public:
|
||||
#endif
|
||||
#ifdef SK_METAL
|
||||
kMetal_BackendType,
|
||||
#endif
|
||||
#ifdef SK_DIRECT3D
|
||||
kDirect3D_BackendType,
|
||||
#endif
|
||||
kRaster_BackendType,
|
||||
|
||||
|
242
tools/sk_app/win/D3D12WindowContext_win.cpp
Normal file
242
tools/sk_app/win/D3D12WindowContext_win.cpp
Normal 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
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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([=]() {
|
||||
|
Loading…
Reference in New Issue
Block a user