• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/Texture.h"
16 
17 #include <algorithm>
18 
19 #include "common/Assert.h"
20 #include "common/Constants.h"
21 #include "common/Math.h"
22 #include "dawn_native/Device.h"
23 #include "dawn_native/ValidationUtils_autogen.h"
24 
25 namespace dawn_native {
26     namespace {
27         // TODO(jiawei.shao@intel.com): implement texture view format compatibility rule
ValidateTextureViewFormatCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)28         MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture,
29                                                           const TextureViewDescriptor* descriptor) {
30             if (texture->GetFormat().format != descriptor->format) {
31                 return DAWN_VALIDATION_ERROR(
32                     "The format of texture view is not compatible to the original texture");
33             }
34 
35             return {};
36         }
37 
38         // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions
IsTextureViewDimensionCompatibleWithTextureDimension(dawn::TextureViewDimension textureViewDimension,dawn::TextureDimension textureDimension)39         bool IsTextureViewDimensionCompatibleWithTextureDimension(
40             dawn::TextureViewDimension textureViewDimension,
41             dawn::TextureDimension textureDimension) {
42             switch (textureViewDimension) {
43                 case dawn::TextureViewDimension::e2D:
44                 case dawn::TextureViewDimension::e2DArray:
45                 case dawn::TextureViewDimension::Cube:
46                 case dawn::TextureViewDimension::CubeArray:
47                     return textureDimension == dawn::TextureDimension::e2D;
48                 default:
49                     UNREACHABLE();
50                     return false;
51             }
52         }
53 
54         // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions
IsArrayLayerValidForTextureViewDimension(dawn::TextureViewDimension textureViewDimension,uint32_t textureViewArrayLayer)55         bool IsArrayLayerValidForTextureViewDimension(
56             dawn::TextureViewDimension textureViewDimension,
57             uint32_t textureViewArrayLayer) {
58             switch (textureViewDimension) {
59                 case dawn::TextureViewDimension::e2D:
60                     return textureViewArrayLayer == 1u;
61                 case dawn::TextureViewDimension::e2DArray:
62                     return true;
63                 case dawn::TextureViewDimension::Cube:
64                     return textureViewArrayLayer == 6u;
65                 case dawn::TextureViewDimension::CubeArray:
66                     return textureViewArrayLayer % 6 == 0;
67                 default:
68                     UNREACHABLE();
69                     return false;
70             }
71         }
72 
IsTextureSizeValidForTextureViewDimension(dawn::TextureViewDimension textureViewDimension,const Extent3D & textureSize)73         bool IsTextureSizeValidForTextureViewDimension(
74             dawn::TextureViewDimension textureViewDimension,
75             const Extent3D& textureSize) {
76             switch (textureViewDimension) {
77                 case dawn::TextureViewDimension::Cube:
78                 case dawn::TextureViewDimension::CubeArray:
79                     return textureSize.width == textureSize.height;
80                 case dawn::TextureViewDimension::e2D:
81                 case dawn::TextureViewDimension::e2DArray:
82                     return true;
83                 default:
84                     UNREACHABLE();
85                     return false;
86             }
87         }
88 
89         // TODO(jiawei.shao@intel.com): support more sample count.
ValidateSampleCount(const TextureDescriptor * descriptor,const Format * format)90         MaybeError ValidateSampleCount(const TextureDescriptor* descriptor, const Format* format) {
91             if (!IsValidSampleCount(descriptor->sampleCount)) {
92                 return DAWN_VALIDATION_ERROR("The sample count of the texture is not supported.");
93             }
94 
95             if (descriptor->sampleCount > 1) {
96                 if (descriptor->mipLevelCount > 1) {
97                     return DAWN_VALIDATION_ERROR(
98                         "The mipmap level count of a multisampled texture must be 1.");
99                 }
100 
101                 // Multisampled 2D array texture is not supported because on Metal it requires the
102                 // version of macOS be greater than 10.14.
103                 if (descriptor->arrayLayerCount > 1) {
104                     return DAWN_VALIDATION_ERROR("Multisampled 2D array texture is not supported.");
105                 }
106 
107                 if (format->isCompressed) {
108                     return DAWN_VALIDATION_ERROR(
109                         "The sample counts of the textures in BC formats must be 1.");
110                 }
111             }
112 
113             return {};
114         }
115 
ValidateTextureViewDimensionCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)116         MaybeError ValidateTextureViewDimensionCompatibility(
117             const TextureBase* texture,
118             const TextureViewDescriptor* descriptor) {
119             if (!IsArrayLayerValidForTextureViewDimension(descriptor->dimension,
120                                                           descriptor->arrayLayerCount)) {
121                 return DAWN_VALIDATION_ERROR(
122                     "The dimension of the texture view is not compatible with the layer count");
123             }
124 
125             if (!IsTextureViewDimensionCompatibleWithTextureDimension(descriptor->dimension,
126                                                                       texture->GetDimension())) {
127                 return DAWN_VALIDATION_ERROR(
128                     "The dimension of the texture view is not compatible with the dimension of the"
129                     "original texture");
130             }
131 
132             if (!IsTextureSizeValidForTextureViewDimension(descriptor->dimension,
133                                                            texture->GetSize())) {
134                 return DAWN_VALIDATION_ERROR(
135                     "The dimension of the texture view is not compatible with the size of the"
136                     "original texture");
137             }
138 
139             return {};
140         }
141 
MakeDefaultTextureViewDescriptor(const TextureBase * texture)142         TextureViewDescriptor MakeDefaultTextureViewDescriptor(const TextureBase* texture) {
143             TextureViewDescriptor descriptor;
144             descriptor.format = texture->GetFormat().format;
145             descriptor.baseArrayLayer = 0;
146             descriptor.arrayLayerCount = texture->GetArrayLayers();
147             descriptor.baseMipLevel = 0;
148             descriptor.mipLevelCount = texture->GetNumMipLevels();
149 
150             // TODO(jiawei.shao@intel.com): support all texture dimensions.
151             switch (texture->GetDimension()) {
152                 case dawn::TextureDimension::e2D:
153                     if (texture->GetArrayLayers() == 1u) {
154                         descriptor.dimension = dawn::TextureViewDimension::e2D;
155                     } else {
156                         descriptor.dimension = dawn::TextureViewDimension::e2DArray;
157                     }
158                     break;
159                 default:
160                     UNREACHABLE();
161             }
162 
163             return descriptor;
164         }
165 
ValidateTextureSize(const TextureDescriptor * descriptor,const Format * format)166         MaybeError ValidateTextureSize(const TextureDescriptor* descriptor, const Format* format) {
167             ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0);
168 
169             if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 <
170                 descriptor->mipLevelCount) {
171                 return DAWN_VALIDATION_ERROR("Texture has too many mip levels");
172             }
173 
174             if (format->isCompressed && (descriptor->size.width % format->blockWidth != 0 ||
175                                          descriptor->size.height % format->blockHeight != 0)) {
176                 return DAWN_VALIDATION_ERROR(
177                     "The size of the texture is incompatible with the texture format");
178             }
179 
180             return {};
181         }
182 
ValidateTextureUsage(const TextureDescriptor * descriptor,const Format * format)183         MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor, const Format* format) {
184             DAWN_TRY(ValidateTextureUsageBit(descriptor->usage));
185 
186             constexpr dawn::TextureUsageBit kValidCompressedUsages =
187                 dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::CopySrc |
188                 dawn::TextureUsageBit::CopyDst;
189             if (format->isCompressed && (descriptor->usage & (~kValidCompressedUsages))) {
190                 return DAWN_VALIDATION_ERROR(
191                     "Compressed texture format is incompatible with the texture usage");
192             }
193 
194             if (!format->isRenderable &&
195                 (descriptor->usage & dawn::TextureUsageBit::OutputAttachment)) {
196                 return DAWN_VALIDATION_ERROR(
197                     "Non-renderable format used with OutputAttachment usage");
198             }
199 
200             if (descriptor->usage & dawn::TextureUsageBit::Storage) {
201                 return DAWN_VALIDATION_ERROR("storage textures aren't supported (yet)");
202             }
203 
204             return {};
205         }
206 
207     }  // anonymous namespace
208 
ValidateTextureDescriptor(const DeviceBase * device,const TextureDescriptor * descriptor)209     MaybeError ValidateTextureDescriptor(const DeviceBase* device,
210                                          const TextureDescriptor* descriptor) {
211         if (descriptor->nextInChain != nullptr) {
212             return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
213         }
214 
215         const Format* format;
216         DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format));
217 
218         DAWN_TRY(ValidateTextureUsage(descriptor, format));
219         DAWN_TRY(ValidateTextureDimension(descriptor->dimension));
220         DAWN_TRY(ValidateSampleCount(descriptor, format));
221 
222         // TODO(jiawei.shao@intel.com): check stuff based on the dimension
223         if (descriptor->size.width == 0 || descriptor->size.height == 0 ||
224             descriptor->size.depth == 0 || descriptor->arrayLayerCount == 0 ||
225             descriptor->mipLevelCount == 0) {
226             return DAWN_VALIDATION_ERROR("Cannot create an empty texture");
227         }
228 
229         if (descriptor->dimension != dawn::TextureDimension::e2D) {
230             return DAWN_VALIDATION_ERROR("Texture dimension must be 2D (for now)");
231         }
232 
233         DAWN_TRY(ValidateTextureSize(descriptor, format));
234 
235         return {};
236     }
237 
ValidateTextureViewDescriptor(const DeviceBase * device,const TextureBase * texture,const TextureViewDescriptor * descriptor)238     MaybeError ValidateTextureViewDescriptor(const DeviceBase* device,
239                                              const TextureBase* texture,
240                                              const TextureViewDescriptor* descriptor) {
241         if (descriptor->nextInChain != nullptr) {
242             return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
243         }
244 
245         DAWN_TRY(device->ValidateObject(texture));
246         if (texture->GetTextureState() == TextureBase::TextureState::Destroyed) {
247             return DAWN_VALIDATION_ERROR("Destroyed texture used to create texture view");
248         }
249 
250         DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension));
251         if (descriptor->dimension == dawn::TextureViewDimension::e1D ||
252             descriptor->dimension == dawn::TextureViewDimension::e3D) {
253             return DAWN_VALIDATION_ERROR("Texture view dimension must be 2D compatible.");
254         }
255 
256         DAWN_TRY(ValidateTextureFormat(descriptor->format));
257 
258         // TODO(jiawei.shao@intel.com): check stuff based on resource limits
259         if (descriptor->arrayLayerCount == 0 || descriptor->mipLevelCount == 0) {
260             return DAWN_VALIDATION_ERROR("Cannot create an empty texture view");
261         }
262 
263         if (uint64_t(descriptor->baseArrayLayer) + uint64_t(descriptor->arrayLayerCount) >
264             uint64_t(texture->GetArrayLayers())) {
265             return DAWN_VALIDATION_ERROR("Texture view array-layer out of range");
266         }
267 
268         if (uint64_t(descriptor->baseMipLevel) + uint64_t(descriptor->mipLevelCount) >
269             uint64_t(texture->GetNumMipLevels())) {
270             return DAWN_VALIDATION_ERROR("Texture view mip-level out of range");
271         }
272 
273         DAWN_TRY(ValidateTextureViewFormatCompatibility(texture, descriptor));
274         DAWN_TRY(ValidateTextureViewDimensionCompatibility(texture, descriptor));
275 
276         return {};
277     }
278 
IsValidSampleCount(uint32_t sampleCount)279     bool IsValidSampleCount(uint32_t sampleCount) {
280         switch (sampleCount) {
281             case 1:
282             case 4:
283                 return true;
284 
285             default:
286                 return false;
287         }
288     }
289 
290     // TextureBase
291 
TextureBase(DeviceBase * device,const TextureDescriptor * descriptor,TextureState state)292     TextureBase::TextureBase(DeviceBase* device,
293                              const TextureDescriptor* descriptor,
294                              TextureState state)
295         : ObjectBase(device),
296           mDimension(descriptor->dimension),
297           mFormat(device->GetValidInternalFormat(descriptor->format)),
298           mSize(descriptor->size),
299           mArrayLayerCount(descriptor->arrayLayerCount),
300           mMipLevelCount(descriptor->mipLevelCount),
301           mSampleCount(descriptor->sampleCount),
302           mUsage(descriptor->usage),
303           mState(state) {
304         uint32_t subresourceCount =
305             GetSubresourceIndex(descriptor->mipLevelCount, descriptor->arrayLayerCount);
306         mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false);
307     }
308 
309     static Format kUnusedFormat;
310 
TextureBase(DeviceBase * device,ObjectBase::ErrorTag tag)311     TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag)
312         : ObjectBase(device, tag), mFormat(kUnusedFormat) {
313     }
314 
315     // static
MakeError(DeviceBase * device)316     TextureBase* TextureBase::MakeError(DeviceBase* device) {
317         return new TextureBase(device, ObjectBase::kError);
318     }
319 
GetDimension() const320     dawn::TextureDimension TextureBase::GetDimension() const {
321         ASSERT(!IsError());
322         return mDimension;
323     }
324 
325     // TODO(jiawei.shao@intel.com): return more information about texture format
GetFormat() const326     const Format& TextureBase::GetFormat() const {
327         ASSERT(!IsError());
328         return mFormat;
329     }
GetSize() const330     const Extent3D& TextureBase::GetSize() const {
331         ASSERT(!IsError());
332         return mSize;
333     }
GetArrayLayers() const334     uint32_t TextureBase::GetArrayLayers() const {
335         ASSERT(!IsError());
336         return mArrayLayerCount;
337     }
GetNumMipLevels() const338     uint32_t TextureBase::GetNumMipLevels() const {
339         ASSERT(!IsError());
340         return mMipLevelCount;
341     }
GetSampleCount() const342     uint32_t TextureBase::GetSampleCount() const {
343         ASSERT(!IsError());
344         return mSampleCount;
345     }
GetUsage() const346     dawn::TextureUsageBit TextureBase::GetUsage() const {
347         ASSERT(!IsError());
348         return mUsage;
349     }
350 
GetTextureState() const351     TextureBase::TextureState TextureBase::GetTextureState() const {
352         ASSERT(!IsError());
353         return mState;
354     }
355 
GetSubresourceIndex(uint32_t mipLevel,uint32_t arraySlice) const356     uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const {
357         ASSERT(arraySlice <= kMaxTexture2DArrayLayers);
358         ASSERT(mipLevel <= kMaxTexture2DMipLevels);
359         static_assert(kMaxTexture2DMipLevels <=
360                           std::numeric_limits<uint32_t>::max() / kMaxTexture2DArrayLayers,
361                       "texture size overflows uint32_t");
362         return GetNumMipLevels() * arraySlice + mipLevel;
363     }
364 
IsSubresourceContentInitialized(uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount) const365     bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel,
366                                                       uint32_t levelCount,
367                                                       uint32_t baseArrayLayer,
368                                                       uint32_t layerCount) const {
369         ASSERT(!IsError());
370         for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) {
371             for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
372                  ++arrayLayer) {
373                 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
374                 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
375                 if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) {
376                     return false;
377                 }
378             }
379         }
380         return true;
381     }
382 
SetIsSubresourceContentInitialized(uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount)383     void TextureBase::SetIsSubresourceContentInitialized(uint32_t baseMipLevel,
384                                                          uint32_t levelCount,
385                                                          uint32_t baseArrayLayer,
386                                                          uint32_t layerCount) {
387         ASSERT(!IsError());
388         for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) {
389             for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
390                  ++arrayLayer) {
391                 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
392                 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
393                 mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true;
394             }
395         }
396     }
397 
ValidateCanUseInSubmitNow() const398     MaybeError TextureBase::ValidateCanUseInSubmitNow() const {
399         ASSERT(!IsError());
400         if (mState == TextureState::Destroyed) {
401             return DAWN_VALIDATION_ERROR("Destroyed texture used in a submit");
402         }
403         return {};
404     }
405 
IsMultisampledTexture() const406     bool TextureBase::IsMultisampledTexture() const {
407         ASSERT(!IsError());
408         return mSampleCount > 1;
409     }
410 
GetMipLevelVirtualSize(uint64_t level) const411     Extent3D TextureBase::GetMipLevelVirtualSize(uint64_t level) const {
412         Extent3D extent;
413         extent.width = std::max(mSize.width >> level, 1u);
414         extent.height = std::max(mSize.height >> level, 1u);
415         extent.depth = std::max(mSize.depth >> level, 1u);
416         return extent;
417     }
418 
GetMipLevelPhysicalSize(uint64_t level) const419     Extent3D TextureBase::GetMipLevelPhysicalSize(uint64_t level) const {
420         Extent3D extent = GetMipLevelVirtualSize(level);
421 
422         // Compressed Textures will have paddings if their width or height is not a multiple of
423         // 4 at non-zero mipmap levels.
424         if (mFormat.isCompressed) {
425             // TODO(jiawei.shao@intel.com): check if there are any overflows.
426             uint32_t blockWidth = mFormat.blockWidth;
427             uint32_t blockHeight = mFormat.blockHeight;
428             extent.width = (extent.width + blockWidth - 1) / blockWidth * blockWidth;
429             extent.height = (extent.height + blockHeight - 1) / blockHeight * blockHeight;
430         }
431 
432         return extent;
433     }
434 
CreateDefaultView()435     TextureViewBase* TextureBase::CreateDefaultView() {
436         TextureViewDescriptor descriptor = {};
437 
438         if (!IsError()) {
439             descriptor = MakeDefaultTextureViewDescriptor(this);
440         }
441 
442         return GetDevice()->CreateTextureView(this, &descriptor);
443     }
444 
CreateView(const TextureViewDescriptor * descriptor)445     TextureViewBase* TextureBase::CreateView(const TextureViewDescriptor* descriptor) {
446         return GetDevice()->CreateTextureView(this, descriptor);
447     }
448 
Destroy()449     void TextureBase::Destroy() {
450         if (GetDevice()->ConsumedError(ValidateDestroy())) {
451             return;
452         }
453         ASSERT(!IsError());
454         DestroyInternal();
455     }
456 
DestroyImpl()457     void TextureBase::DestroyImpl() {
458     }
459 
DestroyInternal()460     void TextureBase::DestroyInternal() {
461         if (mState == TextureState::OwnedInternal) {
462             DestroyImpl();
463         }
464         mState = TextureState::Destroyed;
465     }
466 
ValidateDestroy() const467     MaybeError TextureBase::ValidateDestroy() const {
468         DAWN_TRY(GetDevice()->ValidateObject(this));
469         return {};
470     }
471 
472     // TextureViewBase
473 
TextureViewBase(TextureBase * texture,const TextureViewDescriptor * descriptor)474     TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor)
475         : ObjectBase(texture->GetDevice()),
476           mTexture(texture),
477           mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)),
478           mBaseMipLevel(descriptor->baseMipLevel),
479           mMipLevelCount(descriptor->mipLevelCount),
480           mBaseArrayLayer(descriptor->baseArrayLayer),
481           mArrayLayerCount(descriptor->arrayLayerCount) {
482     }
483 
TextureViewBase(DeviceBase * device,ObjectBase::ErrorTag tag)484     TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag)
485         : ObjectBase(device, tag), mFormat(kUnusedFormat) {
486     }
487 
488     // static
MakeError(DeviceBase * device)489     TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) {
490         return new TextureViewBase(device, ObjectBase::kError);
491     }
492 
GetTexture() const493     const TextureBase* TextureViewBase::GetTexture() const {
494         ASSERT(!IsError());
495         return mTexture.Get();
496     }
497 
GetTexture()498     TextureBase* TextureViewBase::GetTexture() {
499         ASSERT(!IsError());
500         return mTexture.Get();
501     }
502 
GetFormat() const503     const Format& TextureViewBase::GetFormat() const {
504         ASSERT(!IsError());
505         return mFormat;
506     }
507 
GetBaseMipLevel() const508     uint32_t TextureViewBase::GetBaseMipLevel() const {
509         ASSERT(!IsError());
510         return mBaseMipLevel;
511     }
512 
GetLevelCount() const513     uint32_t TextureViewBase::GetLevelCount() const {
514         ASSERT(!IsError());
515         return mMipLevelCount;
516     }
517 
GetBaseArrayLayer() const518     uint32_t TextureViewBase::GetBaseArrayLayer() const {
519         ASSERT(!IsError());
520         return mBaseArrayLayer;
521     }
522 
GetLayerCount() const523     uint32_t TextureViewBase::GetLayerCount() const {
524         ASSERT(!IsError());
525         return mArrayLayerCount;
526     }
527 }  // namespace dawn_native
528