1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/d3d12/TextureD3D12.h"
16 
17 #include "common/Constants.h"
18 #include "common/Math.h"
19 #include "dawn_native/DynamicUploader.h"
20 #include "dawn_native/EnumMaskIterator.h"
21 #include "dawn_native/Error.h"
22 #include "dawn_native/d3d12/BufferD3D12.h"
23 #include "dawn_native/d3d12/CommandRecordingContext.h"
24 #include "dawn_native/d3d12/D3D12Error.h"
25 #include "dawn_native/d3d12/DeviceD3D12.h"
26 #include "dawn_native/d3d12/HeapD3D12.h"
27 #include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h"
28 #include "dawn_native/d3d12/StagingBufferD3D12.h"
29 #include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h"
30 #include "dawn_native/d3d12/TextureCopySplitter.h"
31 #include "dawn_native/d3d12/UtilsD3D12.h"
32 
33 namespace dawn_native { namespace d3d12 {
34 
35     namespace {
D3D12TextureUsage(wgpu::TextureUsage usage,const Format & format)36         D3D12_RESOURCE_STATES D3D12TextureUsage(wgpu::TextureUsage usage, const Format& format) {
37             D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;
38 
39             if (usage & kPresentTextureUsage) {
40                 // The present usage is only used internally by the swapchain and is never used in
41                 // combination with other usages.
42                 ASSERT(usage == kPresentTextureUsage);
43                 return D3D12_RESOURCE_STATE_PRESENT;
44             }
45 
46             if (usage & wgpu::TextureUsage::CopySrc) {
47                 resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
48             }
49             if (usage & wgpu::TextureUsage::CopyDst) {
50                 resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
51             }
52             if (usage & (wgpu::TextureUsage::Sampled | kReadonlyStorageTexture)) {
53                 resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
54                                   D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
55             }
56             if (usage & wgpu::TextureUsage::Storage) {
57                 resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
58             }
59             if (usage & wgpu::TextureUsage::RenderAttachment) {
60                 if (format.HasDepthOrStencil()) {
61                     resourceState |= D3D12_RESOURCE_STATE_DEPTH_WRITE;
62                 } else {
63                     resourceState |= D3D12_RESOURCE_STATE_RENDER_TARGET;
64                 }
65             }
66 
67             return resourceState;
68         }
69 
D3D12ResourceFlags(wgpu::TextureUsage usage,const Format & format,bool isMultisampledTexture)70         D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::TextureUsage usage,
71                                                 const Format& format,
72                                                 bool isMultisampledTexture) {
73             D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
74 
75             if (usage & wgpu::TextureUsage::Storage) {
76                 flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
77             }
78 
79             // A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or
80             // D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags.
81             // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource_desc
82             if ((usage & wgpu::TextureUsage::RenderAttachment) != 0 || isMultisampledTexture) {
83                 if (format.HasDepthOrStencil()) {
84                     flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
85                 } else {
86                     flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
87                 }
88             }
89 
90             ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
91                    flags == D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
92             return flags;
93         }
94 
D3D12TextureDimension(wgpu::TextureDimension dimension)95         D3D12_RESOURCE_DIMENSION D3D12TextureDimension(wgpu::TextureDimension dimension) {
96             switch (dimension) {
97                 case wgpu::TextureDimension::e2D:
98                     return D3D12_RESOURCE_DIMENSION_TEXTURE2D;
99 
100                 case wgpu::TextureDimension::e1D:
101                 case wgpu::TextureDimension::e3D:
102                     UNREACHABLE();
103             }
104         }
105 
D3D12TypelessTextureFormat(wgpu::TextureFormat format)106         DXGI_FORMAT D3D12TypelessTextureFormat(wgpu::TextureFormat format) {
107             switch (format) {
108                 case wgpu::TextureFormat::R8Unorm:
109                 case wgpu::TextureFormat::R8Snorm:
110                 case wgpu::TextureFormat::R8Uint:
111                 case wgpu::TextureFormat::R8Sint:
112                     return DXGI_FORMAT_R8_TYPELESS;
113 
114                 case wgpu::TextureFormat::R16Uint:
115                 case wgpu::TextureFormat::R16Sint:
116                 case wgpu::TextureFormat::R16Float:
117                     return DXGI_FORMAT_R16_TYPELESS;
118 
119                 case wgpu::TextureFormat::RG8Unorm:
120                 case wgpu::TextureFormat::RG8Snorm:
121                 case wgpu::TextureFormat::RG8Uint:
122                 case wgpu::TextureFormat::RG8Sint:
123                     return DXGI_FORMAT_R8G8_TYPELESS;
124 
125                 case wgpu::TextureFormat::R32Uint:
126                 case wgpu::TextureFormat::R32Sint:
127                 case wgpu::TextureFormat::R32Float:
128                     return DXGI_FORMAT_R32_TYPELESS;
129 
130                 case wgpu::TextureFormat::RG16Uint:
131                 case wgpu::TextureFormat::RG16Sint:
132                 case wgpu::TextureFormat::RG16Float:
133                     return DXGI_FORMAT_R16G16_TYPELESS;
134 
135                 case wgpu::TextureFormat::RGBA8Unorm:
136                 case wgpu::TextureFormat::RGBA8UnormSrgb:
137                 case wgpu::TextureFormat::RGBA8Snorm:
138                 case wgpu::TextureFormat::RGBA8Uint:
139                 case wgpu::TextureFormat::RGBA8Sint:
140                     return DXGI_FORMAT_R8G8B8A8_TYPELESS;
141 
142                 case wgpu::TextureFormat::BGRA8Unorm:
143                 case wgpu::TextureFormat::BGRA8UnormSrgb:
144                     return DXGI_FORMAT_B8G8R8A8_TYPELESS;
145 
146                 case wgpu::TextureFormat::RGB10A2Unorm:
147                     return DXGI_FORMAT_R10G10B10A2_TYPELESS;
148 
149                 case wgpu::TextureFormat::RG11B10Ufloat:
150                     return DXGI_FORMAT_R11G11B10_FLOAT;
151                 case wgpu::TextureFormat::RGB9E5Ufloat:
152                     return DXGI_FORMAT_R9G9B9E5_SHAREDEXP;
153 
154                 case wgpu::TextureFormat::RG32Uint:
155                 case wgpu::TextureFormat::RG32Sint:
156                 case wgpu::TextureFormat::RG32Float:
157                     return DXGI_FORMAT_R32G32_TYPELESS;
158 
159                 case wgpu::TextureFormat::RGBA16Uint:
160                 case wgpu::TextureFormat::RGBA16Sint:
161                 case wgpu::TextureFormat::RGBA16Float:
162                     return DXGI_FORMAT_R16G16B16A16_TYPELESS;
163 
164                 case wgpu::TextureFormat::RGBA32Uint:
165                 case wgpu::TextureFormat::RGBA32Sint:
166                 case wgpu::TextureFormat::RGBA32Float:
167                     return DXGI_FORMAT_R32G32B32A32_TYPELESS;
168 
169                 case wgpu::TextureFormat::Depth32Float:
170                 case wgpu::TextureFormat::Depth24Plus:
171                     return DXGI_FORMAT_R32_TYPELESS;
172 
173                 case wgpu::TextureFormat::Depth24PlusStencil8:
174                     return DXGI_FORMAT_R32G8X24_TYPELESS;
175 
176                 case wgpu::TextureFormat::BC1RGBAUnorm:
177                 case wgpu::TextureFormat::BC1RGBAUnormSrgb:
178                     return DXGI_FORMAT_BC1_TYPELESS;
179 
180                 case wgpu::TextureFormat::BC2RGBAUnorm:
181                 case wgpu::TextureFormat::BC2RGBAUnormSrgb:
182                     return DXGI_FORMAT_BC2_TYPELESS;
183 
184                 case wgpu::TextureFormat::BC3RGBAUnorm:
185                 case wgpu::TextureFormat::BC3RGBAUnormSrgb:
186                     return DXGI_FORMAT_BC3_TYPELESS;
187 
188                 case wgpu::TextureFormat::BC4RSnorm:
189                 case wgpu::TextureFormat::BC4RUnorm:
190                     return DXGI_FORMAT_BC4_TYPELESS;
191 
192                 case wgpu::TextureFormat::BC5RGSnorm:
193                 case wgpu::TextureFormat::BC5RGUnorm:
194                     return DXGI_FORMAT_BC5_TYPELESS;
195 
196                 case wgpu::TextureFormat::BC6HRGBFloat:
197                 case wgpu::TextureFormat::BC6HRGBUfloat:
198                     return DXGI_FORMAT_BC6H_TYPELESS;
199 
200                 case wgpu::TextureFormat::BC7RGBAUnorm:
201                 case wgpu::TextureFormat::BC7RGBAUnormSrgb:
202                     return DXGI_FORMAT_BC7_TYPELESS;
203 
204                 case wgpu::TextureFormat::Undefined:
205                     UNREACHABLE();
206             }
207         }
208 
209     }  // namespace
210 
D3D12TextureFormat(wgpu::TextureFormat format)211     DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format) {
212         switch (format) {
213             case wgpu::TextureFormat::R8Unorm:
214                 return DXGI_FORMAT_R8_UNORM;
215             case wgpu::TextureFormat::R8Snorm:
216                 return DXGI_FORMAT_R8_SNORM;
217             case wgpu::TextureFormat::R8Uint:
218                 return DXGI_FORMAT_R8_UINT;
219             case wgpu::TextureFormat::R8Sint:
220                 return DXGI_FORMAT_R8_SINT;
221 
222             case wgpu::TextureFormat::R16Uint:
223                 return DXGI_FORMAT_R16_UINT;
224             case wgpu::TextureFormat::R16Sint:
225                 return DXGI_FORMAT_R16_SINT;
226             case wgpu::TextureFormat::R16Float:
227                 return DXGI_FORMAT_R16_FLOAT;
228             case wgpu::TextureFormat::RG8Unorm:
229                 return DXGI_FORMAT_R8G8_UNORM;
230             case wgpu::TextureFormat::RG8Snorm:
231                 return DXGI_FORMAT_R8G8_SNORM;
232             case wgpu::TextureFormat::RG8Uint:
233                 return DXGI_FORMAT_R8G8_UINT;
234             case wgpu::TextureFormat::RG8Sint:
235                 return DXGI_FORMAT_R8G8_SINT;
236 
237             case wgpu::TextureFormat::R32Uint:
238                 return DXGI_FORMAT_R32_UINT;
239             case wgpu::TextureFormat::R32Sint:
240                 return DXGI_FORMAT_R32_SINT;
241             case wgpu::TextureFormat::R32Float:
242                 return DXGI_FORMAT_R32_FLOAT;
243             case wgpu::TextureFormat::RG16Uint:
244                 return DXGI_FORMAT_R16G16_UINT;
245             case wgpu::TextureFormat::RG16Sint:
246                 return DXGI_FORMAT_R16G16_SINT;
247             case wgpu::TextureFormat::RG16Float:
248                 return DXGI_FORMAT_R16G16_FLOAT;
249             case wgpu::TextureFormat::RGBA8Unorm:
250                 return DXGI_FORMAT_R8G8B8A8_UNORM;
251             case wgpu::TextureFormat::RGBA8UnormSrgb:
252                 return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
253             case wgpu::TextureFormat::RGBA8Snorm:
254                 return DXGI_FORMAT_R8G8B8A8_SNORM;
255             case wgpu::TextureFormat::RGBA8Uint:
256                 return DXGI_FORMAT_R8G8B8A8_UINT;
257             case wgpu::TextureFormat::RGBA8Sint:
258                 return DXGI_FORMAT_R8G8B8A8_SINT;
259             case wgpu::TextureFormat::BGRA8Unorm:
260                 return DXGI_FORMAT_B8G8R8A8_UNORM;
261             case wgpu::TextureFormat::BGRA8UnormSrgb:
262                 return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
263             case wgpu::TextureFormat::RGB10A2Unorm:
264                 return DXGI_FORMAT_R10G10B10A2_UNORM;
265             case wgpu::TextureFormat::RG11B10Ufloat:
266                 return DXGI_FORMAT_R11G11B10_FLOAT;
267             case wgpu::TextureFormat::RGB9E5Ufloat:
268                 return DXGI_FORMAT_R9G9B9E5_SHAREDEXP;
269 
270             case wgpu::TextureFormat::RG32Uint:
271                 return DXGI_FORMAT_R32G32_UINT;
272             case wgpu::TextureFormat::RG32Sint:
273                 return DXGI_FORMAT_R32G32_SINT;
274             case wgpu::TextureFormat::RG32Float:
275                 return DXGI_FORMAT_R32G32_FLOAT;
276             case wgpu::TextureFormat::RGBA16Uint:
277                 return DXGI_FORMAT_R16G16B16A16_UINT;
278             case wgpu::TextureFormat::RGBA16Sint:
279                 return DXGI_FORMAT_R16G16B16A16_SINT;
280             case wgpu::TextureFormat::RGBA16Float:
281                 return DXGI_FORMAT_R16G16B16A16_FLOAT;
282 
283             case wgpu::TextureFormat::RGBA32Uint:
284                 return DXGI_FORMAT_R32G32B32A32_UINT;
285             case wgpu::TextureFormat::RGBA32Sint:
286                 return DXGI_FORMAT_R32G32B32A32_SINT;
287             case wgpu::TextureFormat::RGBA32Float:
288                 return DXGI_FORMAT_R32G32B32A32_FLOAT;
289 
290             case wgpu::TextureFormat::Depth32Float:
291                 return DXGI_FORMAT_D32_FLOAT;
292             case wgpu::TextureFormat::Depth24Plus:
293                 return DXGI_FORMAT_D32_FLOAT;
294             case wgpu::TextureFormat::Depth24PlusStencil8:
295                 return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
296 
297             case wgpu::TextureFormat::BC1RGBAUnorm:
298                 return DXGI_FORMAT_BC1_UNORM;
299             case wgpu::TextureFormat::BC1RGBAUnormSrgb:
300                 return DXGI_FORMAT_BC1_UNORM_SRGB;
301             case wgpu::TextureFormat::BC2RGBAUnorm:
302                 return DXGI_FORMAT_BC2_UNORM;
303             case wgpu::TextureFormat::BC2RGBAUnormSrgb:
304                 return DXGI_FORMAT_BC2_UNORM_SRGB;
305             case wgpu::TextureFormat::BC3RGBAUnorm:
306                 return DXGI_FORMAT_BC3_UNORM;
307             case wgpu::TextureFormat::BC3RGBAUnormSrgb:
308                 return DXGI_FORMAT_BC3_UNORM_SRGB;
309             case wgpu::TextureFormat::BC4RSnorm:
310                 return DXGI_FORMAT_BC4_SNORM;
311             case wgpu::TextureFormat::BC4RUnorm:
312                 return DXGI_FORMAT_BC4_UNORM;
313             case wgpu::TextureFormat::BC5RGSnorm:
314                 return DXGI_FORMAT_BC5_SNORM;
315             case wgpu::TextureFormat::BC5RGUnorm:
316                 return DXGI_FORMAT_BC5_UNORM;
317             case wgpu::TextureFormat::BC6HRGBFloat:
318                 return DXGI_FORMAT_BC6H_SF16;
319             case wgpu::TextureFormat::BC6HRGBUfloat:
320                 return DXGI_FORMAT_BC6H_UF16;
321             case wgpu::TextureFormat::BC7RGBAUnorm:
322                 return DXGI_FORMAT_BC7_UNORM;
323             case wgpu::TextureFormat::BC7RGBAUnormSrgb:
324                 return DXGI_FORMAT_BC7_UNORM_SRGB;
325 
326             case wgpu::TextureFormat::Undefined:
327                 UNREACHABLE();
328         }
329     }
330 
ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor * descriptor)331     MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor) {
332         if (descriptor->dimension != wgpu::TextureDimension::e2D) {
333             return DAWN_VALIDATION_ERROR("Texture must be 2D");
334         }
335 
336         if (descriptor->mipLevelCount != 1) {
337             return DAWN_VALIDATION_ERROR("Mip level count must be 1");
338         }
339 
340         if (descriptor->size.depth != 1) {
341             return DAWN_VALIDATION_ERROR("Depth must be 1");
342         }
343 
344         if (descriptor->sampleCount != 1) {
345             return DAWN_VALIDATION_ERROR("Sample count must be 1");
346         }
347 
348         return {};
349     }
350 
ValidateD3D12TextureCanBeWrapped(ID3D12Resource * d3d12Resource,const TextureDescriptor * dawnDescriptor)351     MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
352                                                 const TextureDescriptor* dawnDescriptor) {
353         const D3D12_RESOURCE_DESC d3dDescriptor = d3d12Resource->GetDesc();
354         if ((dawnDescriptor->size.width != d3dDescriptor.Width) ||
355             (dawnDescriptor->size.height != d3dDescriptor.Height) ||
356             (dawnDescriptor->size.depth != 1)) {
357             return DAWN_VALIDATION_ERROR("D3D12 texture size doesn't match descriptor");
358         }
359 
360         const DXGI_FORMAT dxgiFormatFromDescriptor = D3D12TextureFormat(dawnDescriptor->format);
361         if (dxgiFormatFromDescriptor != d3dDescriptor.Format) {
362             return DAWN_VALIDATION_ERROR(
363                 "D3D12 texture format must be compatible with descriptor format.");
364         }
365 
366         if (d3dDescriptor.MipLevels != 1) {
367             return DAWN_VALIDATION_ERROR("D3D12 texture number of miplevels must be 1.");
368         }
369 
370         if (d3dDescriptor.DepthOrArraySize != 1) {
371             return DAWN_VALIDATION_ERROR("D3D12 texture array size must be 1.");
372         }
373 
374         // Shared textures cannot be multi-sample so no need to check those.
375         ASSERT(d3dDescriptor.SampleDesc.Count == 1);
376         ASSERT(d3dDescriptor.SampleDesc.Quality == 0);
377 
378         return {};
379     }
380 
Create(Device * device,const TextureDescriptor * descriptor)381     ResultOrError<Ref<TextureBase>> Texture::Create(Device* device,
382                                                     const TextureDescriptor* descriptor) {
383         Ref<Texture> dawnTexture =
384             AcquireRef(new Texture(device, descriptor, TextureState::OwnedInternal));
385         DAWN_TRY(dawnTexture->InitializeAsInternalTexture());
386         return std::move(dawnTexture);
387     }
388 
Create(Device * device,const ExternalImageDescriptor * descriptor,HANDLE sharedHandle,ExternalMutexSerial acquireMutexKey,bool isSwapChainTexture)389     ResultOrError<Ref<TextureBase>> Texture::Create(Device* device,
390                                                     const ExternalImageDescriptor* descriptor,
391                                                     HANDLE sharedHandle,
392                                                     ExternalMutexSerial acquireMutexKey,
393                                                     bool isSwapChainTexture) {
394         const TextureDescriptor* textureDescriptor =
395             reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
396 
397         Ref<Texture> dawnTexture =
398             AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
399         DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
400                                                           acquireMutexKey, isSwapChainTexture));
401         dawnTexture->SetIsSubresourceContentInitialized(descriptor->isInitialized,
402                                                         dawnTexture->GetAllSubresources());
403         return std::move(dawnTexture);
404     }
405 
InitializeAsExternalTexture(const TextureDescriptor * descriptor,HANDLE sharedHandle,ExternalMutexSerial acquireMutexKey,bool isSwapChainTexture)406     MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor,
407                                                     HANDLE sharedHandle,
408                                                     ExternalMutexSerial acquireMutexKey,
409                                                     bool isSwapChainTexture) {
410         Device* dawnDevice = ToBackend(GetDevice());
411         DAWN_TRY(ValidateTextureDescriptor(dawnDevice, descriptor));
412         DAWN_TRY(ValidateTextureDescriptorCanBeWrapped(descriptor));
413 
414         ComPtr<ID3D12Resource> d3d12Resource;
415         DAWN_TRY(CheckHRESULT(dawnDevice->GetD3D12Device()->OpenSharedHandle(
416                                   sharedHandle, IID_PPV_ARGS(&d3d12Resource)),
417                               "D3D12 opening shared handle"));
418 
419         DAWN_TRY(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor));
420 
421         ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
422         DAWN_TRY_ASSIGN(dxgiKeyedMutex,
423                         dawnDevice->CreateKeyedMutexForTexture(d3d12Resource.Get()));
424 
425         DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(uint64_t(acquireMutexKey), INFINITE),
426                               "D3D12 acquiring shared mutex"));
427 
428         mAcquireMutexKey = acquireMutexKey;
429         mDxgiKeyedMutex = std::move(dxgiKeyedMutex);
430         mSwapChainTexture = isSwapChainTexture;
431 
432         AllocationInfo info;
433         info.mMethod = AllocationMethod::kExternal;
434         // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
435         // texture is owned externally. The texture's owning entity must remain responsible for
436         // memory management.
437         mResourceAllocation = {info, 0, std::move(d3d12Resource), nullptr};
438 
439         return {};
440     }
441 
InitializeAsInternalTexture()442     MaybeError Texture::InitializeAsInternalTexture() {
443         D3D12_RESOURCE_DESC resourceDescriptor;
444         resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension());
445         resourceDescriptor.Alignment = 0;
446 
447         const Extent3D& size = GetSize();
448         resourceDescriptor.Width = size.width;
449         resourceDescriptor.Height = size.height;
450         resourceDescriptor.DepthOrArraySize = size.depth;
451 
452         // This will need to be much more nuanced when WebGPU has
453         // texture view compatibility rules.
454         const bool needsTypelessFormat =
455             GetFormat().HasDepthOrStencil() && (GetUsage() & wgpu::TextureUsage::Sampled) != 0;
456 
457         DXGI_FORMAT dxgiFormat = needsTypelessFormat
458                                      ? D3D12TypelessTextureFormat(GetFormat().format)
459                                      : D3D12TextureFormat(GetFormat().format);
460 
461         resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
462         resourceDescriptor.Format = dxgiFormat;
463         resourceDescriptor.SampleDesc.Count = GetSampleCount();
464         // TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern.
465         resourceDescriptor.SampleDesc.Quality = 0;
466         resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
467         resourceDescriptor.Flags =
468             D3D12ResourceFlags(GetUsage(), GetFormat(), IsMultisampledTexture());
469 
470         DAWN_TRY_ASSIGN(mResourceAllocation,
471                         ToBackend(GetDevice())
472                             ->AllocateMemory(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor,
473                                              D3D12_RESOURCE_STATE_COMMON));
474 
475         Device* device = ToBackend(GetDevice());
476 
477         if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
478             CommandRecordingContext* commandContext;
479             DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
480 
481             DAWN_TRY(ClearTexture(commandContext, GetAllSubresources(),
482                                   TextureBase::ClearValue::NonZero));
483         }
484 
485         return {};
486     }
487 
Texture(Device * device,const TextureDescriptor * descriptor,TextureState state)488     Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
489         : TextureBase(device, descriptor, state),
490           mSubresourceStateAndDecay(
491               GetSubresourceCount(),
492               {D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, kMaxExecutionSerial, false}) {
493     }
494 
Texture(Device * device,const TextureDescriptor * descriptor,ComPtr<ID3D12Resource> nativeTexture)495     Texture::Texture(Device* device,
496                      const TextureDescriptor* descriptor,
497                      ComPtr<ID3D12Resource> nativeTexture)
498         : Texture(device, descriptor, TextureState::OwnedExternal) {
499         AllocationInfo info;
500         info.mMethod = AllocationMethod::kExternal;
501         // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
502         // texture is owned externally. The texture's owning entity must remain responsible for
503         // memory management.
504         mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
505 
506         SetIsSubresourceContentInitialized(true, GetAllSubresources());
507     }
508 
~Texture()509     Texture::~Texture() {
510         DestroyInternal();
511     }
512 
DestroyImpl()513     void Texture::DestroyImpl() {
514         Device* device = ToBackend(GetDevice());
515 
516         // In PIX's D3D12-only mode, there is no way to determine frame boundaries
517         // for WebGPU since Dawn does not manage DXGI swap chains. Without assistance,
518         // PIX will wait forever for a present that never happens.
519         // If we know we're dealing with a swapbuffer texture, inform PIX we've
520         // "presented" the texture so it can determine frame boundaries and use its
521         // contents for the UI.
522         if (mSwapChainTexture) {
523             ID3D12SharingContract* d3dSharingContract = device->GetSharingContract();
524             if (d3dSharingContract != nullptr) {
525                 d3dSharingContract->Present(mResourceAllocation.GetD3D12Resource(), 0, 0);
526             }
527         }
528 
529         device->DeallocateMemory(mResourceAllocation);
530 
531         if (mDxgiKeyedMutex != nullptr) {
532             mDxgiKeyedMutex->ReleaseSync(uint64_t(mAcquireMutexKey) + 1);
533             device->ReleaseKeyedMutexForTexture(std::move(mDxgiKeyedMutex));
534         }
535     }
536 
GetD3D12Format() const537     DXGI_FORMAT Texture::GetD3D12Format() const {
538         return D3D12TextureFormat(GetFormat().format);
539     }
540 
GetD3D12Resource() const541     ID3D12Resource* Texture::GetD3D12Resource() const {
542         return mResourceAllocation.GetD3D12Resource();
543     }
544 
GetD3D12CopyableSubresourceFormat(Aspect aspect) const545     DXGI_FORMAT Texture::GetD3D12CopyableSubresourceFormat(Aspect aspect) const {
546         ASSERT(GetFormat().aspects & aspect);
547 
548         switch (GetFormat().format) {
549             case wgpu::TextureFormat::Depth24PlusStencil8:
550                 switch (aspect) {
551                     case Aspect::Depth:
552                         return DXGI_FORMAT_R32_FLOAT;
553                     case Aspect::Stencil:
554                         return DXGI_FORMAT_R8_UINT;
555                     default:
556                         UNREACHABLE();
557                 }
558             default:
559                 ASSERT(HasOneBit(GetFormat().aspects));
560                 return GetD3D12Format();
561         }
562     }
563 
TrackUsageAndTransitionNow(CommandRecordingContext * commandContext,wgpu::TextureUsage usage,const SubresourceRange & range)564     void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
565                                              wgpu::TextureUsage usage,
566                                              const SubresourceRange& range) {
567         TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), range);
568     }
569 
TrackAllUsageAndTransitionNow(CommandRecordingContext * commandContext,wgpu::TextureUsage usage)570     void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
571                                                 wgpu::TextureUsage usage) {
572         TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()),
573                                    GetAllSubresources());
574     }
575 
TrackAllUsageAndTransitionNow(CommandRecordingContext * commandContext,D3D12_RESOURCE_STATES newState)576     void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
577                                                 D3D12_RESOURCE_STATES newState) {
578         TrackUsageAndTransitionNow(commandContext, newState, GetAllSubresources());
579     }
580 
TrackUsageAndTransitionNow(CommandRecordingContext * commandContext,D3D12_RESOURCE_STATES newState,const SubresourceRange & range)581     void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
582                                              D3D12_RESOURCE_STATES newState,
583                                              const SubresourceRange& range) {
584         if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) {
585             // Track the underlying heap to ensure residency.
586             Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
587             commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial());
588         }
589 
590         std::vector<D3D12_RESOURCE_BARRIER> barriers;
591 
592         // TODO(enga): Consider adding a Count helper.
593         uint32_t aspectCount = 0;
594         for (Aspect aspect : IterateEnumMask(range.aspects)) {
595             aspectCount++;
596             DAWN_UNUSED(aspect);
597         }
598 
599         barriers.reserve(range.levelCount * range.layerCount * aspectCount);
600 
601         TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, range);
602         if (barriers.size()) {
603             commandContext->GetCommandList()->ResourceBarrier(barriers.size(), barriers.data());
604         }
605     }
606 
TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER> * barriers,uint32_t index,D3D12_RESOURCE_STATES newState,ExecutionSerial pendingCommandSerial,bool allSubresources)607     void Texture::TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
608                                                     uint32_t index,
609                                                     D3D12_RESOURCE_STATES newState,
610                                                     ExecutionSerial pendingCommandSerial,
611                                                     bool allSubresources) {
612         StateAndDecay* state = &mSubresourceStateAndDecay[index];
613         // Reuse the subresource(s) directly and avoid transition when it isn't needed, and
614         // return false.
615         // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
616         if (state->lastState == newState) {
617             return;
618         }
619 
620         D3D12_RESOURCE_STATES lastState = state->lastState;
621 
622         // The COMMON state represents a state where no write operations can be pending, and
623         // where all pixels are uncompressed. This makes it possible to transition to and
624         // from some states without synchronization (i.e. without an explicit
625         // ResourceBarrier call). Textures can be implicitly promoted to 1) a single write
626         // state, or 2) multiple read states. Textures will implicitly decay to the COMMON
627         // state when all of the following are true: 1) the texture is accessed on a command
628         // list, 2) the ExecuteCommandLists call that uses that command list has ended, and
629         // 3) the texture was promoted implicitly to a read-only state and is still in that
630         // state.
631         // https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
632 
633         // To track implicit decays, we must record the pending serial on which that
634         // transition will occur. When that texture is used again, the previously recorded
635         // serial must be compared to the last completed serial to determine if the texture
636         // has implicity decayed to the common state.
637         if (state->isValidToDecay && pendingCommandSerial > state->lastDecaySerial) {
638             lastState = D3D12_RESOURCE_STATE_COMMON;
639         }
640 
641         // Update the tracked state.
642         state->lastState = newState;
643 
644         // Destination states that qualify for an implicit promotion for a
645         // non-simultaneous-access texture: NON_PIXEL_SHADER_RESOURCE,
646         // PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST.
647         {
648             static constexpr D3D12_RESOURCE_STATES kD3D12PromotableReadOnlyStates =
649                 D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
650                 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
651 
652             if (lastState == D3D12_RESOURCE_STATE_COMMON) {
653                 if (IsSubset(newState, kD3D12PromotableReadOnlyStates)) {
654                     // Implicit texture state decays can only occur when the texture was implicitly
655                     // transitioned to a read-only state. isValidToDecay is needed to differentiate
656                     // between resources that were implictly or explicitly transitioned to a
657                     // read-only state.
658                     state->isValidToDecay = true;
659                     state->lastDecaySerial = pendingCommandSerial;
660                     return;
661                 } else if (newState == D3D12_RESOURCE_STATE_COPY_DEST) {
662                     state->isValidToDecay = false;
663                     return;
664                 }
665             }
666         }
667 
668         D3D12_RESOURCE_BARRIER barrier;
669         barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
670         barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
671         barrier.Transition.pResource = GetD3D12Resource();
672         barrier.Transition.StateBefore = lastState;
673         barrier.Transition.StateAfter = newState;
674         barrier.Transition.Subresource =
675             allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index;
676         barriers->push_back(barrier);
677 
678         state->isValidToDecay = false;
679     }
680 
HandleTransitionSpecialCases(CommandRecordingContext * commandContext)681     void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) {
682         // Textures with keyed mutexes can be written from other graphics queues. Hence, they
683         // must be acquired before command list submission to ensure work from the other queues
684         // has finished. See Device::ExecuteCommandContext.
685         if (mDxgiKeyedMutex != nullptr) {
686             commandContext->AddToSharedTextureList(this);
687         }
688     }
689 
TransitionUsageAndGetResourceBarrier(CommandRecordingContext * commandContext,std::vector<D3D12_RESOURCE_BARRIER> * barrier,wgpu::TextureUsage usage,const SubresourceRange & range)690     void Texture::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
691                                                        std::vector<D3D12_RESOURCE_BARRIER>* barrier,
692                                                        wgpu::TextureUsage usage,
693                                                        const SubresourceRange& range) {
694         TransitionUsageAndGetResourceBarrier(commandContext, barrier,
695                                              D3D12TextureUsage(usage, GetFormat()), range);
696     }
697 
TransitionUsageAndGetResourceBarrier(CommandRecordingContext * commandContext,std::vector<D3D12_RESOURCE_BARRIER> * barriers,D3D12_RESOURCE_STATES newState,const SubresourceRange & range)698     void Texture::TransitionUsageAndGetResourceBarrier(
699         CommandRecordingContext* commandContext,
700         std::vector<D3D12_RESOURCE_BARRIER>* barriers,
701         D3D12_RESOURCE_STATES newState,
702         const SubresourceRange& range) {
703         HandleTransitionSpecialCases(commandContext);
704 
705         const ExecutionSerial pendingCommandSerial =
706             ToBackend(GetDevice())->GetPendingCommandSerial();
707 
708         // This transitions assume it is a 2D texture
709         ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
710 
711         // If the usages transitions can cover all subresources, and old usages of all subresources
712         // are the same, then we can use one barrier to do state transition for all subresources.
713         // Note that if the texture has only one mip level and one array slice, it will fall into
714         // this category.
715         bool areAllSubresourcesCovered = (range.levelCount == GetNumMipLevels() &&  //
716                                           range.layerCount == GetArrayLayers() &&   //
717                                           range.aspects == GetFormat().aspects);
718         if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
719             TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
720 
721             // TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
722             // same states. We may need to retain mSubresourceStateAndDecay[0] only.
723             for (uint32_t i = 1; i < GetSubresourceCount(); ++i) {
724                 mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
725             }
726 
727             return;
728         }
729         for (Aspect aspect : IterateEnumMask(range.aspects)) {
730             for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) {
731                 for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) {
732                     uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel,
733                                                          range.baseArrayLayer + arrayLayer, aspect);
734 
735                     TransitionSingleOrAllSubresources(barriers, index, newState,
736                                                       pendingCommandSerial, false);
737                 }
738             }
739         }
740         mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
741     }
742 
TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext * commandContext,std::vector<D3D12_RESOURCE_BARRIER> * barriers,const PassTextureUsage & textureUsages)743     void Texture::TrackUsageAndGetResourceBarrierForPass(
744         CommandRecordingContext* commandContext,
745         std::vector<D3D12_RESOURCE_BARRIER>* barriers,
746         const PassTextureUsage& textureUsages) {
747         if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) {
748             // Track the underlying heap to ensure residency.
749             Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
750             commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial());
751         }
752 
753         HandleTransitionSpecialCases(commandContext);
754 
755         const ExecutionSerial pendingCommandSerial =
756             ToBackend(GetDevice())->GetPendingCommandSerial();
757         uint32_t subresourceCount = GetSubresourceCount();
758         ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
759         // This transitions assume it is a 2D texture
760         ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
761 
762         // If new usages of all subresources are the same and old usages of all subresources are
763         // the same too, we can use one barrier to do state transition for all subresources.
764         // Note that if the texture has only one mip level and one array slice, it will fall into
765         // this category.
766         if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
767             D3D12_RESOURCE_STATES newState = D3D12TextureUsage(textureUsages.usage, GetFormat());
768             TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
769 
770             // TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
771             // same states. We may need to retain mSubresourceStateAndDecay[0] only.
772             for (uint32_t i = 1; i < subresourceCount; ++i) {
773                 mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
774             }
775 
776             return;
777         }
778 
779         for (Aspect aspect : IterateEnumMask(GetFormat().aspects)) {
780             for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
781                 for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
782                     uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer, aspect);
783 
784                     // Skip if this subresource is not used during the current pass
785                     if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) {
786                         continue;
787                     }
788 
789                     D3D12_RESOURCE_STATES newState =
790                         D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat());
791 
792                     TransitionSingleOrAllSubresources(barriers, index, newState,
793                                                       pendingCommandSerial, false);
794                 }
795             }
796         }
797         mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
798     }
799 
GetRTVDescriptor(uint32_t mipLevel,uint32_t baseArrayLayer,uint32_t layerCount) const800     D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,
801                                                             uint32_t baseArrayLayer,
802                                                             uint32_t layerCount) const {
803         ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
804         D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
805         rtvDesc.Format = GetD3D12Format();
806         if (IsMultisampledTexture()) {
807             ASSERT(GetNumMipLevels() == 1);
808             ASSERT(layerCount == 1);
809             ASSERT(baseArrayLayer == 0);
810             ASSERT(mipLevel == 0);
811             rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
812         } else {
813             // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array
814             // layer and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as
815             // 1-layer 2D array textures. (Just like how we treat SRVs)
816             // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv
817             // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array
818             // _rtv
819             rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
820             rtvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer;
821             rtvDesc.Texture2DArray.ArraySize = layerCount;
822             rtvDesc.Texture2DArray.MipSlice = mipLevel;
823             rtvDesc.Texture2DArray.PlaneSlice = 0;
824         }
825         return rtvDesc;
826     }
827 
GetDSVDescriptor(uint32_t mipLevel,uint32_t baseArrayLayer,uint32_t layerCount) const828     D3D12_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(uint32_t mipLevel,
829                                                             uint32_t baseArrayLayer,
830                                                             uint32_t layerCount) const {
831         D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
832         dsvDesc.Format = GetD3D12Format();
833         dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
834 
835         if (IsMultisampledTexture()) {
836             ASSERT(GetNumMipLevels() == 1);
837             ASSERT(layerCount == 1);
838             ASSERT(baseArrayLayer == 0);
839             ASSERT(mipLevel == 0);
840             dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
841         } else {
842             dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
843             dsvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer;
844             dsvDesc.Texture2DArray.ArraySize = layerCount;
845             dsvDesc.Texture2DArray.MipSlice = mipLevel;
846         }
847 
848         return dsvDesc;
849     }
850 
ClearTexture(CommandRecordingContext * commandContext,const SubresourceRange & range,TextureBase::ClearValue clearValue)851     MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
852                                      const SubresourceRange& range,
853                                      TextureBase::ClearValue clearValue) {
854         // TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
855         if (GetFormat().isCompressed) {
856             SetIsSubresourceContentInitialized(true, range);
857             return {};
858         }
859 
860         ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
861 
862         Device* device = ToBackend(GetDevice());
863 
864         uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
865         float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
866 
867         if ((GetUsage() & wgpu::TextureUsage::RenderAttachment) != 0) {
868             if (GetFormat().HasDepthOrStencil()) {
869                 TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE, range);
870 
871                 for (uint32_t level = range.baseMipLevel;
872                      level < range.baseMipLevel + range.levelCount; ++level) {
873                     for (uint32_t layer = range.baseArrayLayer;
874                          layer < range.baseArrayLayer + range.layerCount; ++layer) {
875                         // Iterate the aspects individually to determine which clear flags to use.
876                         D3D12_CLEAR_FLAGS clearFlags = {};
877                         for (Aspect aspect : IterateEnumMask(range.aspects)) {
878                             if (clearValue == TextureBase::ClearValue::Zero &&
879                                 IsSubresourceContentInitialized(
880                                     SubresourceRange::SingleMipAndLayer(level, layer, aspect))) {
881                                 // Skip lazy clears if already initialized.
882                                 continue;
883                             }
884 
885                             switch (aspect) {
886                                 case Aspect::Depth:
887                                     clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
888                                     break;
889                                 case Aspect::Stencil:
890                                     clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
891                                     break;
892                                 default:
893                                     UNREACHABLE();
894                             }
895                         }
896 
897                         if (clearFlags == 0) {
898                             continue;
899                         }
900 
901                         CPUDescriptorHeapAllocation dsvHandle;
902                         DAWN_TRY_ASSIGN(dsvHandle, device->GetDepthStencilViewAllocator()
903                                                        ->AllocateTransientCPUDescriptors());
904                         const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor =
905                             dsvHandle.GetBaseDescriptor();
906                         D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(level, layer, 1);
907                         device->GetD3D12Device()->CreateDepthStencilView(GetD3D12Resource(),
908                                                                          &dsvDesc, baseDescriptor);
909 
910                         commandList->ClearDepthStencilView(baseDescriptor, clearFlags, fClearColor,
911                                                            clearColor, 0, nullptr);
912                     }
913                 }
914             } else {
915                 TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
916                                            range);
917 
918                 const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
919                                                  fClearColor};
920 
921                 ASSERT(range.aspects == Aspect::Color);
922                 for (uint32_t level = range.baseMipLevel;
923                      level < range.baseMipLevel + range.levelCount; ++level) {
924                     for (uint32_t layer = range.baseArrayLayer;
925                          layer < range.baseArrayLayer + range.layerCount; ++layer) {
926                         if (clearValue == TextureBase::ClearValue::Zero &&
927                             IsSubresourceContentInitialized(
928                                 SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) {
929                             // Skip lazy clears if already initialized.
930                             continue;
931                         }
932 
933                         CPUDescriptorHeapAllocation rtvHeap;
934                         DAWN_TRY_ASSIGN(rtvHeap, device->GetRenderTargetViewAllocator()
935                                                      ->AllocateTransientCPUDescriptors());
936                         const D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetBaseDescriptor();
937 
938                         D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = GetRTVDescriptor(level, layer, 1);
939                         device->GetD3D12Device()->CreateRenderTargetView(GetD3D12Resource(),
940                                                                          &rtvDesc, rtvHandle);
941                         commandList->ClearRenderTargetView(rtvHandle, clearColorRGBA, 0, nullptr);
942                     }
943                 }
944             }
945         } else {
946             // create temp buffer with clear color to copy to the texture image
947             TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, range);
948 
949             for (Aspect aspect : IterateEnumMask(range.aspects)) {
950                 const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(aspect).block;
951 
952                 uint32_t bytesPerRow = Align((GetWidth() / blockInfo.width) * blockInfo.byteSize,
953                                              kTextureBytesPerRowAlignment);
954                 uint64_t bufferSize64 = bytesPerRow * (GetHeight() / blockInfo.height);
955                 if (bufferSize64 > std::numeric_limits<uint32_t>::max()) {
956                     return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
957                 }
958                 uint32_t bufferSize = static_cast<uint32_t>(bufferSize64);
959 
960                 DynamicUploader* uploader = device->GetDynamicUploader();
961                 UploadHandle uploadHandle;
962                 DAWN_TRY_ASSIGN(uploadHandle,
963                                 uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
964                                                    blockInfo.byteSize));
965                 memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
966 
967                 for (uint32_t level = range.baseMipLevel;
968                      level < range.baseMipLevel + range.levelCount; ++level) {
969                     // compute d3d12 texture copy locations for texture and buffer
970                     Extent3D copySize = GetMipLevelVirtualSize(level);
971 
972                     uint32_t rowsPerImage = GetHeight() / blockInfo.height;
973                     Texture2DCopySplit copySplit = ComputeTextureCopySplit(
974                         {0, 0, 0}, copySize, blockInfo, uploadHandle.startOffset, bytesPerRow,
975                         rowsPerImage);
976 
977                     for (uint32_t layer = range.baseArrayLayer;
978                          layer < range.baseArrayLayer + range.layerCount; ++layer) {
979                         if (clearValue == TextureBase::ClearValue::Zero &&
980                             IsSubresourceContentInitialized(
981                                 SubresourceRange::SingleMipAndLayer(level, layer, aspect))) {
982                             // Skip lazy clears if already initialized.
983                             continue;
984                         }
985 
986                         D3D12_TEXTURE_COPY_LOCATION textureLocation =
987                             ComputeTextureCopyLocationForTexture(this, level, layer, aspect);
988                         for (uint32_t i = 0; i < copySplit.count; ++i) {
989                             Texture2DCopySplit::CopyInfo& info = copySplit.copies[i];
990 
991                             D3D12_TEXTURE_COPY_LOCATION bufferLocation =
992                                 ComputeBufferLocationForCopyTextureRegion(
993                                     this, ToBackend(uploadHandle.stagingBuffer)->GetResource(),
994                                     info.bufferSize, copySplit.offset, bytesPerRow, aspect);
995                             D3D12_BOX sourceRegion =
996                                 ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize);
997 
998                             // copy the buffer filled with clear color to the texture
999                             commandList->CopyTextureRegion(
1000                                 &textureLocation, info.textureOffset.x, info.textureOffset.y,
1001                                 info.textureOffset.z, &bufferLocation, &sourceRegion);
1002                         }
1003                     }
1004                 }
1005             }
1006         }
1007         if (clearValue == TextureBase::ClearValue::Zero) {
1008             SetIsSubresourceContentInitialized(true, range);
1009             GetDevice()->IncrementLazyClearCountForTesting();
1010         }
1011         return {};
1012     }
1013 
EnsureSubresourceContentInitialized(CommandRecordingContext * commandContext,const SubresourceRange & range)1014     void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
1015                                                       const SubresourceRange& range) {
1016         if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
1017             return;
1018         }
1019         if (!IsSubresourceContentInitialized(range)) {
1020             // If subresource has not been initialized, clear it to black as it could contain
1021             // dirty bits from recycled memory
1022             GetDevice()->ConsumedError(
1023                 ClearTexture(commandContext, range, TextureBase::ClearValue::Zero));
1024         }
1025     }
1026 
TextureView(TextureBase * texture,const TextureViewDescriptor * descriptor)1027     TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
1028         : TextureViewBase(texture, descriptor) {
1029         mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
1030         mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
1031 
1032         // TODO(enga): This will need to be much more nuanced when WebGPU has
1033         // texture view compatibility rules.
1034         UINT planeSlice = 0;
1035         if (GetFormat().HasDepthOrStencil()) {
1036             // Configure the SRV descriptor to reinterpret the texture allocated as
1037             // TYPELESS as a single-plane shader-accessible view.
1038             switch (descriptor->format) {
1039                 case wgpu::TextureFormat::Depth32Float:
1040                 case wgpu::TextureFormat::Depth24Plus:
1041                     mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT;
1042                     break;
1043                 case wgpu::TextureFormat::Depth24PlusStencil8:
1044                     switch (descriptor->aspect) {
1045                         case wgpu::TextureAspect::DepthOnly:
1046                             planeSlice = 0;
1047                             mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
1048                             break;
1049                         case wgpu::TextureAspect::StencilOnly:
1050                             planeSlice = 1;
1051                             mSrvDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
1052                             // Stencil is accessed using the .g component in the shader.
1053                             // Map it to the zeroth component to match other APIs.
1054                             mSrvDesc.Shader4ComponentMapping =
1055                                 D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
1056                                     D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
1057                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
1058                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
1059                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
1060                             break;
1061                         case wgpu::TextureAspect::All:
1062                             // A single aspect is not selected. The texture view must not be
1063                             // sampled.
1064                             mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
1065                             break;
1066                     }
1067                     break;
1068                 default:
1069                     UNREACHABLE();
1070                     break;
1071             }
1072         }
1073 
1074         // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
1075         // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
1076         // array textures.
1077         // Multisampled textures may only be one array layer, so we use
1078         // D3D12_SRV_DIMENSION_TEXTURE2DMS.
1079         // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
1080         // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
1081         // TODO(jiawei.shao@intel.com): support more texture view dimensions.
1082         if (GetTexture()->IsMultisampledTexture()) {
1083             switch (descriptor->dimension) {
1084                 case wgpu::TextureViewDimension::e2DArray:
1085                     ASSERT(texture->GetArrayLayers() == 1);
1086                     DAWN_FALLTHROUGH;
1087                 case wgpu::TextureViewDimension::e2D:
1088                     ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
1089                     mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
1090                     break;
1091 
1092                 default:
1093                     UNREACHABLE();
1094             }
1095         } else {
1096             switch (descriptor->dimension) {
1097                 case wgpu::TextureViewDimension::e2D:
1098                 case wgpu::TextureViewDimension::e2DArray:
1099                     ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
1100                     mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
1101                     mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount;
1102                     mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer;
1103                     mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount;
1104                     mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel;
1105                     mSrvDesc.Texture2DArray.PlaneSlice = planeSlice;
1106                     mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
1107                     break;
1108                 case wgpu::TextureViewDimension::Cube:
1109                 case wgpu::TextureViewDimension::CubeArray:
1110                     ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
1111                     ASSERT(descriptor->arrayLayerCount % 6 == 0);
1112                     mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
1113                     mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer;
1114                     mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6;
1115                     mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel;
1116                     mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount;
1117                     mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
1118                     break;
1119 
1120                 case wgpu::TextureViewDimension::e1D:
1121                 case wgpu::TextureViewDimension::e3D:
1122                 case wgpu::TextureViewDimension::Undefined:
1123                     UNREACHABLE();
1124             }
1125         }
1126     }
1127 
GetD3D12Format() const1128     DXGI_FORMAT TextureView::GetD3D12Format() const {
1129         return D3D12TextureFormat(GetFormat().format);
1130     }
1131 
GetSRVDescriptor() const1132     const D3D12_SHADER_RESOURCE_VIEW_DESC& TextureView::GetSRVDescriptor() const {
1133         ASSERT(mSrvDesc.Format != DXGI_FORMAT_UNKNOWN);
1134         return mSrvDesc;
1135     }
1136 
GetRTVDescriptor() const1137     D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const {
1138         return ToBackend(GetTexture())
1139             ->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount());
1140     }
1141 
GetDSVDescriptor() const1142     D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const {
1143         ASSERT(GetLevelCount() == 1);
1144         return ToBackend(GetTexture())
1145             ->GetDSVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount());
1146     }
1147 
GetUAVDescriptor() const1148     D3D12_UNORDERED_ACCESS_VIEW_DESC TextureView::GetUAVDescriptor() const {
1149         D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
1150         uavDesc.Format = GetD3D12Format();
1151 
1152         ASSERT(!GetTexture()->IsMultisampledTexture());
1153         uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
1154         uavDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer();
1155         uavDesc.Texture2DArray.ArraySize = GetLayerCount();
1156         uavDesc.Texture2DArray.MipSlice = GetBaseMipLevel();
1157         uavDesc.Texture2DArray.PlaneSlice = 0;
1158         return uavDesc;
1159     }
1160 
1161 }}  // namespace dawn_native::d3d12
1162