mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-19 21:13:30 +00:00
This patch optimizes the implementation of buffer-to-texture and texture-to-buffer computations on D3D12 backends by computing TextureCopySplits once for all the 2D texture array layers in the copy instead of computing it once per layer. You can see the comments in the function D3D12::ComputeTextureCopySplits() for more details. BUG=dawn:453 TEST=dawn_end2end_tests Change-Id: I1b66d24d2418147957fbe03e2c25144bd043a62e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24643 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
1075 lines
51 KiB
C++
1075 lines
51 KiB
C++
// Copyright 2017 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "dawn_native/d3d12/TextureD3D12.h"
|
|
|
|
#include "common/Constants.h"
|
|
#include "common/Math.h"
|
|
#include "dawn_native/DynamicUploader.h"
|
|
#include "dawn_native/Error.h"
|
|
#include "dawn_native/d3d12/BufferD3D12.h"
|
|
#include "dawn_native/d3d12/CommandRecordingContext.h"
|
|
#include "dawn_native/d3d12/D3D12Error.h"
|
|
#include "dawn_native/d3d12/DeviceD3D12.h"
|
|
#include "dawn_native/d3d12/HeapD3D12.h"
|
|
#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h"
|
|
#include "dawn_native/d3d12/StagingBufferD3D12.h"
|
|
#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h"
|
|
#include "dawn_native/d3d12/TextureCopySplitter.h"
|
|
#include "dawn_native/d3d12/UtilsD3D12.h"
|
|
|
|
namespace dawn_native { namespace d3d12 {
|
|
|
|
namespace {
|
|
D3D12_RESOURCE_STATES D3D12TextureUsage(wgpu::TextureUsage usage, const Format& format) {
|
|
D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;
|
|
|
|
if (usage & kPresentTextureUsage) {
|
|
// The present usage is only used internally by the swapchain and is never used in
|
|
// combination with other usages.
|
|
ASSERT(usage == kPresentTextureUsage);
|
|
return D3D12_RESOURCE_STATE_PRESENT;
|
|
}
|
|
|
|
if (usage & wgpu::TextureUsage::CopySrc) {
|
|
resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
|
|
}
|
|
if (usage & wgpu::TextureUsage::CopyDst) {
|
|
resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
|
|
}
|
|
if (usage & (wgpu::TextureUsage::Sampled | kReadonlyStorageTexture)) {
|
|
resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
|
|
}
|
|
if (usage & wgpu::TextureUsage::Storage) {
|
|
resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
|
}
|
|
if (usage & wgpu::TextureUsage::OutputAttachment) {
|
|
if (format.HasDepthOrStencil()) {
|
|
resourceState |= D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
|
} else {
|
|
resourceState |= D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
}
|
|
}
|
|
|
|
return resourceState;
|
|
}
|
|
|
|
D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::TextureUsage usage,
|
|
const Format& format,
|
|
bool isMultisampledTexture) {
|
|
D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
|
|
|
|
if (usage & wgpu::TextureUsage::Storage) {
|
|
flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
}
|
|
|
|
// A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or
|
|
// D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags.
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource_desc
|
|
// Currently all textures are zero-initialized via the render-target path so always add
|
|
// the render target flag, except for compressed textures for which the render-target
|
|
// flag is invalid.
|
|
// TODO(natlee@microsoft.com, jiawei.shao@intel.com): do not require render target for
|
|
// lazy clearing.
|
|
if ((usage & wgpu::TextureUsage::OutputAttachment) || isMultisampledTexture ||
|
|
!format.isCompressed) {
|
|
if (format.HasDepthOrStencil()) {
|
|
flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
|
} else {
|
|
flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
|
}
|
|
}
|
|
|
|
ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
|
|
flags == D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
|
|
return flags;
|
|
}
|
|
|
|
D3D12_RESOURCE_DIMENSION D3D12TextureDimension(wgpu::TextureDimension dimension) {
|
|
switch (dimension) {
|
|
case wgpu::TextureDimension::e2D:
|
|
return D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
DXGI_FORMAT D3D12TypelessTextureFormat(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
case wgpu::TextureFormat::R8Unorm:
|
|
case wgpu::TextureFormat::R8Snorm:
|
|
case wgpu::TextureFormat::R8Uint:
|
|
case wgpu::TextureFormat::R8Sint:
|
|
return DXGI_FORMAT_R8_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::R16Uint:
|
|
case wgpu::TextureFormat::R16Sint:
|
|
case wgpu::TextureFormat::R16Float:
|
|
return DXGI_FORMAT_R16_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RG8Unorm:
|
|
case wgpu::TextureFormat::RG8Snorm:
|
|
case wgpu::TextureFormat::RG8Uint:
|
|
case wgpu::TextureFormat::RG8Sint:
|
|
return DXGI_FORMAT_R8G8_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::R32Uint:
|
|
case wgpu::TextureFormat::R32Sint:
|
|
case wgpu::TextureFormat::R32Float:
|
|
return DXGI_FORMAT_R32_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RG16Uint:
|
|
case wgpu::TextureFormat::RG16Sint:
|
|
case wgpu::TextureFormat::RG16Float:
|
|
return DXGI_FORMAT_R16G16_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
|
case wgpu::TextureFormat::RGBA8Snorm:
|
|
case wgpu::TextureFormat::RGBA8Uint:
|
|
case wgpu::TextureFormat::RGBA8Sint:
|
|
return DXGI_FORMAT_R8G8B8A8_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
|
return DXGI_FORMAT_B8G8R8A8_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RGB10A2Unorm:
|
|
return DXGI_FORMAT_R10G10B10A2_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RG11B10Float:
|
|
return DXGI_FORMAT_R11G11B10_FLOAT;
|
|
|
|
case wgpu::TextureFormat::RG32Uint:
|
|
case wgpu::TextureFormat::RG32Sint:
|
|
case wgpu::TextureFormat::RG32Float:
|
|
return DXGI_FORMAT_R32G32_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RGBA16Uint:
|
|
case wgpu::TextureFormat::RGBA16Sint:
|
|
case wgpu::TextureFormat::RGBA16Float:
|
|
return DXGI_FORMAT_R16G16B16A16_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::RGBA32Uint:
|
|
case wgpu::TextureFormat::RGBA32Sint:
|
|
case wgpu::TextureFormat::RGBA32Float:
|
|
return DXGI_FORMAT_R32G32B32A32_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::Depth32Float:
|
|
case wgpu::TextureFormat::Depth24Plus:
|
|
return DXGI_FORMAT_R32_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::Depth24PlusStencil8:
|
|
return DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
|
|
|
|
case wgpu::TextureFormat::BC1RGBAUnorm:
|
|
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC1_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC2RGBAUnorm:
|
|
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC2_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC3RGBAUnorm:
|
|
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC3_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC4RSnorm:
|
|
case wgpu::TextureFormat::BC4RUnorm:
|
|
return DXGI_FORMAT_BC4_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC5RGSnorm:
|
|
case wgpu::TextureFormat::BC5RGUnorm:
|
|
return DXGI_FORMAT_BC5_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC6HRGBSfloat:
|
|
case wgpu::TextureFormat::BC6HRGBUfloat:
|
|
return DXGI_FORMAT_BC6H_TYPELESS;
|
|
|
|
case wgpu::TextureFormat::BC7RGBAUnorm:
|
|
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC7_TYPELESS;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
case wgpu::TextureFormat::R8Unorm:
|
|
return DXGI_FORMAT_R8_UNORM;
|
|
case wgpu::TextureFormat::R8Snorm:
|
|
return DXGI_FORMAT_R8_SNORM;
|
|
case wgpu::TextureFormat::R8Uint:
|
|
return DXGI_FORMAT_R8_UINT;
|
|
case wgpu::TextureFormat::R8Sint:
|
|
return DXGI_FORMAT_R8_SINT;
|
|
|
|
case wgpu::TextureFormat::R16Uint:
|
|
return DXGI_FORMAT_R16_UINT;
|
|
case wgpu::TextureFormat::R16Sint:
|
|
return DXGI_FORMAT_R16_SINT;
|
|
case wgpu::TextureFormat::R16Float:
|
|
return DXGI_FORMAT_R16_FLOAT;
|
|
case wgpu::TextureFormat::RG8Unorm:
|
|
return DXGI_FORMAT_R8G8_UNORM;
|
|
case wgpu::TextureFormat::RG8Snorm:
|
|
return DXGI_FORMAT_R8G8_SNORM;
|
|
case wgpu::TextureFormat::RG8Uint:
|
|
return DXGI_FORMAT_R8G8_UINT;
|
|
case wgpu::TextureFormat::RG8Sint:
|
|
return DXGI_FORMAT_R8G8_SINT;
|
|
|
|
case wgpu::TextureFormat::R32Uint:
|
|
return DXGI_FORMAT_R32_UINT;
|
|
case wgpu::TextureFormat::R32Sint:
|
|
return DXGI_FORMAT_R32_SINT;
|
|
case wgpu::TextureFormat::R32Float:
|
|
return DXGI_FORMAT_R32_FLOAT;
|
|
case wgpu::TextureFormat::RG16Uint:
|
|
return DXGI_FORMAT_R16G16_UINT;
|
|
case wgpu::TextureFormat::RG16Sint:
|
|
return DXGI_FORMAT_R16G16_SINT;
|
|
case wgpu::TextureFormat::RG16Float:
|
|
return DXGI_FORMAT_R16G16_FLOAT;
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
|
return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
case wgpu::TextureFormat::RGBA8Snorm:
|
|
return DXGI_FORMAT_R8G8B8A8_SNORM;
|
|
case wgpu::TextureFormat::RGBA8Uint:
|
|
return DXGI_FORMAT_R8G8B8A8_UINT;
|
|
case wgpu::TextureFormat::RGBA8Sint:
|
|
return DXGI_FORMAT_R8G8B8A8_SINT;
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
return DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
|
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
|
|
case wgpu::TextureFormat::RGB10A2Unorm:
|
|
return DXGI_FORMAT_R10G10B10A2_UNORM;
|
|
case wgpu::TextureFormat::RG11B10Float:
|
|
return DXGI_FORMAT_R11G11B10_FLOAT;
|
|
|
|
case wgpu::TextureFormat::RG32Uint:
|
|
return DXGI_FORMAT_R32G32_UINT;
|
|
case wgpu::TextureFormat::RG32Sint:
|
|
return DXGI_FORMAT_R32G32_SINT;
|
|
case wgpu::TextureFormat::RG32Float:
|
|
return DXGI_FORMAT_R32G32_FLOAT;
|
|
case wgpu::TextureFormat::RGBA16Uint:
|
|
return DXGI_FORMAT_R16G16B16A16_UINT;
|
|
case wgpu::TextureFormat::RGBA16Sint:
|
|
return DXGI_FORMAT_R16G16B16A16_SINT;
|
|
case wgpu::TextureFormat::RGBA16Float:
|
|
return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
|
|
|
case wgpu::TextureFormat::RGBA32Uint:
|
|
return DXGI_FORMAT_R32G32B32A32_UINT;
|
|
case wgpu::TextureFormat::RGBA32Sint:
|
|
return DXGI_FORMAT_R32G32B32A32_SINT;
|
|
case wgpu::TextureFormat::RGBA32Float:
|
|
return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
|
|
case wgpu::TextureFormat::Depth32Float:
|
|
return DXGI_FORMAT_D32_FLOAT;
|
|
case wgpu::TextureFormat::Depth24Plus:
|
|
return DXGI_FORMAT_D32_FLOAT;
|
|
case wgpu::TextureFormat::Depth24PlusStencil8:
|
|
return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
|
|
|
|
case wgpu::TextureFormat::BC1RGBAUnorm:
|
|
return DXGI_FORMAT_BC1_UNORM;
|
|
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC1_UNORM_SRGB;
|
|
case wgpu::TextureFormat::BC2RGBAUnorm:
|
|
return DXGI_FORMAT_BC2_UNORM;
|
|
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC2_UNORM_SRGB;
|
|
case wgpu::TextureFormat::BC3RGBAUnorm:
|
|
return DXGI_FORMAT_BC3_UNORM;
|
|
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC3_UNORM_SRGB;
|
|
case wgpu::TextureFormat::BC4RSnorm:
|
|
return DXGI_FORMAT_BC4_SNORM;
|
|
case wgpu::TextureFormat::BC4RUnorm:
|
|
return DXGI_FORMAT_BC4_UNORM;
|
|
case wgpu::TextureFormat::BC5RGSnorm:
|
|
return DXGI_FORMAT_BC5_SNORM;
|
|
case wgpu::TextureFormat::BC5RGUnorm:
|
|
return DXGI_FORMAT_BC5_UNORM;
|
|
case wgpu::TextureFormat::BC6HRGBSfloat:
|
|
return DXGI_FORMAT_BC6H_SF16;
|
|
case wgpu::TextureFormat::BC6HRGBUfloat:
|
|
return DXGI_FORMAT_BC6H_UF16;
|
|
case wgpu::TextureFormat::BC7RGBAUnorm:
|
|
return DXGI_FORMAT_BC7_UNORM;
|
|
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
|
return DXGI_FORMAT_BC7_UNORM_SRGB;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor) {
|
|
if (descriptor->dimension != wgpu::TextureDimension::e2D) {
|
|
return DAWN_VALIDATION_ERROR("Texture must be 2D");
|
|
}
|
|
|
|
if (descriptor->mipLevelCount != 1) {
|
|
return DAWN_VALIDATION_ERROR("Mip level count must be 1");
|
|
}
|
|
|
|
if (descriptor->size.depth != 1) {
|
|
return DAWN_VALIDATION_ERROR("Depth must be 1");
|
|
}
|
|
|
|
if (descriptor->sampleCount != 1) {
|
|
return DAWN_VALIDATION_ERROR("Sample count must be 1");
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
|
|
const TextureDescriptor* dawnDescriptor) {
|
|
const D3D12_RESOURCE_DESC d3dDescriptor = d3d12Resource->GetDesc();
|
|
if ((dawnDescriptor->size.width != d3dDescriptor.Width) ||
|
|
(dawnDescriptor->size.height != d3dDescriptor.Height) ||
|
|
(dawnDescriptor->size.depth != 1)) {
|
|
return DAWN_VALIDATION_ERROR("D3D12 texture size doesn't match descriptor");
|
|
}
|
|
|
|
const DXGI_FORMAT dxgiFormatFromDescriptor = D3D12TextureFormat(dawnDescriptor->format);
|
|
if (dxgiFormatFromDescriptor != d3dDescriptor.Format) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"D3D12 texture format must be compatible with descriptor format.");
|
|
}
|
|
|
|
if (d3dDescriptor.MipLevels != 1) {
|
|
return DAWN_VALIDATION_ERROR("D3D12 texture number of miplevels must be 1.");
|
|
}
|
|
|
|
if (d3dDescriptor.DepthOrArraySize != 1) {
|
|
return DAWN_VALIDATION_ERROR("D3D12 texture array size must be 1.");
|
|
}
|
|
|
|
// Shared textures cannot be multi-sample so no need to check those.
|
|
ASSERT(d3dDescriptor.SampleDesc.Count == 1);
|
|
ASSERT(d3dDescriptor.SampleDesc.Quality == 0);
|
|
|
|
return {};
|
|
}
|
|
|
|
ResultOrError<Ref<TextureBase>> Texture::Create(Device* device,
|
|
const TextureDescriptor* descriptor) {
|
|
Ref<Texture> dawnTexture =
|
|
AcquireRef(new Texture(device, descriptor, TextureState::OwnedInternal));
|
|
DAWN_TRY(dawnTexture->InitializeAsInternalTexture());
|
|
return std::move(dawnTexture);
|
|
}
|
|
|
|
ResultOrError<Ref<TextureBase>> Texture::Create(Device* device,
|
|
const ExternalImageDescriptor* descriptor,
|
|
HANDLE sharedHandle,
|
|
uint64_t acquireMutexKey,
|
|
bool isSwapChainTexture) {
|
|
const TextureDescriptor* textureDescriptor =
|
|
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
|
|
|
|
// TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to
|
|
// GPUTextureDescriptor.size.depth is done.
|
|
TextureDescriptor fixedDescriptor;
|
|
DAWN_TRY_ASSIGN(fixedDescriptor, FixTextureDescriptor(device, textureDescriptor));
|
|
textureDescriptor = &fixedDescriptor;
|
|
|
|
Ref<Texture> dawnTexture =
|
|
AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
|
|
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
|
|
acquireMutexKey, isSwapChainTexture));
|
|
dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared,
|
|
dawnTexture->GetAllSubresources());
|
|
return std::move(dawnTexture);
|
|
}
|
|
|
|
MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor,
|
|
HANDLE sharedHandle,
|
|
uint64_t acquireMutexKey,
|
|
bool isSwapChainTexture) {
|
|
Device* dawnDevice = ToBackend(GetDevice());
|
|
DAWN_TRY(ValidateTextureDescriptor(dawnDevice, descriptor));
|
|
DAWN_TRY(ValidateTextureDescriptorCanBeWrapped(descriptor));
|
|
|
|
ComPtr<ID3D12Resource> d3d12Resource;
|
|
DAWN_TRY(CheckHRESULT(dawnDevice->GetD3D12Device()->OpenSharedHandle(
|
|
sharedHandle, IID_PPV_ARGS(&d3d12Resource)),
|
|
"D3D12 opening shared handle"));
|
|
|
|
DAWN_TRY(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor));
|
|
|
|
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
|
|
DAWN_TRY_ASSIGN(dxgiKeyedMutex,
|
|
dawnDevice->CreateKeyedMutexForTexture(d3d12Resource.Get()));
|
|
|
|
DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(acquireMutexKey, INFINITE),
|
|
"D3D12 acquiring shared mutex"));
|
|
|
|
mAcquireMutexKey = acquireMutexKey;
|
|
mDxgiKeyedMutex = std::move(dxgiKeyedMutex);
|
|
mSwapChainTexture = isSwapChainTexture;
|
|
|
|
AllocationInfo info;
|
|
info.mMethod = AllocationMethod::kExternal;
|
|
// When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
|
|
// texture is owned externally. The texture's owning entity must remain responsible for
|
|
// memory management.
|
|
mResourceAllocation = {info, 0, std::move(d3d12Resource), nullptr};
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError Texture::InitializeAsInternalTexture() {
|
|
D3D12_RESOURCE_DESC resourceDescriptor;
|
|
resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension());
|
|
resourceDescriptor.Alignment = 0;
|
|
|
|
const Extent3D& size = GetSize();
|
|
resourceDescriptor.Width = size.width;
|
|
resourceDescriptor.Height = size.height;
|
|
resourceDescriptor.DepthOrArraySize = size.depth;
|
|
|
|
// This will need to be much more nuanced when WebGPU has
|
|
// texture view compatibility rules.
|
|
bool needsTypelessFormat = GetFormat().format == wgpu::TextureFormat::Depth32Float &&
|
|
(GetUsage() & wgpu::TextureUsage::Sampled) != 0;
|
|
|
|
DXGI_FORMAT dxgiFormat = needsTypelessFormat
|
|
? D3D12TypelessTextureFormat(GetFormat().format)
|
|
: D3D12TextureFormat(GetFormat().format);
|
|
|
|
resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
|
|
resourceDescriptor.Format = dxgiFormat;
|
|
resourceDescriptor.SampleDesc.Count = GetSampleCount();
|
|
// TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern.
|
|
resourceDescriptor.SampleDesc.Quality = 0;
|
|
resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
resourceDescriptor.Flags =
|
|
D3D12ResourceFlags(GetUsage(), GetFormat(), IsMultisampledTexture());
|
|
|
|
DAWN_TRY_ASSIGN(mResourceAllocation,
|
|
ToBackend(GetDevice())
|
|
->AllocateMemory(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor,
|
|
D3D12_RESOURCE_STATE_COMMON));
|
|
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
|
CommandRecordingContext* commandContext;
|
|
DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
|
|
|
|
DAWN_TRY(ClearTexture(commandContext, GetAllSubresources(),
|
|
TextureBase::ClearValue::NonZero));
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
|
|
: TextureBase(device, descriptor, state),
|
|
mSubresourceStateAndDecay(
|
|
GetSubresourceCount(),
|
|
{D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, UINT64_MAX, false}) {
|
|
}
|
|
|
|
Texture::Texture(Device* device,
|
|
const TextureDescriptor* descriptor,
|
|
ComPtr<ID3D12Resource> nativeTexture)
|
|
: Texture(device, descriptor, TextureState::OwnedExternal) {
|
|
AllocationInfo info;
|
|
info.mMethod = AllocationMethod::kExternal;
|
|
// When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
|
|
// texture is owned externally. The texture's owning entity must remain responsible for
|
|
// memory management.
|
|
mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
|
|
|
|
SetIsSubresourceContentInitialized(true, GetAllSubresources());
|
|
}
|
|
|
|
Texture::~Texture() {
|
|
DestroyInternal();
|
|
}
|
|
|
|
void Texture::DestroyImpl() {
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
// In PIX's D3D12-only mode, there is no way to determine frame boundaries
|
|
// for WebGPU since Dawn does not manage DXGI swap chains. Without assistance,
|
|
// PIX will wait forever for a present that never happens.
|
|
// If we know we're dealing with a swapbuffer texture, inform PIX we've
|
|
// "presented" the texture so it can determine frame boundaries and use its
|
|
// contents for the UI.
|
|
if (mSwapChainTexture) {
|
|
ID3D12SharingContract* d3dSharingContract = device->GetSharingContract();
|
|
if (d3dSharingContract != nullptr) {
|
|
d3dSharingContract->Present(mResourceAllocation.GetD3D12Resource(), 0, 0);
|
|
}
|
|
}
|
|
|
|
device->DeallocateMemory(mResourceAllocation);
|
|
|
|
if (mDxgiKeyedMutex != nullptr) {
|
|
mDxgiKeyedMutex->ReleaseSync(mAcquireMutexKey + 1);
|
|
device->ReleaseKeyedMutexForTexture(std::move(mDxgiKeyedMutex));
|
|
}
|
|
}
|
|
|
|
DXGI_FORMAT Texture::GetD3D12Format() const {
|
|
return D3D12TextureFormat(GetFormat().format);
|
|
}
|
|
|
|
ID3D12Resource* Texture::GetD3D12Resource() const {
|
|
return mResourceAllocation.GetD3D12Resource();
|
|
}
|
|
|
|
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
|
wgpu::TextureUsage usage,
|
|
const SubresourceRange& range) {
|
|
TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), range);
|
|
}
|
|
|
|
void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
|
wgpu::TextureUsage usage) {
|
|
TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()),
|
|
GetAllSubresources());
|
|
}
|
|
|
|
void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
|
D3D12_RESOURCE_STATES newState) {
|
|
TrackUsageAndTransitionNow(commandContext, newState, GetAllSubresources());
|
|
}
|
|
|
|
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
|
D3D12_RESOURCE_STATES newState,
|
|
const SubresourceRange& range) {
|
|
if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) {
|
|
// Track the underlying heap to ensure residency.
|
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
|
commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial());
|
|
}
|
|
|
|
std::vector<D3D12_RESOURCE_BARRIER> barriers;
|
|
barriers.reserve(range.levelCount * range.layerCount);
|
|
|
|
TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, range);
|
|
if (barriers.size()) {
|
|
commandContext->GetCommandList()->ResourceBarrier(barriers.size(), barriers.data());
|
|
}
|
|
}
|
|
|
|
void Texture::TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
|
uint32_t index,
|
|
D3D12_RESOURCE_STATES newState,
|
|
const Serial pendingCommandSerial,
|
|
bool allSubresources) {
|
|
StateAndDecay* state = &mSubresourceStateAndDecay[index];
|
|
// Reuse the subresource(s) directly and avoid transition when it isn't needed, and
|
|
// return false.
|
|
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
|
|
if (state->lastState == newState) {
|
|
return;
|
|
}
|
|
|
|
D3D12_RESOURCE_STATES lastState = state->lastState;
|
|
|
|
// The COMMON state represents a state where no write operations can be pending, and
|
|
// where all pixels are uncompressed. This makes it possible to transition to and
|
|
// from some states without synchronization (i.e. without an explicit
|
|
// ResourceBarrier call). Textures can be implicitly promoted to 1) a single write
|
|
// state, or 2) multiple read states. Textures will implicitly decay to the COMMON
|
|
// state when all of the following are true: 1) the texture is accessed on a command
|
|
// list, 2) the ExecuteCommandLists call that uses that command list has ended, and
|
|
// 3) the texture was promoted implicitly to a read-only state and is still in that
|
|
// state.
|
|
// https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
|
|
|
|
// To track implicit decays, we must record the pending serial on which that
|
|
// transition will occur. When that texture is used again, the previously recorded
|
|
// serial must be compared to the last completed serial to determine if the texture
|
|
// has implicity decayed to the common state.
|
|
if (state->isValidToDecay && pendingCommandSerial > state->lastDecaySerial) {
|
|
lastState = D3D12_RESOURCE_STATE_COMMON;
|
|
}
|
|
|
|
// Update the tracked state.
|
|
state->lastState = newState;
|
|
|
|
// Destination states that qualify for an implicit promotion for a
|
|
// non-simultaneous-access texture: NON_PIXEL_SHADER_RESOURCE,
|
|
// PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST.
|
|
{
|
|
static constexpr D3D12_RESOURCE_STATES kD3D12PromotableReadOnlyStates =
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
|
|
|
if (lastState == D3D12_RESOURCE_STATE_COMMON) {
|
|
if (newState == (newState & kD3D12PromotableReadOnlyStates)) {
|
|
// Implicit texture state decays can only occur when the texture was implicitly
|
|
// transitioned to a read-only state. isValidToDecay is needed to differentiate
|
|
// between resources that were implictly or explicitly transitioned to a
|
|
// read-only state.
|
|
state->isValidToDecay = true;
|
|
state->lastDecaySerial = pendingCommandSerial;
|
|
return;
|
|
} else if (newState == D3D12_RESOURCE_STATE_COPY_DEST) {
|
|
state->isValidToDecay = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
D3D12_RESOURCE_BARRIER barrier;
|
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
|
barrier.Transition.pResource = GetD3D12Resource();
|
|
barrier.Transition.StateBefore = lastState;
|
|
barrier.Transition.StateAfter = newState;
|
|
barrier.Transition.Subresource =
|
|
allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index;
|
|
barriers->push_back(barrier);
|
|
// TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil
|
|
// texture has different plane slices. While the current implementation only has differernt
|
|
// mip slices and array slices for subresources.
|
|
// This is a hack because Dawn doesn't handle subresource of multiplanar resources
|
|
// correctly. We force the transition to be the same for all planes to match what the
|
|
// frontend validation checks for. This hack might be incorrect for stencil-only texture
|
|
// because we always set transition barrier for depth plane.
|
|
if (!allSubresources && newState == D3D12_RESOURCE_STATE_DEPTH_WRITE &&
|
|
GetFormat().HasStencil()) {
|
|
D3D12_RESOURCE_BARRIER barrierStencil = barrier;
|
|
barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels();
|
|
barriers->push_back(barrierStencil);
|
|
}
|
|
|
|
state->isValidToDecay = false;
|
|
}
|
|
|
|
void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) {
|
|
// Textures with keyed mutexes can be written from other graphics queues. Hence, they
|
|
// must be acquired before command list submission to ensure work from the other queues
|
|
// has finished. See Device::ExecuteCommandContext.
|
|
if (mDxgiKeyedMutex != nullptr) {
|
|
commandContext->AddToSharedTextureList(this);
|
|
}
|
|
}
|
|
|
|
void Texture::TransitionUsageAndGetResourceBarrier(
|
|
CommandRecordingContext* commandContext,
|
|
std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
|
D3D12_RESOURCE_STATES newState,
|
|
const SubresourceRange& range) {
|
|
HandleTransitionSpecialCases(commandContext);
|
|
|
|
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
|
|
uint32_t subresourceCount = GetSubresourceCount();
|
|
|
|
// This transitions assume it is a 2D texture
|
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
|
|
|
// If the usages transitions can cover all subresources, and old usages of all subresources
|
|
// are the same, then we can use one barrier to do state transition for all subresources.
|
|
// Note that if the texture has only one mip level and one array slice, it will fall into
|
|
// this category.
|
|
bool areAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount;
|
|
if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
|
|
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
|
|
|
|
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
|
|
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
|
|
for (uint32_t i = 1; i < subresourceCount; ++i) {
|
|
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
|
|
}
|
|
|
|
return;
|
|
}
|
|
for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) {
|
|
for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) {
|
|
uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel,
|
|
range.baseArrayLayer + arrayLayer);
|
|
|
|
TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial,
|
|
false);
|
|
}
|
|
}
|
|
mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
|
|
}
|
|
|
|
void Texture::TrackUsageAndGetResourceBarrierForPass(
|
|
CommandRecordingContext* commandContext,
|
|
std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
|
const PassTextureUsage& textureUsages) {
|
|
HandleTransitionSpecialCases(commandContext);
|
|
|
|
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
|
|
uint32_t subresourceCount = GetSubresourceCount();
|
|
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
|
|
// This transitions assume it is a 2D texture
|
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
|
|
|
// If new usages of all subresources are the same and old usages of all subresources are
|
|
// the same too, we can use one barrier to do state transition for all subresources.
|
|
// Note that if the texture has only one mip level and one array slice, it will fall into
|
|
// this category.
|
|
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
|
|
D3D12_RESOURCE_STATES newState = D3D12TextureUsage(textureUsages.usage, GetFormat());
|
|
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
|
|
|
|
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
|
|
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
|
|
for (uint32_t i = 1; i < subresourceCount; ++i) {
|
|
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
|
|
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
|
|
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer);
|
|
|
|
// Skip if this subresource is not used during the current pass
|
|
if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) {
|
|
continue;
|
|
}
|
|
|
|
D3D12_RESOURCE_STATES newState =
|
|
D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat());
|
|
|
|
TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial,
|
|
false);
|
|
}
|
|
}
|
|
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
|
|
}
|
|
|
|
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount) const {
|
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
rtvDesc.Format = GetD3D12Format();
|
|
if (IsMultisampledTexture()) {
|
|
ASSERT(GetNumMipLevels() == 1);
|
|
ASSERT(layerCount == 1);
|
|
ASSERT(baseArrayLayer == 0);
|
|
ASSERT(mipLevel == 0);
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
|
|
} else {
|
|
// Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array
|
|
// layer and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as
|
|
// 1-layer 2D array textures. (Just like how we treat SRVs)
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array
|
|
// _rtv
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
|
rtvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer;
|
|
rtvDesc.Texture2DArray.ArraySize = layerCount;
|
|
rtvDesc.Texture2DArray.MipSlice = mipLevel;
|
|
rtvDesc.Texture2DArray.PlaneSlice = 0;
|
|
}
|
|
return rtvDesc;
|
|
}
|
|
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(uint32_t mipLevel,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount) const {
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
|
|
dsvDesc.Format = GetD3D12Format();
|
|
dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
|
|
|
|
if (IsMultisampledTexture()) {
|
|
ASSERT(GetNumMipLevels() == 1);
|
|
ASSERT(layerCount == 1);
|
|
ASSERT(baseArrayLayer == 0);
|
|
ASSERT(mipLevel == 0);
|
|
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
|
|
} else {
|
|
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
|
dsvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer;
|
|
dsvDesc.Texture2DArray.ArraySize = layerCount;
|
|
dsvDesc.Texture2DArray.MipSlice = mipLevel;
|
|
}
|
|
|
|
return dsvDesc;
|
|
}
|
|
|
|
MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
|
|
const SubresourceRange& range,
|
|
TextureBase::ClearValue clearValue) {
|
|
// TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
|
|
if (GetFormat().isCompressed) {
|
|
SetIsSubresourceContentInitialized(true, range);
|
|
return {};
|
|
}
|
|
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
|
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
|
|
|
if (GetFormat().isRenderable) {
|
|
if (GetFormat().HasDepthOrStencil()) {
|
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE, range);
|
|
|
|
D3D12_CLEAR_FLAGS clearFlags = {};
|
|
|
|
for (uint32_t level = range.baseMipLevel;
|
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
|
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;
|
|
}
|
|
|
|
CPUDescriptorHeapAllocation dsvHandle;
|
|
DAWN_TRY_ASSIGN(dsvHandle, device->GetDepthStencilViewAllocator()
|
|
->AllocateTransientCPUDescriptors());
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor =
|
|
dsvHandle.GetBaseDescriptor();
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(level, layer, 1);
|
|
device->GetD3D12Device()->CreateDepthStencilView(GetD3D12Resource(),
|
|
&dsvDesc, baseDescriptor);
|
|
|
|
if (GetFormat().HasDepth()) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
|
|
}
|
|
if (GetFormat().HasStencil()) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
|
}
|
|
|
|
commandList->ClearDepthStencilView(baseDescriptor, clearFlags, fClearColor,
|
|
clearColor, 0, nullptr);
|
|
}
|
|
}
|
|
} else {
|
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
range);
|
|
|
|
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
|
|
fClearColor};
|
|
|
|
for (uint32_t level = range.baseMipLevel;
|
|
level < range.baseMipLevel + range.levelCount; ++level) {
|
|
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;
|
|
}
|
|
|
|
CPUDescriptorHeapAllocation rtvHeap;
|
|
DAWN_TRY_ASSIGN(rtvHeap, device->GetRenderTargetViewAllocator()
|
|
->AllocateTransientCPUDescriptors());
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetBaseDescriptor();
|
|
|
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = GetRTVDescriptor(level, layer, 1);
|
|
device->GetD3D12Device()->CreateRenderTargetView(GetD3D12Resource(),
|
|
&rtvDesc, rtvHandle);
|
|
commandList->ClearRenderTargetView(rtvHandle, clearColorRGBA, 0, nullptr);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// TODO(natlee@microsoft.com): test compressed textures are cleared
|
|
// create temp buffer with clear color to copy to the texture image
|
|
uint32_t bytesPerRow =
|
|
Align((GetWidth() / GetFormat().blockWidth) * GetFormat().blockByteSize,
|
|
kTextureBytesPerRowAlignment);
|
|
uint64_t bufferSize64 = bytesPerRow * (GetHeight() / GetFormat().blockHeight);
|
|
if (bufferSize64 > std::numeric_limits<uint32_t>::max()) {
|
|
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
|
|
}
|
|
uint32_t bufferSize = static_cast<uint32_t>(bufferSize64);
|
|
DynamicUploader* uploader = device->GetDynamicUploader();
|
|
UploadHandle uploadHandle;
|
|
DAWN_TRY_ASSIGN(uploadHandle,
|
|
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
|
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
|
|
|
|
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, range);
|
|
|
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
|
++level) {
|
|
// compute d3d12 texture copy locations for texture and buffer
|
|
Extent3D copySize = GetMipLevelVirtualSize(level);
|
|
|
|
uint32_t rowsPerImage = GetHeight();
|
|
Texture2DCopySplit copySplit =
|
|
ComputeTextureCopySplit({0, 0, 0}, copySize, GetFormat(),
|
|
uploadHandle.startOffset, bytesPerRow, rowsPerImage);
|
|
|
|
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;
|
|
}
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION textureLocation =
|
|
ComputeTextureCopyLocationForTexture(this, level, layer);
|
|
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
|
Texture2DCopySplit::CopyInfo& info = copySplit.copies[i];
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION bufferLocation =
|
|
ComputeBufferLocationForCopyTextureRegion(
|
|
this, ToBackend(uploadHandle.stagingBuffer)->GetResource(),
|
|
info.bufferSize, copySplit.offset, bytesPerRow);
|
|
D3D12_BOX sourceRegion =
|
|
ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize);
|
|
|
|
// copy the buffer filled with clear color to the texture
|
|
commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x,
|
|
info.textureOffset.y, info.textureOffset.z,
|
|
&bufferLocation, &sourceRegion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
|
SetIsSubresourceContentInitialized(true, range);
|
|
GetDevice()->IncrementLazyClearCountForTesting();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
|
|
const SubresourceRange& range) {
|
|
if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
|
return;
|
|
}
|
|
if (!IsSubresourceContentInitialized(range)) {
|
|
// If subresource has not been initialized, clear it to black as it could contain
|
|
// dirty bits from recycled memory
|
|
GetDevice()->ConsumedError(
|
|
ClearTexture(commandContext, range, TextureBase::ClearValue::Zero));
|
|
}
|
|
}
|
|
|
|
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
|
: TextureViewBase(texture, descriptor) {
|
|
mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
|
|
if (descriptor->format == wgpu::TextureFormat::Depth32Float) {
|
|
// TODO(enga): This will need to be much more nuanced when WebGPU has
|
|
// texture view compatibility rules.
|
|
mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT;
|
|
}
|
|
mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
|
|
// Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
|
|
// and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
|
|
// array textures.
|
|
// Multisampled textures may only be one array layer, so we use
|
|
// D3D12_SRV_DIMENSION_TEXTURE2DMS.
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
|
|
// TODO(jiawei.shao@intel.com): support more texture view dimensions.
|
|
if (GetTexture()->IsMultisampledTexture()) {
|
|
switch (descriptor->dimension) {
|
|
case wgpu::TextureViewDimension::e2DArray:
|
|
ASSERT(texture->GetArrayLayers() == 1);
|
|
DAWN_FALLTHROUGH;
|
|
case wgpu::TextureViewDimension::e2D:
|
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
|
mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
} else {
|
|
switch (descriptor->dimension) {
|
|
case wgpu::TextureViewDimension::e2D:
|
|
case wgpu::TextureViewDimension::e2DArray:
|
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
|
mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
|
mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount;
|
|
mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer;
|
|
mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount;
|
|
mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel;
|
|
mSrvDesc.Texture2DArray.PlaneSlice = 0;
|
|
mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
|
|
break;
|
|
case wgpu::TextureViewDimension::Cube:
|
|
case wgpu::TextureViewDimension::CubeArray:
|
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
|
ASSERT(descriptor->arrayLayerCount % 6 == 0);
|
|
mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
|
mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer;
|
|
mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6;
|
|
mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel;
|
|
mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount;
|
|
mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
|
|
DXGI_FORMAT TextureView::GetD3D12Format() const {
|
|
return D3D12TextureFormat(GetFormat().format);
|
|
}
|
|
|
|
const D3D12_SHADER_RESOURCE_VIEW_DESC& TextureView::GetSRVDescriptor() const {
|
|
return mSrvDesc;
|
|
}
|
|
|
|
D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const {
|
|
return ToBackend(GetTexture())
|
|
->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount());
|
|
}
|
|
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const {
|
|
ASSERT(GetLevelCount() == 1);
|
|
return ToBackend(GetTexture())
|
|
->GetDSVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount());
|
|
}
|
|
|
|
D3D12_UNORDERED_ACCESS_VIEW_DESC TextureView::GetUAVDescriptor() const {
|
|
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
|
uavDesc.Format = GetD3D12Format();
|
|
|
|
ASSERT(!GetTexture()->IsMultisampledTexture());
|
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
|
uavDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer();
|
|
uavDesc.Texture2DArray.ArraySize = GetLayerCount();
|
|
uavDesc.Texture2DArray.MipSlice = GetBaseMipLevel();
|
|
uavDesc.Texture2DArray.PlaneSlice = 0;
|
|
return uavDesc;
|
|
}
|
|
|
|
}} // namespace dawn_native::d3d12
|