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