blob: b8bc2a4a9d23324648cdb3b08143c8f2403a2099 [file] [log] [blame]
// 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, flipY.
static const char sCopyTextureForBrowserVertex[] = R"(
[[block]] struct Uniforms {
[[offset(0)]] u_scale : vec2<f32>;
[[offset(8)]] 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_idx)]] var<in> VertexIndex : u32;
[[binding(0), set(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 sPassthrough2D4ChannelFrag[] = R"(
[[binding(1), set(0)]] var<uniform_constant> mySampler: sampler;
[[binding(2), set(0)]] var<uniform_constant> myTexture: texture_2d<f32>;
[[location(0)]] var<in> v_texcoord : vec2<f32>;
[[location(0)]] var<out> rgbaColor : 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)) {
rgbaColor = textureSample(myTexture, mySampler, v_texcoord);
}
}
)";
// TODO(shaobo.yan@intel.com): Expand supported texture formats
MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcFormat,
const wgpu::TextureFormat dstFormat) {
switch (srcFormat) {
case wgpu::TextureFormat::RGBA8Unorm:
break;
default:
return DAWN_VALIDATION_ERROR(
"Unsupported src texture format for CopyTextureForBrowser.");
}
switch (dstFormat) {
case wgpu::TextureFormat::RGBA8Unorm:
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* GetOrCreateCopyTextureForBrowserPipeline(DeviceBase* device) {
InternalPipelineStore* store = device->GetInternalPipelineStore();
if (store->copyTextureForBrowserPipeline == 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);
store->copyTextureForBrowserVS =
AcquireRef(device->CreateShaderModule(&descriptor));
}
ShaderModuleBase* vertexModule = store->copyTextureForBrowserVS.Get();
// Create fragment shader module if not cached before.
if (store->copyTextureForBrowserFS == nullptr) {
ShaderModuleDescriptor descriptor;
ShaderModuleWGSLDescriptor wgslDesc;
wgslDesc.source = sPassthrough2D4ChannelFrag;
descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
store->copyTextureForBrowserFS =
AcquireRef(device->CreateShaderModule(&descriptor));
}
ShaderModuleBase* fragmentModule = store->copyTextureForBrowserFS.Get();
// Prepare vertex stage.
ProgrammableStageDescriptor vertexStage = {};
vertexStage.module = vertexModule;
vertexStage.entryPoint = "main";
// Prepare frgament stage.
ProgrammableStageDescriptor fragmentStage = {};
fragmentStage.module = fragmentModule;
fragmentStage.entryPoint = "main";
// Prepare color state.
ColorStateDescriptor colorState = {};
colorState.format = wgpu::TextureFormat::RGBA8Unorm;
// Create RenderPipeline.
RenderPipelineDescriptor renderPipelineDesc = {};
// Generate the layout based on shader modules.
renderPipelineDesc.layout = nullptr;
renderPipelineDesc.vertexStage = vertexStage;
renderPipelineDesc.fragmentStage = &fragmentStage;
renderPipelineDesc.primitiveTopology = wgpu::PrimitiveTopology::TriangleList;
renderPipelineDesc.colorStateCount = 1;
renderPipelineDesc.colorStates = &colorState;
store->copyTextureForBrowserPipeline =
AcquireRef(device->CreateRenderPipeline(&renderPipelineDesc));
}
return store->copyTextureForBrowserPipeline.Get();
}
} // anonymous namespace
MaybeError ValidateCopyTextureForBrowser(DeviceBase* device,
const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize,
const CopyTextureForBrowserOptions* options) {
DAWN_TRY(device->ValidateObject(source->texture));
DAWN_TRY(device->ValidateObject(destination->texture));
DAWN_TRY(ValidateTextureCopyView(device, *source, *copySize));
DAWN_TRY(ValidateTextureCopyView(device, *destination, *copySize));
DAWN_TRY(ValidateTextureToTextureCopyRestrictions(*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 TextureCopyView* source,
const TextureCopyView* 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);
// Prepare bind group layout.
Ref<BindGroupLayoutBase> layout = AcquireRef(pipeline->GetBindGroupLayout(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);
Ref<BufferBase> uniformBuffer = AcquireRef(device->CreateBuffer(&uniformDesc));
device->GetDefaultQueue()->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 = {};
Ref<SamplerBase> sampler = AcquireRef(device->CreateSampler(&samplerDesc));
// Prepare binding 2 resource: sampled texture
TextureViewDescriptor srcTextureViewDesc = {};
srcTextureViewDesc.baseMipLevel = source->mipLevel;
srcTextureViewDesc.mipLevelCount = 1;
Ref<TextureViewBase> srcTextureView =
AcquireRef(source->texture->CreateView(&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.
Ref<BindGroupBase> bindGroup = AcquireRef(device->CreateBindGroup(&bgDesc));
// Create command encoder.
CommandEncoderDescriptor encoderDesc = {};
Ref<CommandEncoder> encoder = AcquireRef(device->CreateCommandEncoder(&encoderDesc));
// Prepare dst texture view as color Attachment.
TextureViewDescriptor dstTextureViewDesc;
dstTextureViewDesc.baseMipLevel = destination->mipLevel;
dstTextureViewDesc.mipLevelCount = 1;
Ref<TextureViewBase> dstView =
AcquireRef(destination->texture->CreateView(&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;
Ref<RenderPassEncoder> passEncoder = AcquireRef(encoder->BeginRenderPass(&renderPassDesc));
// Start pipeline and encode commands to complete
// the copy from src texture to dst texture with transformation.
passEncoder->SetPipeline(pipeline);
passEncoder->SetBindGroup(0, bindGroup.Get());
passEncoder->Draw(3);
passEncoder->EndPass();
// Finsh encoding.
Ref<CommandBufferBase> commandBuffer = AcquireRef(encoder->Finish());
CommandBufferBase* submitCommandBuffer = commandBuffer.Get();
// Submit command buffer.
Ref<QueueBase> queue = AcquireRef(device->GetDefaultQueue());
queue->Submit(1, &submitCommandBuffer);
return {};
}
} // namespace dawn_native