xref: /third_party/skia/src/gpu/d3d/GrD3DGpu.cpp (revision cb93a386)
1/*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/gpu/d3d/GrD3DGpu.h"
9
10#include "include/gpu/GrBackendSurface.h"
11#include "include/gpu/d3d/GrD3DBackendContext.h"
12#include "src/core/SkConvertPixels.h"
13#include "src/core/SkMipmap.h"
14#include "src/gpu/GrBackendUtils.h"
15#include "src/gpu/GrDataUtils.h"
16#include "src/gpu/GrTexture.h"
17#include "src/gpu/GrThreadSafePipelineBuilder.h"
18#include "src/gpu/d3d/GrD3DAMDMemoryAllocator.h"
19#include "src/gpu/d3d/GrD3DAttachment.h"
20#include "src/gpu/d3d/GrD3DBuffer.h"
21#include "src/gpu/d3d/GrD3DCaps.h"
22#include "src/gpu/d3d/GrD3DOpsRenderPass.h"
23#include "src/gpu/d3d/GrD3DSemaphore.h"
24#include "src/gpu/d3d/GrD3DTexture.h"
25#include "src/gpu/d3d/GrD3DTextureRenderTarget.h"
26#include "src/gpu/d3d/GrD3DUtil.h"
27#include "src/sksl/SkSLCompiler.h"
28
29#if GR_TEST_UTILS
30#include <DXProgrammableCapture.h>
31#endif
32
33GrThreadSafePipelineBuilder* GrD3DGpu::pipelineBuilder() {
34    return nullptr;
35}
36
37sk_sp<GrThreadSafePipelineBuilder> GrD3DGpu::refPipelineBuilder() {
38    return nullptr;
39}
40
41
42sk_sp<GrGpu> GrD3DGpu::Make(const GrD3DBackendContext& backendContext,
43                            const GrContextOptions& contextOptions, GrDirectContext* direct) {
44    sk_sp<GrD3DMemoryAllocator> memoryAllocator = backendContext.fMemoryAllocator;
45    if (!memoryAllocator) {
46        // We were not given a memory allocator at creation
47        memoryAllocator = GrD3DAMDMemoryAllocator::Make(
48                backendContext.fAdapter.get(), backendContext.fDevice.get());
49    }
50    if (!memoryAllocator) {
51        SkDEBUGFAIL("No supplied Direct3D memory allocator and unable to create one internally.");
52        return nullptr;
53    }
54
55    return sk_sp<GrGpu>(new GrD3DGpu(direct, contextOptions, backendContext, memoryAllocator));
56}
57
58// This constant determines how many OutstandingCommandLists are allocated together as a block in
59// the deque. As such it needs to balance allocating too much memory vs. incurring
60// allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding
61// command lists we expect to see.
62static const int kDefaultOutstandingAllocCnt = 8;
63
64// constants have to be aligned to 256
65constexpr int kConstantAlignment = 256;
66
67GrD3DGpu::GrD3DGpu(GrDirectContext* direct, const GrContextOptions& contextOptions,
68                   const GrD3DBackendContext& backendContext,
69                   sk_sp<GrD3DMemoryAllocator> allocator)
70        : INHERITED(direct)
71        , fDevice(backendContext.fDevice)
72        , fQueue(backendContext.fQueue)
73        , fMemoryAllocator(std::move(allocator))
74        , fResourceProvider(this)
75        , fStagingBufferManager(this)
76        , fConstantsRingBuffer(this, 128 * 1024, kConstantAlignment, GrGpuBufferType::kVertex)
77        , fOutstandingCommandLists(sizeof(OutstandingCommandList), kDefaultOutstandingAllocCnt) {
78    this->initCapsAndCompiler(sk_make_sp<GrD3DCaps>(contextOptions,
79                                                    backendContext.fAdapter.get(),
80                                                    backendContext.fDevice.get()));
81
82    fCurrentDirectCommandList = fResourceProvider.findOrCreateDirectCommandList();
83    SkASSERT(fCurrentDirectCommandList);
84
85    SkASSERT(fCurrentFenceValue == 0);
86    GR_D3D_CALL_ERRCHECK(fDevice->CreateFence(fCurrentFenceValue, D3D12_FENCE_FLAG_NONE,
87                                              IID_PPV_ARGS(&fFence)));
88
89#if GR_TEST_UTILS
90    HRESULT getAnalysis = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&fGraphicsAnalysis));
91    if (FAILED(getAnalysis)) {
92        fGraphicsAnalysis = nullptr;
93    }
94#endif
95}
96
97GrD3DGpu::~GrD3DGpu() {
98    this->destroyResources();
99}
100
101void GrD3DGpu::destroyResources() {
102    if (fCurrentDirectCommandList) {
103        fCurrentDirectCommandList->close();
104        fCurrentDirectCommandList->reset();
105    }
106
107    // We need to make sure everything has finished on the queue.
108    this->waitForQueueCompletion();
109
110    SkDEBUGCODE(uint64_t fenceValue = fFence->GetCompletedValue();)
111
112    // We used a placement new for each object in fOutstandingCommandLists, so we're responsible
113    // for calling the destructor on each of them as well.
114    while (!fOutstandingCommandLists.empty()) {
115        OutstandingCommandList* list = (OutstandingCommandList*)fOutstandingCommandLists.front();
116        SkASSERT(list->fFenceValue <= fenceValue);
117        // No reason to recycle the command lists since we are destroying all resources anyways.
118        list->~OutstandingCommandList();
119        fOutstandingCommandLists.pop_front();
120    }
121
122    fStagingBufferManager.reset();
123
124    fResourceProvider.destroyResources();
125}
126
127GrOpsRenderPass* GrD3DGpu::onGetOpsRenderPass(
128        GrRenderTarget* rt,
129        bool /*useMSAASurface*/,
130        GrAttachment*,
131        GrSurfaceOrigin origin,
132        const SkIRect& bounds,
133        const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
134        const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
135        const SkTArray<GrSurfaceProxy*, true>& sampledProxies,
136        GrXferBarrierFlags renderPassXferBarriers) {
137    if (!fCachedOpsRenderPass) {
138        fCachedOpsRenderPass.reset(new GrD3DOpsRenderPass(this));
139    }
140
141    if (!fCachedOpsRenderPass->set(rt, origin, bounds, colorInfo, stencilInfo, sampledProxies)) {
142        return nullptr;
143    }
144    return fCachedOpsRenderPass.get();
145}
146
147bool GrD3DGpu::submitDirectCommandList(SyncQueue sync) {
148    SkASSERT(fCurrentDirectCommandList);
149
150    fResourceProvider.prepForSubmit();
151    for (int i = 0; i < fMipmapCPUDescriptors.count(); ++i) {
152        fResourceProvider.recycleShaderView(fMipmapCPUDescriptors[i]);
153    }
154    fMipmapCPUDescriptors.reset();
155
156    GrD3DDirectCommandList::SubmitResult result = fCurrentDirectCommandList->submit(fQueue.get());
157    if (result == GrD3DDirectCommandList::SubmitResult::kFailure) {
158        fCurrentDirectCommandList = fResourceProvider.findOrCreateDirectCommandList();
159        return false;
160    } else if (result == GrD3DDirectCommandList::SubmitResult::kNoWork) {
161        if (sync == SyncQueue::kForce) {
162            this->waitForQueueCompletion();
163            this->checkForFinishedCommandLists();
164        }
165        return true;
166    }
167
168    // We just submitted the command list so make sure all GrD3DPipelineState's mark their cached
169    // uniform data as dirty.
170    fResourceProvider.markPipelineStateUniformsDirty();
171
172    GrFence fence = this->insertFence();
173    new (fOutstandingCommandLists.push_back()) OutstandingCommandList(
174            std::move(fCurrentDirectCommandList), fence);
175
176    if (sync == SyncQueue::kForce) {
177        this->waitForQueueCompletion();
178    }
179
180    fCurrentDirectCommandList = fResourceProvider.findOrCreateDirectCommandList();
181
182    // This should be done after we have a new command list in case the freeing of any resources
183    // held by a finished command list causes us send a new command to the gpu (like changing the
184    // resource state.
185    this->checkForFinishedCommandLists();
186
187    SkASSERT(fCurrentDirectCommandList);
188    return true;
189}
190
191void GrD3DGpu::checkForFinishedCommandLists() {
192    uint64_t currentFenceValue = fFence->GetCompletedValue();
193
194    // Iterate over all the outstanding command lists to see if any have finished. The commands
195    // lists are in order from oldest to newest, so we start at the front to check if their fence
196    // value is less than the last signaled value. If so we pop it off and move onto the next.
197    // Repeat till we find a command list that has not finished yet (and all others afterwards are
198    // also guaranteed to not have finished).
199    OutstandingCommandList* front = (OutstandingCommandList*)fOutstandingCommandLists.front();
200    while (front && front->fFenceValue <= currentFenceValue) {
201        std::unique_ptr<GrD3DDirectCommandList> currList(std::move(front->fCommandList));
202        // Since we used placement new we are responsible for calling the destructor manually.
203        front->~OutstandingCommandList();
204        fOutstandingCommandLists.pop_front();
205        fResourceProvider.recycleDirectCommandList(std::move(currList));
206        front = (OutstandingCommandList*)fOutstandingCommandLists.front();
207    }
208}
209
210void GrD3DGpu::waitForQueueCompletion() {
211    if (fFence->GetCompletedValue() < fCurrentFenceValue) {
212        HANDLE fenceEvent;
213        fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
214        SkASSERT(fenceEvent);
215        GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fCurrentFenceValue, fenceEvent));
216        WaitForSingleObject(fenceEvent, INFINITE);
217        CloseHandle(fenceEvent);
218    }
219}
220
221void GrD3DGpu::submit(GrOpsRenderPass* renderPass) {
222    SkASSERT(fCachedOpsRenderPass.get() == renderPass);
223
224    fCachedOpsRenderPass->submit();
225    fCachedOpsRenderPass.reset();
226}
227
228void GrD3DGpu::endRenderPass(GrRenderTarget* target, GrSurfaceOrigin origin,
229                             const SkIRect& bounds) {
230    this->didWriteToSurface(target, origin, &bounds);
231}
232
233void GrD3DGpu::addFinishedProc(GrGpuFinishedProc finishedProc,
234                               GrGpuFinishedContext finishedContext) {
235    SkASSERT(finishedProc);
236    this->addFinishedCallback(GrRefCntedCallback::Make(finishedProc, finishedContext));
237}
238
239void GrD3DGpu::addFinishedCallback(sk_sp<GrRefCntedCallback> finishedCallback) {
240    SkASSERT(finishedCallback);
241    // Besides the current command list, we also add the finishedCallback to the newest outstanding
242    // command list. Our contract for calling the proc is that all previous submitted command lists
243    // have finished when we call it. However, if our current command list has no work when it is
244    // flushed it will drop its ref to the callback immediately. But the previous work may not have
245    // finished. It is safe to only add the proc to the newest outstanding commandlist cause that
246    // must finish after all previously submitted command lists.
247    OutstandingCommandList* back = (OutstandingCommandList*)fOutstandingCommandLists.back();
248    if (back) {
249        back->fCommandList->addFinishedCallback(finishedCallback);
250    }
251    fCurrentDirectCommandList->addFinishedCallback(std::move(finishedCallback));
252}
253
254sk_sp<GrD3DTexture> GrD3DGpu::createD3DTexture(SkISize dimensions,
255                                               DXGI_FORMAT dxgiFormat,
256                                               GrRenderable renderable,
257                                               int renderTargetSampleCnt,
258                                               SkBudgeted budgeted,
259                                               GrProtected isProtected,
260                                               int mipLevelCount,
261                                               GrMipmapStatus mipmapStatus) {
262    D3D12_RESOURCE_FLAGS usageFlags = D3D12_RESOURCE_FLAG_NONE;
263    if (renderable == GrRenderable::kYes) {
264        usageFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
265    }
266
267    // This desc refers to a texture that will be read by the client. Thus even if msaa is
268    // requested, this describes the resolved texture. Therefore we always have samples set
269    // to 1.
270    SkASSERT(mipLevelCount > 0);
271    D3D12_RESOURCE_DESC resourceDesc = {};
272    resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
273    // TODO: will use 4MB alignment for MSAA textures and 64KB for everything else
274    //       might want to manually set alignment to 4KB for smaller textures
275    resourceDesc.Alignment = 0;
276    resourceDesc.Width = dimensions.fWidth;
277    resourceDesc.Height = dimensions.fHeight;
278    resourceDesc.DepthOrArraySize = 1;
279    resourceDesc.MipLevels = mipLevelCount;
280    resourceDesc.Format = dxgiFormat;
281    resourceDesc.SampleDesc.Count = 1;
282    resourceDesc.SampleDesc.Quality = DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN;
283    resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;  // use driver-selected swizzle
284    resourceDesc.Flags = usageFlags;
285
286    if (renderable == GrRenderable::kYes) {
287        return GrD3DTextureRenderTarget::MakeNewTextureRenderTarget(
288                this, budgeted, dimensions, renderTargetSampleCnt, resourceDesc, isProtected,
289                mipmapStatus);
290    } else {
291        return GrD3DTexture::MakeNewTexture(this, budgeted, dimensions, resourceDesc, isProtected,
292                                            mipmapStatus);
293    }
294}
295
296sk_sp<GrTexture> GrD3DGpu::onCreateTexture(SkISize dimensions,
297                                           const GrBackendFormat& format,
298                                           GrRenderable renderable,
299                                           int renderTargetSampleCnt,
300                                           SkBudgeted budgeted,
301                                           GrProtected isProtected,
302                                           int mipLevelCount,
303                                           uint32_t levelClearMask) {
304    DXGI_FORMAT dxgiFormat;
305    SkAssertResult(format.asDxgiFormat(&dxgiFormat));
306    SkASSERT(!GrDxgiFormatIsCompressed(dxgiFormat));
307
308    GrMipmapStatus mipmapStatus = mipLevelCount > 1 ? GrMipmapStatus::kDirty
309                                                    : GrMipmapStatus::kNotAllocated;
310
311    sk_sp<GrD3DTexture> tex = this->createD3DTexture(dimensions, dxgiFormat, renderable,
312                                                     renderTargetSampleCnt, budgeted, isProtected,
313                                                     mipLevelCount, mipmapStatus);
314    if (!tex) {
315        return nullptr;
316    }
317
318    if (levelClearMask) {
319        // TODO
320    }
321
322    return std::move(tex);
323}
324
325static void copy_compressed_data(char* mapPtr, DXGI_FORMAT dxgiFormat,
326                                 D3D12_PLACED_SUBRESOURCE_FOOTPRINT* placedFootprints,
327                                 UINT* numRows, UINT64* rowSizeInBytes,
328                                 const void* compressedData, int numMipLevels) {
329    SkASSERT(compressedData && numMipLevels);
330    SkASSERT(GrDxgiFormatIsCompressed(dxgiFormat));
331    SkASSERT(mapPtr);
332
333    const char* src = static_cast<const char*>(compressedData);
334    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
335        // copy data into the buffer, skipping any trailing bytes
336        char* dst = mapPtr + placedFootprints[currentMipLevel].Offset;
337        SkRectMemcpy(dst, placedFootprints[currentMipLevel].Footprint.RowPitch,
338                     src, rowSizeInBytes[currentMipLevel], rowSizeInBytes[currentMipLevel],
339                     numRows[currentMipLevel]);
340        src += numRows[currentMipLevel] * rowSizeInBytes[currentMipLevel];
341    }
342}
343
344sk_sp<GrTexture> GrD3DGpu::onCreateCompressedTexture(SkISize dimensions,
345                                                     const GrBackendFormat& format,
346                                                     SkBudgeted budgeted,
347                                                     GrMipmapped mipMapped,
348                                                     GrProtected isProtected,
349                                                     const void* data, size_t dataSize) {
350    DXGI_FORMAT dxgiFormat;
351    SkAssertResult(format.asDxgiFormat(&dxgiFormat));
352    SkASSERT(GrDxgiFormatIsCompressed(dxgiFormat));
353
354    SkDEBUGCODE(SkImage::CompressionType compression = GrBackendFormatToCompressionType(format));
355    SkASSERT(dataSize == SkCompressedFormatDataSize(compression, dimensions,
356                                                    mipMapped == GrMipmapped::kYes));
357
358    int mipLevelCount = 1;
359    if (mipMapped == GrMipmapped::kYes) {
360        mipLevelCount = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
361    }
362    GrMipmapStatus mipmapStatus = mipLevelCount > 1 ? GrMipmapStatus::kValid
363                                                    : GrMipmapStatus::kNotAllocated;
364
365    sk_sp<GrD3DTexture> d3dTex = this->createD3DTexture(dimensions, dxgiFormat, GrRenderable::kNo,
366                                                     1, budgeted, isProtected,
367                                                     mipLevelCount, mipmapStatus);
368    if (!d3dTex) {
369        return nullptr;
370    }
371
372    ID3D12Resource* d3dResource = d3dTex->d3dResource();
373    SkASSERT(d3dResource);
374    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
375    // Either upload only the first miplevel or all miplevels
376    SkASSERT(1 == mipLevelCount || mipLevelCount == (int)desc.MipLevels);
377
378    SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
379    SkAutoTMalloc<UINT> numRows(mipLevelCount);
380    SkAutoTMalloc<UINT64> rowSizeInBytes(mipLevelCount);
381    UINT64 combinedBufferSize;
382    // We reset the width and height in the description to match our subrectangle size
383    // so we don't end up allocating more space than we need.
384    desc.Width = dimensions.width();
385    desc.Height = dimensions.height();
386    fDevice->GetCopyableFootprints(&desc, 0, mipLevelCount, 0, placedFootprints.get(),
387                                   numRows.get(), rowSizeInBytes.get(), &combinedBufferSize);
388    SkASSERT(combinedBufferSize);
389
390    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
391            combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
392    if (!slice.fBuffer) {
393        return nullptr;
394    }
395
396    char* bufferData = (char*)slice.fOffsetMapPtr;
397
398    copy_compressed_data(bufferData, desc.Format, placedFootprints.get(), numRows.get(),
399                         rowSizeInBytes.get(), data, mipLevelCount);
400
401    // Update the offsets in the footprints to be relative to the slice's offset
402    for (int i = 0; i < mipLevelCount; ++i) {
403        placedFootprints[i].Offset += slice.fOffset;
404    }
405
406    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
407    fCurrentDirectCommandList->copyBufferToTexture(d3dBuffer, d3dTex.get(), mipLevelCount,
408                                                   placedFootprints.get(), 0, 0);
409
410    return std::move(d3dTex);
411}
412
413sk_sp<GrTexture> GrD3DGpu::onCreateCompressedTexture(SkISize dimensions,
414                                                const GrBackendFormat& format,
415                                                SkBudgeted budgeted,
416                                                GrMipmapped mipMapped,
417                                                GrProtected isProtected,
418                                                OH_NativeBuffer* nativeBuffer,
419                                                size_t bufferSize) {
420    SkASSERT(!"unimplemented");
421    return nullptr;
422}
423
424static int get_surface_sample_cnt(GrSurface* surf) {
425    if (const GrRenderTarget* rt = surf->asRenderTarget()) {
426        return rt->numSamples();
427    }
428    return 0;
429}
430
431bool GrD3DGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
432                   const SkIPoint& dstPoint) {
433
434    if (src->isProtected() && !dst->isProtected()) {
435        SkDebugf("Can't copy from protected memory to non-protected");
436        return false;
437    }
438
439    int dstSampleCnt = get_surface_sample_cnt(dst);
440    int srcSampleCnt = get_surface_sample_cnt(src);
441
442    GrD3DTextureResource* dstTexResource;
443    GrD3DTextureResource* srcTexResource;
444    GrRenderTarget* dstRT = dst->asRenderTarget();
445    if (dstRT) {
446        GrD3DRenderTarget* d3dRT = static_cast<GrD3DRenderTarget*>(dstRT);
447        dstTexResource = d3dRT->numSamples() > 1 ? d3dRT->msaaTextureResource() : d3dRT;
448    } else {
449        SkASSERT(dst->asTexture());
450        dstTexResource = static_cast<GrD3DTexture*>(dst->asTexture());
451    }
452    GrRenderTarget* srcRT = src->asRenderTarget();
453    if (srcRT) {
454        GrD3DRenderTarget* d3dRT = static_cast<GrD3DRenderTarget*>(srcRT);
455        srcTexResource = d3dRT->numSamples() > 1 ? d3dRT->msaaTextureResource() : d3dRT;
456    } else {
457        SkASSERT(src->asTexture());
458        srcTexResource = static_cast<GrD3DTexture*>(src->asTexture());
459    }
460
461    DXGI_FORMAT dstFormat = dstTexResource->dxgiFormat();
462    DXGI_FORMAT srcFormat = srcTexResource->dxgiFormat();
463
464    if (this->d3dCaps().canCopyAsResolve(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt)) {
465        this->copySurfaceAsResolve(dst, src, srcRect, dstPoint);
466        return true;
467    }
468
469    if (this->d3dCaps().canCopyTexture(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt)) {
470        this->copySurfaceAsCopyTexture(dst, src, dstTexResource, srcTexResource, srcRect, dstPoint);
471        return true;
472    }
473
474    return false;
475}
476
477void GrD3DGpu::copySurfaceAsCopyTexture(GrSurface* dst, GrSurface* src,
478                                        GrD3DTextureResource* dstResource,
479                                        GrD3DTextureResource* srcResource,
480                                        const SkIRect& srcRect, const SkIPoint& dstPoint) {
481#ifdef SK_DEBUG
482    int dstSampleCnt = get_surface_sample_cnt(dst);
483    int srcSampleCnt = get_surface_sample_cnt(src);
484    DXGI_FORMAT dstFormat = dstResource->dxgiFormat();
485    DXGI_FORMAT srcFormat;
486    SkAssertResult(dst->backendFormat().asDxgiFormat(&srcFormat));
487    SkASSERT(this->d3dCaps().canCopyTexture(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt));
488#endif
489    if (src->isProtected() && !dst->isProtected()) {
490        SkDebugf("Can't copy from protected memory to non-protected");
491        return;
492    }
493
494    dstResource->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
495    srcResource->setResourceState(this, D3D12_RESOURCE_STATE_COPY_SOURCE);
496
497    D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
498    dstLocation.pResource = dstResource->d3dResource();
499    dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
500    dstLocation.SubresourceIndex = 0;
501
502    D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
503    srcLocation.pResource = srcResource->d3dResource();
504    srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
505    srcLocation.SubresourceIndex = 0;
506
507    D3D12_BOX srcBox = {};
508    srcBox.left = srcRect.fLeft;
509    srcBox.top = srcRect.fTop;
510    srcBox.right = srcRect.fRight;
511    srcBox.bottom = srcRect.fBottom;
512    srcBox.front = 0;
513    srcBox.back = 1;
514    // TODO: use copyResource if copying full resource and sizes match
515    fCurrentDirectCommandList->copyTextureRegionToTexture(dstResource->resource(),
516                                                          &dstLocation,
517                                                          dstPoint.fX, dstPoint.fY,
518                                                          srcResource->resource(),
519                                                          &srcLocation,
520                                                          &srcBox);
521
522    SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
523                                        srcRect.width(), srcRect.height());
524    // The rect is already in device space so we pass in kTopLeft so no flip is done.
525    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
526}
527
528void GrD3DGpu::copySurfaceAsResolve(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
529                                    const SkIPoint& dstPoint) {
530    GrD3DRenderTarget* srcRT = static_cast<GrD3DRenderTarget*>(src->asRenderTarget());
531    SkASSERT(srcRT);
532
533    this->resolveTexture(dst, dstPoint.fX, dstPoint.fY, srcRT, srcRect);
534    SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
535                                        srcRect.width(), srcRect.height());
536    // The rect is already in device space so we pass in kTopLeft so no flip is done.
537    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
538}
539
540void GrD3DGpu::resolveTexture(GrSurface* dst, int32_t dstX, int32_t dstY,
541                              GrD3DRenderTarget* src, const SkIRect& srcIRect) {
542    SkASSERT(dst);
543    SkASSERT(src && src->numSamples() > 1 && src->msaaTextureResource());
544
545    D3D12_RECT srcRect = { srcIRect.fLeft, srcIRect.fTop, srcIRect.fRight, srcIRect.fBottom };
546
547    GrD3DTextureResource* dstTextureResource;
548    GrRenderTarget* dstRT = dst->asRenderTarget();
549    if (dstRT) {
550        dstTextureResource = static_cast<GrD3DRenderTarget*>(dstRT);
551    } else {
552        SkASSERT(dst->asTexture());
553        dstTextureResource = static_cast<GrD3DTexture*>(dst->asTexture());
554    }
555
556    dstTextureResource->setResourceState(this, D3D12_RESOURCE_STATE_RESOLVE_DEST);
557    src->msaaTextureResource()->setResourceState(this, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
558
559    fCurrentDirectCommandList->resolveSubresourceRegion(dstTextureResource, dstX, dstY,
560                                                        src->msaaTextureResource(), &srcRect);
561}
562
563void GrD3DGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect& resolveRect) {
564    SkASSERT(target->numSamples() > 1);
565    GrD3DRenderTarget* rt = static_cast<GrD3DRenderTarget*>(target);
566    SkASSERT(rt->msaaTextureResource() && rt != rt->msaaTextureResource());
567
568    this->resolveTexture(target, resolveRect.fLeft, resolveRect.fTop, rt, resolveRect);
569}
570
571bool GrD3DGpu::onReadPixels(GrSurface* surface,
572                            SkIRect rect,
573                            GrColorType surfaceColorType,
574                            GrColorType dstColorType,
575                            void* buffer,
576                            size_t rowBytes) {
577    SkASSERT(surface);
578
579    if (surfaceColorType != dstColorType) {
580        return false;
581    }
582
583    GrD3DTextureResource* texResource = nullptr;
584    GrD3DRenderTarget* rt = static_cast<GrD3DRenderTarget*>(surface->asRenderTarget());
585    if (rt) {
586        texResource = rt;
587    } else {
588        texResource = static_cast<GrD3DTexture*>(surface->asTexture());
589    }
590
591    if (!texResource) {
592        return false;
593    }
594
595    D3D12_RESOURCE_DESC desc = texResource->d3dResource()->GetDesc();
596    D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedFootprint;
597    UINT64 transferTotalBytes;
598    fDevice->GetCopyableFootprints(&desc, 0, 1, 0, &placedFootprint,
599                                   nullptr, nullptr, &transferTotalBytes);
600    SkASSERT(transferTotalBytes);
601    // TODO: implement some way of reusing buffers instead of making a new one every time.
602    sk_sp<GrGpuBuffer> transferBuffer = this->createBuffer(transferTotalBytes,
603                                                           GrGpuBufferType::kXferGpuToCpu,
604                                                           kDynamic_GrAccessPattern);
605
606    this->readOrTransferPixels(texResource, rect, transferBuffer, placedFootprint);
607    this->submitDirectCommandList(SyncQueue::kForce);
608
609    // Copy back to CPU buffer
610    size_t bpp = GrColorTypeBytesPerPixel(dstColorType);
611    if (GrDxgiFormatBytesPerBlock(texResource->dxgiFormat()) != bpp) {
612        return false;
613    }
614    size_t tightRowBytes = bpp * rect.width();
615
616    const void* mappedMemory = transferBuffer->map();
617
618    SkRectMemcpy(buffer,
619                 rowBytes,
620                 mappedMemory,
621                 placedFootprint.Footprint.RowPitch,
622                 tightRowBytes,
623                 rect.height());
624
625    transferBuffer->unmap();
626
627    return true;
628}
629
630void GrD3DGpu::readOrTransferPixels(GrD3DTextureResource* texResource,
631                                    SkIRect rect,
632                                    sk_sp<GrGpuBuffer> transferBuffer,
633                                    const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& placedFootprint) {
634    // Set up src location and box
635    D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
636    srcLocation.pResource = texResource->d3dResource();
637    SkASSERT(srcLocation.pResource);
638    srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
639    srcLocation.SubresourceIndex = 0;
640
641    D3D12_BOX srcBox = {};
642    srcBox.left = rect.left();
643    srcBox.top = rect.top();
644    srcBox.right = rect.right();
645    srcBox.bottom = rect.bottom();
646    srcBox.front = 0;
647    srcBox.back = 1;
648
649    // Set up dst location
650    D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
651    dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
652    dstLocation.PlacedFootprint = placedFootprint;
653    GrD3DBuffer* d3dBuf = static_cast<GrD3DBuffer*>(transferBuffer.get());
654    dstLocation.pResource = d3dBuf->d3dResource();
655
656    // Need to change the resource state to COPY_SOURCE in order to download from it
657    texResource->setResourceState(this, D3D12_RESOURCE_STATE_COPY_SOURCE);
658
659    fCurrentDirectCommandList->copyTextureRegionToBuffer(transferBuffer, &dstLocation, 0, 0,
660                                                         texResource->resource(), &srcLocation,
661                                                         &srcBox);
662}
663
664bool GrD3DGpu::onWritePixels(GrSurface* surface,
665                             SkIRect rect,
666                             GrColorType surfaceColorType,
667                             GrColorType srcColorType,
668                             const GrMipLevel texels[],
669                             int mipLevelCount,
670                             bool prepForTexSampling) {
671    GrD3DTexture* d3dTex = static_cast<GrD3DTexture*>(surface->asTexture());
672    if (!d3dTex) {
673        return false;
674    }
675
676    // Make sure we have at least the base level
677    if (!mipLevelCount || !texels[0].fPixels) {
678        return false;
679    }
680
681    SkASSERT(!GrDxgiFormatIsCompressed(d3dTex->dxgiFormat()));
682    bool success = false;
683
684    // Need to change the resource state to COPY_DEST in order to upload to it
685    d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
686
687    SkASSERT(mipLevelCount <= d3dTex->maxMipmapLevel() + 1);
688    success = this->uploadToTexture(d3dTex, rect, srcColorType, texels, mipLevelCount);
689
690    if (prepForTexSampling) {
691        d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
692    }
693
694    return success;
695}
696
697bool GrD3DGpu::uploadToTexture(GrD3DTexture* tex,
698                               SkIRect rect,
699                               GrColorType colorType,
700                               const GrMipLevel* texels,
701                               int mipLevelCount) {
702    SkASSERT(this->d3dCaps().isFormatTexturable(tex->dxgiFormat()));
703    // The assumption is either that we have no mipmaps, or that our rect is the entire texture
704    SkASSERT(mipLevelCount == 1 || rect == SkIRect::MakeSize(tex->dimensions()));
705
706    // We assume that if the texture has mip levels, we either upload to all the levels or just the
707    // first.
708    SkASSERT(mipLevelCount == 1 || mipLevelCount == (tex->maxMipmapLevel() + 1));
709
710    if (rect.isEmpty()) {
711        return false;
712    }
713
714    SkASSERT(this->d3dCaps().surfaceSupportsWritePixels(tex));
715    SkASSERT(this->d3dCaps().areColorTypeAndFormatCompatible(colorType, tex->backendFormat()));
716
717    ID3D12Resource* d3dResource = tex->d3dResource();
718    SkASSERT(d3dResource);
719    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
720    // Either upload only the first miplevel or all miplevels
721    SkASSERT(1 == mipLevelCount || mipLevelCount == (int)desc.MipLevels);
722
723    if (1 == mipLevelCount && !texels[0].fPixels) {
724        return true;   // no data to upload
725    }
726
727    for (int i = 0; i < mipLevelCount; ++i) {
728        // We do not allow any gaps in the mip data
729        if (!texels[i].fPixels) {
730            return false;
731        }
732    }
733
734    SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
735    UINT64 combinedBufferSize;
736    // We reset the width and height in the description to match our subrectangle size
737    // so we don't end up allocating more space than we need.
738    desc.Width = rect.width();
739    desc.Height = rect.height();
740    fDevice->GetCopyableFootprints(&desc, 0, mipLevelCount, 0, placedFootprints.get(),
741                                   nullptr, nullptr, &combinedBufferSize);
742    size_t bpp = GrColorTypeBytesPerPixel(colorType);
743    SkASSERT(combinedBufferSize);
744
745    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
746            combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
747    if (!slice.fBuffer) {
748        return false;
749    }
750
751    char* bufferData = (char*)slice.fOffsetMapPtr;
752
753    int currentWidth = rect.width();
754    int currentHeight = rect.height();
755    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
756        if (texels[currentMipLevel].fPixels) {
757
758            const size_t trimRowBytes = currentWidth * bpp;
759            const size_t srcRowBytes = texels[currentMipLevel].fRowBytes;
760
761            char* dst = bufferData + placedFootprints[currentMipLevel].Offset;
762
763            // copy data into the buffer, skipping any trailing bytes
764            const char* src = (const char*)texels[currentMipLevel].fPixels;
765            SkRectMemcpy(dst, placedFootprints[currentMipLevel].Footprint.RowPitch,
766                         src, srcRowBytes, trimRowBytes, currentHeight);
767        }
768        currentWidth = std::max(1, currentWidth / 2);
769        currentHeight = std::max(1, currentHeight / 2);
770    }
771
772    // Update the offsets in the footprints to be relative to the slice's offset
773    for (int i = 0; i < mipLevelCount; ++i) {
774        placedFootprints[i].Offset += slice.fOffset;
775    }
776
777    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
778    fCurrentDirectCommandList->copyBufferToTexture(d3dBuffer,
779                                                   tex,
780                                                   mipLevelCount,
781                                                   placedFootprints.get(),
782                                                   rect.left(),
783                                                   rect.top());
784
785    if (mipLevelCount < (int)desc.MipLevels) {
786        tex->markMipmapsDirty();
787    }
788
789    return true;
790}
791
792bool GrD3DGpu::onTransferPixelsTo(GrTexture* texture,
793                                  SkIRect rect,
794                                  GrColorType surfaceColorType,
795                                  GrColorType bufferColorType,
796                                  sk_sp<GrGpuBuffer> transferBuffer,
797                                  size_t bufferOffset,
798                                  size_t rowBytes) {
799    if (!this->currentCommandList()) {
800        return false;
801    }
802
803    if (!transferBuffer) {
804        return false;
805    }
806
807    size_t bpp = GrColorTypeBytesPerPixel(bufferColorType);
808    if (GrBackendFormatBytesPerPixel(texture->backendFormat()) != bpp) {
809        return false;
810    }
811
812    // D3D requires offsets for texture transfers to be aligned to this value
813    if (SkToBool(bufferOffset & (D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1))) {
814        return false;
815    }
816
817    GrD3DTexture* d3dTex = static_cast<GrD3DTexture*>(texture);
818    if (!d3dTex) {
819        return false;
820    }
821
822    SkDEBUGCODE(DXGI_FORMAT format = d3dTex->dxgiFormat());
823
824    // Can't transfer compressed data
825    SkASSERT(!GrDxgiFormatIsCompressed(format));
826
827    SkASSERT(GrDxgiFormatBytesPerBlock(format) == GrColorTypeBytesPerPixel(bufferColorType));
828
829    SkASSERT(SkIRect::MakeSize(texture->dimensions()).contains(rect));
830
831    // Set up copy region
832    D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedFootprint = {};
833    ID3D12Resource* d3dResource = d3dTex->d3dResource();
834    SkASSERT(d3dResource);
835    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
836    desc.Width = rect.width();
837    desc.Height = rect.height();
838    UINT64 totalBytes;
839    fDevice->GetCopyableFootprints(&desc, 0, 1, 0, &placedFootprint,
840                                   nullptr, nullptr, &totalBytes);
841    placedFootprint.Offset = bufferOffset;
842
843    // Change state of our target so it can be copied to
844    d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
845
846    // Copy the buffer to the image.
847    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(transferBuffer.get())->d3dResource();
848    fCurrentDirectCommandList->copyBufferToTexture(d3dBuffer,
849                                                   d3dTex,
850                                                   1,
851                                                   &placedFootprint,
852                                                   rect.left(),
853                                                   rect.top());
854    this->currentCommandList()->addGrBuffer(std::move(transferBuffer));
855
856    d3dTex->markMipmapsDirty();
857    return true;
858}
859
860bool GrD3DGpu::onTransferPixelsFrom(GrSurface* surface,
861                                    SkIRect rect,
862                                    GrColorType surfaceColorType,
863                                    GrColorType bufferColorType,
864                                    sk_sp<GrGpuBuffer> transferBuffer,
865                                    size_t offset) {
866    if (!this->currentCommandList()) {
867        return false;
868    }
869    SkASSERT(surface);
870    SkASSERT(transferBuffer);
871    // TODO
872    //if (fProtectedContext == GrProtected::kYes) {
873    //    return false;
874    //}
875
876    // D3D requires offsets for texture transfers to be aligned to this value
877    if (SkToBool(offset & (D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1))) {
878        return false;
879    }
880
881    GrD3DTextureResource* texResource = nullptr;
882    GrD3DRenderTarget* rt = static_cast<GrD3DRenderTarget*>(surface->asRenderTarget());
883    if (rt) {
884        texResource = rt;
885    } else {
886        texResource = static_cast<GrD3DTexture*>(surface->asTexture());
887    }
888
889    if (!texResource) {
890        return false;
891    }
892
893    SkDEBUGCODE(DXGI_FORMAT format = texResource->dxgiFormat());
894    SkASSERT(GrDxgiFormatBytesPerBlock(format) == GrColorTypeBytesPerPixel(bufferColorType));
895
896    D3D12_RESOURCE_DESC desc = texResource->d3dResource()->GetDesc();
897    desc.Width = rect.width();
898    desc.Height = rect.height();
899    D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedFootprint;
900    UINT64 transferTotalBytes;
901    fDevice->GetCopyableFootprints(&desc, 0, 1, offset, &placedFootprint,
902                                   nullptr, nullptr, &transferTotalBytes);
903    SkASSERT(transferTotalBytes);
904
905    this->readOrTransferPixels(texResource, rect, transferBuffer, placedFootprint);
906
907    // TODO: It's not clear how to ensure the transfer is done before we read from the buffer,
908    // other than maybe doing a resource state transition.
909
910    return true;
911}
912
913static bool check_resource_info(const GrD3DTextureResourceInfo& info) {
914    if (!info.fResource.get()) {
915        return false;
916    }
917    return true;
918}
919
920static bool check_tex_resource_info(const GrD3DCaps& caps, const GrD3DTextureResourceInfo& info) {
921    if (!caps.isFormatTexturable(info.fFormat)) {
922        return false;
923    }
924    // We don't support sampling from multisampled textures.
925    if (info.fSampleCount != 1) {
926        return false;
927    }
928    return true;
929}
930
931static bool check_rt_resource_info(const GrD3DCaps& caps, const GrD3DTextureResourceInfo& info,
932                                int sampleCnt) {
933    if (!caps.isFormatRenderable(info.fFormat, sampleCnt)) {
934        return false;
935    }
936    return true;
937}
938
939sk_sp<GrTexture> GrD3DGpu::onWrapBackendTexture(const GrBackendTexture& tex,
940                                                GrWrapOwnership,
941                                                GrWrapCacheable wrapType,
942                                                GrIOType ioType) {
943    GrD3DTextureResourceInfo textureInfo;
944    if (!tex.getD3DTextureResourceInfo(&textureInfo)) {
945        return nullptr;
946    }
947
948    if (!check_resource_info(textureInfo)) {
949        return nullptr;
950    }
951
952    if (!check_tex_resource_info(this->d3dCaps(), textureInfo)) {
953        return nullptr;
954    }
955
956    // TODO: support protected context
957    if (tex.isProtected()) {
958        return nullptr;
959    }
960
961    sk_sp<GrD3DResourceState> state = tex.getGrD3DResourceState();
962    SkASSERT(state);
963    return GrD3DTexture::MakeWrappedTexture(this, tex.dimensions(), wrapType, ioType, textureInfo,
964                                            std::move(state));
965}
966
967sk_sp<GrTexture> GrD3DGpu::onWrapCompressedBackendTexture(const GrBackendTexture& tex,
968                                                          GrWrapOwnership ownership,
969                                                          GrWrapCacheable wrapType) {
970    return this->onWrapBackendTexture(tex, ownership, wrapType, kRead_GrIOType);
971}
972
973sk_sp<GrTexture> GrD3DGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex,
974                                                          int sampleCnt,
975                                                          GrWrapOwnership ownership,
976                                                          GrWrapCacheable cacheable) {
977    GrD3DTextureResourceInfo textureInfo;
978    if (!tex.getD3DTextureResourceInfo(&textureInfo)) {
979        return nullptr;
980    }
981
982    if (!check_resource_info(textureInfo)) {
983        return nullptr;
984    }
985
986    if (!check_tex_resource_info(this->d3dCaps(), textureInfo)) {
987        return nullptr;
988    }
989    if (!check_rt_resource_info(this->d3dCaps(), textureInfo, sampleCnt)) {
990        return nullptr;
991    }
992
993    // TODO: support protected context
994    if (tex.isProtected()) {
995        return nullptr;
996    }
997
998    sampleCnt = this->d3dCaps().getRenderTargetSampleCount(sampleCnt, textureInfo.fFormat);
999
1000    sk_sp<GrD3DResourceState> state = tex.getGrD3DResourceState();
1001    SkASSERT(state);
1002
1003    return GrD3DTextureRenderTarget::MakeWrappedTextureRenderTarget(this, tex.dimensions(),
1004                                                                    sampleCnt, cacheable,
1005                                                                    textureInfo, std::move(state));
1006}
1007
1008sk_sp<GrRenderTarget> GrD3DGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& rt) {
1009    GrD3DTextureResourceInfo info;
1010    if (!rt.getD3DTextureResourceInfo(&info)) {
1011        return nullptr;
1012    }
1013
1014    if (!check_resource_info(info)) {
1015        return nullptr;
1016    }
1017
1018    if (!check_rt_resource_info(this->d3dCaps(), info, rt.sampleCnt())) {
1019        return nullptr;
1020    }
1021
1022    // TODO: support protected context
1023    if (rt.isProtected()) {
1024        return nullptr;
1025    }
1026
1027    sk_sp<GrD3DResourceState> state = rt.getGrD3DResourceState();
1028
1029    sk_sp<GrD3DRenderTarget> tgt = GrD3DRenderTarget::MakeWrappedRenderTarget(
1030            this, rt.dimensions(), rt.sampleCnt(), info, std::move(state));
1031
1032    // We don't allow the client to supply a premade stencil buffer. We always create one if needed.
1033    SkASSERT(!rt.stencilBits());
1034    if (tgt) {
1035        SkASSERT(tgt->canAttemptStencilAttachment(tgt->numSamples() > 1));
1036    }
1037
1038    return std::move(tgt);
1039}
1040
1041static bool is_odd(int x) {
1042    return x > 1 && SkToBool(x & 0x1);
1043}
1044
1045// TODO: enable when sRGB shader supported
1046//static bool is_srgb(DXGI_FORMAT format) {
1047//    // the only one we support at the moment
1048//    return (format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
1049//}
1050
1051static bool is_bgra(DXGI_FORMAT format) {
1052    // the only one we support at the moment
1053    return (format == DXGI_FORMAT_B8G8R8A8_UNORM);
1054}
1055
1056bool GrD3DGpu::onRegenerateMipMapLevels(GrTexture * tex) {
1057    auto * d3dTex = static_cast<GrD3DTexture*>(tex);
1058    SkASSERT(tex->textureType() == GrTextureType::k2D);
1059    int width = tex->width();
1060    int height = tex->height();
1061
1062    // determine if we can read from and mipmap this format
1063    const GrD3DCaps & caps = this->d3dCaps();
1064    if (!caps.isFormatTexturable(d3dTex->dxgiFormat()) ||
1065        !caps.mipmapSupport()) {
1066        return false;
1067    }
1068
1069    sk_sp<GrD3DTexture> uavTexture;
1070    sk_sp<GrD3DTexture> bgraAliasTexture;
1071    DXGI_FORMAT originalFormat = d3dTex->dxgiFormat();
1072    D3D12_RESOURCE_DESC originalDesc = d3dTex->d3dResource()->GetDesc();
1073    // if the format is unordered accessible and resource flag is set, use resource for uav
1074    if (caps.isFormatUnorderedAccessible(originalFormat) &&
1075        (originalDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) {
1076        uavTexture = sk_ref_sp(d3dTex);
1077    } else {
1078        // need to make a copy and use that for our uav
1079        D3D12_RESOURCE_DESC uavDesc = originalDesc;
1080        uavDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
1081        // if the format is unordered accessible, copy to resource with same format and flag set
1082        if (!caps.isFormatUnorderedAccessible(originalFormat)) {
1083            // for the BGRA and sRGB cases, we find a suitable RGBA format to use instead
1084            if (is_bgra(originalFormat)) {
1085                uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1086                // Technically if this support is not available we should not be doing
1087                // aliasing. However, on Intel the BGRA and RGBA swizzle appears to be
1088                // the same so it still works. We may need to disable BGRA support
1089                // on a case-by-base basis if this doesn't hold true in general.
1090                if (caps.standardSwizzleLayoutSupport()) {
1091                    uavDesc.Layout = D3D12_TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE;
1092                }
1093            // TODO: enable when sRGB shader supported
1094            //} else if (is_srgb(originalFormat)) {
1095            //    uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1096            } else {
1097                return false;
1098            }
1099        }
1100        // TODO: make this a scratch texture
1101        GrProtected grProtected = tex->isProtected() ? GrProtected::kYes : GrProtected::kNo;
1102        uavTexture = GrD3DTexture::MakeNewTexture(this, SkBudgeted::kNo, tex->dimensions(),
1103                                                  uavDesc, grProtected, GrMipmapStatus::kDirty);
1104        if (!uavTexture) {
1105            return false;
1106        }
1107
1108        d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_SOURCE);
1109        if (!caps.isFormatUnorderedAccessible(originalFormat) && is_bgra(originalFormat)) {
1110            // for BGRA, we alias this uavTexture with a BGRA texture and copy to that
1111            bgraAliasTexture = GrD3DTexture::MakeAliasingTexture(this, uavTexture, originalDesc,
1112                                                                 D3D12_RESOURCE_STATE_COPY_DEST);
1113            // make the BGRA version the active alias
1114            this->currentCommandList()->aliasingBarrier(nullptr,
1115                                                        nullptr,
1116                                                        bgraAliasTexture->resource(),
1117                                                        bgraAliasTexture->d3dResource());
1118            // copy top miplevel to bgraAliasTexture (should already be in COPY_DEST state)
1119            this->currentCommandList()->copyTextureToTexture(bgraAliasTexture.get(), d3dTex, 0);
1120            // make the RGBA version the active alias
1121            this->currentCommandList()->aliasingBarrier(bgraAliasTexture->resource(),
1122                                                        bgraAliasTexture->d3dResource(),
1123                                                        uavTexture->resource(),
1124                                                        uavTexture->d3dResource());
1125        } else {
1126            // copy top miplevel to uavTexture
1127            uavTexture->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
1128            this->currentCommandList()->copyTextureToTexture(uavTexture.get(), d3dTex, 0);
1129        }
1130    }
1131
1132    uint32_t levelCount = d3dTex->mipLevels();
1133    // SkMipmap doesn't include the base level in the level count so we have to add 1
1134    SkASSERT((int)levelCount == SkMipmap::ComputeLevelCount(tex->width(), tex->height()) + 1);
1135
1136    sk_sp<GrD3DRootSignature> rootSig = fResourceProvider.findOrCreateRootSignature(1, 1);
1137    this->currentCommandList()->setComputeRootSignature(rootSig);
1138
1139    // TODO: use linear vs. srgb shader based on texture format
1140    sk_sp<GrD3DPipeline> pipeline = this->resourceProvider().findOrCreateMipmapPipeline();
1141    SkASSERT(pipeline);
1142    this->currentCommandList()->setPipelineState(std::move(pipeline));
1143
1144    // set sampler
1145    GrSamplerState samplerState(SkFilterMode::kLinear, SkMipmapMode::kNearest);
1146    std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> samplers(1);
1147    samplers[0] = fResourceProvider.findOrCreateCompatibleSampler(samplerState);
1148    this->currentCommandList()->addSampledTextureRef(uavTexture.get());
1149    sk_sp<GrD3DDescriptorTable> samplerTable = fResourceProvider.findOrCreateSamplerTable(samplers);
1150
1151    // Transition the top subresource to be readable in the compute shader
1152    D3D12_RESOURCE_STATES currentResourceState = uavTexture->currentState();
1153    D3D12_RESOURCE_TRANSITION_BARRIER barrier;
1154    barrier.pResource = uavTexture->d3dResource();
1155    barrier.Subresource = 0;
1156    barrier.StateBefore = currentResourceState;
1157    barrier.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1158    this->addResourceBarriers(uavTexture->resource(), 1, &barrier);
1159
1160    // Generate the miplevels
1161    for (unsigned int dstMip = 1; dstMip < levelCount; ++dstMip) {
1162        unsigned int srcMip = dstMip - 1;
1163        width = std::max(1, width / 2);
1164        height = std::max(1, height / 2);
1165
1166        unsigned int sampleMode = 0;
1167        if (is_odd(width) && is_odd(height)) {
1168            sampleMode = 1;
1169        } else if (is_odd(width)) {
1170            sampleMode = 2;
1171        } else if (is_odd(height)) {
1172            sampleMode = 3;
1173        }
1174
1175        // set constants
1176        struct {
1177            SkSize inverseSize;
1178            uint32_t mipLevel;
1179            uint32_t sampleMode;
1180        } constantData = { {1.f / width, 1.f / height}, srcMip, sampleMode };
1181
1182        D3D12_GPU_VIRTUAL_ADDRESS constantsAddress =
1183            fResourceProvider.uploadConstantData(&constantData, sizeof(constantData));
1184        this->currentCommandList()->setComputeRootConstantBufferView(
1185                (unsigned int)GrD3DRootSignature::ParamIndex::kConstantBufferView,
1186                constantsAddress);
1187
1188        std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> shaderViews;
1189        // create SRV
1190        GrD3DDescriptorHeap::CPUHandle srvHandle =
1191                fResourceProvider.createShaderResourceView(uavTexture->d3dResource(), srcMip, 1);
1192        shaderViews.push_back(srvHandle.fHandle);
1193        fMipmapCPUDescriptors.push_back(srvHandle);
1194        // create UAV
1195        GrD3DDescriptorHeap::CPUHandle uavHandle =
1196                fResourceProvider.createUnorderedAccessView(uavTexture->d3dResource(), dstMip);
1197        shaderViews.push_back(uavHandle.fHandle);
1198        fMipmapCPUDescriptors.push_back(uavHandle);
1199
1200        // set up shaderView descriptor table
1201        sk_sp<GrD3DDescriptorTable> srvTable =
1202                fResourceProvider.findOrCreateShaderViewTable(shaderViews);
1203
1204        // bind both descriptor tables
1205        this->currentCommandList()->setDescriptorHeaps(srvTable->heap(), samplerTable->heap());
1206        this->currentCommandList()->setComputeRootDescriptorTable(
1207                (unsigned int)GrD3DRootSignature::ParamIndex::kShaderViewDescriptorTable,
1208                srvTable->baseGpuDescriptor());
1209        this->currentCommandList()->setComputeRootDescriptorTable(
1210                static_cast<unsigned int>(GrD3DRootSignature::ParamIndex::kSamplerDescriptorTable),
1211                samplerTable->baseGpuDescriptor());
1212
1213        // Transition resource state of dstMip subresource so we can write to it
1214        barrier.Subresource = dstMip;
1215        barrier.StateBefore = currentResourceState;
1216        barrier.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
1217        this->addResourceBarriers(uavTexture->resource(), 1, &barrier);
1218
1219        // Using the form (x+7)/8 ensures that the remainder is covered as well
1220        this->currentCommandList()->dispatch((width+7)/8, (height+7)/8);
1221
1222        // guarantee UAV writes have completed
1223        this->currentCommandList()->uavBarrier(uavTexture->resource(), uavTexture->d3dResource());
1224
1225        // Transition resource state of dstMip subresource so we can read it in the next stage
1226        barrier.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
1227        barrier.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1228        this->addResourceBarriers(uavTexture->resource(), 1, &barrier);
1229    }
1230
1231    // copy back if necessary
1232    if (uavTexture.get() != d3dTex) {
1233        d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
1234        if (bgraAliasTexture) {
1235            // make the BGRA version the active alias
1236            this->currentCommandList()->aliasingBarrier(uavTexture->resource(),
1237                                                        uavTexture->d3dResource(),
1238                                                        bgraAliasTexture->resource(),
1239                                                        bgraAliasTexture->d3dResource());
1240            // copy from bgraAliasTexture to d3dTex
1241            bgraAliasTexture->setResourceState(this, D3D12_RESOURCE_STATE_COPY_SOURCE);
1242            this->currentCommandList()->copyTextureToTexture(d3dTex, bgraAliasTexture.get());
1243        } else {
1244            barrier.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1245            barrier.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1246            barrier.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
1247            this->addResourceBarriers(uavTexture->resource(), 1, &barrier);
1248            this->currentCommandList()->copyTextureToTexture(d3dTex, uavTexture.get());
1249        }
1250    } else {
1251        // For simplicity our resource state tracking considers all subresources to have the same
1252        // state. However, we've changed that state one subresource at a time without going through
1253        // the tracking system, so we need to patch up the resource states back to the original.
1254        barrier.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1255        barrier.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1256        barrier.StateAfter = currentResourceState;
1257        this->addResourceBarriers(d3dTex->resource(), 1, &barrier);
1258    }
1259
1260    return true;
1261}
1262
1263sk_sp<GrGpuBuffer> GrD3DGpu::onCreateBuffer(size_t sizeInBytes, GrGpuBufferType type,
1264                                             GrAccessPattern accessPattern, const void* data) {
1265    sk_sp<GrD3DBuffer> buffer = GrD3DBuffer::Make(this, sizeInBytes, type, accessPattern);
1266    if (data && buffer) {
1267        buffer->updateData(data, sizeInBytes);
1268    }
1269
1270    return std::move(buffer);
1271}
1272
1273sk_sp<GrAttachment> GrD3DGpu::makeStencilAttachment(const GrBackendFormat& /*colorFormat*/,
1274                                                    SkISize dimensions, int numStencilSamples) {
1275    DXGI_FORMAT sFmt = this->d3dCaps().preferredStencilFormat();
1276
1277    fStats.incStencilAttachmentCreates();
1278    return GrD3DAttachment::MakeStencil(this, dimensions, numStencilSamples, sFmt);
1279}
1280
1281bool GrD3DGpu::createTextureResourceForBackendSurface(DXGI_FORMAT dxgiFormat,
1282                                                      SkISize dimensions,
1283                                                      GrTexturable texturable,
1284                                                      GrRenderable renderable,
1285                                                      GrMipmapped mipMapped,
1286                                                      int sampleCnt,
1287                                                      GrD3DTextureResourceInfo* info,
1288                                                      GrProtected isProtected) {
1289    SkASSERT(texturable == GrTexturable::kYes || renderable == GrRenderable::kYes);
1290
1291    if (this->protectedContext() != (isProtected == GrProtected::kYes)) {
1292        return false;
1293    }
1294
1295    if (texturable == GrTexturable::kYes && !this->d3dCaps().isFormatTexturable(dxgiFormat)) {
1296        return false;
1297    }
1298
1299    if (renderable == GrRenderable::kYes && !this->d3dCaps().isFormatRenderable(dxgiFormat, 1)) {
1300        return false;
1301    }
1302
1303    int numMipLevels = 1;
1304    if (mipMapped == GrMipmapped::kYes) {
1305        numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
1306    }
1307
1308    // create the texture
1309    D3D12_RESOURCE_FLAGS usageFlags = D3D12_RESOURCE_FLAG_NONE;
1310    if (renderable == GrRenderable::kYes) {
1311        usageFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
1312    }
1313
1314    D3D12_RESOURCE_DESC resourceDesc = {};
1315    resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
1316    resourceDesc.Alignment = 0;  // use default alignment
1317    resourceDesc.Width = dimensions.fWidth;
1318    resourceDesc.Height = dimensions.fHeight;
1319    resourceDesc.DepthOrArraySize = 1;
1320    resourceDesc.MipLevels = numMipLevels;
1321    resourceDesc.Format = dxgiFormat;
1322    resourceDesc.SampleDesc.Count = sampleCnt;
1323    resourceDesc.SampleDesc.Quality = DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN;
1324    resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;  // use driver-selected swizzle
1325    resourceDesc.Flags = usageFlags;
1326
1327    D3D12_CLEAR_VALUE* clearValuePtr = nullptr;
1328    D3D12_CLEAR_VALUE clearValue = {};
1329    if (renderable == GrRenderable::kYes) {
1330        clearValue.Format = dxgiFormat;
1331        // Assume transparent black
1332        clearValue.Color[0] = 0;
1333        clearValue.Color[1] = 0;
1334        clearValue.Color[2] = 0;
1335        clearValue.Color[3] = 0;
1336        clearValuePtr = &clearValue;
1337    }
1338
1339    D3D12_RESOURCE_STATES initialState = (renderable == GrRenderable::kYes)
1340                                                 ? D3D12_RESOURCE_STATE_RENDER_TARGET
1341                                                 : D3D12_RESOURCE_STATE_COPY_DEST;
1342    if (!GrD3DTextureResource::InitTextureResourceInfo(this, resourceDesc, initialState,
1343                                                       isProtected, clearValuePtr, info)) {
1344        SkDebugf("Failed to init texture resource info\n");
1345        return false;
1346    }
1347
1348    return true;
1349}
1350
1351GrBackendTexture GrD3DGpu::onCreateBackendTexture(SkISize dimensions,
1352                                                  const GrBackendFormat& format,
1353                                                  GrRenderable renderable,
1354                                                  GrMipmapped mipMapped,
1355                                                  GrProtected isProtected) {
1356    const GrD3DCaps& caps = this->d3dCaps();
1357
1358    if (this->protectedContext() != (isProtected == GrProtected::kYes)) {
1359        return {};
1360    }
1361
1362    DXGI_FORMAT dxgiFormat;
1363    if (!format.asDxgiFormat(&dxgiFormat)) {
1364        return {};
1365    }
1366
1367    // TODO: move the texturability check up to GrGpu::createBackendTexture and just assert here
1368    if (!caps.isFormatTexturable(dxgiFormat)) {
1369        return {};
1370    }
1371
1372    GrD3DTextureResourceInfo info;
1373    if (!this->createTextureResourceForBackendSurface(dxgiFormat, dimensions, GrTexturable::kYes,
1374                                                      renderable, mipMapped, 1, &info,
1375                                                      isProtected)) {
1376        return {};
1377    }
1378
1379    return GrBackendTexture(dimensions.width(), dimensions.height(), info);
1380}
1381
1382static bool copy_color_data(const GrD3DCaps& caps,
1383                            char* mapPtr,
1384                            DXGI_FORMAT dxgiFormat,
1385                            SkISize dimensions,
1386                            D3D12_PLACED_SUBRESOURCE_FOOTPRINT* placedFootprints,
1387                            std::array<float, 4> color) {
1388    auto colorType = caps.getFormatColorType(dxgiFormat);
1389    if (colorType == GrColorType::kUnknown) {
1390        return false;
1391    }
1392    GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, dimensions);
1393    if (!GrClearImage(ii, mapPtr, placedFootprints[0].Footprint.RowPitch, color)) {
1394        return false;
1395    }
1396
1397    return true;
1398}
1399
1400bool GrD3DGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
1401                                     sk_sp<GrRefCntedCallback> finishedCallback,
1402                                     std::array<float, 4> color) {
1403    GrD3DTextureResourceInfo info;
1404    SkAssertResult(backendTexture.getD3DTextureResourceInfo(&info));
1405    SkASSERT(!GrDxgiFormatIsCompressed(info.fFormat));
1406
1407    sk_sp<GrD3DResourceState> state = backendTexture.getGrD3DResourceState();
1408    SkASSERT(state);
1409    sk_sp<GrD3DTexture> texture =
1410            GrD3DTexture::MakeWrappedTexture(this, backendTexture.dimensions(),
1411                                             GrWrapCacheable::kNo,
1412                                             kRW_GrIOType, info, std::move(state));
1413    if (!texture) {
1414        return false;
1415    }
1416
1417    GrD3DDirectCommandList* cmdList = this->currentCommandList();
1418    if (!cmdList) {
1419        return false;
1420    }
1421
1422    texture->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
1423
1424    ID3D12Resource* d3dResource = texture->d3dResource();
1425    SkASSERT(d3dResource);
1426    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
1427    unsigned int mipLevelCount = 1;
1428    if (backendTexture.fMipmapped == GrMipmapped::kYes) {
1429        mipLevelCount = SkMipmap::ComputeLevelCount(backendTexture.dimensions()) + 1;
1430    }
1431    SkASSERT(mipLevelCount == info.fLevelCount);
1432    SkAutoSTMalloc<15, D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
1433    UINT numRows;
1434    UINT64 rowSizeInBytes;
1435    UINT64 combinedBufferSize;
1436    // We reuse the same top-level buffer area for all levels, hence passing 1 for level count.
1437    fDevice->GetCopyableFootprints(&desc,
1438                                   /* first resource  */ 0,
1439                                   /* mip level count */ 1,
1440                                   /* base offset     */ 0,
1441                                   placedFootprints.get(),
1442                                   &numRows,
1443                                   &rowSizeInBytes,
1444                                   &combinedBufferSize);
1445    SkASSERT(combinedBufferSize);
1446
1447    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
1448            combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
1449    if (!slice.fBuffer) {
1450        return false;
1451    }
1452
1453    char* bufferData = (char*)slice.fOffsetMapPtr;
1454    SkASSERT(bufferData);
1455    if (!copy_color_data(this->d3dCaps(),
1456                         bufferData,
1457                         info.fFormat,
1458                         backendTexture.dimensions(),
1459                         placedFootprints,
1460                         color)) {
1461        return false;
1462    }
1463    // Update the offsets in the footprint to be relative to the slice's offset
1464    placedFootprints[0].Offset += slice.fOffset;
1465    // Since we're sharing data for all the levels, set all the upper level footprints to the base.
1466    UINT w = placedFootprints[0].Footprint.Width;
1467    UINT h = placedFootprints[0].Footprint.Height;
1468    for (unsigned int i = 1; i < mipLevelCount; ++i) {
1469        w = std::max(1U, w/2);
1470        h = std::max(1U, h/2);
1471        placedFootprints[i].Offset = placedFootprints[0].Offset;
1472        placedFootprints[i].Footprint.Format   = placedFootprints[0].Footprint.Format;
1473        placedFootprints[i].Footprint.Width    = w;
1474        placedFootprints[i].Footprint.Height   = h;
1475        placedFootprints[i].Footprint.Depth    = 1;
1476        placedFootprints[i].Footprint.RowPitch = placedFootprints[0].Footprint.RowPitch;
1477    }
1478
1479    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
1480    cmdList->copyBufferToTexture(d3dBuffer,
1481                                 texture.get(),
1482                                 mipLevelCount,
1483                                 placedFootprints.get(),
1484                                 /*left*/ 0,
1485                                 /*top */ 0);
1486
1487    if (finishedCallback) {
1488        this->addFinishedCallback(std::move(finishedCallback));
1489    }
1490
1491    return true;
1492}
1493
1494GrBackendTexture GrD3DGpu::onCreateCompressedBackendTexture(
1495    SkISize dimensions, const GrBackendFormat& format, GrMipmapped mipMapped,
1496    GrProtected isProtected) {
1497    return this->onCreateBackendTexture(dimensions, format, GrRenderable::kNo, mipMapped,
1498                                        isProtected);
1499}
1500
1501bool GrD3DGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
1502                                                sk_sp<GrRefCntedCallback> finishedCallback,
1503                                                const void* data,
1504                                                size_t size) {
1505    GrD3DTextureResourceInfo info;
1506    SkAssertResult(backendTexture.getD3DTextureResourceInfo(&info));
1507
1508    sk_sp<GrD3DResourceState> state = backendTexture.getGrD3DResourceState();
1509    SkASSERT(state);
1510    sk_sp<GrD3DTexture> texture = GrD3DTexture::MakeWrappedTexture(this,
1511                                                                   backendTexture.dimensions(),
1512                                                                   GrWrapCacheable::kNo,
1513                                                                   kRW_GrIOType,
1514                                                                   info,
1515                                                                   std::move(state));
1516    if (!texture) {
1517        return false;
1518    }
1519
1520    GrD3DDirectCommandList* cmdList = this->currentCommandList();
1521    if (!cmdList) {
1522        return false;
1523    }
1524
1525    texture->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
1526
1527    ID3D12Resource* d3dResource = texture->d3dResource();
1528    SkASSERT(d3dResource);
1529    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
1530    unsigned int mipLevelCount = 1;
1531    if (backendTexture.hasMipmaps()) {
1532        mipLevelCount = SkMipmap::ComputeLevelCount(backendTexture.dimensions().width(),
1533                                                    backendTexture.dimensions().height()) + 1;
1534    }
1535    SkASSERT(mipLevelCount == info.fLevelCount);
1536    SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
1537    UINT64 combinedBufferSize;
1538    SkAutoTMalloc<UINT> numRows(mipLevelCount);
1539    SkAutoTMalloc<UINT64> rowSizeInBytes(mipLevelCount);
1540    fDevice->GetCopyableFootprints(&desc,
1541                                   0,
1542                                   mipLevelCount,
1543                                   0,
1544                                   placedFootprints.get(),
1545                                   numRows.get(),
1546                                   rowSizeInBytes.get(),
1547                                   &combinedBufferSize);
1548    SkASSERT(combinedBufferSize);
1549    SkASSERT(GrDxgiFormatIsCompressed(info.fFormat));
1550
1551    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
1552            combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
1553    if (!slice.fBuffer) {
1554        return false;
1555    }
1556
1557    char* bufferData = (char*)slice.fOffsetMapPtr;
1558    SkASSERT(bufferData);
1559    copy_compressed_data(bufferData,
1560                         info.fFormat,
1561                         placedFootprints.get(),
1562                         numRows.get(),
1563                         rowSizeInBytes.get(),
1564                         data,
1565                         info.fLevelCount);
1566
1567    // Update the offsets in the footprints to be relative to the slice's offset
1568    for (unsigned int i = 0; i < mipLevelCount; ++i) {
1569        placedFootprints[i].Offset += slice.fOffset;
1570    }
1571
1572    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
1573    cmdList->copyBufferToTexture(d3dBuffer,
1574                                 texture.get(),
1575                                 mipLevelCount,
1576                                 placedFootprints.get(),
1577                                 0,
1578                                 0);
1579
1580    if (finishedCallback) {
1581        this->addFinishedCallback(std::move(finishedCallback));
1582    }
1583
1584    return true;
1585}
1586
1587void GrD3DGpu::deleteBackendTexture(const GrBackendTexture& tex) {
1588    SkASSERT(GrBackendApi::kDirect3D == tex.fBackend);
1589    // Nothing to do here, will get cleaned up when the GrBackendTexture object goes away
1590}
1591
1592bool GrD3DGpu::compile(const GrProgramDesc&, const GrProgramInfo&) {
1593    return false;
1594}
1595
1596#if GR_TEST_UTILS
1597bool GrD3DGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
1598    SkASSERT(GrBackendApi::kDirect3D == tex.backend());
1599
1600    GrD3DTextureResourceInfo info;
1601    if (!tex.getD3DTextureResourceInfo(&info)) {
1602        return false;
1603    }
1604    ID3D12Resource* textureResource = info.fResource.get();
1605    if (!textureResource) {
1606        return false;
1607    }
1608    return !(textureResource->GetDesc().Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE);
1609}
1610
1611GrBackendRenderTarget GrD3DGpu::createTestingOnlyBackendRenderTarget(SkISize dimensions,
1612                                                                     GrColorType colorType,
1613                                                                     int sampleCnt,
1614                                                                     GrProtected isProtected) {
1615    if (dimensions.width()  > this->caps()->maxRenderTargetSize() ||
1616        dimensions.height() > this->caps()->maxRenderTargetSize()) {
1617        return {};
1618    }
1619
1620    DXGI_FORMAT dxgiFormat = this->d3dCaps().getFormatFromColorType(colorType);
1621
1622    GrD3DTextureResourceInfo info;
1623    if (!this->createTextureResourceForBackendSurface(dxgiFormat, dimensions, GrTexturable::kNo,
1624                                                      GrRenderable::kYes, GrMipmapped::kNo,
1625                                                      sampleCnt, &info, isProtected)) {
1626        return {};
1627    }
1628
1629    return GrBackendRenderTarget(dimensions.width(), dimensions.height(), info);
1630}
1631
1632void GrD3DGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
1633    SkASSERT(GrBackendApi::kDirect3D == rt.backend());
1634
1635    GrD3DTextureResourceInfo info;
1636    if (rt.getD3DTextureResourceInfo(&info)) {
1637        this->submitToGpu(true);
1638        // Nothing else to do here, will get cleaned up when the GrBackendRenderTarget
1639        // is deleted.
1640    }
1641}
1642
1643void GrD3DGpu::testingOnly_startCapture() {
1644    if (fGraphicsAnalysis) {
1645        fGraphicsAnalysis->BeginCapture();
1646    }
1647}
1648
1649void GrD3DGpu::testingOnly_endCapture() {
1650    if (fGraphicsAnalysis) {
1651        fGraphicsAnalysis->EndCapture();
1652    }
1653}
1654#endif
1655
1656///////////////////////////////////////////////////////////////////////////////
1657
1658void GrD3DGpu::addResourceBarriers(sk_sp<GrManagedResource> resource,
1659                                   int numBarriers,
1660                                   D3D12_RESOURCE_TRANSITION_BARRIER* barriers) const {
1661    SkASSERT(fCurrentDirectCommandList);
1662    SkASSERT(resource);
1663
1664    fCurrentDirectCommandList->resourceBarrier(std::move(resource), numBarriers, barriers);
1665}
1666
1667void GrD3DGpu::addBufferResourceBarriers(GrD3DBuffer* buffer,
1668                                         int numBarriers,
1669                                         D3D12_RESOURCE_TRANSITION_BARRIER* barriers) const {
1670    SkASSERT(fCurrentDirectCommandList);
1671    SkASSERT(buffer);
1672
1673    fCurrentDirectCommandList->resourceBarrier(nullptr, numBarriers, barriers);
1674    fCurrentDirectCommandList->addGrBuffer(sk_ref_sp<const GrBuffer>(buffer));
1675}
1676
1677
1678void GrD3DGpu::prepareSurfacesForBackendAccessAndStateUpdates(
1679        SkSpan<GrSurfaceProxy*> proxies,
1680        SkSurface::BackendSurfaceAccess access,
1681        const GrBackendSurfaceMutableState* newState) {
1682    // prepare proxies by transitioning to PRESENT renderState
1683    if (!proxies.empty() && access == SkSurface::BackendSurfaceAccess::kPresent) {
1684        GrD3DTextureResource* resource;
1685        for (GrSurfaceProxy* proxy : proxies) {
1686            SkASSERT(proxy->isInstantiated());
1687            if (GrTexture* tex = proxy->peekTexture()) {
1688                resource = static_cast<GrD3DTexture*>(tex);
1689            } else {
1690                GrRenderTarget* rt = proxy->peekRenderTarget();
1691                SkASSERT(rt);
1692                resource = static_cast<GrD3DRenderTarget*>(rt);
1693            }
1694            resource->prepareForPresent(this);
1695        }
1696    }
1697}
1698
1699void GrD3DGpu::takeOwnershipOfBuffer(sk_sp<GrGpuBuffer> buffer) {
1700    fCurrentDirectCommandList->addGrBuffer(std::move(buffer));
1701}
1702
1703bool GrD3DGpu::onSubmitToGpu(bool syncCpu) {
1704    if (syncCpu) {
1705        return this->submitDirectCommandList(SyncQueue::kForce);
1706    } else {
1707        return this->submitDirectCommandList(SyncQueue::kSkip);
1708    }
1709}
1710
1711std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrD3DGpu::makeSemaphore(bool) {
1712    return GrD3DSemaphore::Make(this);
1713}
1714std::unique_ptr<GrSemaphore> GrD3DGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
1715                                                            GrSemaphoreWrapType /* wrapType */,
1716                                                            GrWrapOwnership /* ownership */) {
1717    SkASSERT(this->caps()->semaphoreSupport());
1718    GrD3DFenceInfo fenceInfo;
1719    if (!semaphore.getD3DFenceInfo(&fenceInfo)) {
1720        return nullptr;
1721    }
1722    return GrD3DSemaphore::MakeWrapped(fenceInfo);
1723}
1724
1725void GrD3DGpu::insertSemaphore(GrSemaphore* semaphore) {
1726    SkASSERT(semaphore);
1727    GrD3DSemaphore* d3dSem = static_cast<GrD3DSemaphore*>(semaphore);
1728    // TODO: Do we need to track the lifetime of this? How do we know it's done?
1729    fQueue->Signal(d3dSem->fence(), d3dSem->value());
1730}
1731
1732void GrD3DGpu::waitSemaphore(GrSemaphore* semaphore) {
1733    SkASSERT(semaphore);
1734    GrD3DSemaphore* d3dSem = static_cast<GrD3DSemaphore*>(semaphore);
1735    // TODO: Do we need to track the lifetime of this?
1736    fQueue->Wait(d3dSem->fence(), d3dSem->value());
1737}
1738
1739GrFence SK_WARN_UNUSED_RESULT GrD3DGpu::insertFence() {
1740    GR_D3D_CALL_ERRCHECK(fQueue->Signal(fFence.get(), ++fCurrentFenceValue));
1741    return fCurrentFenceValue;
1742}
1743
1744bool GrD3DGpu::waitFence(GrFence fence) {
1745    return (fFence->GetCompletedValue() >= fence);
1746}
1747
1748void GrD3DGpu::finishOutstandingGpuWork() {
1749    this->waitForQueueCompletion();
1750}
1751