| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/webgpu/texture_utils.h" |
| |
| #include "third_party/blink/renderer/modules/webgpu/gpu_device.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| struct TexelBlockInfo { |
| uint32_t byteSize; |
| uint32_t width; |
| uint32_t height; |
| }; |
| |
| TexelBlockInfo GetTexelBlockInfoForCopy(wgpu::TextureFormat format, |
| wgpu::TextureAspect aspect) { |
| constexpr TexelBlockInfo kInvalidTexelBlockInfo = {0, 0, 0}; |
| |
| switch (aspect) { |
| case wgpu::TextureAspect::All: |
| switch (format) { |
| case wgpu::TextureFormat::R8Unorm: |
| case wgpu::TextureFormat::R8Snorm: |
| case wgpu::TextureFormat::R8Uint: |
| case wgpu::TextureFormat::R8Sint: |
| return {1u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::R16Uint: |
| case wgpu::TextureFormat::R16Sint: |
| case wgpu::TextureFormat::R16Float: |
| case wgpu::TextureFormat::R16Snorm: |
| case wgpu::TextureFormat::R16Unorm: |
| case wgpu::TextureFormat::RG8Unorm: |
| case wgpu::TextureFormat::RG8Snorm: |
| case wgpu::TextureFormat::RG8Uint: |
| case wgpu::TextureFormat::RG8Sint: |
| return {2u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::R32Float: |
| case wgpu::TextureFormat::R32Uint: |
| case wgpu::TextureFormat::R32Sint: |
| case wgpu::TextureFormat::RG16Uint: |
| case wgpu::TextureFormat::RG16Sint: |
| case wgpu::TextureFormat::RG16Float: |
| case wgpu::TextureFormat::RG16Snorm: |
| case wgpu::TextureFormat::RG16Unorm: |
| case wgpu::TextureFormat::RGBA8Unorm: |
| case wgpu::TextureFormat::RGBA8UnormSrgb: |
| case wgpu::TextureFormat::RGBA8Snorm: |
| case wgpu::TextureFormat::RGBA8Uint: |
| case wgpu::TextureFormat::RGBA8Sint: |
| case wgpu::TextureFormat::BGRA8Unorm: |
| case wgpu::TextureFormat::BGRA8UnormSrgb: |
| case wgpu::TextureFormat::RGB10A2Uint: |
| case wgpu::TextureFormat::RGB10A2Unorm: |
| case wgpu::TextureFormat::RG11B10Ufloat: |
| case wgpu::TextureFormat::RGB9E5Ufloat: |
| return {4u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::RG32Float: |
| case wgpu::TextureFormat::RG32Uint: |
| case wgpu::TextureFormat::RG32Sint: |
| case wgpu::TextureFormat::RGBA16Uint: |
| case wgpu::TextureFormat::RGBA16Sint: |
| case wgpu::TextureFormat::RGBA16Float: |
| case wgpu::TextureFormat::RGBA16Snorm: |
| case wgpu::TextureFormat::RGBA16Unorm: |
| return {8u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::RGBA32Float: |
| case wgpu::TextureFormat::RGBA32Uint: |
| case wgpu::TextureFormat::RGBA32Sint: |
| return {16u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::Depth16Unorm: |
| return {2u, 1u, 1u}; |
| case wgpu::TextureFormat::Stencil8: |
| return {1u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::BC1RGBAUnorm: |
| case wgpu::TextureFormat::BC1RGBAUnormSrgb: |
| case wgpu::TextureFormat::BC4RUnorm: |
| case wgpu::TextureFormat::BC4RSnorm: |
| return {8u, 4u, 4u}; |
| |
| case wgpu::TextureFormat::BC2RGBAUnorm: |
| case wgpu::TextureFormat::BC2RGBAUnormSrgb: |
| case wgpu::TextureFormat::BC3RGBAUnorm: |
| case wgpu::TextureFormat::BC3RGBAUnormSrgb: |
| case wgpu::TextureFormat::BC5RGUnorm: |
| case wgpu::TextureFormat::BC5RGSnorm: |
| case wgpu::TextureFormat::BC6HRGBUfloat: |
| case wgpu::TextureFormat::BC6HRGBFloat: |
| case wgpu::TextureFormat::BC7RGBAUnorm: |
| case wgpu::TextureFormat::BC7RGBAUnormSrgb: |
| return {16u, 4u, 4u}; |
| |
| case wgpu::TextureFormat::ETC2RGB8Unorm: |
| case wgpu::TextureFormat::ETC2RGB8UnormSrgb: |
| case wgpu::TextureFormat::ETC2RGB8A1Unorm: |
| case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: |
| case wgpu::TextureFormat::EACR11Unorm: |
| case wgpu::TextureFormat::EACR11Snorm: |
| return {8u, 4u, 4u}; |
| |
| case wgpu::TextureFormat::ETC2RGBA8Unorm: |
| case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: |
| case wgpu::TextureFormat::EACRG11Unorm: |
| case wgpu::TextureFormat::EACRG11Snorm: |
| return {16u, 4u, 4u}; |
| |
| case wgpu::TextureFormat::ASTC4x4Unorm: |
| case wgpu::TextureFormat::ASTC4x4UnormSrgb: |
| return {16u, 4u, 4u}; |
| case wgpu::TextureFormat::ASTC5x4Unorm: |
| case wgpu::TextureFormat::ASTC5x4UnormSrgb: |
| return {16u, 5u, 4u}; |
| case wgpu::TextureFormat::ASTC5x5Unorm: |
| case wgpu::TextureFormat::ASTC5x5UnormSrgb: |
| return {16u, 5u, 5u}; |
| case wgpu::TextureFormat::ASTC6x5Unorm: |
| case wgpu::TextureFormat::ASTC6x5UnormSrgb: |
| return {16u, 6u, 5u}; |
| case wgpu::TextureFormat::ASTC6x6Unorm: |
| case wgpu::TextureFormat::ASTC6x6UnormSrgb: |
| return {16u, 6u, 6u}; |
| case wgpu::TextureFormat::ASTC8x5Unorm: |
| case wgpu::TextureFormat::ASTC8x5UnormSrgb: |
| return {16u, 8u, 5u}; |
| case wgpu::TextureFormat::ASTC8x6Unorm: |
| case wgpu::TextureFormat::ASTC8x6UnormSrgb: |
| return {16u, 8u, 6u}; |
| case wgpu::TextureFormat::ASTC8x8Unorm: |
| case wgpu::TextureFormat::ASTC8x8UnormSrgb: |
| return {16u, 8u, 8u}; |
| case wgpu::TextureFormat::ASTC10x5Unorm: |
| case wgpu::TextureFormat::ASTC10x5UnormSrgb: |
| return {16u, 10u, 5u}; |
| case wgpu::TextureFormat::ASTC10x6Unorm: |
| case wgpu::TextureFormat::ASTC10x6UnormSrgb: |
| return {16u, 10u, 6u}; |
| case wgpu::TextureFormat::ASTC10x8Unorm: |
| case wgpu::TextureFormat::ASTC10x8UnormSrgb: |
| return {16u, 10u, 8u}; |
| case wgpu::TextureFormat::ASTC10x10Unorm: |
| case wgpu::TextureFormat::ASTC10x10UnormSrgb: |
| return {16u, 10u, 10u}; |
| case wgpu::TextureFormat::ASTC12x10Unorm: |
| case wgpu::TextureFormat::ASTC12x10UnormSrgb: |
| return {16u, 12u, 10u}; |
| case wgpu::TextureFormat::ASTC12x12Unorm: |
| case wgpu::TextureFormat::ASTC12x12UnormSrgb: |
| return {16u, 12u, 12u}; |
| |
| default: |
| return kInvalidTexelBlockInfo; |
| } |
| |
| // Copies to depth/stencil aspects are fairly restricted, see |
| // https://gpuweb.github.io/gpuweb/#depth-formats so we only list |
| // combinations of format and aspects that can be copied to with a |
| // WriteTexture. |
| case wgpu::TextureAspect::DepthOnly: |
| switch (format) { |
| case wgpu::TextureFormat::Depth16Unorm: |
| return GetTexelBlockInfoForCopy(format, wgpu::TextureAspect::All); |
| |
| default: |
| return kInvalidTexelBlockInfo; |
| } |
| |
| case wgpu::TextureAspect::StencilOnly: |
| switch (format) { |
| case wgpu::TextureFormat::Depth24PlusStencil8: |
| case wgpu::TextureFormat::Depth32FloatStencil8: |
| return {1u, 1u, 1u}; |
| |
| case wgpu::TextureFormat::Stencil8: |
| return GetTexelBlockInfoForCopy(format, wgpu::TextureAspect::All); |
| |
| default: |
| return kInvalidTexelBlockInfo; |
| } |
| |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| size_t EstimateWriteTextureBytesUpperBound(wgpu::TexelCopyBufferLayout layout, |
| wgpu::Extent3D extent, |
| wgpu::TextureFormat format, |
| wgpu::TextureAspect aspect) { |
| // Check for empty copies because of depth first so we can early out. Note |
| // that we can't early out because of height or width being 0 because padding |
| // images still need to be accounted for. |
| if (extent.depthOrArrayLayers == 0) { |
| return 0; |
| } |
| |
| TexelBlockInfo blockInfo = GetTexelBlockInfoForCopy(format, aspect); |
| |
| // Unknown format/aspect combination will be validated by the GPU process |
| // again. |
| if (blockInfo.byteSize == 0) { |
| return 0; |
| } |
| |
| // If the block size doesn't divide the extent, a validation error will be |
| // produced on the GPU process side so we don't need to guard against it. |
| uint32_t widthInBlocks = extent.width / blockInfo.width; |
| uint32_t heightInBlocks = extent.height / blockInfo.height; |
| |
| // Use checked numerics even though the GPU process will guard against OOB |
| // because otherwise UBSan will complain about overflows. Note that if |
| // bytesPerRow or rowsPerImage are wgpu::kCopyStrideUndefined and used, the |
| // GPU process will also create a validation error because it means that they |
| // are used when copySize.height/depthOrArrayLayers > 1. |
| base::CheckedNumeric<size_t> requiredBytesInCopy = 0; |
| |
| // WebGPU requires that the padding bytes for images are counted, even if the |
| // copy is empty. |
| if (extent.depthOrArrayLayers > 1) { |
| requiredBytesInCopy = layout.bytesPerRow; |
| requiredBytesInCopy *= layout.rowsPerImage; |
| requiredBytesInCopy *= (extent.depthOrArrayLayers - 1); |
| } |
| |
| if (heightInBlocks != 0) { |
| base::CheckedNumeric<size_t> lastRowBytes = widthInBlocks; |
| lastRowBytes *= blockInfo.byteSize; |
| |
| base::CheckedNumeric<size_t> lastImageBytes = layout.bytesPerRow; |
| lastImageBytes *= (heightInBlocks - 1); |
| lastImageBytes += lastRowBytes; |
| |
| requiredBytesInCopy += lastImageBytes; |
| } |
| |
| return requiredBytesInCopy.ValueOrDefault(0); |
| } |
| |
| } // namespace blink |