dawn-cmake/src/dawn/native/CopyTextureForBrowserHelper.cpp
Yan,Shaobo e8dd681f19 Provide origin info for ExternalTexture
Current ExternalTexture visible related info only contains width and height.

But not all the visible rect started at (0, 0). This CL add visibleOrigin to contain origin info and record (width, height) in
visibleSize

Bug: chromium:1361363
Change-Id: I3d8931e490c97740f152653383f07d0a2d984dd3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108024
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
2022-11-03 09:03:01 +00:00

788 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2020 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn/native/CopyTextureForBrowserHelper.h"
#include <unordered_set>
#include <utility>
#include "dawn/common/Log.h"
#include "dawn/native/BindGroup.h"
#include "dawn/native/BindGroupLayout.h"
#include "dawn/native/Buffer.h"
#include "dawn/native/CommandBuffer.h"
#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"
#include "dawn/native/RenderPipeline.h"
#include "dawn/native/Sampler.h"
#include "dawn/native/Texture.h"
#include "dawn/native/ValidationUtils_autogen.h"
#include "dawn/native/utils/WGPUHelpers.h"
namespace dawn::native {
namespace {
static const char sCopyForBrowserShader[] = R"(
struct GammaTransferParamsInternal {
G: f32,
A: f32,
B: f32,
C: f32,
D: f32,
E: f32,
F: f32,
padding: u32,
};
struct Uniforms { // offset align size
scale: vec2<f32>, // 0 8 8
offset: vec2<f32>, // 8 8 8
steps_mask: u32, // 16 4 4
// implicit padding; // 20 12
conversion_matrix: mat3x3<f32>, // 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<uniform> uniforms : Uniforms;
struct VertexOutputs {
@location(0) texcoords : vec2<f32>,
@builtin(position) position : vec4<f32>,
};
// Chromium uses unified equation to construct gamma decoding function
// and gamma encoding function.
// The logic is:
// if x < D
// linear = C * x + F
// 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: GammaTransferParamsInternal) -> f32 {
// Linear part: C * x + F
if (abs(v) < params.D) {
return sign(v) * (params.C * abs(v) + params.F);
}
// Gamma part: pow(A * x + B, G) + E
return sign(v) * (pow(params.A * abs(v) + params.B, params.G) + params.E);
}
@vertex
fn vs_main(
@builtin(vertex_index) VertexIndex : u32
) -> VertexOutputs {
var texcoord = array<vec2<f32>, 3>(
vec2<f32>(-0.5, 0.0),
vec2<f32>( 1.5, 0.0),
vec2<f32>( 0.5, 2.0));
var output : VertexOutputs;
output.position = vec4<f32>((texcoord[VertexIndex] * 2.0 - vec2<f32>(1.0, 1.0)), 0.0, 1.0);
// Y component of scale is calculated by the copySizeHeight / textureHeight. Only
// flipY case can get negative number.
var flipY = uniforms.scale.y < 0.0;
// Texture coordinate takes top-left as origin point. We need to map the
// texture to triangle carefully.
if (flipY) {
// We need to get the mirror positions(mirrored based on y = 0.5) on flip cases.
// Adopt transform to src texture and then mapping it to triangle coord which
// do a +1 shift on Y dimension will help us got that mirror position perfectly.
output.texcoords = (texcoord[VertexIndex] * uniforms.scale + uniforms.offset) *
vec2<f32>(1.0, -1.0) + vec2<f32>(0.0, 1.0);
} else {
// For the normal case, we need to get the exact position.
// So mapping texture to triangle firstly then adopt the transform.
output.texcoords = (texcoord[VertexIndex] *
vec2<f32>(1.0, -1.0) + vec2<f32>(0.0, 1.0)) *
uniforms.scale + uniforms.offset;
}
return output;
}
@binding(1) @group(0) var mySampler: sampler;
// Resource used in copyTexture entry point only.
@binding(2) @group(0) var mySourceTexture: texture_2d<f32>;
// Resource used in copyExternalTexture entry point only.
@binding(2) @group(0) var mySourceExternalTexture: texture_external;
fn discardIfOutsideOfCopy(texcoord : vec2<f32>) {
var clampedTexcoord =
clamp(texcoord, vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 1.0));
if (!all(clampedTexcoord == texcoord)) {
discard;
}
}
fn transform(srcColor : vec4<f32>) -> vec4<f32> {
var color = srcColor;
let kUnpremultiplyStep = 0x01u;
let kDecodeToLinearStep = 0x02u;
let kConvertToDstGamutStep = 0x04u;
let kEncodeToGammaStep = 0x08u;
let kPremultiplyStep = 0x10u;
let kDecodeForSrgbDstFormat = 0x20u;
let kClearSrcAlphaToOne = 0x40u;
// Unpremultiply step. Appling color space conversion op on premultiplied source texture
// also needs to unpremultiply first.
// This step is exclusive with clear src alpha to one step.
if (bool(uniforms.steps_mask & kUnpremultiplyStep)) {
if (color.a != 0.0) {
color = vec4<f32>(color.rgb / color.a, color.a);
}
}
// Linearize the source color using the source color spaces
// transfer function if it is non-linear.
if (bool(uniforms.steps_mask & kDecodeToLinearStep)) {
color = vec4<f32>(gamma_conversion(color.r, uniforms.gamma_decoding_params),
gamma_conversion(color.g, uniforms.gamma_decoding_params),
gamma_conversion(color.b, uniforms.gamma_decoding_params),
color.a);
}
// Convert unpremultiplied, linear source colors to the destination gamut by
// multiplying by a 3x3 matrix. Calculate transformFromXYZD50 * transformToXYZD50
// in CPU side and upload the final result in uniforms.
if (bool(uniforms.steps_mask & kConvertToDstGamutStep)) {
color = vec4<f32>(uniforms.conversion_matrix * color.rgb, color.a);
}
// Encode that color using the inverse of the destination color
// spaces transfer function if it is non-linear.
if (bool(uniforms.steps_mask & kEncodeToGammaStep)) {
color = vec4<f32>(gamma_conversion(color.r, uniforms.gamma_encoding_params),
gamma_conversion(color.g, uniforms.gamma_encoding_params),
gamma_conversion(color.b, uniforms.gamma_encoding_params),
color.a);
}
// Premultiply step.
// This step is exclusive with clear src alpha to one step.
if (bool(uniforms.steps_mask & kPremultiplyStep)) {
color = vec4<f32>(color.rgb * color.a, color.a);
}
// Decode for copying from non-srgb formats to srgb formats
if (bool(uniforms.steps_mask & kDecodeForSrgbDstFormat)) {
color = vec4<f32>(gamma_conversion(color.r, uniforms.gamma_decoding_for_dst_srgb_params),
gamma_conversion(color.g, uniforms.gamma_decoding_for_dst_srgb_params),
gamma_conversion(color.b, uniforms.gamma_decoding_for_dst_srgb_params),
color.a);
}
// Clear alpha to one step.
// This step is exclusive with premultiply/unpremultiply step.
if (bool(uniforms.steps_mask & kClearSrcAlphaToOne)) {
color.a = 1.0;
}
return color;
}
@fragment
fn copyTexture(@location(0) texcoord : vec2<f32>
) -> @location(0) vec4<f32> {
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<f32>
) -> @location(0) vec4<f32> {
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 GammaTransferParamsInternal {
float G = 0.0;
float A = 0.0;
float B = 0.0;
float C = 0.0;
float D = 0.0;
float E = 0.0;
float F = 0.0;
uint32_t padding = 0;
};
struct Uniform {
float scaleX;
float scaleY;
float offsetX;
float offsetY;
uint32_t stepsMask = 0;
const std::array<uint32_t, 3> padding = {}; // 12 bytes padding
std::array<float, 12> conversionMatrix = {};
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 ValidateCopyTextureSourceFormat(const wgpu::TextureFormat srcFormat) {
switch (srcFormat) {
case wgpu::TextureFormat::BGRA8Unorm:
case wgpu::TextureFormat::RGBA8Unorm:
case wgpu::TextureFormat::RGBA16Float:
break;
default:
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:
case wgpu::TextureFormat::R32Float:
case wgpu::TextureFormat::RG8Unorm:
case wgpu::TextureFormat::RG16Float:
case wgpu::TextureFormat::RG32Float:
case wgpu::TextureFormat::RGBA8Unorm:
case wgpu::TextureFormat::RGBA8UnormSrgb:
case wgpu::TextureFormat::BGRA8Unorm:
case wgpu::TextureFormat::BGRA8UnormSrgb:
case wgpu::TextureFormat::RGB10A2Unorm:
case wgpu::TextureFormat::RGBA16Float:
case wgpu::TextureFormat::RGBA32Float:
break;
default:
return DAWN_VALIDATION_ERROR("Destination texture format (%s) is not supported.",
dstFormat);
}
return {};
}
RenderPipelineBase* GetCachedCopyTexturePipeline(InternalPipelineStore* store,
wgpu::TextureFormat dstFormat) {
auto pipeline = store->copyTextureForBrowserPipelines.find(dstFormat);
if (pipeline != store->copyTextureForBrowserPipelines.end()) {
return pipeline->second.Get();
}
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<Ref<RenderPipelineBase>> 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 = &target;
return device->CreateRenderPipeline(&renderPipelineDesc);
}
ResultOrError<ShaderModuleBase*> GetOrCreateCopyForBrowserShaderModule(
DeviceBase* device,
InternalPipelineStore* store) {
if (store->copyForBrowser == nullptr) {
DAWN_TRY_ASSIGN(store->copyForBrowser,
utils::CreateShaderModule(device, sCopyForBrowserShader));
}
return store->copyForBrowser.Get();
}
ResultOrError<RenderPipelineBase*> GetOrCreateCopyTextureForBrowserPipeline(
DeviceBase* device,
wgpu::TextureFormat dstFormat) {
InternalPipelineStore* store = device->GetInternalPipelineStore();
if (GetCachedCopyTexturePipeline(store, dstFormat) == nullptr) {
ShaderModuleBase* shaderModule;
DAWN_TRY_ASSIGN(shaderModule, GetOrCreateCopyForBrowserShaderModule(device, store));
Ref<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(
pipeline, CreateCopyForBrowserPipeline(device, dstFormat, shaderModule, "copyTexture"));
store->copyTextureForBrowserPipelines.insert({dstFormat, std::move(pipeline)});
}
return GetCachedCopyTexturePipeline(store, dstFormat);
}
ResultOrError<RenderPipelineBase*> 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<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(pipeline, CreateCopyForBrowserPipeline(device, dstFormat, shaderModule,
"copyExternalTexture"));
store->copyExternalTextureForBrowserPipelines.insert({dstFormat, std::move(pipeline)});
}
return GetCachedCopyExternalTexturePipeline(store, dstFormat);
}
// Whether the format of dst texture of CopyTextureForBrowser() is srgb or non-srgb.
bool IsSrgbDstFormat(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::RGBA8UnormSrgb:
case wgpu::TextureFormat::BGRA8UnormSrgb:
return true;
default:
return false;
}
}
template <typename T>
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.
// Noop copy
if (copySize->width == 0 || copySize->height == 0 || copySize->depthOrArrayLayers == 0) {
return {};
}
// Prepare bind group layout.
Ref<BindGroupLayoutBase> layout;
DAWN_TRY_ASSIGN(layout, pipeline->GetBindGroupLayout(0));
// Prepare binding 0 resource: uniform buffer.
Uniform uniformData = {
copySize->width / static_cast<float>(sourceInfo->size.width),
copySize->height / static_cast<float>(sourceInfo->size.height), // scale
sourceInfo->origin.x / static_cast<float>(sourceInfo->size.width),
sourceInfo->origin.y / static_cast<float>(sourceInfo->size.height) // offset
};
// Handle flipY. FlipY here means we flip the source texture firstly and then
// do copy. This helps on the case which source texture is flipped and the copy
// need to unpack the flip.
if (options->flipY) {
uniformData.scaleY *= -1.0;
uniformData.offsetY += copySize->height / static_cast<float>(sourceInfo->size.height);
}
uint32_t stepsMask = 0u;
// Steps to do color space conversion
// From https://skia.org/docs/user/color/
// - unpremultiply if the source color is premultiplied; Alpha is not involved in color
// management, and we need to divide it out if its multiplied in.
// - linearize the source color using the source color spaces transfer function
// - convert those unpremultiplied, linear source colors to XYZ D50 gamut by multiplying by
// a 3x3 matrix.
// - convert those XYZ D50 colors to the destination gamut by multiplying by a 3x3 matrix.
// - encode that color using the inverse of the destination color spaces transfer function.
// - premultiply by alpha if the destination is premultiplied.
// The reason to choose XYZ D50 as intermediate color space:
// From http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html
// "Since the Lab TIFF specification, the ICC profile specification and
// Adobe Photoshop all use a D50"
constexpr uint32_t kUnpremultiplyStep = 0x01;
constexpr uint32_t kDecodeToLinearStep = 0x02;
constexpr uint32_t kConvertToDstGamutStep = 0x04;
constexpr uint32_t kEncodeToGammaStep = 0x08;
constexpr uint32_t kPremultiplyStep = 0x10;
constexpr uint32_t kDecodeForSrgbDstFormat = 0x20;
constexpr uint32_t kClearSrcAlphaToOne = 0x40;
if (options->srcAlphaMode == wgpu::AlphaMode::Premultiplied) {
if (options->needsColorSpaceConversion ||
options->dstAlphaMode == wgpu::AlphaMode::Unpremultiplied) {
stepsMask |= kUnpremultiplyStep;
}
} else if (options->srcAlphaMode == wgpu::AlphaMode::Opaque) {
// Simply clear src alpha channel to 1.0
stepsMask |= kClearSrcAlphaToOne;
}
if (options->needsColorSpaceConversion) {
stepsMask |= kDecodeToLinearStep;
const float* decodingParams = options->srcTransferFunctionParameters;
uniformData.gammaDecodingParams = {decodingParams[0], decodingParams[1], decodingParams[2],
decodingParams[3], decodingParams[4], decodingParams[5],
decodingParams[6]};
stepsMask |= kConvertToDstGamutStep;
const float* matrix = options->conversionMatrix;
uniformData.conversionMatrix = {{
matrix[0],
matrix[1],
matrix[2],
0.0,
matrix[3],
matrix[4],
matrix[5],
0.0,
matrix[6],
matrix[7],
matrix[8],
0.0,
}};
stepsMask |= kEncodeToGammaStep;
const float* encodingParams = options->dstTransferFunctionParameters;
uniformData.gammaEncodingParams = {encodingParams[0], encodingParams[1], encodingParams[2],
encodingParams[3], encodingParams[4], encodingParams[5],
encodingParams[6]};
}
if (options->dstAlphaMode == wgpu::AlphaMode::Premultiplied) {
if (options->needsColorSpaceConversion ||
options->srcAlphaMode == wgpu::AlphaMode::Unpremultiplied) {
stepsMask |= kPremultiplyStep;
}
}
// Copy to *-srgb texture should keep the bytes exactly the same as copy
// to non-srgb texture. Add an extra decode-to-linear step so that after the
// sampler of *-srgb format texture applying encoding, the bytes keeps the same
// as non-srgb format texture.
// NOTE: CopyTextureForBrowser() doesn't need to accept *-srgb format texture as
// source input. But above operation also valid for *-srgb format texture input and
// non-srgb format dst texture.
// TODO(crbug.com/dawn/1195): Reinterpret to non-srgb texture view on *-srgb texture
// 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
// mathematics. Order: {G, A, B, C, D, E, F, }
uniformData.gammaDecodingForDstSrgbParams = {
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<BufferBase> uniformBuffer;
DAWN_TRY_ASSIGN(
uniformBuffer,
utils::CreateBufferFromData(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform,
{uniformData}));
// Prepare binding 1 resource: sampler
// Use default configuration, filterMode set to Nearest for min and mag.
SamplerDescriptor samplerDesc = {};
Ref<SamplerBase> sampler;
DAWN_TRY_ASSIGN(sampler, device->CreateSampler(&samplerDesc));
// Create bind group after all binding entries are set.
UsageValidationMode mode =
options->internalUsage ? UsageValidationMode::Internal : UsageValidationMode::Default;
Ref<BindGroupBase> bindGroup;
DAWN_TRY_ASSIGN(bindGroup, utils::MakeBindGroup(
device, layout,
{{0, uniformBuffer}, {1, sampler}, {2, sourceResource}}, mode));
// Create command encoder.
CommandEncoderDescriptor commandEncoderDesc;
DawnEncoderInternalUsageDescriptor internalUsageDesc;
if (options->internalUsage) {
internalUsageDesc.useInternalUsages = true;
commandEncoderDesc.nextInChain = &internalUsageDesc;
}
Ref<CommandEncoder> encoder;
DAWN_TRY_ASSIGN(encoder, device->CreateCommandEncoder(&commandEncoderDesc));
// Prepare dst texture view as color Attachment.
TextureViewDescriptor dstTextureViewDesc;
dstTextureViewDesc.dimension = wgpu::TextureViewDimension::e2D;
dstTextureViewDesc.baseMipLevel = destination->mipLevel;
dstTextureViewDesc.mipLevelCount = 1;
dstTextureViewDesc.baseArrayLayer = destination->origin.z;
dstTextureViewDesc.arrayLayerCount = 1;
Ref<TextureViewBase> dstView;
DAWN_TRY_ASSIGN(dstView, device->CreateTextureView(destination->texture, &dstTextureViewDesc));
// Prepare render pass color attachment descriptor.
RenderPassColorAttachment colorAttachmentDesc;
colorAttachmentDesc.view = dstView.Get();
colorAttachmentDesc.loadOp = wgpu::LoadOp::Load;
colorAttachmentDesc.storeOp = wgpu::StoreOp::Store;
colorAttachmentDesc.clearValue = {0.0, 0.0, 0.0, 1.0};
// Create render pass.
RenderPassDescriptor renderPassDesc;
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &colorAttachmentDesc;
Ref<RenderPassEncoder> passEncoder = encoder->BeginRenderPass(&renderPassDesc);
// Start pipeline and encode commands to complete
// the copy from src texture to dst texture with transformation.
passEncoder->APISetPipeline(pipeline);
passEncoder->APISetBindGroup(0, bindGroup.Get());
passEncoder->APISetViewport(destination->origin.x, destination->origin.y, copySize->width,
copySize->height, 0.0, 1.0);
passEncoder->APIDraw(3);
passEncoder->APIEnd();
// Finsh encoding.
Ref<CommandBufferBase> commandBuffer;
DAWN_TRY_ASSIGN(commandBuffer, encoder->Finish());
CommandBufferBase* submitCommandBuffer = commandBuffer.Get();
// Submit command buffer.
device->GetQueue()->APISubmit(1, &submitCommandBuffer);
return {};
}
MaybeError ValidateCopyForBrowserDestination(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(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);
return {};
}
MaybeError ValidateCopyForBrowserOptions(const CopyTextureForBrowserOptions& options) {
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) {
// Validate source
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_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));
// Validate destination
DAWN_TRY(ValidateCopyForBrowserDestination(device, *destination, *copySize, *options));
// Validate copy common rules and copySize.
DAWN_INVALID_IF(copySize->depthOrArrayLayers > 1, "Copy is for more than one array layer (%u)",
copySize->depthOrArrayLayers);
DAWN_TRY(ValidateTextureToTextureCopyCommonRestrictions(*source, *destination, *copySize));
// Validate options
DAWN_TRY(ValidateCopyForBrowserOptions(*options));
return {};
}
MaybeError ValidateCopyExternalTextureForBrowser(DeviceBase* device,
const ImageCopyExternalTexture* source,
const ImageCopyTexture* destination,
const Extent3D* copySize,
const CopyTextureForBrowserOptions* options) {
// Validate source
DAWN_TRY(device->ValidateObject(source->externalTexture));
DAWN_TRY(source->externalTexture->ValidateCanUseInSubmitNow());
const Extent2D& sourceVisibleSize = source->externalTexture->GetVisibleSize();
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows.
DAWN_INVALID_IF(
static_cast<uint64_t>(source->origin.x) + static_cast<uint64_t>(copySize->width) >
static_cast<uint64_t>(sourceVisibleSize.width) ||
static_cast<uint64_t>(source->origin.y) + static_cast<uint64_t>(copySize->height) >
static_cast<uint64_t>(sourceVisibleSize.height) ||
static_cast<uint64_t>(source->origin.z) > 0,
"Texture copy range (origin: %s, copySize: %s) touches outside of %s visible size (%s).",
&source->origin, copySize, source->externalTexture, &sourceVisibleSize);
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.");
// Validate destination
DAWN_TRY(ValidateCopyForBrowserDestination(device, *destination, *copySize, *options));
// Validate copySize
DAWN_INVALID_IF(copySize->depthOrArrayLayers > 1, "Copy is for more than one array layer (%u)",
copySize->depthOrArrayLayers);
// Validate options
DAWN_TRY(ValidateCopyForBrowserOptions(*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& visibleSize = source->externalTexture->GetVisibleSize();
info.size = {visibleSize.width, visibleSize.height, 1};
RenderPipelineBase* pipeline;
DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyExternalTextureForBrowserPipeline(
device, destination->texture->GetFormat().format));
return DoCopyForBrowser<ExternalTextureBase>(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<TextureViewBase> 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<TextureViewBase>(device, &info, srcTextureView.Get(), destination,
copySize, options, pipeline);
}
} // namespace dawn::native