Refactor subresource related variables to a struct
This patch put subresource related variables like baseMipLevel, levelCount, baseArrayLayer, layerCount into a single struct at front-end. We have a lot more at backend too, a following patch will do that. Bug: dawn:157 Change-Id: Iab5633a4246b6ae89b80c39f5672dbb31d7a3e78 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22704 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
parent
c7778a27cb
commit
5fafb49c7b
|
@ -59,11 +59,11 @@ namespace dawn_native {
|
||||||
|
|
||||||
ASSERT(view->GetLayerCount() == 1);
|
ASSERT(view->GetLayerCount() == 1);
|
||||||
ASSERT(view->GetLevelCount() == 1);
|
ASSERT(view->GetLevelCount() == 1);
|
||||||
|
SubresourceRange range = view->GetSubresourceRange();
|
||||||
|
|
||||||
// If the loadOp is Load, but the subresource is not initialized, use Clear instead.
|
// If the loadOp is Load, but the subresource is not initialized, use Clear instead.
|
||||||
if (attachmentInfo.loadOp == wgpu::LoadOp::Load &&
|
if (attachmentInfo.loadOp == wgpu::LoadOp::Load &&
|
||||||
!view->GetTexture()->IsSubresourceContentInitialized(
|
!view->GetTexture()->IsSubresourceContentInitialized(range)) {
|
||||||
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
|
|
||||||
attachmentInfo.loadOp = wgpu::LoadOp::Clear;
|
attachmentInfo.loadOp = wgpu::LoadOp::Clear;
|
||||||
attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
|
attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
|
||||||
}
|
}
|
||||||
|
@ -73,20 +73,19 @@ namespace dawn_native {
|
||||||
// cleared later in the pipeline. The texture will be resolved from the
|
// cleared later in the pipeline. The texture will be resolved from the
|
||||||
// source color attachment, which will be correctly initialized.
|
// source color attachment, which will be correctly initialized.
|
||||||
TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get();
|
TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get();
|
||||||
|
ASSERT(resolveView->GetLayerCount() == 1);
|
||||||
|
ASSERT(resolveView->GetLevelCount() == 1);
|
||||||
resolveView->GetTexture()->SetIsSubresourceContentInitialized(
|
resolveView->GetTexture()->SetIsSubresourceContentInitialized(
|
||||||
true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
|
true, resolveView->GetSubresourceRange());
|
||||||
resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (attachmentInfo.storeOp) {
|
switch (attachmentInfo.storeOp) {
|
||||||
case wgpu::StoreOp::Store:
|
case wgpu::StoreOp::Store:
|
||||||
view->GetTexture()->SetIsSubresourceContentInitialized(
|
view->GetTexture()->SetIsSubresourceContentInitialized(true, range);
|
||||||
true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wgpu::StoreOp::Clear:
|
case wgpu::StoreOp::Clear:
|
||||||
view->GetTexture()->SetIsSubresourceContentInitialized(
|
view->GetTexture()->SetIsSubresourceContentInitialized(false, range);
|
||||||
false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -98,12 +97,13 @@ namespace dawn_native {
|
||||||
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
|
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
|
||||||
auto& attachmentInfo = renderPass->depthStencilAttachment;
|
auto& attachmentInfo = renderPass->depthStencilAttachment;
|
||||||
TextureViewBase* view = attachmentInfo.view.Get();
|
TextureViewBase* view = attachmentInfo.view.Get();
|
||||||
|
ASSERT(view->GetLayerCount() == 1);
|
||||||
|
ASSERT(view->GetLevelCount() == 1);
|
||||||
|
SubresourceRange range = view->GetSubresourceRange();
|
||||||
|
|
||||||
// If the depth stencil texture has not been initialized, we want to use loadop
|
// If the depth stencil texture has not been initialized, we want to use loadop
|
||||||
// clear to init the contents to 0's
|
// clear to init the contents to 0's
|
||||||
if (!view->GetTexture()->IsSubresourceContentInitialized(
|
if (!view->GetTexture()->IsSubresourceContentInitialized(range)) {
|
||||||
view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
|
|
||||||
view->GetLayerCount())) {
|
|
||||||
if (view->GetTexture()->GetFormat().HasDepth() &&
|
if (view->GetTexture()->GetFormat().HasDepth() &&
|
||||||
attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
|
attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
|
||||||
attachmentInfo.clearDepth = 0.0f;
|
attachmentInfo.clearDepth = 0.0f;
|
||||||
|
@ -125,15 +125,11 @@ namespace dawn_native {
|
||||||
|
|
||||||
if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store &&
|
if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store &&
|
||||||
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) {
|
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) {
|
||||||
view->GetTexture()->SetIsSubresourceContentInitialized(
|
view->GetTexture()->SetIsSubresourceContentInitialized(true, range);
|
||||||
true, view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
|
|
||||||
view->GetLayerCount());
|
|
||||||
} else {
|
} else {
|
||||||
ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear &&
|
ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear &&
|
||||||
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear);
|
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear);
|
||||||
view->GetTexture()->SetIsSubresourceContentInitialized(
|
view->GetTexture()->SetIsSubresourceContentInitialized(false, range);
|
||||||
false, view->GetBaseMipLevel(), view->GetLevelCount(),
|
|
||||||
view->GetBaseArrayLayer(), view->GetLayerCount());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,8 +178,8 @@ namespace dawn_native {
|
||||||
uint32_t layerCount) {
|
uint32_t layerCount) {
|
||||||
dawn_native::TextureBase* textureBase =
|
dawn_native::TextureBase* textureBase =
|
||||||
reinterpret_cast<dawn_native::TextureBase*>(texture);
|
reinterpret_cast<dawn_native::TextureBase*>(texture);
|
||||||
return textureBase->IsSubresourceContentInitialized(baseMipLevel, levelCount,
|
SubresourceRange range = {baseMipLevel, levelCount, baseArrayLayer, layerCount};
|
||||||
baseArrayLayer, layerCount);
|
return textureBase->IsSubresourceContentInitialized(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char*> GetProcMapNamesForTestingInternal();
|
std::vector<const char*> GetProcMapNamesForTestingInternal();
|
||||||
|
|
|
@ -346,8 +346,7 @@ namespace dawn_native {
|
||||||
mDimension(descriptor->dimension),
|
mDimension(descriptor->dimension),
|
||||||
mFormat(device->GetValidInternalFormat(descriptor->format)),
|
mFormat(device->GetValidInternalFormat(descriptor->format)),
|
||||||
mSize(descriptor->size),
|
mSize(descriptor->size),
|
||||||
mArrayLayerCount(descriptor->arrayLayerCount),
|
mRange({0, descriptor->mipLevelCount, 0, descriptor->arrayLayerCount}),
|
||||||
mMipLevelCount(descriptor->mipLevelCount),
|
|
||||||
mSampleCount(descriptor->sampleCount),
|
mSampleCount(descriptor->sampleCount),
|
||||||
mUsage(descriptor->usage),
|
mUsage(descriptor->usage),
|
||||||
mState(state) {
|
mState(state) {
|
||||||
|
@ -388,11 +387,15 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
uint32_t TextureBase::GetArrayLayers() const {
|
uint32_t TextureBase::GetArrayLayers() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mArrayLayerCount;
|
return mRange.layerCount;
|
||||||
}
|
}
|
||||||
uint32_t TextureBase::GetNumMipLevels() const {
|
uint32_t TextureBase::GetNumMipLevels() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mMipLevelCount;
|
return mRange.levelCount;
|
||||||
|
}
|
||||||
|
const SubresourceRange& TextureBase::GetAllSubresources() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
return mRange;
|
||||||
}
|
}
|
||||||
uint32_t TextureBase::GetSampleCount() const {
|
uint32_t TextureBase::GetSampleCount() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
@ -400,7 +403,7 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
uint32_t TextureBase::GetSubresourceCount() const {
|
uint32_t TextureBase::GetSubresourceCount() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mMipLevelCount * mArrayLayerCount;
|
return mRange.levelCount * mRange.layerCount;
|
||||||
}
|
}
|
||||||
wgpu::TextureUsage TextureBase::GetUsage() const {
|
wgpu::TextureUsage TextureBase::GetUsage() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
@ -421,15 +424,12 @@ namespace dawn_native {
|
||||||
return GetNumMipLevels() * arraySlice + mipLevel;
|
return GetNumMipLevels() * arraySlice + mipLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel,
|
bool TextureBase::IsSubresourceContentInitialized(const SubresourceRange& range) const {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) const {
|
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
|
for (uint32_t arrayLayer = range.baseArrayLayer;
|
||||||
++arrayLayer) {
|
arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
|
||||||
for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount;
|
for (uint32_t mipLevel = range.baseMipLevel;
|
||||||
++mipLevel) {
|
mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) {
|
||||||
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
|
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
|
||||||
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
|
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
|
||||||
if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) {
|
if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) {
|
||||||
|
@ -441,15 +441,12 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized,
|
void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range) {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) {
|
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
|
for (uint32_t arrayLayer = range.baseArrayLayer;
|
||||||
++arrayLayer) {
|
arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
|
||||||
for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount;
|
for (uint32_t mipLevel = range.baseMipLevel;
|
||||||
++mipLevel) {
|
mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) {
|
||||||
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
|
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
|
||||||
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
|
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
|
||||||
mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized;
|
mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized;
|
||||||
|
@ -526,10 +523,8 @@ namespace dawn_native {
|
||||||
mTexture(texture),
|
mTexture(texture),
|
||||||
mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)),
|
mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)),
|
||||||
mDimension(descriptor->dimension),
|
mDimension(descriptor->dimension),
|
||||||
mBaseMipLevel(descriptor->baseMipLevel),
|
mRange({descriptor->baseMipLevel, descriptor->mipLevelCount, descriptor->baseArrayLayer,
|
||||||
mMipLevelCount(descriptor->mipLevelCount),
|
descriptor->arrayLayerCount}) {
|
||||||
mBaseArrayLayer(descriptor->baseArrayLayer),
|
|
||||||
mArrayLayerCount(descriptor->arrayLayerCount) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
||||||
|
@ -563,21 +558,26 @@ namespace dawn_native {
|
||||||
|
|
||||||
uint32_t TextureViewBase::GetBaseMipLevel() const {
|
uint32_t TextureViewBase::GetBaseMipLevel() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mBaseMipLevel;
|
return mRange.baseMipLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextureViewBase::GetLevelCount() const {
|
uint32_t TextureViewBase::GetLevelCount() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mMipLevelCount;
|
return mRange.levelCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextureViewBase::GetBaseArrayLayer() const {
|
uint32_t TextureViewBase::GetBaseArrayLayer() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mBaseArrayLayer;
|
return mRange.baseArrayLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextureViewBase::GetLayerCount() const {
|
uint32_t TextureViewBase::GetLayerCount() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
return mArrayLayerCount;
|
return mRange.layerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubresourceRange& TextureViewBase::GetSubresourceRange() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
return mRange;
|
||||||
}
|
}
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -41,6 +41,17 @@ namespace dawn_native {
|
||||||
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage |
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage |
|
||||||
wgpu::TextureUsage::OutputAttachment;
|
wgpu::TextureUsage::OutputAttachment;
|
||||||
|
|
||||||
|
struct SubresourceRange {
|
||||||
|
uint32_t baseMipLevel;
|
||||||
|
uint32_t levelCount;
|
||||||
|
uint32_t baseArrayLayer;
|
||||||
|
uint32_t layerCount;
|
||||||
|
|
||||||
|
static SubresourceRange SingleSubresource(uint32_t baseMipLevel, uint32_t baseArrayLayer) {
|
||||||
|
return {baseMipLevel, 1, baseArrayLayer, 1};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class TextureBase : public ObjectBase {
|
class TextureBase : public ObjectBase {
|
||||||
public:
|
public:
|
||||||
enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
|
enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
|
||||||
|
@ -54,20 +65,14 @@ namespace dawn_native {
|
||||||
const Extent3D& GetSize() const;
|
const Extent3D& GetSize() const;
|
||||||
uint32_t GetArrayLayers() const;
|
uint32_t GetArrayLayers() const;
|
||||||
uint32_t GetNumMipLevels() const;
|
uint32_t GetNumMipLevels() const;
|
||||||
|
const SubresourceRange& GetAllSubresources() const;
|
||||||
uint32_t GetSampleCount() const;
|
uint32_t GetSampleCount() const;
|
||||||
uint32_t GetSubresourceCount() const;
|
uint32_t GetSubresourceCount() const;
|
||||||
wgpu::TextureUsage GetUsage() const;
|
wgpu::TextureUsage GetUsage() const;
|
||||||
TextureState GetTextureState() const;
|
TextureState GetTextureState() const;
|
||||||
uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const;
|
uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const;
|
||||||
bool IsSubresourceContentInitialized(uint32_t baseMipLevel,
|
bool IsSubresourceContentInitialized(const SubresourceRange& range) const;
|
||||||
uint32_t levelCount,
|
void SetIsSubresourceContentInitialized(bool isInitialized, const SubresourceRange& range);
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) const;
|
|
||||||
void SetIsSubresourceContentInitialized(bool isInitialized,
|
|
||||||
uint32_t baseMipLevel,
|
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount);
|
|
||||||
|
|
||||||
MaybeError ValidateCanUseInSubmitNow() const;
|
MaybeError ValidateCanUseInSubmitNow() const;
|
||||||
|
|
||||||
|
@ -97,8 +102,7 @@ namespace dawn_native {
|
||||||
// TODO(cwallez@chromium.org): This should be deduplicated in the Device
|
// TODO(cwallez@chromium.org): This should be deduplicated in the Device
|
||||||
const Format& mFormat;
|
const Format& mFormat;
|
||||||
Extent3D mSize;
|
Extent3D mSize;
|
||||||
uint32_t mArrayLayerCount;
|
SubresourceRange mRange;
|
||||||
uint32_t mMipLevelCount;
|
|
||||||
uint32_t mSampleCount;
|
uint32_t mSampleCount;
|
||||||
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
|
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
|
||||||
TextureState mState;
|
TextureState mState;
|
||||||
|
@ -122,6 +126,7 @@ namespace dawn_native {
|
||||||
uint32_t GetLevelCount() const;
|
uint32_t GetLevelCount() const;
|
||||||
uint32_t GetBaseArrayLayer() const;
|
uint32_t GetBaseArrayLayer() const;
|
||||||
uint32_t GetLayerCount() const;
|
uint32_t GetLayerCount() const;
|
||||||
|
const SubresourceRange& GetSubresourceRange() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||||
|
@ -131,10 +136,7 @@ namespace dawn_native {
|
||||||
// TODO(cwallez@chromium.org): This should be deduplicated in the Device
|
// TODO(cwallez@chromium.org): This should be deduplicated in the Device
|
||||||
const Format& mFormat;
|
const Format& mFormat;
|
||||||
wgpu::TextureViewDimension mDimension;
|
wgpu::TextureViewDimension mDimension;
|
||||||
uint32_t mBaseMipLevel;
|
SubresourceRange mRange;
|
||||||
uint32_t mMipLevelCount;
|
|
||||||
uint32_t mBaseArrayLayer;
|
|
||||||
uint32_t mArrayLayerCount;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -512,9 +512,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// cleared during record render pass if the texture subresource has not been
|
// cleared during record render pass if the texture subresource has not been
|
||||||
// initialized before the render pass.
|
// initialized before the render pass.
|
||||||
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
||||||
texture->EnsureSubresourceContentInitialized(commandContext, 0,
|
texture->EnsureSubresourceContentInitialized(commandContext,
|
||||||
texture->GetNumMipLevels(), 0,
|
texture->GetAllSubresources());
|
||||||
texture->GetArrayLayers());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,14 +589,15 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Buffer* buffer = ToBackend(copy->source.buffer.Get());
|
Buffer* buffer = ToBackend(copy->source.buffer.Get());
|
||||||
Texture* texture = ToBackend(copy->destination.texture.Get());
|
Texture* texture = ToBackend(copy->destination.texture.Get());
|
||||||
|
|
||||||
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
ASSERT(copy->copySize.depth == 1);
|
||||||
|
SubresourceRange subresource = SubresourceRange::SingleSubresource(
|
||||||
|
copy->destination.mipLevel, copy->destination.arrayLayer);
|
||||||
if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
|
||||||
copy->destination.mipLevel)) {
|
copy->destination.mipLevel)) {
|
||||||
texture->SetIsSubresourceContentInitialized(
|
texture->SetIsSubresourceContentInitialized(true, subresource);
|
||||||
true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
|
|
||||||
} else {
|
} else {
|
||||||
texture->EnsureSubresourceContentInitialized(
|
texture->EnsureSubresourceContentInitialized(commandContext, subresource);
|
||||||
commandContext, copy->destination.mipLevel, 1,
|
|
||||||
copy->destination.arrayLayer, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc);
|
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc);
|
||||||
|
@ -635,8 +635,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Texture* texture = ToBackend(copy->source.texture.Get());
|
Texture* texture = ToBackend(copy->source.texture.Get());
|
||||||
Buffer* buffer = ToBackend(copy->destination.buffer.Get());
|
Buffer* buffer = ToBackend(copy->destination.buffer.Get());
|
||||||
|
|
||||||
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
ASSERT(copy->copySize.depth == 1);
|
||||||
texture->EnsureSubresourceContentInitialized(
|
texture->EnsureSubresourceContentInitialized(
|
||||||
commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
|
commandContext, SubresourceRange::SingleSubresource(
|
||||||
|
copy->source.mipLevel, copy->source.arrayLayer));
|
||||||
|
|
||||||
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
|
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
|
||||||
copy->source.mipLevel, 1,
|
copy->source.mipLevel, 1,
|
||||||
|
@ -676,19 +679,18 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
Texture* source = ToBackend(copy->source.texture.Get());
|
Texture* source = ToBackend(copy->source.texture.Get());
|
||||||
Texture* destination = ToBackend(copy->destination.texture.Get());
|
Texture* destination = ToBackend(copy->destination.texture.Get());
|
||||||
|
SubresourceRange srcRange = {copy->source.mipLevel, 1, copy->source.arrayLayer,
|
||||||
|
copy->copySize.depth};
|
||||||
|
SubresourceRange dstRange = {copy->destination.mipLevel, 1,
|
||||||
|
copy->destination.arrayLayer,
|
||||||
|
copy->copySize.depth};
|
||||||
|
|
||||||
source->EnsureSubresourceContentInitialized(
|
source->EnsureSubresourceContentInitialized(commandContext, srcRange);
|
||||||
commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer,
|
|
||||||
copy->copySize.depth);
|
|
||||||
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
|
||||||
copy->destination.mipLevel)) {
|
copy->destination.mipLevel)) {
|
||||||
destination->SetIsSubresourceContentInitialized(
|
destination->SetIsSubresourceContentInitialized(true, dstRange);
|
||||||
true, copy->destination.mipLevel, 1, copy->destination.arrayLayer,
|
|
||||||
copy->copySize.depth);
|
|
||||||
} else {
|
} else {
|
||||||
destination->EnsureSubresourceContentInitialized(
|
destination->EnsureSubresourceContentInitialized(commandContext, dstRange);
|
||||||
commandContext, copy->destination.mipLevel, 1,
|
|
||||||
copy->destination.arrayLayer, copy->copySize.depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copy->source.texture.Get() == copy->destination.texture.Get() &&
|
if (copy->source.texture.Get() == copy->destination.texture.Get() &&
|
||||||
|
|
|
@ -397,10 +397,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
|
AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
|
||||||
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
|
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
|
||||||
acquireMutexKey, isSwapChainTexture));
|
acquireMutexKey, isSwapChainTexture));
|
||||||
|
dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared,
|
||||||
dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared, 0,
|
dawnTexture->GetAllSubresources());
|
||||||
textureDescriptor->mipLevelCount, 0,
|
|
||||||
textureDescriptor->arrayLayerCount);
|
|
||||||
return std::move(dawnTexture);
|
return std::move(dawnTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,7 +477,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
CommandRecordingContext* commandContext;
|
CommandRecordingContext* commandContext;
|
||||||
DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
|
DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
|
||||||
|
|
||||||
DAWN_TRY(ClearTexture(commandContext, 0, GetNumMipLevels(), 0, GetArrayLayers(),
|
DAWN_TRY(ClearTexture(commandContext, GetAllSubresources(),
|
||||||
TextureBase::ClearValue::NonZero));
|
TextureBase::ClearValue::NonZero));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,8 +502,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// memory management.
|
// memory management.
|
||||||
mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
|
mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
|
||||||
|
|
||||||
SetIsSubresourceContentInitialized(true, 0, descriptor->mipLevelCount, 0,
|
SetIsSubresourceContentInitialized(true, GetAllSubresources());
|
||||||
descriptor->arrayLayerCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture() {
|
Texture::~Texture() {
|
||||||
|
@ -786,15 +783,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
|
MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range,
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue clearValue) {
|
TextureBase::ClearValue clearValue) {
|
||||||
// TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
|
// TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
|
||||||
if (GetFormat().isCompressed) {
|
if (GetFormat().isCompressed) {
|
||||||
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
|
SetIsSubresourceContentInitialized(true, range);
|
||||||
layerCount);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,15 +801,18 @@ namespace dawn_native { namespace d3d12 {
|
||||||
if (GetFormat().isRenderable) {
|
if (GetFormat().isRenderable) {
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE,
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE,
|
||||||
baseMipLevel, levelCount, baseArrayLayer, layerCount);
|
range.baseMipLevel, range.levelCount,
|
||||||
|
range.baseArrayLayer, range.layerCount);
|
||||||
|
|
||||||
D3D12_CLEAR_FLAGS clearFlags = {};
|
D3D12_CLEAR_FLAGS clearFlags = {};
|
||||||
|
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
++layer) {
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -843,16 +839,19 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||||
baseMipLevel, levelCount, baseArrayLayer, layerCount);
|
range.baseMipLevel, range.levelCount,
|
||||||
|
range.baseArrayLayer, range.layerCount);
|
||||||
|
|
||||||
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
|
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
|
||||||
fClearColor};
|
fClearColor};
|
||||||
|
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
++layer) {
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -886,10 +885,12 @@ namespace dawn_native { namespace d3d12 {
|
||||||
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
||||||
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
|
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
|
||||||
|
|
||||||
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, baseMipLevel,
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
levelCount, baseArrayLayer, layerCount);
|
range.baseMipLevel, range.levelCount, range.baseArrayLayer,
|
||||||
|
range.layerCount);
|
||||||
|
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
||||||
|
++level) {
|
||||||
// compute d3d12 texture copy locations for texture and buffer
|
// compute d3d12 texture copy locations for texture and buffer
|
||||||
Extent3D copySize = GetMipLevelVirtualSize(level);
|
Extent3D copySize = GetMipLevelVirtualSize(level);
|
||||||
|
|
||||||
|
@ -898,10 +899,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
ComputeTextureCopySplit({0, 0, 0}, copySize, GetFormat(),
|
ComputeTextureCopySplit({0, 0, 0}, copySize, GetFormat(),
|
||||||
uploadHandle.startOffset, bytesPerRow, rowsPerImage);
|
uploadHandle.startOffset, bytesPerRow, rowsPerImage);
|
||||||
|
|
||||||
for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
++layer) {
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -927,28 +929,22 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clearValue == TextureBase::ClearValue::Zero) {
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
||||||
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
|
SetIsSubresourceContentInitialized(true, range);
|
||||||
layerCount);
|
|
||||||
GetDevice()->IncrementLazyClearCountForTesting();
|
GetDevice()->IncrementLazyClearCountForTesting();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
|
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range) {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) {
|
|
||||||
if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
|
if (!IsSubresourceContentInitialized(range)) {
|
||||||
layerCount)) {
|
|
||||||
// If subresource has not been initialized, clear it to black as it could contain
|
// If subresource has not been initialized, clear it to black as it could contain
|
||||||
// dirty bits from recycled memory
|
// dirty bits from recycled memory
|
||||||
GetDevice()->ConsumedError(ClearTexture(commandContext, baseMipLevel, levelCount,
|
GetDevice()->ConsumedError(
|
||||||
baseArrayLayer, layerCount,
|
ClearTexture(commandContext, range, TextureBase::ClearValue::Zero));
|
||||||
TextureBase::ClearValue::Zero));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
uint32_t baseArrayLayer,
|
uint32_t baseArrayLayer,
|
||||||
uint32_t layerCount) const;
|
uint32_t layerCount) const;
|
||||||
void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
|
void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range);
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount);
|
|
||||||
|
|
||||||
void TrackUsageAndGetResourceBarrierForPass(
|
void TrackUsageAndGetResourceBarrierForPass(
|
||||||
CommandRecordingContext* commandContext,
|
CommandRecordingContext* commandContext,
|
||||||
|
@ -95,10 +92,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// Dawn API
|
// Dawn API
|
||||||
void DestroyImpl() override;
|
void DestroyImpl() override;
|
||||||
MaybeError ClearTexture(CommandRecordingContext* commandContext,
|
MaybeError ClearTexture(CommandRecordingContext* commandContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range,
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue clearValue);
|
TextureBase::ClearValue clearValue);
|
||||||
|
|
||||||
UINT16 GetDepthOrArraySize();
|
UINT16 GetDepthOrArraySize();
|
||||||
|
|
|
@ -444,19 +444,18 @@ namespace dawn_native { namespace metal {
|
||||||
void EnsureSourceTextureInitialized(Texture* texture,
|
void EnsureSourceTextureInitialized(Texture* texture,
|
||||||
const Extent3D& size,
|
const Extent3D& size,
|
||||||
const TextureCopy& src) {
|
const TextureCopy& src) {
|
||||||
texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
texture->EnsureSubresourceContentInitialized(
|
||||||
size.depth);
|
{src.mipLevel, 1, src.arrayLayer, size.depth});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureDestinationTextureInitialized(Texture* texture,
|
void EnsureDestinationTextureInitialized(Texture* texture,
|
||||||
const Extent3D& size,
|
const Extent3D& size,
|
||||||
const TextureCopy& dst) {
|
const TextureCopy& dst) {
|
||||||
|
SubresourceRange range = {dst.mipLevel, 1, dst.arrayLayer, size.depth};
|
||||||
if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
|
if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
|
||||||
texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1, dst.arrayLayer,
|
texture->SetIsSubresourceContentInitialized(true, range);
|
||||||
size.depth);
|
|
||||||
} else {
|
} else {
|
||||||
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer,
|
texture->EnsureSubresourceContentInitialized(range);
|
||||||
size.depth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,8 +690,7 @@ namespace dawn_native { namespace metal {
|
||||||
// cleared in CreateMTLRenderPassDescriptor by setting the loadop to clear when the
|
// cleared in CreateMTLRenderPassDescriptor by setting the loadop to clear when the
|
||||||
// texture subresource has not been initialized before the render pass.
|
// texture subresource has not been initialized before the render pass.
|
||||||
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
||||||
texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
|
texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources());
|
||||||
texture->GetArrayLayers());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,21 +42,14 @@ namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
id<MTLTexture> GetMTLTexture();
|
id<MTLTexture> GetMTLTexture();
|
||||||
|
|
||||||
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
void EnsureSubresourceContentInitialized(const SubresourceRange& range);
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Texture() override;
|
~Texture() override;
|
||||||
|
|
||||||
void DestroyImpl() override;
|
void DestroyImpl() override;
|
||||||
|
|
||||||
MaybeError ClearTexture(uint32_t baseMipLevel,
|
MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue clearValue);
|
|
||||||
|
|
||||||
id<MTLTexture> mMtlTexture = nil;
|
id<MTLTexture> mMtlTexture = nil;
|
||||||
};
|
};
|
||||||
|
|
|
@ -326,8 +326,8 @@ namespace dawn_native { namespace metal {
|
||||||
[mtlDesc release];
|
[mtlDesc release];
|
||||||
|
|
||||||
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
device->ConsumedError(ClearTexture(0, GetNumMipLevels(), 0, GetArrayLayers(),
|
device->ConsumedError(
|
||||||
TextureBase::ClearValue::NonZero));
|
ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +351,7 @@ namespace dawn_native { namespace metal {
|
||||||
plane:plane];
|
plane:plane];
|
||||||
[mtlDesc release];
|
[mtlDesc release];
|
||||||
|
|
||||||
SetIsSubresourceContentInitialized(descriptor->isCleared, 0, 1, 0, 1);
|
SetIsSubresourceContentInitialized(descriptor->isCleared, {0, 1, 0, 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture() {
|
Texture::~Texture() {
|
||||||
|
@ -369,10 +369,7 @@ namespace dawn_native { namespace metal {
|
||||||
return mMtlTexture;
|
return mMtlTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Texture::ClearTexture(uint32_t baseMipLevel,
|
MaybeError Texture::ClearTexture(const SubresourceRange& range,
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue clearValue) {
|
TextureBase::ClearValue clearValue) {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
@ -389,11 +386,13 @@ namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
// Create a render pass to clear each subresource.
|
// Create a render pass to clear each subresource.
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
for (uint32_t arrayLayer = baseArrayLayer;
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
|
for (uint32_t arrayLayer = range.baseArrayLayer;
|
||||||
|
arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, arrayLayer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -421,17 +420,19 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ASSERT(GetFormat().IsColor());
|
ASSERT(GetFormat().IsColor());
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
// Create multiple render passes with each subresource as a color attachment to
|
// Create multiple render passes with each subresource as a color attachment to
|
||||||
// clear them all. Only do this for array layers to ensure all attachments have
|
// clear them all. Only do this for array layers to ensure all attachments have
|
||||||
// the same size.
|
// the same size.
|
||||||
MTLRenderPassDescriptor* descriptor = nil;
|
MTLRenderPassDescriptor* descriptor = nil;
|
||||||
uint32_t attachment = 0;
|
uint32_t attachment = 0;
|
||||||
|
|
||||||
for (uint32_t arrayLayer = baseArrayLayer;
|
for (uint32_t arrayLayer = range.baseArrayLayer;
|
||||||
arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
|
arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, arrayLayer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -466,7 +467,7 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Compute the buffer size big enough to fill the largest mip.
|
// Compute the buffer size big enough to fill the largest mip.
|
||||||
Extent3D largestMipSize = GetMipLevelVirtualSize(baseMipLevel);
|
Extent3D largestMipSize = GetMipLevelVirtualSize(range.baseMipLevel);
|
||||||
|
|
||||||
// Metal validation layers: sourceBytesPerRow must be at least 64.
|
// Metal validation layers: sourceBytesPerRow must be at least 64.
|
||||||
uint32_t largestMipBytesPerRow = std::max(
|
uint32_t largestMipBytesPerRow = std::max(
|
||||||
|
@ -496,13 +497,15 @@ namespace dawn_native { namespace metal {
|
||||||
id<MTLBuffer> uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
|
id<MTLBuffer> uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
|
||||||
|
|
||||||
// Encode a buffer to texture copy to clear each subresource.
|
// Encode a buffer to texture copy to clear each subresource.
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
||||||
|
++level) {
|
||||||
Extent3D virtualSize = GetMipLevelVirtualSize(level);
|
Extent3D virtualSize = GetMipLevelVirtualSize(level);
|
||||||
|
|
||||||
for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
|
for (uint32_t arrayLayer = range.baseArrayLayer;
|
||||||
++arrayLayer) {
|
arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, arrayLayer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -539,26 +542,20 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearValue == TextureBase::ClearValue::Zero) {
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
||||||
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
|
SetIsSubresourceContentInitialized(true, range);
|
||||||
layerCount);
|
|
||||||
device->IncrementLazyClearCountForTesting();
|
device->IncrementLazyClearCountForTesting();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) {
|
|
||||||
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
|
if (!IsSubresourceContentInitialized(range)) {
|
||||||
layerCount)) {
|
|
||||||
// If subresource has not been initialized, clear it to black as it could
|
// If subresource has not been initialized, clear it to black as it could
|
||||||
// contain dirty bits from recycled memory
|
// contain dirty bits from recycled memory
|
||||||
GetDevice()->ConsumedError(ClearTexture(baseMipLevel, levelCount, baseArrayLayer,
|
GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero));
|
||||||
layerCount, TextureBase::ClearValue::Zero));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -458,8 +458,7 @@ namespace dawn_native { namespace opengl {
|
||||||
// cleared in BeginRenderPass by setting the loadop to clear when the
|
// cleared in BeginRenderPass by setting the loadop to clear when the
|
||||||
// texture subresource has not been initialized before the render pass.
|
// texture subresource has not been initialized before the render pass.
|
||||||
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
||||||
texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
|
texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources());
|
||||||
texture->GetArrayLayers());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -513,12 +512,15 @@ namespace dawn_native { namespace opengl {
|
||||||
Texture* texture = ToBackend(dst.texture.Get());
|
Texture* texture = ToBackend(dst.texture.Get());
|
||||||
GLenum target = texture->GetGLTarget();
|
GLenum target = texture->GetGLTarget();
|
||||||
const GLFormat& format = texture->GetGLFormat();
|
const GLFormat& format = texture->GetGLFormat();
|
||||||
|
|
||||||
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
ASSERT(copy->copySize.depth == 1);
|
||||||
|
SubresourceRange subresource =
|
||||||
|
SubresourceRange::SingleSubresource(dst.mipLevel, dst.arrayLayer);
|
||||||
if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) {
|
if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) {
|
||||||
texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
|
texture->SetIsSubresourceContentInitialized(true, subresource);
|
||||||
dst.arrayLayer, 1);
|
|
||||||
} else {
|
} else {
|
||||||
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1,
|
texture->EnsureSubresourceContentInitialized(subresource);
|
||||||
dst.arrayLayer, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
|
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
|
||||||
|
@ -602,8 +604,10 @@ namespace dawn_native { namespace opengl {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
1);
|
ASSERT(copy->copySize.depth == 1);
|
||||||
|
texture->EnsureSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(src.mipLevel, src.arrayLayer));
|
||||||
// The only way to move data from a texture to a buffer in GL is via
|
// The only way to move data from a texture to a buffer in GL is via
|
||||||
// glReadPixels with a pack buffer. Create a temporary FBO for the copy.
|
// glReadPixels with a pack buffer. Create a temporary FBO for the copy.
|
||||||
gl.BindTexture(target, texture->GetHandle());
|
gl.BindTexture(target, texture->GetHandle());
|
||||||
|
@ -675,14 +679,16 @@ namespace dawn_native { namespace opengl {
|
||||||
Extent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize);
|
Extent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize);
|
||||||
Texture* srcTexture = ToBackend(src.texture.Get());
|
Texture* srcTexture = ToBackend(src.texture.Get());
|
||||||
Texture* dstTexture = ToBackend(dst.texture.Get());
|
Texture* dstTexture = ToBackend(dst.texture.Get());
|
||||||
srcTexture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
SubresourceRange srcRange = {src.mipLevel, 1, src.arrayLayer,
|
||||||
1);
|
copy->copySize.depth};
|
||||||
|
SubresourceRange dstRange = {dst.mipLevel, 1, dst.arrayLayer,
|
||||||
|
copy->copySize.depth};
|
||||||
|
|
||||||
|
srcTexture->EnsureSubresourceContentInitialized(srcRange);
|
||||||
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
|
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
|
||||||
dstTexture->SetIsSubresourceContentInitialized(
|
dstTexture->SetIsSubresourceContentInitialized(true, dstRange);
|
||||||
true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
|
||||||
} else {
|
} else {
|
||||||
dstTexture->EnsureSubresourceContentInitialized(
|
dstTexture->EnsureSubresourceContentInitialized(dstRange);
|
||||||
dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
|
||||||
}
|
}
|
||||||
gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
||||||
src.mipLevel, src.origin.x, src.origin.y, src.arrayLayer,
|
src.mipLevel, src.origin.x, src.origin.y, src.arrayLayer,
|
||||||
|
|
|
@ -145,8 +145,8 @@ namespace dawn_native { namespace opengl {
|
||||||
gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
|
gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
|
||||||
|
|
||||||
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
GetDevice()->ConsumedError(ClearTexture(0, GetNumMipLevels(), 0, GetArrayLayers(),
|
GetDevice()->ConsumedError(
|
||||||
TextureBase::ClearValue::NonZero));
|
ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,10 +181,7 @@ namespace dawn_native { namespace opengl {
|
||||||
return ToBackend(GetDevice())->GetGLFormat(GetFormat());
|
return ToBackend(GetDevice())->GetGLFormat(GetFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Texture::ClearTexture(GLint baseMipLevel,
|
MaybeError Texture::ClearTexture(const SubresourceRange& range,
|
||||||
GLint levelCount,
|
|
||||||
GLint baseArrayLayer,
|
|
||||||
GLint layerCount,
|
|
||||||
TextureBase::ClearValue clearValue) {
|
TextureBase::ClearValue clearValue) {
|
||||||
// TODO(jiawei.shao@intel.com): initialize the textures with compressed formats.
|
// TODO(jiawei.shao@intel.com): initialize the textures with compressed formats.
|
||||||
if (GetFormat().isCompressed) {
|
if (GetFormat().isCompressed) {
|
||||||
|
@ -224,31 +221,35 @@ namespace dawn_native { namespace opengl {
|
||||||
gl.GenFramebuffers(1, &framebuffer);
|
gl.GenFramebuffers(1, &framebuffer);
|
||||||
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||||
|
|
||||||
for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
switch (GetDimension()) {
|
switch (GetDimension()) {
|
||||||
case wgpu::TextureDimension::e2D:
|
case wgpu::TextureDimension::e2D:
|
||||||
if (GetArrayLayers() == 1) {
|
if (GetArrayLayers() == 1) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, 0, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, 0))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
||||||
GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(),
|
GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(),
|
||||||
GetHandle(), level);
|
GetHandle(), static_cast<GLint>(level));
|
||||||
DoClear();
|
DoClear();
|
||||||
} else {
|
} else {
|
||||||
for (GLint layer = baseArrayLayer;
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
layer < baseArrayLayer + layerCount; ++layer) {
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER,
|
gl.FramebufferTextureLayer(
|
||||||
GL_DEPTH_STENCIL_ATTACHMENT,
|
GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
GetHandle(), level, layer);
|
GetHandle(), static_cast<GLint>(level),
|
||||||
|
static_cast<GLint>(layer));
|
||||||
DoClear();
|
DoClear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,16 +269,19 @@ namespace dawn_native { namespace opengl {
|
||||||
clearColorData.fill(clearColor);
|
clearColorData.fill(clearColor);
|
||||||
|
|
||||||
const GLFormat& glFormat = GetGLFormat();
|
const GLFormat& glFormat = GetGLFormat();
|
||||||
for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel;
|
||||||
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
||||||
Extent3D mipSize = GetMipLevelPhysicalSize(level);
|
Extent3D mipSize = GetMipLevelPhysicalSize(level);
|
||||||
for (GLint layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
++layer) {
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gl.ClearTexSubImage(mHandle, level, 0, 0, layer, mipSize.width,
|
gl.ClearTexSubImage(mHandle, static_cast<GLint>(level), 0, 0,
|
||||||
|
static_cast<GLint>(layer), mipSize.width,
|
||||||
mipSize.height, 1, glFormat.format, glFormat.type,
|
mipSize.height, 1, glFormat.format, glFormat.type,
|
||||||
clearColorData.data());
|
clearColorData.data());
|
||||||
}
|
}
|
||||||
|
@ -319,7 +323,8 @@ namespace dawn_native { namespace opengl {
|
||||||
gl.PixelStorei(GL_UNPACK_ROW_LENGTH,
|
gl.PixelStorei(GL_UNPACK_ROW_LENGTH,
|
||||||
(bytesPerRow / GetFormat().blockByteSize) * GetFormat().blockWidth);
|
(bytesPerRow / GetFormat().blockByteSize) * GetFormat().blockWidth);
|
||||||
gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||||
for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
||||||
|
++level) {
|
||||||
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle());
|
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle());
|
||||||
gl.ActiveTexture(GL_TEXTURE0);
|
gl.ActiveTexture(GL_TEXTURE0);
|
||||||
gl.BindTexture(GetGLTarget(), GetHandle());
|
gl.BindTexture(GetGLTarget(), GetHandle());
|
||||||
|
@ -329,23 +334,26 @@ namespace dawn_native { namespace opengl {
|
||||||
case wgpu::TextureDimension::e2D:
|
case wgpu::TextureDimension::e2D:
|
||||||
if (GetArrayLayers() == 1) {
|
if (GetArrayLayers() == 1) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, 0, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, 0))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gl.TexSubImage2D(GetGLTarget(), level, 0, 0, size.width, size.height,
|
gl.TexSubImage2D(GetGLTarget(), static_cast<GLint>(level), 0, 0,
|
||||||
GetGLFormat().format, GetGLFormat().type, 0);
|
size.width, size.height, GetGLFormat().format,
|
||||||
} else {
|
|
||||||
for (GLint layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
|
||||||
++layer) {
|
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
|
||||||
// Skip lazy clears if already initialized.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
gl.TexSubImage3D(GetGLTarget(), level, 0, 0, layer, size.width,
|
|
||||||
size.height, 1, GetGLFormat().format,
|
|
||||||
GetGLFormat().type, 0);
|
GetGLFormat().type, 0);
|
||||||
|
} else {
|
||||||
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
|
// Skip lazy clears if already initialized.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gl.TexSubImage3D(GetGLTarget(), static_cast<GLint>(level), 0, 0,
|
||||||
|
static_cast<GLint>(layer), size.width, size.height,
|
||||||
|
1, GetGLFormat().format, GetGLFormat().type, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -360,24 +368,18 @@ namespace dawn_native { namespace opengl {
|
||||||
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
}
|
}
|
||||||
if (clearValue == TextureBase::ClearValue::Zero) {
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
||||||
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
|
SetIsSubresourceContentInitialized(true, range);
|
||||||
layerCount);
|
|
||||||
device->IncrementLazyClearCountForTesting();
|
device->IncrementLazyClearCountForTesting();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) {
|
|
||||||
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
|
if (!IsSubresourceContentInitialized(range)) {
|
||||||
layerCount)) {
|
GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero));
|
||||||
GetDevice()->ConsumedError(ClearTexture(baseMipLevel, levelCount, baseArrayLayer,
|
|
||||||
layerCount, TextureBase::ClearValue::Zero));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,20 +36,13 @@ namespace dawn_native { namespace opengl {
|
||||||
GLenum GetGLTarget() const;
|
GLenum GetGLTarget() const;
|
||||||
const GLFormat& GetGLFormat() const;
|
const GLFormat& GetGLFormat() const;
|
||||||
|
|
||||||
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
void EnsureSubresourceContentInitialized(const SubresourceRange& range);
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Texture() override;
|
~Texture() override;
|
||||||
|
|
||||||
void DestroyImpl() override;
|
void DestroyImpl() override;
|
||||||
MaybeError ClearTexture(GLint baseMipLevel,
|
MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
|
||||||
GLint levelCount,
|
|
||||||
GLint baseArrayLayer,
|
|
||||||
GLint layerCount,
|
|
||||||
TextureBase::ClearValue clearValue);
|
|
||||||
|
|
||||||
GLuint mHandle;
|
GLuint mHandle;
|
||||||
GLenum mTarget;
|
GLenum mTarget;
|
||||||
|
|
|
@ -395,9 +395,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
// cleared in RecordBeginRenderPass by setting the loadop to clear when the
|
// cleared in RecordBeginRenderPass by setting the loadop to clear when the
|
||||||
// texture subresource has not been initialized before the render pass.
|
// texture subresource has not been initialized before the render pass.
|
||||||
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
|
||||||
texture->EnsureSubresourceContentInitialized(recordingContext, 0,
|
texture->EnsureSubresourceContentInitialized(recordingContext,
|
||||||
texture->GetNumMipLevels(), 0,
|
texture->GetAllSubresources());
|
||||||
texture->GetArrayLayers());
|
|
||||||
}
|
}
|
||||||
texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i],
|
texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i],
|
||||||
&imageBarriers, &srcStages, &dstStages);
|
&imageBarriers, &srcStages, &dstStages);
|
||||||
|
@ -446,16 +445,17 @@ namespace dawn_native { namespace vulkan {
|
||||||
ComputeBufferImageCopyRegion(src, dst, copy->copySize);
|
ComputeBufferImageCopyRegion(src, dst, copy->copySize);
|
||||||
VkImageSubresourceLayers subresource = region.imageSubresource;
|
VkImageSubresourceLayers subresource = region.imageSubresource;
|
||||||
|
|
||||||
|
ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
ASSERT(copy->copySize.depth == 1);
|
||||||
|
SubresourceRange range = SubresourceRange::SingleSubresource(
|
||||||
|
subresource.mipLevel, subresource.baseArrayLayer);
|
||||||
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
||||||
subresource.mipLevel)) {
|
subresource.mipLevel)) {
|
||||||
// Since texture has been overwritten, it has been "initialized"
|
// Since texture has been overwritten, it has been "initialized"
|
||||||
dst.texture->SetIsSubresourceContentInitialized(
|
dst.texture->SetIsSubresourceContentInitialized(true, range);
|
||||||
true, subresource.mipLevel, 1, subresource.baseArrayLayer, 1);
|
|
||||||
} else {
|
} else {
|
||||||
ToBackend(dst.texture)
|
ToBackend(dst.texture)
|
||||||
->EnsureSubresourceContentInitialized(recordingContext,
|
->EnsureSubresourceContentInitialized(recordingContext, range);
|
||||||
subresource.mipLevel, 1,
|
|
||||||
subresource.baseArrayLayer, 1);
|
|
||||||
}
|
}
|
||||||
ToBackend(src.buffer)
|
ToBackend(src.buffer)
|
||||||
->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc);
|
->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc);
|
||||||
|
@ -483,10 +483,13 @@ namespace dawn_native { namespace vulkan {
|
||||||
ComputeBufferImageCopyRegion(dst, src, copy->copySize);
|
ComputeBufferImageCopyRegion(dst, src, copy->copySize);
|
||||||
VkImageSubresourceLayers subresource = region.imageSubresource;
|
VkImageSubresourceLayers subresource = region.imageSubresource;
|
||||||
|
|
||||||
|
ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
ASSERT(copy->copySize.depth == 1);
|
||||||
ToBackend(src.texture)
|
ToBackend(src.texture)
|
||||||
->EnsureSubresourceContentInitialized(recordingContext,
|
->EnsureSubresourceContentInitialized(
|
||||||
subresource.mipLevel, 1,
|
recordingContext,
|
||||||
subresource.baseArrayLayer, 1);
|
SubresourceRange::SingleSubresource(subresource.mipLevel,
|
||||||
|
subresource.baseArrayLayer));
|
||||||
|
|
||||||
ToBackend(src.texture)
|
ToBackend(src.texture)
|
||||||
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
|
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
|
||||||
|
@ -508,20 +511,20 @@ namespace dawn_native { namespace vulkan {
|
||||||
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
||||||
TextureCopy& src = copy->source;
|
TextureCopy& src = copy->source;
|
||||||
TextureCopy& dst = copy->destination;
|
TextureCopy& dst = copy->destination;
|
||||||
|
SubresourceRange srcRange = {src.mipLevel, 1, src.arrayLayer,
|
||||||
|
copy->copySize.depth};
|
||||||
|
SubresourceRange dstRange = {dst.mipLevel, 1, dst.arrayLayer,
|
||||||
|
copy->copySize.depth};
|
||||||
|
|
||||||
ToBackend(src.texture)
|
ToBackend(src.texture)
|
||||||
->EnsureSubresourceContentInitialized(recordingContext, src.mipLevel, 1,
|
->EnsureSubresourceContentInitialized(recordingContext, srcRange);
|
||||||
src.arrayLayer, 1);
|
|
||||||
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
||||||
dst.mipLevel)) {
|
dst.mipLevel)) {
|
||||||
// Since destination texture has been overwritten, it has been "initialized"
|
// Since destination texture has been overwritten, it has been "initialized"
|
||||||
dst.texture->SetIsSubresourceContentInitialized(
|
dst.texture->SetIsSubresourceContentInitialized(true, dstRange);
|
||||||
true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
|
||||||
} else {
|
} else {
|
||||||
ToBackend(dst.texture)
|
ToBackend(dst.texture)
|
||||||
->EnsureSubresourceContentInitialized(recordingContext, dst.mipLevel, 1,
|
->EnsureSubresourceContentInitialized(recordingContext, dstRange);
|
||||||
dst.arrayLayer,
|
|
||||||
copy->copySize.depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) {
|
if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) {
|
||||||
|
|
|
@ -539,9 +539,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
"BindImageMemory"));
|
"BindImageMemory"));
|
||||||
|
|
||||||
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(), 0,
|
DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(),
|
||||||
GetNumMipLevels(), 0, GetArrayLayers(),
|
GetAllSubresources(), TextureBase::ClearValue::NonZero));
|
||||||
TextureBase::ClearValue::NonZero));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -595,7 +594,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
// Don't clear imported texture if already cleared
|
// Don't clear imported texture if already cleared
|
||||||
if (descriptor->isCleared) {
|
if (descriptor->isCleared) {
|
||||||
SetIsSubresourceContentInitialized(true, 0, 1, 0, 1);
|
SetIsSubresourceContentInitialized(true, {0, 1, 0, 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success, acquire all the external objects.
|
// Success, acquire all the external objects.
|
||||||
|
@ -843,50 +842,49 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
|
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range,
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue clearValue) {
|
TextureBase::ClearValue clearValue) {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
||||||
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
||||||
|
|
||||||
TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, baseMipLevel, levelCount,
|
TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range.baseMipLevel,
|
||||||
baseArrayLayer, layerCount);
|
range.levelCount, range.baseArrayLayer, range.layerCount);
|
||||||
if (GetFormat().isRenderable) {
|
if (GetFormat().isRenderable) {
|
||||||
VkImageSubresourceRange range = {};
|
VkImageSubresourceRange imageRange = {};
|
||||||
range.aspectMask = GetVkAspectMask();
|
imageRange.aspectMask = GetVkAspectMask();
|
||||||
range.levelCount = 1;
|
imageRange.levelCount = 1;
|
||||||
range.layerCount = 1;
|
imageRange.layerCount = 1;
|
||||||
|
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
||||||
range.baseMipLevel = level;
|
++level) {
|
||||||
for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
imageRange.baseMipLevel = level;
|
||||||
++layer) {
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
range.baseArrayLayer = layer;
|
imageRange.baseArrayLayer = layer;
|
||||||
|
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
VkClearDepthStencilValue clearDepthStencilValue[1];
|
VkClearDepthStencilValue clearDepthStencilValue[1];
|
||||||
clearDepthStencilValue[0].depth = fClearColor;
|
clearDepthStencilValue[0].depth = fClearColor;
|
||||||
clearDepthStencilValue[0].stencil = clearColor;
|
clearDepthStencilValue[0].stencil = clearColor;
|
||||||
device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer,
|
device->fn.CmdClearDepthStencilImage(
|
||||||
GetHandle(),
|
recordingContext->commandBuffer, GetHandle(),
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearDepthStencilValue, 1,
|
||||||
clearDepthStencilValue, 1, &range);
|
&imageRange);
|
||||||
} else {
|
} else {
|
||||||
VkClearColorValue clearColorValue = {
|
VkClearColorValue clearColorValue = {
|
||||||
{fClearColor, fClearColor, fClearColor, fClearColor}};
|
{fClearColor, fClearColor, fClearColor, fClearColor}};
|
||||||
device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
|
device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
&clearColorValue, 1, &range);
|
&clearColorValue, 1, &imageRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -913,13 +911,15 @@ namespace dawn_native { namespace vulkan {
|
||||||
bufferCopy.offset = uploadHandle.startOffset;
|
bufferCopy.offset = uploadHandle.startOffset;
|
||||||
bufferCopy.bytesPerRow = bytesPerRow;
|
bufferCopy.bytesPerRow = bytesPerRow;
|
||||||
|
|
||||||
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
||||||
|
++level) {
|
||||||
Extent3D copySize = GetMipLevelVirtualSize(level);
|
Extent3D copySize = GetMipLevelVirtualSize(level);
|
||||||
|
|
||||||
for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
|
for (uint32_t layer = range.baseArrayLayer;
|
||||||
++layer) {
|
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
||||||
if (clearValue == TextureBase::ClearValue::Zero &&
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
||||||
IsSubresourceContentInitialized(level, 1, layer, 1)) {
|
IsSubresourceContentInitialized(
|
||||||
|
SubresourceRange::SingleSubresource(level, layer))) {
|
||||||
// Skip lazy clears if already initialized.
|
// Skip lazy clears if already initialized.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -942,23 +942,18 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clearValue == TextureBase::ClearValue::Zero) {
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
||||||
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
|
SetIsSubresourceContentInitialized(true, range);
|
||||||
layerCount);
|
|
||||||
device->IncrementLazyClearCountForTesting();
|
device->IncrementLazyClearCountForTesting();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
|
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range) {
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount) {
|
|
||||||
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
|
if (!IsSubresourceContentInitialized(range)) {
|
||||||
layerCount)) {
|
|
||||||
// TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture
|
// TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture
|
||||||
// copies.
|
// copies.
|
||||||
if (GetFormat().isCompressed) {
|
if (GetFormat().isCompressed) {
|
||||||
|
@ -967,9 +962,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
// If subresource has not been initialized, clear it to black as it could contain dirty
|
// If subresource has not been initialized, clear it to black as it could contain dirty
|
||||||
// bits from recycled memory
|
// bits from recycled memory
|
||||||
GetDevice()->ConsumedError(ClearTexture(recordingContext, baseMipLevel, levelCount,
|
GetDevice()->ConsumedError(
|
||||||
baseArrayLayer, layerCount,
|
ClearTexture(recordingContext, range, TextureBase::ClearValue::Zero));
|
||||||
TextureBase::ClearValue::Zero));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,10 +80,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
VkPipelineStageFlags* dstStages);
|
VkPipelineStageFlags* dstStages);
|
||||||
|
|
||||||
void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
|
void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range);
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount);
|
|
||||||
|
|
||||||
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
|
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
|
||||||
// Binds externally allocated memory to the VkImage and on success, takes ownership of
|
// Binds externally allocated memory to the VkImage and on success, takes ownership of
|
||||||
|
@ -104,10 +101,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
void DestroyImpl() override;
|
void DestroyImpl() override;
|
||||||
MaybeError ClearTexture(CommandRecordingContext* recordingContext,
|
MaybeError ClearTexture(CommandRecordingContext* recordingContext,
|
||||||
uint32_t baseMipLevel,
|
const SubresourceRange& range,
|
||||||
uint32_t levelCount,
|
|
||||||
uint32_t baseArrayLayer,
|
|
||||||
uint32_t layerCount,
|
|
||||||
TextureBase::ClearValue);
|
TextureBase::ClearValue);
|
||||||
|
|
||||||
void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
|
void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
|
||||||
|
|
Loading…
Reference in New Issue