mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-12 01:23:33 +00:00
This CL modifies the way adapter creating devices, adds `shader-f16` feature, and deprecates the `dawn-shader-float16` feature which is no longer used. Details: 1. Parse the toggles chained with device descriptor in `adapter::CreateDeviceInternal`, which are then used to validate features requirement within `CreateDeviceInternal` and passed to device constructor as initializer. 2. When creating device, validate features requirement in `CreateDeviceInternal` with toggles known, make sure to fail the device creation if a required feature is not supported by adapter or is guarded by certain toggles which were not enabled/disabled. Feature ShaderF16 and ChromiumExperimentalDp4a are validated in this way. Unittest is added to check creating devices with toggles-guarded features required. 3. Add `shader-f16` feature, which allow `using f16;` in WGSL code. End-to-end tests are added to test a trival f16 WGSL shader could be used if and only if the device has `shader-f16` feature. 4. Deprecate the `dawn-shader-float16` feature, which will be completely removed after cleaning up Blink code. Bug: dawn:1510 Change-Id: I6cb2dcbe1ee584fdd6131c62df1ee850b881dbd2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/100802 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
625 lines
27 KiB
C++
625 lines
27 KiB
C++
// 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/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 sCopyTextureForBrowserShader[] = R"(
|
||
struct GammaTransferParams {
|
||
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: GammaTransferParams, // 80 4 32
|
||
gamma_encoding_params: GammaTransferParams, // 112 4 32
|
||
gamma_decoding_for_dst_srgb_params: GammaTransferParams, // 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: GammaTransferParams) -> 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;
|
||
@binding(2) @group(0) var myTexture: texture_2d<f32>;
|
||
|
||
@fragment
|
||
fn fs_main(
|
||
@location(0) texcoord : vec2<f32>
|
||
) -> @location(0) vec4<f32> {
|
||
// Clamp the texcoord and discard the out-of-bound pixels.
|
||
var clampedTexcoord =
|
||
clamp(texcoord, vec2<f32>(0.0, 0.0), vec2<f32>(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;
|
||
}
|
||
|
||
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 space’s
|
||
// 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
|
||
// space’s 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;
|
||
}
|
||
)";
|
||
|
||
// 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 {
|
||
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 = {};
|
||
GammaTransferParams gammaDecodingParams = {};
|
||
GammaTransferParams gammaEncodingParams = {};
|
||
GammaTransferParams gammaDecodingForDstSrgbParams = {};
|
||
};
|
||
static_assert(sizeof(Uniform) == 176);
|
||
|
||
// 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) {
|
||
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);
|
||
}
|
||
|
||
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* GetCachedPipeline(InternalPipelineStore* store, wgpu::TextureFormat dstFormat) {
|
||
auto pipeline = store->copyTextureForBrowserPipelines.find(dstFormat);
|
||
if (pipeline != store->copyTextureForBrowserPipelines.end()) {
|
||
return pipeline->second.Get();
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
ResultOrError<RenderPipelineBase*> 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 = ⌖
|
||
|
||
Ref<RenderPipelineBase> pipeline;
|
||
DAWN_TRY_ASSIGN(pipeline, device->CreateRenderPipeline(&renderPipelineDesc));
|
||
store->copyTextureForBrowserPipelines.insert({dstFormat, std::move(pipeline)});
|
||
}
|
||
|
||
return GetCachedPipeline(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");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
MaybeError DoCopyTextureForBrowser(DeviceBase* device,
|
||
const ImageCopyTexture* source,
|
||
const ImageCopyTexture* destination,
|
||
const Extent3D* copySize,
|
||
const CopyTextureForBrowserOptions* options) {
|
||
// 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 {};
|
||
}
|
||
|
||
bool isSrgbDstFormat = IsSrgbDstFormat(destination->texture->GetFormat().format);
|
||
RenderPipelineBase* pipeline;
|
||
DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyTextureForBrowserPipeline(
|
||
device, destination->texture->GetFormat().format));
|
||
|
||
// Prepare bind group layout.
|
||
Ref<BindGroupLayoutBase> 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<float>(srcTextureSize.width),
|
||
copySize->height / static_cast<float>(srcTextureSize.height), // scale
|
||
source->origin.x / static_cast<float>(srcTextureSize.width),
|
||
source->origin.y / static_cast<float>(srcTextureSize.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>(srcTextureSize.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 it’s multiplied in.
|
||
// - linearize the source color using the source color space’s 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 space’s 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.
|
||
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};
|
||
}
|
||
|
||
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));
|
||
|
||
// Prepare binding 2 resource: sampled texture
|
||
TextureViewDescriptor srcTextureViewDesc = {};
|
||
srcTextureViewDesc.dimension = wgpu::TextureViewDimension::e2D;
|
||
srcTextureViewDesc.baseMipLevel = source->mipLevel;
|
||
srcTextureViewDesc.mipLevelCount = 1;
|
||
srcTextureViewDesc.arrayLayerCount = 1;
|
||
Ref<TextureViewBase> 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<BindGroupBase> bindGroup;
|
||
DAWN_TRY_ASSIGN(bindGroup, utils::MakeBindGroup(
|
||
device, layout,
|
||
{{0, uniformBuffer}, {1, sampler}, {2, srcTextureView}}, 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 {};
|
||
}
|
||
|
||
} // namespace dawn::native
|