Modified: trunk/Source/WebGPU/WebGPU/CommandEncoder.mm (292199 => 292200)
--- trunk/Source/WebGPU/WebGPU/CommandEncoder.mm 2022-04-01 04:48:09 UTC (rev 292199)
+++ trunk/Source/WebGPU/WebGPU/CommandEncoder.mm 2022-04-01 04:50:58 UTC (rev 292200)
@@ -171,6 +171,21 @@
return false;
}
+static bool refersToAllAspects(WGPUTextureFormat format, WGPUTextureAspect aspect)
+{
+ switch (aspect) {
+ case WGPUTextureAspect_All:
+ return true;
+ case WGPUTextureAspect_StencilOnly:
+ return Texture::containsStencilAspect(format) && !Texture::containsDepthAspect(format);
+ case WGPUTextureAspect_DepthOnly:
+ return Texture::containsDepthAspect(format) && !Texture::containsStencilAspect(format);
+ case WGPUTextureAspect_Force32:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
static bool validateCopyBufferToTexture(const WGPUImageCopyBuffer& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
{
// "Let dstTextureDesc be destination.texture.[[descriptor]]."
@@ -537,11 +552,327 @@
}
}
+static std::optional<WGPUTextureFormat> isSRGBCompatible(WGPUTextureFormat format)
+{
+ switch (format) {
+ case WGPUTextureFormat_R8Unorm:
+ case WGPUTextureFormat_R8Snorm:
+ case WGPUTextureFormat_R8Uint:
+ case WGPUTextureFormat_R8Sint:
+ case WGPUTextureFormat_R16Uint:
+ case WGPUTextureFormat_R16Sint:
+ case WGPUTextureFormat_R16Float:
+ case WGPUTextureFormat_RG8Unorm:
+ case WGPUTextureFormat_RG8Snorm:
+ case WGPUTextureFormat_RG8Uint:
+ case WGPUTextureFormat_RG8Sint:
+ case WGPUTextureFormat_R32Float:
+ case WGPUTextureFormat_R32Uint:
+ case WGPUTextureFormat_R32Sint:
+ case WGPUTextureFormat_RG16Uint:
+ case WGPUTextureFormat_RG16Sint:
+ case WGPUTextureFormat_RG16Float:
+ return std::nullopt;
+ case WGPUTextureFormat_RGBA8Unorm:
+ case WGPUTextureFormat_RGBA8UnormSrgb:
+ return WGPUTextureFormat_RGBA8Unorm;
+ case WGPUTextureFormat_RGBA8Snorm:
+ case WGPUTextureFormat_RGBA8Uint:
+ case WGPUTextureFormat_RGBA8Sint:
+ return std::nullopt;
+ case WGPUTextureFormat_BGRA8Unorm:
+ case WGPUTextureFormat_BGRA8UnormSrgb:
+ return WGPUTextureFormat_BGRA8Unorm;
+ case WGPUTextureFormat_RGB10A2Unorm:
+ case WGPUTextureFormat_RG11B10Ufloat:
+ case WGPUTextureFormat_RGB9E5Ufloat:
+ case WGPUTextureFormat_RG32Float:
+ case WGPUTextureFormat_RG32Uint:
+ case WGPUTextureFormat_RG32Sint:
+ case WGPUTextureFormat_RGBA16Uint:
+ case WGPUTextureFormat_RGBA16Sint:
+ case WGPUTextureFormat_RGBA16Float:
+ case WGPUTextureFormat_RGBA32Float:
+ case WGPUTextureFormat_RGBA32Uint:
+ case WGPUTextureFormat_RGBA32Sint:
+ case WGPUTextureFormat_Stencil8:
+ case WGPUTextureFormat_Depth16Unorm:
+ case WGPUTextureFormat_Depth24Plus:
+ case WGPUTextureFormat_Depth24PlusStencil8:
+ case WGPUTextureFormat_Depth24UnormStencil8:
+ case WGPUTextureFormat_Depth32Float:
+ case WGPUTextureFormat_Depth32FloatStencil8:
+ return std::nullopt;
+ case WGPUTextureFormat_BC1RGBAUnorm:
+ case WGPUTextureFormat_BC1RGBAUnormSrgb:
+ return WGPUTextureFormat_BC1RGBAUnorm;
+ case WGPUTextureFormat_BC2RGBAUnorm:
+ case WGPUTextureFormat_BC2RGBAUnormSrgb:
+ return WGPUTextureFormat_BC2RGBAUnorm;
+ case WGPUTextureFormat_BC3RGBAUnorm:
+ case WGPUTextureFormat_BC3RGBAUnormSrgb:
+ return WGPUTextureFormat_BC3RGBAUnorm;
+ case WGPUTextureFormat_BC4RUnorm:
+ case WGPUTextureFormat_BC4RSnorm:
+ case WGPUTextureFormat_BC5RGUnorm:
+ case WGPUTextureFormat_BC5RGSnorm:
+ case WGPUTextureFormat_BC6HRGBUfloat:
+ case WGPUTextureFormat_BC6HRGBFloat:
+ return std::nullopt;
+ case WGPUTextureFormat_BC7RGBAUnorm:
+ case WGPUTextureFormat_BC7RGBAUnormSrgb:
+ return WGPUTextureFormat_BC7RGBAUnorm;
+ case WGPUTextureFormat_ETC2RGB8Unorm:
+ case WGPUTextureFormat_ETC2RGB8UnormSrgb:
+ return WGPUTextureFormat_ETC2RGB8Unorm;
+ case WGPUTextureFormat_ETC2RGB8A1Unorm:
+ case WGPUTextureFormat_ETC2RGB8A1UnormSrgb:
+ return WGPUTextureFormat_ETC2RGB8A1Unorm;
+ case WGPUTextureFormat_ETC2RGBA8Unorm:
+ case WGPUTextureFormat_ETC2RGBA8UnormSrgb:
+ return WGPUTextureFormat_ETC2RGBA8Unorm;
+ case WGPUTextureFormat_EACR11Unorm:
+ case WGPUTextureFormat_EACR11Snorm:
+ case WGPUTextureFormat_EACRG11Unorm:
+ case WGPUTextureFormat_EACRG11Snorm:
+ return std::nullopt;
+ case WGPUTextureFormat_ASTC4x4Unorm:
+ case WGPUTextureFormat_ASTC4x4UnormSrgb:
+ return WGPUTextureFormat_ASTC4x4Unorm;
+ case WGPUTextureFormat_ASTC5x4Unorm:
+ case WGPUTextureFormat_ASTC5x4UnormSrgb:
+ return WGPUTextureFormat_ASTC5x4Unorm;
+ case WGPUTextureFormat_ASTC5x5Unorm:
+ case WGPUTextureFormat_ASTC5x5UnormSrgb:
+ return WGPUTextureFormat_ASTC5x5Unorm;
+ case WGPUTextureFormat_ASTC6x5Unorm:
+ case WGPUTextureFormat_ASTC6x5UnormSrgb:
+ return WGPUTextureFormat_ASTC6x5Unorm;
+ case WGPUTextureFormat_ASTC6x6Unorm:
+ case WGPUTextureFormat_ASTC6x6UnormSrgb:
+ return WGPUTextureFormat_ASTC6x6Unorm;
+ case WGPUTextureFormat_ASTC8x5Unorm:
+ case WGPUTextureFormat_ASTC8x5UnormSrgb:
+ return WGPUTextureFormat_ASTC8x5Unorm;
+ case WGPUTextureFormat_ASTC8x6Unorm:
+ case WGPUTextureFormat_ASTC8x6UnormSrgb:
+ return WGPUTextureFormat_ASTC8x6Unorm;
+ case WGPUTextureFormat_ASTC8x8Unorm:
+ case WGPUTextureFormat_ASTC8x8UnormSrgb:
+ return WGPUTextureFormat_ASTC8x8Unorm;
+ case WGPUTextureFormat_ASTC10x5Unorm:
+ case WGPUTextureFormat_ASTC10x5UnormSrgb:
+ return WGPUTextureFormat_ASTC10x5Unorm;
+ case WGPUTextureFormat_ASTC10x6Unorm:
+ case WGPUTextureFormat_ASTC10x6UnormSrgb:
+ return WGPUTextureFormat_ASTC10x6Unorm;
+ case WGPUTextureFormat_ASTC10x8Unorm:
+ case WGPUTextureFormat_ASTC10x8UnormSrgb:
+ return WGPUTextureFormat_ASTC10x8Unorm;
+ case WGPUTextureFormat_ASTC10x10Unorm:
+ case WGPUTextureFormat_ASTC10x10UnormSrgb:
+ return WGPUTextureFormat_ASTC10x10Unorm;
+ case WGPUTextureFormat_ASTC12x10Unorm:
+ case WGPUTextureFormat_ASTC12x10UnormSrgb:
+ return WGPUTextureFormat_ASTC12x10Unorm;
+ case WGPUTextureFormat_ASTC12x12Unorm:
+ case WGPUTextureFormat_ASTC12x12UnormSrgb:
+ return WGPUTextureFormat_ASTC12x12Unorm;
+ case WGPUTextureFormat_Undefined:
+ case WGPUTextureFormat_Force32:
+ ASSERT_NOT_REACHED();
+ return std::nullopt;
+ }
+}
+
+static bool areCopyCompatible(WGPUTextureFormat format1, WGPUTextureFormat format2)
+{
+ // https://gpuweb.github.io/gpuweb/#copy-compatible
+
+ // "format1 equals format2"
+ if (format1 == format2)
+ return true;
+
+ // "format1 and format2 differ only in whether they are srgb formats (have the -srgb suffix)."
+ auto canonicalizedFormat1 = isSRGBCompatible(format1);
+ auto canonicalizedFormat2 = isSRGBCompatible(format2);
+ if (!canonicalizedFormat1 || !canonicalizedFormat2)
+ return false;
+ if (canonicalizedFormat1 != canonicalizedFormat1)
+ return false;
+
+ return true;
+}
+
+static bool validateCopyTextureToTexture(const WGPUImageCopyTexture& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
+{
+ // "Let srcTextureDesc be source.texture.[[descriptor]]."
+ const auto& srcTextureDesc = fromAPI(source.texture).descriptor();
+
+ // "Let dstTextureDesc be destination.texture.[[descriptor]]."
+ const auto& dstTextureDesc = fromAPI(destination.texture).descriptor();
+
+ // "validating GPUImageCopyTexture(source, copySize) returns true."
+ if (!Texture::validateImageCopyTexture(source, copySize))
+ return false;
+
+ // "srcTextureDesc.usage contains COPY_SRC."
+ if (!(srcTextureDesc.usage & WGPUTextureUsage_CopySrc))
+ return false;
+
+ // "validating GPUImageCopyTexture(destination, copySize) returns true."
+ if (!Texture::validateImageCopyTexture(destination, copySize))
+ return false;
+
+ // "dstTextureDesc.usage contains COPY_DST."
+ if (!(dstTextureDesc.usage & WGPUTextureUsage_CopyDst))
+ return false;
+
+ // "srcTextureDesc.sampleCount is equal to dstTextureDesc.sampleCount."
+ if (srcTextureDesc.sampleCount != dstTextureDesc.sampleCount)
+ return false;
+
+ // "srcTextureDesc.format and dstTextureDesc.format must be copy-compatible."
+ if (!areCopyCompatible(srcTextureDesc.format, dstTextureDesc.format))
+ return false;
+
+ // "If srcTextureDesc.format is a depth-stencil format:"
+ if (Texture::isDepthOrStencilFormat(srcTextureDesc.format)) {
+ // "source.aspect and destination.aspect must both refer to all aspects of srcTextureDesc.format and dstTextureDesc.format, respectively."
+ if (!refersToAllAspects(srcTextureDesc.format, source.aspect)
+ || !refersToAllAspects(dstTextureDesc.format, destination.aspect))
+ return false;
+ }
+
+ // "validating texture copy range(source, copySize) returns true."
+ if (!Texture::validateTextureCopyRange(source, copySize))
+ return false;
+
+ // "validating texture copy range(destination, copySize) returns true."
+ if (!Texture::validateTextureCopyRange(destination, copySize))
+ return false;
+
+ // "The set of subresources for texture copy(source, copySize) and the set of subresources for texture copy(destination, copySize) is disjoint."
+ // https://gpuweb.github.io/gpuweb/#abstract-opdef-set-of-subresources-for-texture-copy
+ if (source.texture == destination.texture) {
+ // Mip levels are never ranges.
+ if (source.mipLevel == destination.mipLevel) {
+ switch (fromAPI(source.texture).descriptor().dimension) {
+ case WGPUTextureDimension_1D:
+ // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel."
+ return false;
+ case WGPUTextureDimension_2D: {
+ // "For each arrayLayer of the copySize.depthOrArrayLayers array layers starting at imageCopyTexture.origin.z:"
+ // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel and array layer arrayLayer."
+ Range<uint32_t> sourceRange(source.origin.z, source.origin.z + copySize.depthOrArrayLayers);
+ Range<uint32_t> destinationRange(destination.origin.z, source.origin.z + copySize.depthOrArrayLayers);
+ if (sourceRange.overlaps(destinationRange))
+ return false;
+ break;
+ }
+ case WGPUTextureDimension_3D:
+ // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel."
+ return false;
+ case WGPUTextureDimension_Force32:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
void CommandEncoder::copyTextureToTexture(const WGPUImageCopyTexture& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
{
- UNUSED_PARAM(source);
- UNUSED_PARAM(destination);
- UNUSED_PARAM(copySize);
+ if (source.nextInChain || destination.nextInChain)
+ return;
+
+ // https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copytexturetotexture
+
+ // "Prepare the encoder state of this. If it returns false, stop."
+ if (!prepareTheEncoderState())
+ return;
+
+ // "If any of the following conditions are unsatisfied"
+ if (!validateCopyTextureToTexture(source, destination, copySize)) {
+ // "generate a validation error and stop."
+ m_device->generateAValidationError("Validation failure.");
+ return;
+ }
+
+ ensureBlitCommandEncoder();
+
+ auto& sourceDescriptor = fromAPI(source.texture).descriptor();
+ // FIXME(PERFORMANCE): Is it actually faster to use the -[MTLBlitCommandEncoder copyFromTexture:...toTexture:...levelCount:]
+ // variant, where possible, rather than calling the other variant in a loop?
+ switch (sourceDescriptor.dimension) {
+ case WGPUTextureDimension_1D: {
+ // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copyfromtexture?language=objc
+ // "When you copy to a 1D texture, height and depth must be 1."
+ auto sourceSize = MTLSizeMake(copySize.width, 1, 1);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, 1, 1);
+ auto destinationOrigin = MTLOriginMake(destination.origin.x, 1, 1);
+ for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+ // FIXME: Use checked arithmetic.
+ NSUInteger sourceSlice = source.origin.z + layer;
+ NSUInteger destinationSlice = destination.origin.z + layer;
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:sourceSlice
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toTexture:fromAPI(destination.texture).texture()
+ destinationSlice:destinationSlice
+ destinationLevel:destination.mipLevel
+ destinationOrigin:destinationOrigin];
+ }
+ break;
+ }
+ case WGPUTextureDimension_2D: {
+ // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copyfromtexture?language=objc
+ // "When you copy to a 2D texture, depth must be 1."
+ auto sourceSize = MTLSizeMake(copySize.width, copySize.height, 1);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, 1);
+ auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, 1);
+ for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+ // FIXME: Use checked arithmetic.
+ NSUInteger sourceSlice = source.origin.z + layer;
+ NSUInteger destinationSlice = destination.origin.z + layer;
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:sourceSlice
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toTexture:fromAPI(destination.texture).texture()
+ destinationSlice:destinationSlice
+ destinationLevel:destination.mipLevel
+ destinationOrigin:destinationOrigin];
+ }
+ break;
+ }
+ case WGPUTextureDimension_3D: {
+ auto sourceSize = MTLSizeMake(copySize.width, copySize.height, copySize.depthOrArrayLayers);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, source.origin.z);
+ auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, destination.origin.z);
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:0
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toTexture:fromAPI(destination.texture).texture()
+ destinationSlice:0
+ destinationLevel:destination.mipLevel
+ destinationOrigin:destinationOrigin];
+ break;
+ }
+ case WGPUTextureDimension_Force32:
+ ASSERT_NOT_REACHED();
+ return;
+ }
}
static bool validateClearBuffer(const Buffer& buffer, uint64_t offset, uint64_t size)