mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-17 02:26:07 +00:00
This CL propagates errors from reentrant WriteBuffer calls and makes procy methods on BufferBase to show that it is safe to call GetMappedRange and Unmap without handling errors. Bug: dawn:723 Change-Id: I4ea43adc4844505314bf84e2357b2d928f1d1f8c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/46003 Auto-Submit: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Stephen White <senorblanco@chromium.org>
354 lines
16 KiB
C++
354 lines
16 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 "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 <unordered_set>
|
|
|
|
namespace dawn_native {
|
|
namespace {
|
|
// TODO(shaobo.yan@intel.com) : Support premultiplay-alpha
|
|
static const char sCopyTextureForBrowserVertex[] = R"(
|
|
[[block]] struct Uniforms {
|
|
u_scale : vec2<f32>;
|
|
u_offset : vec2<f32>;
|
|
};
|
|
const texcoord : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
|
|
vec2<f32>(-0.5, 0.0),
|
|
vec2<f32>( 1.5, 0.0),
|
|
vec2<f32>( 0.5, 2.0));
|
|
[[location(0)]] var<out> v_texcoord: vec2<f32>;
|
|
[[builtin(position)]] var<out> Position : vec4<f32>;
|
|
[[builtin(vertex_index)]] var<in> VertexIndex : u32;
|
|
[[binding(0), group(0)]] var<uniform> uniforms : Uniforms;
|
|
[[stage(vertex)]] fn main() -> void {
|
|
Position = vec4<f32>((texcoord[VertexIndex] * 2.0 - vec2<f32>(1.0, 1.0)), 0.0, 1.0);
|
|
|
|
// Texture coordinate takes top-left as origin point. We need to map the
|
|
// texture to triangle carefully.
|
|
v_texcoord = (texcoord[VertexIndex] * vec2<f32>(1.0, -1.0) + vec2<f32>(0.0, 1.0)) *
|
|
uniforms.u_scale + uniforms.u_offset;
|
|
}
|
|
)";
|
|
|
|
static const char sCopyTextureForBrowserFragment[] = R"(
|
|
[[binding(1), group(0)]] var mySampler: sampler;
|
|
[[binding(2), group(0)]] var myTexture: texture_2d<f32>;
|
|
[[location(0)]] var<in> v_texcoord : vec2<f32>;
|
|
[[location(0)]] var<out> outputColor : vec4<f32>;
|
|
[[stage(fragment)]] fn main() -> void {
|
|
// Clamp the texcoord and discard the out-of-bound pixels.
|
|
var clampedTexcoord : vec2<f32> =
|
|
clamp(v_texcoord, vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 1.0));
|
|
if (all(clampedTexcoord == v_texcoord)) {
|
|
var srcColor : vec4<f32> = textureSample(myTexture, mySampler, v_texcoord);
|
|
// 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.
|
|
outputColor = srcColor;
|
|
}
|
|
}
|
|
)";
|
|
|
|
// TODO(shaobo.yan@intel.com): Expand copyTextureForBrowser to support any
|
|
// non-depth, non-stencil, non-compressed texture format pair copy. Now this API
|
|
// supports CopyImageBitmapToTexture normal format pairs.
|
|
MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcFormat,
|
|
const wgpu::TextureFormat dstFormat) {
|
|
switch (srcFormat) {
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
break;
|
|
default:
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Unsupported src texture format for CopyTextureForBrowser.");
|
|
}
|
|
|
|
switch (dstFormat) {
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
case wgpu::TextureFormat::RGBA32Float:
|
|
case wgpu::TextureFormat::RG8Unorm:
|
|
case wgpu::TextureFormat::RGBA16Float:
|
|
case wgpu::TextureFormat::RG16Float:
|
|
case wgpu::TextureFormat::RGB10A2Unorm:
|
|
break;
|
|
default:
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Unsupported dst texture format for CopyTextureForBrowser.");
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError ValidateCopyTextureForBrowserOptions(
|
|
const CopyTextureForBrowserOptions* options) {
|
|
if (options->nextInChain != nullptr) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"CopyTextureForBrowserOptions: nextInChain must be nullptr");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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->copyTextureForBrowserVS == nullptr) {
|
|
ShaderModuleDescriptor descriptor;
|
|
ShaderModuleWGSLDescriptor wgslDesc;
|
|
wgslDesc.source = sCopyTextureForBrowserVertex;
|
|
descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
|
|
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
store->copyTextureForBrowserVS =
|
|
AcquireRef(device->APICreateShaderModule(&descriptor));
|
|
}
|
|
|
|
ShaderModuleBase* vertexModule = store->copyTextureForBrowserVS.Get();
|
|
|
|
// Create fragment shader module if not cached before.
|
|
if (store->copyTextureForBrowserFS == nullptr) {
|
|
ShaderModuleDescriptor descriptor;
|
|
ShaderModuleWGSLDescriptor wgslDesc;
|
|
wgslDesc.source = sCopyTextureForBrowserFragment;
|
|
descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
store->copyTextureForBrowserFS =
|
|
AcquireRef(device->APICreateShaderModule(&descriptor));
|
|
}
|
|
|
|
ShaderModuleBase* fragmentModule = store->copyTextureForBrowserFS.Get();
|
|
|
|
// Prepare vertex stage.
|
|
VertexState vertex = {};
|
|
vertex.module = vertexModule;
|
|
vertex.entryPoint = "main";
|
|
|
|
// Prepare frgament stage.
|
|
FragmentState fragment = {};
|
|
fragment.module = fragmentModule;
|
|
fragment.entryPoint = "main";
|
|
|
|
// Prepare color state.
|
|
ColorTargetState target = {};
|
|
target.format = dstFormat;
|
|
|
|
// Create RenderPipeline.
|
|
RenderPipelineDescriptor2 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 = ⌖
|
|
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
store->copyTextureForBrowserPipelines.insert(
|
|
{dstFormat, AcquireRef(device->APICreateRenderPipeline2(&renderPipelineDesc))});
|
|
}
|
|
|
|
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_TRY(ValidateImageCopyTexture(device, *source, *copySize));
|
|
DAWN_TRY(ValidateImageCopyTexture(device, *destination, *copySize));
|
|
|
|
DAWN_TRY(ValidateCopyTextureForBrowserRestrictions(*source, *destination, *copySize));
|
|
|
|
DAWN_TRY(ValidateTextureCopyRange(*source, *copySize));
|
|
DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize));
|
|
|
|
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
|
|
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
|
|
|
|
DAWN_TRY(ValidateCopyTextureFormatConversion(source->texture->GetFormat().format,
|
|
destination->texture->GetFormat().format));
|
|
|
|
DAWN_TRY(ValidateCopyTextureForBrowserOptions(options));
|
|
|
|
// TODO(shaobo.yan@intel.com): Support the simplest case for now that source and destination
|
|
// texture has the same size and do full texture blit. Will address sub texture blit in
|
|
// future and remove these validations.
|
|
if (source->origin.x != 0 || source->origin.y != 0 || source->origin.z != 0 ||
|
|
destination->origin.x != 0 || destination->origin.y != 0 ||
|
|
destination->origin.z != 0 || source->mipLevel != 0 || destination->mipLevel != 0 ||
|
|
source->texture->GetWidth() != destination->texture->GetWidth() ||
|
|
source->texture->GetHeight() != destination->texture->GetHeight()) {
|
|
return DAWN_VALIDATION_ERROR("Cannot support sub blit now.");
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError DoCopyTextureForBrowser(DeviceBase* device,
|
|
const ImageCopyTexture* source,
|
|
const ImageCopyTexture* destination,
|
|
const Extent3D* copySize,
|
|
const CopyTextureForBrowserOptions* options) {
|
|
// TODO(shaobo.yan@intel.com): In D3D12 and Vulkan, compatible texture format can directly
|
|
// copy to each other. This can be a potential fast path.
|
|
|
|
RenderPipelineBase* pipeline = GetOrCreateCopyTextureForBrowserPipeline(
|
|
device, destination->texture->GetFormat().format);
|
|
|
|
// Prepare bind group layout.
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<BindGroupLayoutBase> layout = AcquireRef(pipeline->APIGetBindGroupLayout(0));
|
|
|
|
// Prepare bind group descriptor
|
|
BindGroupEntry bindGroupEntries[3] = {};
|
|
BindGroupDescriptor bgDesc = {};
|
|
bgDesc.layout = layout.Get();
|
|
bgDesc.entryCount = 3;
|
|
bgDesc.entries = bindGroupEntries;
|
|
|
|
// Prepare binding 0 resource: uniform buffer.
|
|
float uniformData[] = {
|
|
1.0, 1.0, // scale
|
|
0.0, 0.0 // offset
|
|
};
|
|
|
|
// Handle flipY
|
|
if (options && options->flipY) {
|
|
uniformData[1] *= -1.0;
|
|
uniformData[3] += 1.0;
|
|
}
|
|
|
|
BufferDescriptor uniformDesc = {};
|
|
uniformDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
|
|
uniformDesc.size = sizeof(uniformData);
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<BufferBase> uniformBuffer = AcquireRef(device->APICreateBuffer(&uniformDesc));
|
|
|
|
DAWN_TRY(device->GetQueue()->WriteBuffer(uniformBuffer.Get(), 0, uniformData,
|
|
sizeof(uniformData)));
|
|
|
|
// Prepare binding 1 resource: sampler
|
|
// Use default configuration, filterMode set to Nearest for min and mag.
|
|
SamplerDescriptor samplerDesc = {};
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<SamplerBase> sampler = AcquireRef(device->APICreateSampler(&samplerDesc));
|
|
|
|
// Prepare binding 2 resource: sampled texture
|
|
TextureViewDescriptor srcTextureViewDesc = {};
|
|
srcTextureViewDesc.baseMipLevel = source->mipLevel;
|
|
srcTextureViewDesc.mipLevelCount = 1;
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<TextureViewBase> srcTextureView =
|
|
AcquireRef(source->texture->APICreateView(&srcTextureViewDesc));
|
|
|
|
// Set bind group entries.
|
|
bindGroupEntries[0].binding = 0;
|
|
bindGroupEntries[0].buffer = uniformBuffer.Get();
|
|
bindGroupEntries[0].size = sizeof(uniformData);
|
|
bindGroupEntries[1].binding = 1;
|
|
bindGroupEntries[1].sampler = sampler.Get();
|
|
bindGroupEntries[2].binding = 2;
|
|
bindGroupEntries[2].textureView = srcTextureView.Get();
|
|
|
|
// Create bind group after all binding entries are set.
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<BindGroupBase> bindGroup = AcquireRef(device->APICreateBindGroup(&bgDesc));
|
|
|
|
// Create command encoder.
|
|
CommandEncoderDescriptor encoderDesc = {};
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<CommandEncoder> encoder = AcquireRef(device->APICreateCommandEncoder(&encoderDesc));
|
|
|
|
// Prepare dst texture view as color Attachment.
|
|
TextureViewDescriptor dstTextureViewDesc;
|
|
dstTextureViewDesc.baseMipLevel = destination->mipLevel;
|
|
dstTextureViewDesc.mipLevelCount = 1;
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<TextureViewBase> dstView =
|
|
AcquireRef(destination->texture->APICreateView(&dstTextureViewDesc));
|
|
|
|
// Prepare render pass color attachment descriptor.
|
|
RenderPassColorAttachmentDescriptor colorAttachmentDesc;
|
|
|
|
colorAttachmentDesc.attachment = dstView.Get();
|
|
colorAttachmentDesc.loadOp = wgpu::LoadOp::Load;
|
|
colorAttachmentDesc.storeOp = wgpu::StoreOp::Store;
|
|
colorAttachmentDesc.clearColor = {0.0, 0.0, 0.0, 1.0};
|
|
|
|
// Create render pass.
|
|
RenderPassDescriptor renderPassDesc;
|
|
renderPassDesc.colorAttachmentCount = 1;
|
|
renderPassDesc.colorAttachments = &colorAttachmentDesc;
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<RenderPassEncoder> passEncoder =
|
|
AcquireRef(encoder->APIBeginRenderPass(&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->APIDraw(3);
|
|
passEncoder->APIEndPass();
|
|
|
|
// Finsh encoding.
|
|
// TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
|
|
Ref<CommandBufferBase> commandBuffer = AcquireRef(encoder->APIFinish());
|
|
CommandBufferBase* submitCommandBuffer = commandBuffer.Get();
|
|
|
|
// Submit command buffer.
|
|
device->GetQueue()->APISubmit(1, &submitCommandBuffer);
|
|
|
|
return {};
|
|
}
|
|
|
|
} // namespace dawn_native
|