From f392c38b67ca8456d448076b70dca87db02bb990 Mon Sep 17 00:00:00 2001 From: "Yan,Shaobo" Date: Mon, 24 Oct 2022 06:03:22 +0000 Subject: [PATCH] Add CopyExternalTextureForBrowser() This API accept ExternalTexture object as copy source and a dawn 2D texture as destination. It has similar functions as CopyTextureForBrowser(). The API is used to support cases that source images are multi-planar format and want to do conversion and uploading to a dawn 2D texture. Bug: chromium:1361363 Change-Id: Ie390acfb95b47d417f4a8faa2d1e19163d549154 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105880 Commit-Queue: Shaobo Yan Reviewed-by: Austin Eng Reviewed-by: Corentin Wallez --- dawn.json | 21 +- .../native/CopyTextureForBrowserHelper.cpp | 478 ++++++++++----- src/dawn/native/CopyTextureForBrowserHelper.h | 11 + src/dawn/native/InternalPipelineStore.h | 4 +- src/dawn/native/Queue.cpp | 23 + src/dawn/native/Queue.h | 8 + src/dawn/native/utils/WGPUHelpers.cpp | 11 + src/dawn/native/utils/WGPUHelpers.h | 3 + src/dawn/native/webgpu_absl_format.cpp | 12 + src/dawn/native/webgpu_absl_format.h | 6 + src/dawn/tests/BUILD.gn | 1 + .../CopyExternalTextureForBrowserTests.cpp | 207 +++++++ .../tests/end2end/ExternalTextureTests.cpp | 22 +- .../validation/CopyTextureForBrowserTests.cpp | 566 ++++++++++++++++-- src/dawn/utils/WGPUHelpers.cpp | 21 + src/dawn/utils/WGPUHelpers.h | 9 + 16 files changed, 1166 insertions(+), 237 deletions(-) create mode 100644 src/dawn/tests/end2end/CopyExternalTextureForBrowserTests.cpp diff --git a/dawn.json b/dawn.json index 4b2f150959..2ed1cc9984 100644 --- a/dawn.json +++ b/dawn.json @@ -916,7 +916,6 @@ "category": "structure", "extensible": "in", "tags": ["dawn"], - "_TODO": "support number as length input", "members": [ {"name": "flip y", "type": "bool", "default": "false"}, {"name": "needs color space conversion", "type": "bool", "default": "false"}, @@ -1367,7 +1366,6 @@ "category": "structure", "extensible": "in", "tags": ["dawn"], - "_TODO": "crbug.com/1316671: Mark 'visible rect' as must have after chromium side changes landed", "members": [ {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, {"name": "plane 0", "type": "texture view"}, @@ -1440,6 +1438,14 @@ {"name": "aspect", "type": "texture aspect", "default": "all"} ] }, + "image copy external texture": { + "category": "structure", + "extensible": "in", + "members": [ + {"name": "external texture", "type": "external texture"}, + {"name": "origin", "type": "origin 3D"} + ] + }, "index format": { "category": "enum", "values": [ @@ -1721,6 +1727,17 @@ {"name": "options", "type": "copy texture for browser options", "annotation": "const*"} ] }, + { + "name": "copy external texture for browser", + "extensible": "in", + "tags": ["dawn"], + "args": [ + {"name": "source", "type": "image copy external texture", "annotation": "const*"}, + {"name": "destination", "type": "image copy texture", "annotation": "const*"}, + {"name": "copy size", "type": "extent 3D", "annotation": "const*"}, + {"name": "options", "type": "copy texture for browser options", "annotation": "const*"} + ] + }, { "name": "set label", "returns": "void", diff --git a/src/dawn/native/CopyTextureForBrowserHelper.cpp b/src/dawn/native/CopyTextureForBrowserHelper.cpp index 6f89842ef7..fb585f9e85 100644 --- a/src/dawn/native/CopyTextureForBrowserHelper.cpp +++ b/src/dawn/native/CopyTextureForBrowserHelper.cpp @@ -25,6 +25,7 @@ #include "dawn/native/CommandEncoder.h" #include "dawn/native/CommandValidation.h" #include "dawn/native/Device.h" +#include "dawn/native/ExternalTexture.h" #include "dawn/native/InternalPipelineStore.h" #include "dawn/native/Queue.h" #include "dawn/native/RenderPassEncoder.h" @@ -36,9 +37,8 @@ namespace dawn::native { namespace { - -static const char sCopyTextureForBrowserShader[] = R"( - struct GammaTransferParams { +static const char sCopyForBrowserShader[] = R"( + struct GammaTransferParamsInternal { G: f32, A: f32, B: f32, @@ -49,15 +49,15 @@ static const char sCopyTextureForBrowserShader[] = R"( padding: u32, }; - struct Uniforms { // offset align size - scale: vec2, // 0 8 8 - offset: vec2, // 8 8 8 - steps_mask: u32, // 16 4 4 - // implicit padding; // 20 12 - conversion_matrix: mat3x3, // 32 16 48 - gamma_decoding_params: GammaTransferParams, // 80 4 32 - gamma_encoding_params: GammaTransferParams, // 112 4 32 - gamma_decoding_for_dst_srgb_params: GammaTransferParams, // 144 4 32 + struct Uniforms { // offset align size + scale: vec2, // 0 8 8 + offset: vec2, // 8 8 8 + steps_mask: u32, // 16 4 4 + // implicit padding; // 20 12 + conversion_matrix: mat3x3, // 32 16 48 + gamma_decoding_params: GammaTransferParamsInternal, // 80 4 32 + gamma_encoding_params: GammaTransferParamsInternal, // 112 4 32 + gamma_decoding_for_dst_srgb_params: GammaTransferParamsInternal, // 144 4 32 }; @binding(0) @group(0) var uniforms : Uniforms; @@ -75,7 +75,7 @@ static const char sCopyTextureForBrowserShader[] = R"( // nonlinear = pow(A * x + B, G) + E // (https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/color_transform.cc;l=541) // Expand the equation with sign() to make it handle all gamma conversions. - fn gamma_conversion(v: f32, params: GammaTransferParams) -> f32 { + fn gamma_conversion(v: f32, params: GammaTransferParamsInternal) -> f32 { // Linear part: C * x + F if (abs(v) < params.D) { return sign(v) * (params.C * abs(v) + params.F); @@ -121,24 +121,23 @@ static const char sCopyTextureForBrowserShader[] = R"( } @binding(1) @group(0) var mySampler: sampler; - @binding(2) @group(0) var myTexture: texture_2d; - @fragment - fn fs_main( - @location(0) texcoord : vec2 - ) -> @location(0) vec4 { - // Clamp the texcoord and discard the out-of-bound pixels. + // Resource used in copyTexture entry point only. + @binding(2) @group(0) var mySourceTexture: texture_2d; + + // Resource used in copyExternalTexture entry point only. + @binding(2) @group(0) var mySourceExternalTexture: texture_external; + + fn discardIfOutsideOfCopy(texcoord : vec2) { var clampedTexcoord = clamp(texcoord, vec2(0.0, 0.0), vec2(1.0, 1.0)); - - // Swizzling of texture formats when sampling / rendering is handled by the - // hardware so we don't need special logic in this shader. This is covered by tests. - var color = textureSample(myTexture, mySampler, texcoord); - if (!all(clampedTexcoord == texcoord)) { discard; } + } + fn transform(srcColor : vec4) -> vec4 { + var color = srcColor; let kUnpremultiplyStep = 0x01u; let kDecodeToLinearStep = 0x02u; let kConvertToDstGamutStep = 0x04u; @@ -203,11 +202,33 @@ static const char sCopyTextureForBrowserShader[] = R"( return color; } + + @fragment + fn copyTexture(@location(0) texcoord : vec2 + ) -> @location(0) vec4 { + var color = textureSample(mySourceTexture, mySampler, texcoord); + + // TODO(crbug.com/tint/1723): Discard before sampling should be valid. + discardIfOutsideOfCopy(texcoord); + + return transform(color); + } + + @fragment + fn copyExternalTexture(@location(0) texcoord : vec2 + ) -> @location(0) vec4 { + var color = textureSampleBaseClampToEdge(mySourceExternalTexture, mySampler, texcoord); + + // TODO(crbug.com/tint/1723): Discard before sampling should be valid. + discardIfOutsideOfCopy(texcoord); + + return transform(color); + } )"; // Follow the same order of skcms_TransferFunction // https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/include/third_party/skcms/skcms.h;l=46; -struct GammaTransferParams { +struct GammaTransferParamsInternal { float G = 0.0; float A = 0.0; float B = 0.0; @@ -226,16 +247,22 @@ struct Uniform { uint32_t stepsMask = 0; const std::array padding = {}; // 12 bytes padding std::array conversionMatrix = {}; - GammaTransferParams gammaDecodingParams = {}; - GammaTransferParams gammaEncodingParams = {}; - GammaTransferParams gammaDecodingForDstSrgbParams = {}; + GammaTransferParamsInternal gammaDecodingParams = {}; + GammaTransferParamsInternal gammaEncodingParams = {}; + GammaTransferParamsInternal gammaDecodingForDstSrgbParams = {}; }; static_assert(sizeof(Uniform) == 176); +enum class SourceTextureType { Texture2D, ExternalTexture }; + +struct TextureInfo { + Origin3D origin; + Extent3D size; +}; + // TODO(crbug.com/dawn/856): Expand copyTextureForBrowser to support any // non-depth, non-stencil, non-compressed texture format pair copy. -MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcFormat, - const wgpu::TextureFormat dstFormat) { +MaybeError ValidateCopyTextureSourceFormat(const wgpu::TextureFormat srcFormat) { switch (srcFormat) { case wgpu::TextureFormat::BGRA8Unorm: case wgpu::TextureFormat::RGBA8Unorm: @@ -245,6 +272,10 @@ MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcForm return DAWN_VALIDATION_ERROR("Source texture format (%s) is not supported.", srcFormat); } + return {}; +} + +MaybeError ValidateCopyForBrowserDestinationFormat(const wgpu::TextureFormat dstFormat) { switch (dstFormat) { case wgpu::TextureFormat::R8Unorm: case wgpu::TextureFormat::R16Float: @@ -268,7 +299,8 @@ MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcForm return {}; } -RenderPipelineBase* GetCachedPipeline(InternalPipelineStore* store, wgpu::TextureFormat dstFormat) { +RenderPipelineBase* GetCachedCopyTexturePipeline(InternalPipelineStore* store, + wgpu::TextureFormat dstFormat) { auto pipeline = store->copyTextureForBrowserPipelines.find(dstFormat); if (pipeline != store->copyTextureForBrowserPipelines.end()) { return pipeline->second.Get(); @@ -276,120 +308,92 @@ RenderPipelineBase* GetCachedPipeline(InternalPipelineStore* store, wgpu::Textur return nullptr; } +RenderPipelineBase* GetCachedCopyExternalTexturePipeline(InternalPipelineStore* store, + wgpu::TextureFormat dstFormat) { + auto pipeline = store->copyExternalTextureForBrowserPipelines.find(dstFormat); + if (pipeline != store->copyExternalTextureForBrowserPipelines.end()) { + return pipeline->second.Get(); + } + return nullptr; +} + +ResultOrError> CreateCopyForBrowserPipeline( + DeviceBase* device, + wgpu::TextureFormat dstFormat, + ShaderModuleBase* shaderModule, + const char* fragmentEntryPoint) { + // Prepare vertex stage. + VertexState vertex = {}; + vertex.module = shaderModule; + vertex.entryPoint = "vs_main"; + + // Prepare frgament stage. + FragmentState fragment = {}; + fragment.module = shaderModule; + fragment.entryPoint = fragmentEntryPoint; + + // Prepare color state. + ColorTargetState target = {}; + target.format = dstFormat; + + // Create RenderPipeline. + RenderPipelineDescriptor renderPipelineDesc = {}; + + // Generate the layout based on shader modules. + renderPipelineDesc.layout = nullptr; + + renderPipelineDesc.vertex = vertex; + renderPipelineDesc.fragment = &fragment; + + renderPipelineDesc.primitive.topology = wgpu::PrimitiveTopology::TriangleList; + + fragment.targetCount = 1; + fragment.targets = ⌖ + + return device->CreateRenderPipeline(&renderPipelineDesc); +} + +ResultOrError GetOrCreateCopyForBrowserShaderModule( + DeviceBase* device, + InternalPipelineStore* store) { + if (store->copyForBrowser == nullptr) { + DAWN_TRY_ASSIGN(store->copyForBrowser, + utils::CreateShaderModule(device, sCopyForBrowserShader)); + } + + return store->copyForBrowser.Get(); +} + ResultOrError GetOrCreateCopyTextureForBrowserPipeline( DeviceBase* device, wgpu::TextureFormat dstFormat) { InternalPipelineStore* store = device->GetInternalPipelineStore(); - - if (GetCachedPipeline(store, dstFormat) == nullptr) { - // Create vertex shader module if not cached before. - if (store->copyTextureForBrowser == nullptr) { - DAWN_TRY_ASSIGN(store->copyTextureForBrowser, - utils::CreateShaderModule(device, sCopyTextureForBrowserShader)); - } - - ShaderModuleBase* shaderModule = store->copyTextureForBrowser.Get(); - - // Prepare vertex stage. - VertexState vertex = {}; - vertex.module = shaderModule; - vertex.entryPoint = "vs_main"; - - // Prepare frgament stage. - FragmentState fragment = {}; - fragment.module = shaderModule; - fragment.entryPoint = "fs_main"; - - // Prepare color state. - ColorTargetState target = {}; - target.format = dstFormat; - - // Create RenderPipeline. - RenderPipelineDescriptor renderPipelineDesc = {}; - - // Generate the layout based on shader modules. - renderPipelineDesc.layout = nullptr; - - renderPipelineDesc.vertex = vertex; - renderPipelineDesc.fragment = &fragment; - - renderPipelineDesc.primitive.topology = wgpu::PrimitiveTopology::TriangleList; - - fragment.targetCount = 1; - fragment.targets = ⌖ - + if (GetCachedCopyTexturePipeline(store, dstFormat) == nullptr) { + ShaderModuleBase* shaderModule; + DAWN_TRY_ASSIGN(shaderModule, GetOrCreateCopyForBrowserShaderModule(device, store)); Ref pipeline; - DAWN_TRY_ASSIGN(pipeline, device->CreateRenderPipeline(&renderPipelineDesc)); + DAWN_TRY_ASSIGN( + pipeline, CreateCopyForBrowserPipeline(device, dstFormat, shaderModule, "copyTexture")); store->copyTextureForBrowserPipelines.insert({dstFormat, std::move(pipeline)}); } - return GetCachedPipeline(store, dstFormat); + return GetCachedCopyTexturePipeline(store, dstFormat); } -} // anonymous namespace -MaybeError ValidateCopyTextureForBrowser(DeviceBase* device, - const ImageCopyTexture* source, - const ImageCopyTexture* destination, - const Extent3D* copySize, - const CopyTextureForBrowserOptions* options) { - DAWN_TRY(device->ValidateObject(source->texture)); - DAWN_TRY(device->ValidateObject(destination->texture)); - - DAWN_INVALID_IF(source->texture->GetTextureState() == TextureBase::TextureState::Destroyed, - "Source texture %s is destroyed.", source->texture); - - DAWN_INVALID_IF(destination->texture->GetTextureState() == TextureBase::TextureState::Destroyed, - "Destination texture %s is destroyed.", destination->texture); - - DAWN_TRY_CONTEXT(ValidateImageCopyTexture(device, *source, *copySize), - "validating the ImageCopyTexture for the source"); - DAWN_TRY_CONTEXT(ValidateImageCopyTexture(device, *destination, *copySize), - "validating the ImageCopyTexture for the destination"); - - DAWN_TRY_CONTEXT(ValidateTextureCopyRange(device, *source, *copySize), - "validating that the copy fits in the source"); - DAWN_TRY_CONTEXT(ValidateTextureCopyRange(device, *destination, *copySize), - "validating that the copy fits in the destination"); - - DAWN_TRY(ValidateTextureToTextureCopyCommonRestrictions(*source, *destination, *copySize)); - - DAWN_INVALID_IF(source->origin.z > 0, "Source has a non-zero z origin (%u).", source->origin.z); - DAWN_INVALID_IF(copySize->depthOrArrayLayers > 1, "Copy is for more than one array layer (%u)", - copySize->depthOrArrayLayers); - - DAWN_INVALID_IF( - source->texture->GetSampleCount() > 1 || destination->texture->GetSampleCount() > 1, - "The source texture sample count (%u) or the destination texture sample count (%u) is " - "not 1.", - source->texture->GetSampleCount(), destination->texture->GetSampleCount()); - - DAWN_INVALID_IF( - options->internalUsage && !device->HasFeature(Feature::DawnInternalUsages), - "The internalUsage is true while the dawn-internal-usages feature is not enabled."); - UsageValidationMode mode = - options->internalUsage ? UsageValidationMode::Internal : UsageValidationMode::Default; - DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc, mode)); - DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::TextureBinding, mode)); - DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst, mode)); - DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::RenderAttachment, mode)); - - DAWN_TRY(ValidateCopyTextureFormatConversion(source->texture->GetFormat().format, - destination->texture->GetFormat().format)); - - DAWN_INVALID_IF(options->nextInChain != nullptr, "nextInChain must be nullptr"); - - DAWN_TRY(ValidateAlphaMode(options->srcAlphaMode)); - DAWN_TRY(ValidateAlphaMode(options->dstAlphaMode)); - - if (options->needsColorSpaceConversion) { - DAWN_INVALID_IF(options->srcTransferFunctionParameters == nullptr, - "srcTransferFunctionParameters is nullptr when doing color conversion"); - DAWN_INVALID_IF(options->conversionMatrix == nullptr, - "conversionMatrix is nullptr when doing color conversion"); - DAWN_INVALID_IF(options->dstTransferFunctionParameters == nullptr, - "dstTransferFunctionParameters is nullptr when doing color conversion"); +ResultOrError GetOrCreateCopyExternalTextureForBrowserPipeline( + DeviceBase* device, + wgpu::TextureFormat dstFormat) { + InternalPipelineStore* store = device->GetInternalPipelineStore(); + if (GetCachedCopyExternalTexturePipeline(store, dstFormat) == nullptr) { + ShaderModuleBase* shaderModule; + DAWN_TRY_ASSIGN(shaderModule, GetOrCreateCopyForBrowserShaderModule(device, store)); + Ref pipeline; + DAWN_TRY_ASSIGN(pipeline, CreateCopyForBrowserPipeline(device, dstFormat, shaderModule, + "copyExternalTexture")); + store->copyExternalTextureForBrowserPipelines.insert({dstFormat, std::move(pipeline)}); } - return {}; + + return GetCachedCopyExternalTexturePipeline(store, dstFormat); } // Whether the format of dst texture of CopyTextureForBrowser() is srgb or non-srgb. @@ -403,11 +407,14 @@ bool IsSrgbDstFormat(wgpu::TextureFormat format) { } } -MaybeError DoCopyTextureForBrowser(DeviceBase* device, - const ImageCopyTexture* source, - const ImageCopyTexture* destination, - const Extent3D* copySize, - const CopyTextureForBrowserOptions* options) { +template +MaybeError DoCopyForBrowser(DeviceBase* device, + const TextureInfo* sourceInfo, + T* sourceResource, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options, + RenderPipelineBase* pipeline) { // TODO(crbug.com/dawn/856): In D3D12 and Vulkan, compatible texture format can directly // copy to each other. This can be a potential fast path. @@ -416,23 +423,16 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, return {}; } - bool isSrgbDstFormat = IsSrgbDstFormat(destination->texture->GetFormat().format); - RenderPipelineBase* pipeline; - DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyTextureForBrowserPipeline( - device, destination->texture->GetFormat().format)); - // Prepare bind group layout. Ref layout; DAWN_TRY_ASSIGN(layout, pipeline->GetBindGroupLayout(0)); - Extent3D srcTextureSize = source->texture->GetSize(); - // Prepare binding 0 resource: uniform buffer. Uniform uniformData = { - copySize->width / static_cast(srcTextureSize.width), - copySize->height / static_cast(srcTextureSize.height), // scale - source->origin.x / static_cast(srcTextureSize.width), - source->origin.y / static_cast(srcTextureSize.height) // offset + copySize->width / static_cast(sourceInfo->size.width), + copySize->height / static_cast(sourceInfo->size.height), // scale + sourceInfo->origin.x / static_cast(sourceInfo->size.width), + sourceInfo->origin.y / static_cast(sourceInfo->size.height) // offset }; // Handle flipY. FlipY here means we flip the source texture firstly and then @@ -440,7 +440,7 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, // need to unpack the flip. if (options->flipY) { uniformData.scaleY *= -1.0; - uniformData.offsetY += copySize->height / static_cast(srcTextureSize.height); + uniformData.offsetY += copySize->height / static_cast(sourceInfo->size.height); } uint32_t stepsMask = 0u; @@ -528,6 +528,7 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, // and use it as render attachment when possible. // TODO(crbug.com/dawn/1195): Opt the condition for this extra step. It is possible to // bypass this extra step in some cases. + bool isSrgbDstFormat = IsSrgbDstFormat(destination->texture->GetFormat().format); if (isSrgbDstFormat) { stepsMask |= kDecodeForSrgbDstFormat; // Get gamma-linear conversion params from https://en.wikipedia.org/wiki/SRGB with some @@ -536,6 +537,7 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 4.045e-02, 0.0, 0.0}; } + // Upload uniform data uniformData.stepsMask = stepsMask; Ref uniformBuffer; @@ -550,23 +552,13 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, Ref sampler; DAWN_TRY_ASSIGN(sampler, device->CreateSampler(&samplerDesc)); - // Prepare binding 2 resource: sampled texture - TextureViewDescriptor srcTextureViewDesc = {}; - srcTextureViewDesc.dimension = wgpu::TextureViewDimension::e2D; - srcTextureViewDesc.baseMipLevel = source->mipLevel; - srcTextureViewDesc.mipLevelCount = 1; - srcTextureViewDesc.arrayLayerCount = 1; - Ref srcTextureView; - DAWN_TRY_ASSIGN(srcTextureView, - device->CreateTextureView(source->texture, &srcTextureViewDesc)); - // Create bind group after all binding entries are set. UsageValidationMode mode = options->internalUsage ? UsageValidationMode::Internal : UsageValidationMode::Default; Ref bindGroup; DAWN_TRY_ASSIGN(bindGroup, utils::MakeBindGroup( device, layout, - {{0, uniformBuffer}, {1, sampler}, {2, srcTextureView}}, mode)); + {{0, uniformBuffer}, {1, sampler}, {2, sourceResource}}, mode)); // Create command encoder. CommandEncoderDescriptor commandEncoderDesc; @@ -621,4 +613,166 @@ MaybeError DoCopyTextureForBrowser(DeviceBase* device, return {}; } +MaybeError ValidateCopyForBrowserCommonRestrictions(DeviceBase* device, + const ImageCopyTexture& destination, + const Extent3D& copySize, + const CopyTextureForBrowserOptions& options) { + DAWN_TRY(device->ValidateObject(destination.texture)); + DAWN_INVALID_IF(destination.texture->GetTextureState() == TextureBase::TextureState::Destroyed, + "Destination texture %s is destroyed.", destination.texture); + DAWN_TRY_CONTEXT(ValidateImageCopyTexture(device, destination, copySize), + "validating the ImageCopyTexture for the destination"); + DAWN_TRY_CONTEXT(ValidateTextureCopyRange(device, destination, copySize), + "validating that the copy fits in the destination"); + + UsageValidationMode mode = + options.internalUsage ? UsageValidationMode::Internal : UsageValidationMode::Default; + DAWN_TRY(ValidateCanUseAs(destination.texture, wgpu::TextureUsage::CopyDst, mode)); + DAWN_TRY(ValidateCanUseAs(destination.texture, wgpu::TextureUsage::RenderAttachment, mode)); + + DAWN_INVALID_IF(copySize.depthOrArrayLayers > 1, "Copy is for more than one array layer (%u)", + copySize.depthOrArrayLayers); + + DAWN_INVALID_IF(destination.texture->GetSampleCount() > 1, + "The destination texture sample count (%u) is not 1.", + destination.texture->GetSampleCount()); + + DAWN_TRY(ValidateCopyForBrowserDestinationFormat(destination.texture->GetFormat().format)); + + // The valid destination formats are all color formats. + DAWN_INVALID_IF( + destination.aspect != wgpu::TextureAspect::All, + "Destination %s aspect (%s) doesn't select all the aspects of the destination format.", + destination.texture, destination.aspect); + + DAWN_INVALID_IF(options.nextInChain != nullptr, "nextInChain must be nullptr"); + + DAWN_TRY(ValidateAlphaMode(options.srcAlphaMode)); + DAWN_TRY(ValidateAlphaMode(options.dstAlphaMode)); + + if (options.needsColorSpaceConversion) { + DAWN_INVALID_IF(options.srcTransferFunctionParameters == nullptr, + "srcTransferFunctionParameters is nullptr when doing color conversion"); + DAWN_INVALID_IF(options.conversionMatrix == nullptr, + "conversionMatrix is nullptr when doing color conversion"); + DAWN_INVALID_IF(options.dstTransferFunctionParameters == nullptr, + "dstTransferFunctionParameters is nullptr when doing color conversion"); + } + return {}; +} +} // anonymous namespace + +MaybeError ValidateCopyTextureForBrowser(DeviceBase* device, + const ImageCopyTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + DAWN_TRY(device->ValidateObject(source->texture)); + + DAWN_INVALID_IF(source->texture->GetTextureState() == TextureBase::TextureState::Destroyed, + "Source texture %s is destroyed.", source->texture); + + DAWN_TRY_CONTEXT(ValidateImageCopyTexture(device, *source, *copySize), + "validating the ImageCopyTexture for the source"); + + DAWN_TRY_CONTEXT(ValidateTextureCopyRange(device, *source, *copySize), + "validating that the copy fits in the source"); + + DAWN_TRY(ValidateTextureToTextureCopyCommonRestrictions(*source, *destination, *copySize)); + + DAWN_INVALID_IF(source->origin.z > 0, "Source has a non-zero z origin (%u).", source->origin.z); + + DAWN_INVALID_IF(source->texture->GetSampleCount() > 1, + "The source texture sample count (%u) is not 1. ", + source->texture->GetSampleCount()); + + DAWN_INVALID_IF( + options->internalUsage && !device->HasFeature(Feature::DawnInternalUsages), + "The internalUsage is true while the dawn-internal-usages feature is not enabled."); + UsageValidationMode mode = + options->internalUsage ? UsageValidationMode::Internal : UsageValidationMode::Default; + DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc, mode)); + DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::TextureBinding, mode)); + + DAWN_TRY(ValidateCopyTextureSourceFormat(source->texture->GetFormat().format)); + + DAWN_TRY(ValidateCopyForBrowserCommonRestrictions(device, *destination, *copySize, *options)); + return {}; +} + +MaybeError ValidateCopyExternalTextureForBrowser(DeviceBase* device, + const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + DAWN_TRY(device->ValidateObject(source->externalTexture)); + + DAWN_TRY(source->externalTexture->ValidateCanUseInSubmitNow()); + + const Extent2D& sourceVisibleRect = source->externalTexture->GetVisibleRect(); + + // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid + // overflows. + DAWN_INVALID_IF( + static_cast(source->origin.x) + static_cast(copySize->width) > + static_cast(sourceVisibleRect.width) || + static_cast(source->origin.y) + static_cast(copySize->height) > + static_cast(sourceVisibleRect.height) || + static_cast(source->origin.z) > 0, + "Texture copy range (origin: %s, copySize: %s) touches outside of %s visible size (%s).", + &source->origin, copySize, source->externalTexture, &sourceVisibleRect); + + DAWN_INVALID_IF(source->origin.z > 0, "Source has a non-zero z origin (%u).", source->origin.z); + + DAWN_INVALID_IF( + options->internalUsage && !device->HasFeature(Feature::DawnInternalUsages), + "The internalUsage is true while the dawn-internal-usages feature is not enabled."); + + DAWN_TRY(ValidateCopyForBrowserCommonRestrictions(device, *destination, *copySize, *options)); + + return {}; +} + +MaybeError DoCopyExternalTextureForBrowser(DeviceBase* device, + const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + TextureInfo info; + info.origin = source->origin; + const Extent2D& visibleRect = source->externalTexture->GetVisibleRect(); + info.size = {visibleRect.width, visibleRect.height, 1}; + + RenderPipelineBase* pipeline; + DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyExternalTextureForBrowserPipeline( + device, destination->texture->GetFormat().format)); + return DoCopyForBrowser(device, &info, source->externalTexture, + destination, copySize, options, pipeline); +} + +MaybeError DoCopyTextureForBrowser(DeviceBase* device, + const ImageCopyTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + TextureInfo info; + info.origin = source->origin; + info.size = source->texture->GetSize(); + + Ref srcTextureView = nullptr; + TextureViewDescriptor srcTextureViewDesc = {}; + srcTextureViewDesc.dimension = wgpu::TextureViewDimension::e2D; + srcTextureViewDesc.baseMipLevel = source->mipLevel; + srcTextureViewDesc.mipLevelCount = 1; + srcTextureViewDesc.arrayLayerCount = 1; + DAWN_TRY_ASSIGN(srcTextureView, + device->CreateTextureView(source->texture, &srcTextureViewDesc)); + + RenderPipelineBase* pipeline; + DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyTextureForBrowserPipeline( + device, destination->texture->GetFormat().format)); + + return DoCopyForBrowser(device, &info, srcTextureView.Get(), destination, + copySize, options, pipeline); +} } // namespace dawn::native diff --git a/src/dawn/native/CopyTextureForBrowserHelper.h b/src/dawn/native/CopyTextureForBrowserHelper.h index 0e427ba8e9..0d496bb69c 100644 --- a/src/dawn/native/CopyTextureForBrowserHelper.h +++ b/src/dawn/native/CopyTextureForBrowserHelper.h @@ -30,12 +30,23 @@ MaybeError ValidateCopyTextureForBrowser(DeviceBase* device, const Extent3D* copySize, const CopyTextureForBrowserOptions* options); +MaybeError ValidateCopyExternalTextureForBrowser(DeviceBase* device, + const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options); + MaybeError DoCopyTextureForBrowser(DeviceBase* device, const ImageCopyTexture* source, const ImageCopyTexture* destination, const Extent3D* copySize, const CopyTextureForBrowserOptions* options); +MaybeError DoCopyExternalTextureForBrowser(DeviceBase* device, + const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options); } // namespace dawn::native #endif // SRC_DAWN_NATIVE_COPYTEXTUREFORBROWSERHELPER_H_ diff --git a/src/dawn/native/InternalPipelineStore.h b/src/dawn/native/InternalPipelineStore.h index b8386d5d1d..6234ec0110 100644 --- a/src/dawn/native/InternalPipelineStore.h +++ b/src/dawn/native/InternalPipelineStore.h @@ -35,8 +35,10 @@ struct InternalPipelineStore { ~InternalPipelineStore(); std::unordered_map> copyTextureForBrowserPipelines; + std::unordered_map> + copyExternalTextureForBrowserPipelines; - Ref copyTextureForBrowser; + Ref copyForBrowser; Ref timestampComputePipeline; Ref timestampCS; diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp index 9881d65897..f8dd67334d 100644 --- a/src/dawn/native/Queue.cpp +++ b/src/dawn/native/Queue.cpp @@ -370,6 +370,14 @@ void QueueBase::APICopyTextureForBrowser(const ImageCopyTexture* source, CopyTextureForBrowserInternal(source, destination, copySize, options)); } +void QueueBase::APICopyExternalTextureForBrowser(const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + GetDevice()->ConsumedError( + CopyExternalTextureForBrowserInternal(source, destination, copySize, options)); +} + MaybeError QueueBase::CopyTextureForBrowserInternal(const ImageCopyTexture* source, const ImageCopyTexture* destination, const Extent3D* copySize, @@ -384,6 +392,21 @@ MaybeError QueueBase::CopyTextureForBrowserInternal(const ImageCopyTexture* sour return DoCopyTextureForBrowser(GetDevice(), source, destination, copySize, options); } +MaybeError QueueBase::CopyExternalTextureForBrowserInternal( + const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options) { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY_CONTEXT(ValidateCopyExternalTextureForBrowser(GetDevice(), source, destination, + copySize, options), + "validating CopyExternalTextureForBrowser from %s to %s", + source->externalTexture, destination->texture); + } + + return DoCopyExternalTextureForBrowser(GetDevice(), source, destination, copySize, options); +} + MaybeError QueueBase::ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const { TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit"); diff --git a/src/dawn/native/Queue.h b/src/dawn/native/Queue.h index 5bfd9a0c92..8495987914 100644 --- a/src/dawn/native/Queue.h +++ b/src/dawn/native/Queue.h @@ -58,6 +58,10 @@ class QueueBase : public ApiObjectBase { const ImageCopyTexture* destination, const Extent3D* copySize, const CopyTextureForBrowserOptions* options); + void APICopyExternalTextureForBrowser(const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options); MaybeError WriteBuffer(BufferBase* buffer, uint64_t bufferOffset, @@ -82,6 +86,10 @@ class QueueBase : public ApiObjectBase { const ImageCopyTexture* destination, const Extent3D* copySize, const CopyTextureForBrowserOptions* options); + MaybeError CopyExternalTextureForBrowserInternal(const ImageCopyExternalTexture* source, + const ImageCopyTexture* destination, + const Extent3D* copySize, + const CopyTextureForBrowserOptions* options); virtual MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) = 0; virtual MaybeError WriteBufferImpl(BufferBase* buffer, diff --git a/src/dawn/native/utils/WGPUHelpers.cpp b/src/dawn/native/utils/WGPUHelpers.cpp index 2a7a5e0a3c..ffa2980229 100644 --- a/src/dawn/native/utils/WGPUHelpers.cpp +++ b/src/dawn/native/utils/WGPUHelpers.cpp @@ -26,6 +26,7 @@ #include "dawn/native/BindGroupLayout.h" #include "dawn/native/Buffer.h" #include "dawn/native/Device.h" +#include "dawn/native/ExternalTexture.h" #include "dawn/native/PipelineLayout.h" #include "dawn/native/Queue.h" #include "dawn/native/Sampler.h" @@ -140,6 +141,12 @@ BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, const Ref& textureView) : binding(binding), textureView(textureView) {} +BindingInitializationHelper::BindingInitializationHelper( + uint32_t binding, + const Ref& externalTexture) + : binding(binding), externalTexture(externalTexture) { + externalBindingEntry.externalTexture = externalTexture.Get(); +} BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, const Ref& buffer, @@ -159,6 +166,10 @@ BindGroupEntry BindingInitializationHelper::GetAsBinding() const { result.offset = offset; result.size = size; + if (externalTexture != nullptr) { + result.nextInChain = &externalBindingEntry; + } + return result; } diff --git a/src/dawn/native/utils/WGPUHelpers.h b/src/dawn/native/utils/WGPUHelpers.h index 45cfa46e2b..036d00d4fd 100644 --- a/src/dawn/native/utils/WGPUHelpers.h +++ b/src/dawn/native/utils/WGPUHelpers.h @@ -95,6 +95,7 @@ ResultOrError> MakeBindGroupLayout( struct BindingInitializationHelper { BindingInitializationHelper(uint32_t binding, const Ref& sampler); BindingInitializationHelper(uint32_t binding, const Ref& textureView); + BindingInitializationHelper(uint32_t binding, const Ref& externalTexture); BindingInitializationHelper(uint32_t binding, const Ref& buffer, uint64_t offset = 0, @@ -107,6 +108,8 @@ struct BindingInitializationHelper { Ref sampler; Ref textureView; Ref buffer; + Ref externalTexture; + ExternalTextureBindingEntry externalBindingEntry; uint64_t offset = 0; uint64_t size = 0; }; diff --git a/src/dawn/native/webgpu_absl_format.cpp b/src/dawn/native/webgpu_absl_format.cpp index 8eddf467bb..a33ab42024 100644 --- a/src/dawn/native/webgpu_absl_format.cpp +++ b/src/dawn/native/webgpu_absl_format.cpp @@ -45,6 +45,18 @@ AbslFormatConvert(const Color* value, const absl::FormatConversionSpec& spec, ab return {true}; } +absl::FormatConvertResult AbslFormatConvert( + const Extent2D* value, + const absl::FormatConversionSpec& spec, + absl::FormatSink* s) { + if (value == nullptr) { + s->Append("[null]"); + return {true}; + } + s->Append(absl::StrFormat("[Extent2D width:%u, height:%u]", value->width, value->height)); + return {true}; +} + absl::FormatConvertResult AbslFormatConvert( const Extent3D* value, const absl::FormatConversionSpec& spec, diff --git a/src/dawn/native/webgpu_absl_format.h b/src/dawn/native/webgpu_absl_format.h index 4c0c667f10..23964ad59f 100644 --- a/src/dawn/native/webgpu_absl_format.h +++ b/src/dawn/native/webgpu_absl_format.h @@ -29,6 +29,12 @@ struct Color; absl::FormatConvertResult AbslFormatConvert(const Color* value, const absl::FormatConversionSpec& spec, absl::FormatSink* s); +struct Extent2D; +absl::FormatConvertResult AbslFormatConvert( + const Extent2D* value, + const absl::FormatConversionSpec& spec, + absl::FormatSink* s); + struct Extent3D; absl::FormatConvertResult AbslFormatConvert( const Extent3D* value, diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn index 6f5ba74209..c1e0137432 100644 --- a/src/dawn/tests/BUILD.gn +++ b/src/dawn/tests/BUILD.gn @@ -438,6 +438,7 @@ source_set("end2end_tests_sources") { "end2end/ComputeLayoutMemoryBufferTests.cpp", "end2end/ComputeSharedMemoryTests.cpp", "end2end/ComputeStorageBufferBarrierTests.cpp", + "end2end/CopyExternalTextureForBrowserTests.cpp", "end2end/CopyTests.cpp", "end2end/CopyTextureForBrowserTests.cpp", "end2end/CreatePipelineAsyncTests.cpp", diff --git a/src/dawn/tests/end2end/CopyExternalTextureForBrowserTests.cpp b/src/dawn/tests/end2end/CopyExternalTextureForBrowserTests.cpp new file mode 100644 index 0000000000..549e712936 --- /dev/null +++ b/src/dawn/tests/end2end/CopyExternalTextureForBrowserTests.cpp @@ -0,0 +1,207 @@ +// Copyright 2022 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 + +#include "dawn/tests/DawnTest.h" +#include "dawn/utils/ComboRenderPipelineDescriptor.h" +#include "dawn/utils/WGPUHelpers.h" + +namespace { + +wgpu::Texture Create2DTexture(wgpu::Device device, + uint32_t width, + uint32_t height, + wgpu::TextureFormat format, + wgpu::TextureUsage usage) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depthOrArrayLayers = 1; + descriptor.sampleCount = 1; + descriptor.format = format; + descriptor.mipLevelCount = 1; + descriptor.usage = usage; + return device.CreateTexture(&descriptor); +} + +static constexpr uint32_t kWidth = 4; +static constexpr uint32_t kHeight = 4; + +std::array, 4> kDefaultSourceRGBA = { + std::array( + {utils::RGBA8::kBlack, utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kRed}), + std::array( + {utils::RGBA8::kBlack, utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kRed}), + std::array( + {utils::RGBA8::kGreen, utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kBlue}), + std::array( + {utils::RGBA8::kGreen, utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kBlue})}; + +template +class CopyExternalTextureForBrowserTests : public Parent { + protected: + wgpu::ExternalTexture CreateDefaultExternalTexture() { + // y plane + wgpu::TextureDescriptor externalTexturePlane0Desc = {}; + externalTexturePlane0Desc.size = {kWidth, kHeight, 1}; + externalTexturePlane0Desc.usage = wgpu::TextureUsage::TextureBinding | + wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::RenderAttachment; + externalTexturePlane0Desc.format = wgpu::TextureFormat::R8Unorm; + wgpu::Texture externalTexturePlane0 = + this->device.CreateTexture(&externalTexturePlane0Desc); + + // The value Ref to ExternalTextureTest.cpp: + // {0.0, .5, .5, utils::RGBA8::kBlack, 0.0f}, + // {0.2126, 0.4172, 1.0, utils::RGBA8::kRed, 1.0f}, + // {0.7152, 0.1402, 0.0175, utils::RGBA8::kGreen, 0.0f}, + // {0.0722, 1.0, 0.4937, utils::RGBA8::kBlue, 0.0f}, + wgpu::ImageCopyTexture plane0 = {}; + plane0.texture = externalTexturePlane0; + std::array yPlaneData = {0, 0, 54, 54, 0, 0, 54, 54, + 182, 182, 18, 18, 182, 182, 18, 18}; + + wgpu::TextureDataLayout externalTexturePlane0DataLayout = {}; + externalTexturePlane0DataLayout.bytesPerRow = 4; + + this->queue.WriteTexture(&plane0, yPlaneData.data(), yPlaneData.size() * sizeof(float), + &externalTexturePlane0DataLayout, &externalTexturePlane0Desc.size); + + // uv plane + wgpu::TextureDescriptor externalTexturePlane1Desc = {}; + externalTexturePlane1Desc.size = {kWidth / 2, kHeight / 2, 1}; + externalTexturePlane1Desc.usage = wgpu::TextureUsage::TextureBinding | + wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::RenderAttachment; + externalTexturePlane1Desc.format = wgpu::TextureFormat::RG8Unorm; + wgpu::Texture externalTexturePlane1 = + this->device.CreateTexture(&externalTexturePlane1Desc); + + wgpu::ImageCopyTexture plane1 = {}; + plane1.texture = externalTexturePlane1; + std::array uvPlaneData = { + 128, 128, 106, 255, 36, 4, 255, 126, + }; + + wgpu::TextureDataLayout externalTexturePlane1DataLayout = {}; + externalTexturePlane1DataLayout.bytesPerRow = 4; + + this->queue.WriteTexture(&plane1, uvPlaneData.data(), uvPlaneData.size() * sizeof(float), + &externalTexturePlane1DataLayout, &externalTexturePlane1Desc.size); + + // Create an ExternalTextureDescriptor from the texture views + wgpu::ExternalTextureDescriptor externalDesc; + utils::ColorSpaceConversionInfo info = + utils::GetYUVBT709ToRGBSRGBColorSpaceConversionInfo(); + externalDesc.yuvToRgbConversionMatrix = info.yuvToRgbConversionMatrix.data(); + externalDesc.gamutConversionMatrix = info.gamutConversionMatrix.data(); + externalDesc.srcTransferFunctionParameters = info.srcTransferFunctionParameters.data(); + externalDesc.dstTransferFunctionParameters = info.dstTransferFunctionParameters.data(); + + externalDesc.plane0 = externalTexturePlane0.CreateView(); + externalDesc.plane1 = externalTexturePlane1.CreateView(); + + externalDesc.visibleRect = {kWidth, kHeight}; + + // Import the external texture + return this->device.CreateExternalTexture(&externalDesc); + } + + std::vector GetDefaultExpectedData(bool flipY, + wgpu::Origin3D origin, + wgpu::Extent3D rect) { + std::vector expected; + for (uint32_t row = origin.y; row < origin.y + rect.height; ++row) { + for (uint32_t col = origin.x; col < origin.x + rect.width; ++col) { + if (flipY) { + uint32_t flippedRow = kHeight - row - 1; + expected.push_back(kDefaultSourceRGBA[flippedRow][col]); + } else { + expected.push_back(kDefaultSourceRGBA[row][col]); + } + } + } + + return expected; + } +}; + +using FlipY = bool; +using SrcOrigin = wgpu::Origin3D; +using DstOrigin = wgpu::Origin3D; + +std::ostream& operator<<(std::ostream& o, wgpu::Origin3D origin) { + o << origin.x << ", " << origin.y << ", " << origin.z; + return o; +} + +DAWN_TEST_PARAM_STRUCT(CopyTestParams, SrcOrigin, DstOrigin, FlipY); + +class CopyExternalTextureForBrowserTests_Basic + : public CopyExternalTextureForBrowserTests> { + protected: + void DoBasicCopyTest(const wgpu::Origin3D& srcOrigin, + const wgpu::Origin3D& dstOrigin, + const wgpu::Extent3D& copySize, + const wgpu::CopyTextureForBrowserOptions options = {}) { + wgpu::ExternalTexture externalTexture = CreateDefaultExternalTexture(); + wgpu::ImageCopyExternalTexture srcImageCopyExternalTexture; + srcImageCopyExternalTexture.externalTexture = externalTexture; + srcImageCopyExternalTexture.origin = srcOrigin; + + wgpu::Texture dstTexture = + Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::CopyDst); + wgpu::ImageCopyTexture dstImageCopyTexture = + utils::CreateImageCopyTexture(dstTexture, 0, dstOrigin); + + queue.CopyExternalTextureForBrowser(&srcImageCopyExternalTexture, &dstImageCopyTexture, + ©Size, &options); + std::vector expected = + GetDefaultExpectedData(options.flipY, srcOrigin, copySize); + + EXPECT_TEXTURE_EQ(expected.data(), dstTexture, dstOrigin, copySize); + } +}; +} // anonymous namespace + +TEST_P(CopyExternalTextureForBrowserTests_Basic, FullCopy) { + DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); + DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); + + wgpu::CopyTextureForBrowserOptions options = {}; + options.flipY = GetParam().mFlipY; + + wgpu::Origin3D srcOrigin = GetParam().mSrcOrigin; + wgpu::Origin3D dstOrigin = GetParam().mDstOrigin; + + wgpu::Extent3D copySize = {kWidth, kHeight}; + + if (srcOrigin.x != 0 || srcOrigin.y != 0 || dstOrigin.x != 0 || dstOrigin.y != 0) { + copySize.width = kWidth / 2; + copySize.height = kHeight / 2; + } + + DoBasicCopyTest(srcOrigin, dstOrigin, copySize, options); +} + +DAWN_INSTANTIATE_TEST_P(CopyExternalTextureForBrowserTests_Basic, + {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), + VulkanBackend()}, + std::vector({{0, 0}, {2, 0}, {0, 2}, {2, 2}}), + std::vector({{0, 0}, {2, 0}, {0, 2}, {2, 2}}), + std::vector({false, true})); diff --git a/src/dawn/tests/end2end/ExternalTextureTests.cpp b/src/dawn/tests/end2end/ExternalTextureTests.cpp index 5511437647..065836b8be 100644 --- a/src/dawn/tests/end2end/ExternalTextureTests.cpp +++ b/src/dawn/tests/end2end/ExternalTextureTests.cpp @@ -39,10 +39,10 @@ class ExternalTextureTests : public DawnTest { protected: wgpu::ExternalTextureDescriptor CreateDefaultExternalTextureDescriptor() { wgpu::ExternalTextureDescriptor desc; - desc.yuvToRgbConversionMatrix = kYuvToRGBMatrixBT709.data(); - desc.gamutConversionMatrix = kGamutConversionMatrixBT709ToSrgb.data(); - desc.srcTransferFunctionParameters = kGammaDecodeBT709.data(); - desc.dstTransferFunctionParameters = kGammaEncodeSrgb.data(); + desc.yuvToRgbConversionMatrix = yuvBT709ToRGBSRGB.yuvToRgbConversionMatrix.data(); + desc.gamutConversionMatrix = yuvBT709ToRGBSRGB.gamutConversionMatrix.data(); + desc.srcTransferFunctionParameters = yuvBT709ToRGBSRGB.srcTransferFunctionParameters.data(); + desc.dstTransferFunctionParameters = yuvBT709ToRGBSRGB.dstTransferFunctionParameters.data(); return desc; } @@ -51,14 +51,8 @@ class ExternalTextureTests : public DawnTest { static constexpr uint32_t kHeight = 4; static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; static constexpr wgpu::TextureUsage kSampledUsage = wgpu::TextureUsage::TextureBinding; - std::array kYuvToRGBMatrixBT709 = {1.164384f, 0.0f, 1.792741f, -0.972945f, - 1.164384f, -0.213249f, -0.532909f, 0.301483f, - 1.164384f, 2.112402f, 0.0f, -1.133402f}; - std::array kGamutConversionMatrixBT709ToSrgb = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - std::array kGammaDecodeBT709 = {2.2, 1.0 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, - 0.0, 0.0}; - std::array kGammaEncodeSrgb = {1 / 2.4, 1.137119, 0.0, 12.92, 0.0031308, -0.055, 0.0}; + utils::ColorSpaceConversionInfo yuvBT709ToRGBSRGB = + utils::GetYUVBT709ToRGBSRGBColorSpaceConversionInfo(); }; } // anonymous namespace @@ -99,7 +93,7 @@ TEST_P(ExternalTextureTests, SampleExternalTexture) { @fragment fn main(@builtin(position) FragCoord : vec4) -> @location(0) vec4 { - return textureSampleLevel(t, s, FragCoord.xy / vec2(4.0, 4.0)); + return textureSampleBaseClampToEdge(t, s, FragCoord.xy / vec2(4.0, 4.0)); })"); wgpu::Texture sampledTexture = @@ -184,7 +178,7 @@ TEST_P(ExternalTextureTests, SampleMultiplanarExternalTexture) { @fragment fn main(@builtin(position) FragCoord : vec4) -> @location(0) vec4 { - return textureSampleLevel(t, s, FragCoord.xy / vec2(4.0, 4.0)); + return textureSampleBaseClampToEdge(t, s, FragCoord.xy / vec2(4.0, 4.0)); })"); wgpu::Texture sampledTexturePlane0 = diff --git a/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp b/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp index 06c1b3d2a2..96fc533ff8 100644 --- a/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp +++ b/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp @@ -20,31 +20,59 @@ #include "dawn/utils/TextureUtils.h" #include "dawn/utils/WGPUHelpers.h" +namespace { +wgpu::Texture Create2DTexture( + wgpu::Device device, + uint32_t width, + uint32_t height, + uint32_t mipLevelCount, + uint32_t arrayLayerCount, + wgpu::TextureFormat format, + wgpu::TextureUsage usage, + uint32_t sampleCount = 1, + const wgpu::DawnTextureInternalUsageDescriptor* internalDesc = nullptr) { + wgpu::TextureDescriptor descriptor; + descriptor.nextInChain = internalDesc; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depthOrArrayLayers = arrayLayerCount; + descriptor.sampleCount = sampleCount; + descriptor.format = format; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + wgpu::Texture tex = device.CreateTexture(&descriptor); + return tex; +} + +wgpu::ExternalTexture CreateExternalTexture(wgpu::Device device, uint32_t width, uint32_t height) { + wgpu::Texture texture = + Create2DTexture(device, width, height, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::TextureBinding); + + // Create a texture view for the external texture + wgpu::TextureView view = texture.CreateView(); + + // Create an ExternalTextureDescriptor from the texture view + wgpu::ExternalTextureDescriptor externalDesc; + utils::ColorSpaceConversionInfo info = utils::GetYUVBT709ToRGBSRGBColorSpaceConversionInfo(); + externalDesc.yuvToRgbConversionMatrix = info.yuvToRgbConversionMatrix.data(); + externalDesc.gamutConversionMatrix = info.gamutConversionMatrix.data(); + externalDesc.srcTransferFunctionParameters = info.srcTransferFunctionParameters.data(); + externalDesc.dstTransferFunctionParameters = info.dstTransferFunctionParameters.data(); + + externalDesc.plane0 = view; + + externalDesc.visibleRect = {width, height}; + + // Import the external texture + return device.CreateExternalTexture(&externalDesc); +} + +} // namespace + class CopyTextureForBrowserTest : public ValidationTest { protected: - wgpu::Texture Create2DTexture( - uint32_t width, - uint32_t height, - uint32_t mipLevelCount, - uint32_t arrayLayerCount, - wgpu::TextureFormat format, - wgpu::TextureUsage usage, - uint32_t sampleCount = 1, - const wgpu::DawnTextureInternalUsageDescriptor* internalDesc = nullptr) { - wgpu::TextureDescriptor descriptor; - descriptor.nextInChain = internalDesc; - descriptor.dimension = wgpu::TextureDimension::e2D; - descriptor.size.width = width; - descriptor.size.height = height; - descriptor.size.depthOrArrayLayers = arrayLayerCount; - descriptor.sampleCount = sampleCount; - descriptor.format = format; - descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = usage; - wgpu::Texture tex = device.CreateTexture(&descriptor); - return tex; - } - void TestCopyTextureForBrowser(utils::Expectation expectation, wgpu::Texture srcTexture, uint32_t srcLevel, @@ -81,13 +109,52 @@ class CopyTextureForBrowserInternalUsageTest : public CopyTextureForBrowserTest } }; +class CopyExternalTextureForBrowserTest : public ValidationTest { + protected: + void TestCopyExternalTextureForBrowser(utils::Expectation expectation, + wgpu::ExternalTexture srcExternalTexture, + wgpu::Origin3D srcOrigin, + wgpu::Texture dstTexture, + uint32_t dstLevel, + wgpu::Origin3D dstOrigin, + wgpu::Extent3D extent3D, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + wgpu::CopyTextureForBrowserOptions options = {}) { + wgpu::ImageCopyExternalTexture srcImageCopyExternalTexture; + srcImageCopyExternalTexture.externalTexture = srcExternalTexture; + srcImageCopyExternalTexture.origin = srcOrigin; + + wgpu::ImageCopyTexture dstImageCopyTexture = + utils::CreateImageCopyTexture(dstTexture, dstLevel, dstOrigin, aspect); + + if (expectation == utils::Expectation::Success) { + device.GetQueue().CopyExternalTextureForBrowser( + &srcImageCopyExternalTexture, &dstImageCopyTexture, &extent3D, &options); + } else { + ASSERT_DEVICE_ERROR(device.GetQueue().CopyExternalTextureForBrowser( + &srcImageCopyExternalTexture, &dstImageCopyTexture, &extent3D, &options)); + } + } +}; + +class CopyExternalTextureForBrowserInternalUsageTest : public CopyExternalTextureForBrowserTest { + protected: + WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override { + wgpu::DeviceDescriptor descriptor; + wgpu::FeatureName feature = wgpu::FeatureName::DawnInternalUsages; + descriptor.requiredFeatures = &feature; + descriptor.requiredFeaturesCount = 1; + return dawnAdapter.CreateDevice(&descriptor); + } +}; + // Tests should be Success TEST_F(CopyTextureForBrowserTest, Success) { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); // Different copies, including some that touch the OOB condition @@ -140,19 +207,20 @@ TEST_F(CopyTextureForBrowserTest, Success) { // Test source or destination texture has wrong usages TEST_F(CopyTextureForBrowserTest, IncorrectUsage) { wgpu::Texture validSource = - Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture validDestination = - Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); - wgpu::Texture noSampledUsageSource = - Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); - wgpu::Texture noRenderAttachmentUsageDestination = - Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); + wgpu::Texture noSampledUsageSource = Create2DTexture( + device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Texture noRenderAttachmentUsageDestination = Create2DTexture( + device, 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); wgpu::Texture noCopySrcUsageSource = Create2DTexture( - 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::TextureBinding); - wgpu::Texture noCopyDstUsageSource = Create2DTexture( - 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::RenderAttachment); + device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::TextureBinding); + wgpu::Texture noCopyDstUsageSource = + Create2DTexture(device, 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::RenderAttachment); // Incorrect source usage causes failure : lack |Sampled| usage TestCopyTextureForBrowser(utils::Expectation::Failure, noSampledUsageSource, 0, {0, 0, 0}, @@ -178,10 +246,10 @@ TEST_F(CopyTextureForBrowserTest, DestroyedTexture) { // Valid src and dst textures. { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, options); @@ -194,10 +262,10 @@ TEST_F(CopyTextureForBrowserTest, DestroyedTexture) { // Destroyed src texture. { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); source.Destroy(); TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, @@ -211,10 +279,10 @@ TEST_F(CopyTextureForBrowserTest, DestroyedTexture) { // Destroyed dst texture. { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); destination.Destroy(); @@ -230,10 +298,10 @@ TEST_F(CopyTextureForBrowserTest, DestroyedTexture) { // Test non-zero value origin in source and OOB copy rects. TEST_F(CopyTextureForBrowserTest, OutOfBounds) { wgpu::Texture source = - Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); // OOB on source @@ -290,10 +358,10 @@ TEST_F(CopyTextureForBrowserTest, OutOfBounds) { // Test destination texture has format that not supported by CopyTextureForBrowser(). TEST_F(CopyTextureForBrowserTest, InvalidDstFormat) { wgpu::Texture source = - Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RG8Uint, + Create2DTexture(device, 16, 16, 5, 2, wgpu::TextureFormat::RG8Uint, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); // Not supported dst texture format. @@ -304,18 +372,18 @@ TEST_F(CopyTextureForBrowserTest, InvalidDstFormat) { // Test source or destination texture are multisampled. TEST_F(CopyTextureForBrowserTest, InvalidSampleCount) { wgpu::Texture sourceMultiSampled1x = - Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding, 1); wgpu::Texture destinationMultiSampled1x = - Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, 1); wgpu::Texture sourceMultiSampled4x = - Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment, 4); wgpu::Texture destinationMultiSampled4x = - Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, 4); // An empty copy with dst texture sample count > 1 failure. @@ -330,10 +398,10 @@ TEST_F(CopyTextureForBrowserTest, InvalidSampleCount) { // Test color space conversion related attributes in CopyTextureForBrowserOptions. TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_ColorSpace) { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); wgpu::CopyTextureForBrowserOptions options = {}; @@ -413,10 +481,10 @@ TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_ColorSpace) { // Test option.srcAlphaMode/dstAlphaMode TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_TextureAlphaState) { wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); wgpu::CopyTextureForBrowserOptions options = {}; @@ -449,6 +517,342 @@ TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_TextureAlphaState) { } } +// Tests should be Success +TEST_F(CopyExternalTextureForBrowserTest, Success) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + // Different copies, including some that touch the OOB condition + { + // Copy a region along top left boundary + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}); + + // Copy entire texture + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {16, 16, 1}); + + // Copy a region along bottom right boundary + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {8, 8, 0}, + destination, 0, {8, 8, 0}, {8, 8, 1}); + + // Copy region into mip + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 2, {0, 0, 0}, {4, 4, 1}); + + // Copy between slices + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 1}, {16, 16, 1}); + } + + // Empty copies are valid + { + // An empty copy + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {0, 0, 1}); + + // An empty copy with depth = 0 + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {0, 0, 0}); + + // An empty copy touching the side of the source texture + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {16, 16, 0}, {0, 0, 1}); + + // An empty copy touching the side of the destination texture + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {16, 16, 0}, {0, 0, 1}); + } +} + +// Test destination texture has wrong usages +TEST_F(CopyExternalTextureForBrowserTest, IncorrectUsage) { + wgpu::ExternalTexture validSource = CreateExternalTexture(device, 16, 16); + + wgpu::Texture validDestination = + Create2DTexture(device, 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + wgpu::Texture noRenderAttachmentUsageDestination = Create2DTexture( + device, 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); + wgpu::Texture noCopyDstUsageSource = + Create2DTexture(device, 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::RenderAttachment); + + // Incorrect destination usage causes failure: lack |RenderAttachement| usage. + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, validSource, {0, 0, 0}, + noRenderAttachmentUsageDestination, 0, {0, 0, 0}, + {16, 16, 1}); + + // Incorrect destination usage causes failure: lack |CopyDst| usage. + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, validSource, {0, 0, 0}, + noCopyDstUsageSource, 0, {0, 0, 0}, {16, 16, 1}); +} + +// Test source or destination texture is destroyed. +TEST_F(CopyExternalTextureForBrowserTest, DestroyedTexture) { + wgpu::CopyTextureForBrowserOptions options = {}; + + // Valid src and dst textures. + { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + // Check noop copy + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {0, 0, 0}, + wgpu::TextureAspect::All, options); + } + + // Destroyed src texture. + { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + source.Destroy(); + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + // Check noop copy + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {0, 0, 0}, + wgpu::TextureAspect::All, options); + } + + // Destroyed dst texture. + { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + destination.Destroy(); + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + // Check noop copy + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {0, 0, 0}, + wgpu::TextureAspect::All, options); + } +} + +// Test non-zero value origin in source and OOB copy rects. +TEST_F(CopyExternalTextureForBrowserTest, OutOfBounds) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + // OOB on source + { + // x + width overflows + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {1, 0, 0}, + destination, 0, {0, 0, 0}, {16, 16, 1}); + + // y + height overflows + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 1, 0}, + destination, 0, {0, 0, 0}, {16, 16, 1}); + + // copy to multiple slices + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 2}, {16, 16, 2}); + + // copy origin z value is non-zero. + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 1}, + destination, 0, {0, 0, 2}, {16, 16, 1}); + } + + // OOB on destination + { + // x + width overflows + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {1, 0, 0}, {16, 16, 1}); + + // y + height overflows + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 1, 0}, {16, 16, 1}); + + // non-zero mip overflows + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 1, {0, 0, 0}, {9, 9, 1}); + + // arrayLayer + depth OOB + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 4}, {16, 16, 1}); + + // empty copy on non-existent mip fails + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 6, {0, 0, 0}, {0, 0, 1}); + + // empty copy on non-existent slice fails + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 4}, {0, 0, 1}); + } +} + +// Test destination texture has format that not supported by CopyTextureForBrowser(). +TEST_F(CopyExternalTextureForBrowserTest, InvalidDstFormat) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 2, wgpu::TextureFormat::RG8Uint, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + // Not supported dst texture format. + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, destination, + 0, {0, 0, 0}, {0, 0, 1}); +} + +// Test destination texture are multisampled. +TEST_F(CopyExternalTextureForBrowserTest, InvalidSampleCount) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destinationMultiSampled4x = + Create2DTexture(device, 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, 4); + + // An empty copy with dst texture sample count > 1 failure. + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destinationMultiSampled4x, 0, {0, 0, 0}, {0, 0, 1}); +} + +// Test color space conversion related attributes in CopyTextureForBrowserOptions. +TEST_F(CopyExternalTextureForBrowserTest, ColorSpaceConversion_ColorSpace) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + wgpu::CopyTextureForBrowserOptions options = {}; + options.needsColorSpaceConversion = true; + + // Valid cases + { + wgpu::CopyTextureForBrowserOptions validOptions = options; + std::array srcTransferFunctionParameters = {}; + std::array dstTransferFunctionParameters = {}; + std::array conversionMatrix = {}; + validOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); + validOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); + validOptions.conversionMatrix = conversionMatrix.data(); + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, validOptions); + + // if no color space conversion, no need to validate related attributes + wgpu::CopyTextureForBrowserOptions noColorSpaceConversion = options; + noColorSpaceConversion.needsColorSpaceConversion = false; + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, noColorSpaceConversion); + } + + // Invalid cases: srcTransferFunctionParameters, dstTransferFunctionParameters or + // conversionMatrix is nullptr or not set + { + // not set srcTransferFunctionParameters + wgpu::CopyTextureForBrowserOptions invalidOptions = options; + std::array dstTransferFunctionParameters = {}; + std::array conversionMatrix = {}; + invalidOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); + invalidOptions.conversionMatrix = conversionMatrix.data(); + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + + // set to nullptr + invalidOptions.srcTransferFunctionParameters = nullptr; + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + } + + { + // not set dstTransferFunctionParameters + wgpu::CopyTextureForBrowserOptions invalidOptions = options; + std::array srcTransferFunctionParameters = {}; + std::array conversionMatrix = {}; + invalidOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); + invalidOptions.conversionMatrix = conversionMatrix.data(); + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + + // set to nullptr + invalidOptions.dstTransferFunctionParameters = nullptr; + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + } + + { + // not set conversionMatrix + wgpu::CopyTextureForBrowserOptions invalidOptions = options; + std::array srcTransferFunctionParameters = {}; + std::array dstTransferFunctionParameters = {}; + invalidOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); + invalidOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + + // set to nullptr + invalidOptions.conversionMatrix = nullptr; + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, invalidOptions); + } +} + +// Test option.srcAlphaMode/dstAlphaMode +TEST_F(CopyExternalTextureForBrowserTest, ColorSpaceConversion_TextureAlphaState) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + wgpu::CopyTextureForBrowserOptions options = {}; + + // Valid src texture alpha state and valid dst texture alpha state + { + options.srcAlphaMode = wgpu::AlphaMode::Premultiplied; + options.dstAlphaMode = wgpu::AlphaMode::Premultiplied; + + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + options.srcAlphaMode = wgpu::AlphaMode::Premultiplied; + options.dstAlphaMode = wgpu::AlphaMode::Unpremultiplied; + + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + options.srcAlphaMode = wgpu::AlphaMode::Unpremultiplied; + options.dstAlphaMode = wgpu::AlphaMode::Premultiplied; + + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + + options.srcAlphaMode = wgpu::AlphaMode::Unpremultiplied; + options.dstAlphaMode = wgpu::AlphaMode::Unpremultiplied; + + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, + destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::All, options); + } +} + // Test that the internal usage can only be set to true when the device internal usage feature is // enabled TEST_F(CopyTextureForBrowserTest, InternalUsage) { @@ -456,15 +860,15 @@ TEST_F(CopyTextureForBrowserTest, InternalUsage) { internalDesc.internalUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding; // Validation should fail because internal descriptor is not empty. - ASSERT_DEVICE_ERROR(Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + ASSERT_DEVICE_ERROR(Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 1, &internalDesc)); wgpu::Texture source = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); wgpu::Texture destination = - Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); // Validation should fail because of device internal usage feature is missing when internal @@ -480,14 +884,15 @@ TEST_F(CopyTextureForBrowserInternalUsageTest, InternalUsage) { wgpu::DawnTextureInternalUsageDescriptor internalDesc1 = {}; internalDesc1.internalUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding; - wgpu::Texture source = Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::Texture source = Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 1, &internalDesc1); wgpu::DawnTextureInternalUsageDescriptor internalDesc2 = {}; internalDesc2.internalUsage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment; - wgpu::Texture destination = Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, - wgpu::TextureUsage::CopyDst, 1, &internalDesc2); + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst, 1, &internalDesc2); // Without internal usage option should fail usage validation TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, @@ -499,3 +904,48 @@ TEST_F(CopyTextureForBrowserInternalUsageTest, InternalUsage) { TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {16, 16, 1}, wgpu::TextureAspect::All, options); } + +// Test that the internal usage can only be set to true when the device internal usage feature is +// enabled +TEST_F(CopyExternalTextureForBrowserTest, InternalUsage) { + wgpu::DawnTextureInternalUsageDescriptor internalDesc = {}; + internalDesc.internalUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding; + + // Validation should fail because internal descriptor is not empty. + ASSERT_DEVICE_ERROR(Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc, 1, &internalDesc)); + + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); + + // Validation should fail because of device internal usage feature is missing when internal + // usage option is on + wgpu::CopyTextureForBrowserOptions options = {}; + options.internalUsage = true; + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, destination, + 0, {0, 0, 0}, {16, 16, 1}, wgpu::TextureAspect::All, options); +} + +// Test that the internal usages are taken into account when interalUsage = true +TEST_F(CopyExternalTextureForBrowserInternalUsageTest, InternalUsage) { + wgpu::ExternalTexture source = CreateExternalTexture(device, 16, 16); + + wgpu::DawnTextureInternalUsageDescriptor internalDesc = {}; + internalDesc.internalUsage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment; + wgpu::Texture destination = + Create2DTexture(device, 16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst, 1, &internalDesc); + + // Without internal usage option should fail usage validation + TestCopyExternalTextureForBrowser(utils::Expectation::Failure, source, {0, 0, 0}, destination, + 0, {0, 0, 0}, {16, 16, 1}); + + // With internal usage option should pass usage validation + wgpu::CopyTextureForBrowserOptions options = {}; + options.internalUsage = true; + TestCopyExternalTextureForBrowser(utils::Expectation::Success, source, {0, 0, 0}, destination, + 0, {0, 0, 0}, {16, 16, 1}, wgpu::TextureAspect::All, options); +} diff --git a/src/dawn/utils/WGPUHelpers.cpp b/src/dawn/utils/WGPUHelpers.cpp index dda2cabe2b..d016620d1b 100644 --- a/src/dawn/utils/WGPUHelpers.cpp +++ b/src/dawn/utils/WGPUHelpers.cpp @@ -26,6 +26,17 @@ #include "spirv-tools/optimizer.hpp" +namespace { +std::array kYuvToRGBMatrixBT709 = {1.164384f, 0.0f, 1.792741f, -0.972945f, + 1.164384f, -0.213249f, -0.532909f, 0.301483f, + 1.164384f, 2.112402f, 0.0f, -1.133402f}; +std::array kGamutConversionMatrixBT709ToSrgb = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; +std::array kGammaDecodeBT709 = {2.2, 1.0 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, + 0.0, 0.0}; +std::array kGammaEncodeSrgb = {1 / 2.4, 1.137119, 0.0, 12.92, 0.0031308, -0.055, 0.0}; +} // namespace + namespace utils { wgpu::ShaderModule CreateShaderModuleFromASM(const wgpu::Device& device, const char* source) { // Use SPIRV-Tools's C API to assemble the SPIR-V assembly text to binary. Because the types @@ -389,4 +400,14 @@ wgpu::BindGroup MakeBindGroup( return device.CreateBindGroup(&descriptor); } +ColorSpaceConversionInfo GetYUVBT709ToRGBSRGBColorSpaceConversionInfo() { + ColorSpaceConversionInfo info; + info.yuvToRgbConversionMatrix = kYuvToRGBMatrixBT709; + info.gamutConversionMatrix = kGamutConversionMatrixBT709ToSrgb; + info.srcTransferFunctionParameters = kGammaDecodeBT709; + info.dstTransferFunctionParameters = kGammaEncodeSrgb; + + return info; +} + } // namespace utils diff --git a/src/dawn/utils/WGPUHelpers.h b/src/dawn/utils/WGPUHelpers.h index 24864e6265..9217ca9216 100644 --- a/src/dawn/utils/WGPUHelpers.h +++ b/src/dawn/utils/WGPUHelpers.h @@ -178,6 +178,15 @@ wgpu::BindGroup MakeBindGroup( const wgpu::BindGroupLayout& layout, std::initializer_list entriesInitializer); +struct ColorSpaceConversionInfo { + std::array yuvToRgbConversionMatrix; + std::array gamutConversionMatrix; + std::array srcTransferFunctionParameters; + std::array dstTransferFunctionParameters; +}; + +ColorSpaceConversionInfo GetYUVBT709ToRGBSRGBColorSpaceConversionInfo(); + } // namespace utils #endif // SRC_DAWN_UTILS_WGPUHELPERS_H_