Support internal pipelines for Dawn

Like copyTextureCHROMIUM in Chromium, CopyImageBitmapToTexture can use
internal pipeline to do the GPU uploading. But Dawn doesn't support
internal pipeline now.

This patch adds the first internal pipeline for Dawn and add the API
CopyTextureForBrowser to use internal pipeline to do gpu uploading.

The patch integrates very simple wgsl vertex/fragment shaders to do
simple direct blit to verify the whole system works.

BUG=dawn:465

Change-Id: I8b566af38a10eea00f7426c39e752958ef057abd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30960
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Yan, Shaobo 2020-11-04 02:30:16 +00:00 committed by Commit Bot service account
parent 131e619fe4
commit db8766bb23
18 changed files with 683 additions and 60 deletions

View File

@ -1035,6 +1035,14 @@
{"name": "data layout", "type": "texture data layout", "annotation": "const*"},
{"name": "write size", "type": "extent 3D", "annotation": "const*"}
]
},
{
"name": "copy texture for browser",
"args": [
{"name": "source", "type": "texture copy view", "annotation": "const*"},
{"name": "destination", "type": "texture copy view", "annotation": "const*"},
{"name": "copy size", "type": "extent 3D", "annotation": "const*"}
]
}
]
},

View File

@ -183,6 +183,8 @@ source_set("dawn_native_sources") {
"ComputePassEncoder.h",
"ComputePipeline.cpp",
"ComputePipeline.h",
"CopyTextureForBrowserHelper.cpp",
"CopyTextureForBrowserHelper.h",
"CreateReadyPipelineTracker.cpp",
"CreateReadyPipelineTracker.h",
"Device.cpp",
@ -213,6 +215,7 @@ source_set("dawn_native_sources") {
"Instance.cpp",
"Instance.h",
"IntegerTypes.h",
"InternalPipelineStore.h",
"ObjectBase.cpp",
"ObjectBase.h",
"PassResourceUsage.h",

View File

@ -70,6 +70,8 @@ target_sources(dawn_native PRIVATE
"ComputePassEncoder.h"
"ComputePipeline.cpp"
"ComputePipeline.h"
"CopyTextureForBrowserHelper.cpp"
"CopyTextureForBrowserHelper.h"
"CreateReadyPipelineTracker.cpp"
"CreateReadyPipelineTracker.h"
"Device.cpp"
@ -99,6 +101,7 @@ target_sources(dawn_native PRIVATE
"Forward.h"
"Instance.cpp"
"Instance.h"
"InternalPipelineStore.h"
"IntegerTypes.h"
"ObjectBase.cpp"
"ObjectBase.h"

View File

@ -66,41 +66,6 @@ namespace dawn_native {
return {};
}
MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
const TextureCopyView& dst,
const Extent3D& copySize) {
const uint32_t srcSamples = src.texture->GetSampleCount();
const uint32_t dstSamples = dst.texture->GetSampleCount();
if (srcSamples != dstSamples) {
return DAWN_VALIDATION_ERROR(
"Source and destination textures must have matching sample counts.");
}
if (src.texture->GetFormat().format != dst.texture->GetFormat().format) {
// Metal requires texture-to-texture copies be the same format
return DAWN_VALIDATION_ERROR("Source and destination texture formats must match.");
}
if (src.aspect != wgpu::TextureAspect::All || dst.aspect != wgpu::TextureAspect::All) {
// Metal cannot select a single aspect for texture-to-texture copies
return DAWN_VALIDATION_ERROR(
"Texture aspect must be \"all\" for texture to texture copies");
}
if (src.texture == dst.texture && src.mipLevel == dst.mipLevel) {
ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D &&
dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
if (IsRangeOverlapped(src.origin.z, dst.origin.z, copySize.depth)) {
return DAWN_VALIDATION_ERROR(
"Copy subresources cannot be overlapped when copying within the same "
"texture.");
}
}
return {};
}
MaybeError ValidateLinearTextureCopyOffset(const TextureDataLayout& layout,
const TexelBlockInfo& blockInfo) {
if (layout.offset % blockInfo.byteSize != 0) {
@ -132,24 +97,6 @@ namespace dawn_native {
return {};
}
MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(buffer->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
}
return {};
}
MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(texture->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
}
return {};
}
MaybeError ValidateAttachmentArrayLayersAndLevelCount(const TextureViewBase* attachment) {
// Currently we do not support layered rendering.
if (attachment->GetLayerCount() > 1) {

View File

@ -72,7 +72,7 @@ namespace dawn_native {
uint64_t destinationOffset);
void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor = nullptr);
private:
MaybeError ValidateFinish(CommandIterator* commands,

View File

@ -616,4 +616,57 @@ namespace dawn_native {
return {};
}
MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
const TextureCopyView& dst,
const Extent3D& copySize) {
const uint32_t srcSamples = src.texture->GetSampleCount();
const uint32_t dstSamples = dst.texture->GetSampleCount();
if (srcSamples != dstSamples) {
return DAWN_VALIDATION_ERROR(
"Source and destination textures must have matching sample counts.");
}
if (src.texture->GetFormat().format != dst.texture->GetFormat().format) {
// Metal requires texture-to-texture copies be the same format
return DAWN_VALIDATION_ERROR("Source and destination texture formats must match.");
}
if (src.aspect != wgpu::TextureAspect::All || dst.aspect != wgpu::TextureAspect::All) {
// Metal cannot select a single aspect for texture-to-texture copies
return DAWN_VALIDATION_ERROR(
"Texture aspect must be \"all\" for texture to texture copies");
}
if (src.texture == dst.texture && src.mipLevel == dst.mipLevel) {
ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D &&
dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
if (IsRangeOverlapped(src.origin.z, dst.origin.z, copySize.depth)) {
return DAWN_VALIDATION_ERROR(
"Copy subresources cannot be overlapped when copying within the same "
"texture.");
}
}
return {};
}
MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(texture->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
}
return {};
}
MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(buffer->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
}
return {};
}
} // namespace dawn_native

View File

@ -78,6 +78,14 @@ namespace dawn_native {
bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length);
MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
const TextureCopyView& dst,
const Extent3D& copySize);
MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage);
MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage);
} // namespace dawn_native
#endif // DAWNNATIVE_COMMANDVALIDATION_H_

View File

@ -0,0 +1,307 @@
// 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): Use ANGLE's strategy(render a large triangle) to handle
// transform and border interop issue.
static const char sCopyTextureForBrowserVertex[] = R"(
[[block]] struct Uniforms {
[[offset(0)]] rotation : mat4x4<f32>;
};
const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
vec2<f32>(1.0, 1.0),
vec2<f32>(1.0, -1.0),
vec2<f32>(-1.0, -1.0),
vec2<f32>(1.0, 1.0),
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0));
const texUV : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
vec2<f32>(1.0, 0.0),
vec2<f32>(1.0, 1.0),
vec2<f32>(0.0, 1.0),
vec2<f32>(1.0, 0.0),
vec2<f32>(0.0, 1.0),
vec2<f32>(0.0, 0.0));
[[location(0)]] var<out> texCoord: vec2<f32>;
[[builtin(position)]] var<out> Position : vec4<f32>;
[[builtin(vertex_idx)]] var<in> VertexIndex : i32;
[[binding(0), set(0)]] var<uniform> uniforms : Uniforms;
[[stage(vertex)]]
fn main() -> void {
Position = uniforms.rotation * vec4<f32>(pos[VertexIndex], 0.0, 1.0);
texCoord = texUV[VertexIndex];
return;
}
)";
static const char sPassthrough2D4ChannelFrag[] = R"(
[[binding(1), set(0)]] var<uniform_constant> mySampler: sampler;
[[binding(2), set(0)]] var<uniform_constant> myTexture: texture_sampled_2d<f32>;
[[location(0)]] var<in> texCoord : vec2<f32>;
[[location(0)]] var<out> rgbaColor : vec4<f32>;
[[stage(fragment)]]
fn main() -> void {
rgbaColor = textureSample(myTexture, mySampler, texCoord);
return;
}
)";
// 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 {};
}
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) {
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));
// 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) {
// 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.
// TODO(shaobo.yan@intel.com): Will use scale vector and offset vector to replace the
// 4x4 rotation matrix here.
const float rotationMatrix[] = {
1.0, 0.0, 0.0, 0.0, //
0.0, 1.0, 0.0, 0.0, //
0.0, 0.0, 1.0, 0.0, //
0.0, 0.0, 0.0, 1.0, //
};
BufferDescriptor rotationUniformDesc = {};
rotationUniformDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
rotationUniformDesc.size = sizeof(rotationMatrix);
Ref<BufferBase> rotationUniform = AcquireRef(device->CreateBuffer(&rotationUniformDesc));
device->GetDefaultQueue()->WriteBuffer(rotationUniform.Get(), 0, rotationMatrix,
sizeof(rotationMatrix));
// 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 = rotationUniform.Get();
bindGroupEntries[0].size = sizeof(rotationMatrix);
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(6);
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

View File

@ -0,0 +1,38 @@
// 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.
#ifndef DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_
#define DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_
#include "dawn_native/Error.h"
#include "dawn_native/ObjectBase.h"
namespace dawn_native {
class DeviceBase;
struct Extent3D;
struct TextureCopyView;
MaybeError ValidateCopyTextureForBrowser(DeviceBase* device,
const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize);
MaybeError DoCopyTextureForBrowser(DeviceBase* device,
const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize);
} // namespace dawn_native
#endif // DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_

View File

@ -30,6 +30,7 @@
#include "dawn_native/ErrorScopeTracker.h"
#include "dawn_native/Fence.h"
#include "dawn_native/Instance.h"
#include "dawn_native/InternalPipelineStore.h"
#include "dawn_native/PipelineLayout.h"
#include "dawn_native/QuerySet.h"
#include "dawn_native/Queue.h"
@ -105,6 +106,7 @@ namespace dawn_native {
mDynamicUploader = std::make_unique<DynamicUploader>(this);
mCreateReadyPipelineTracker = std::make_unique<CreateReadyPipelineTracker>(this);
mDeprecationWarnings = std::make_unique<DeprecationWarnings>();
mInternalPipelineStore = std::make_unique<InternalPipelineStore>();
// Starting from now the backend can start doing reentrant calls so the device is marked as
// alive.
@ -172,6 +174,8 @@ namespace dawn_native {
mEmptyBindGroupLayout = nullptr;
mInternalPipelineStore = nullptr;
AssumeCommandsComplete();
// Tell the backend that it can free all the objects now that the GPU timeline is empty.
ShutDownImpl();
@ -329,6 +333,10 @@ namespace dawn_native {
return mFutureSerial;
}
InternalPipelineStore* DeviceBase::GetInternalPipelineStore() {
return mInternalPipelineStore.get();
}
void DeviceBase::IncrementLastSubmittedCommandSerial() {
mLastSubmittedSerial++;
}

View File

@ -38,6 +38,7 @@ namespace dawn_native {
class ErrorScope;
class ErrorScopeTracker;
class StagingBufferBase;
struct InternalPipelineStore;
class DeviceBase {
public:
@ -161,6 +162,7 @@ namespace dawn_native {
TextureBase* CreateTexture(const TextureDescriptor* descriptor);
TextureViewBase* CreateTextureView(TextureBase* texture,
const TextureViewDescriptor* descriptor);
InternalPipelineStore* GetInternalPipelineStore();
// For Dawn Wire
BufferBase* CreateErrorBuffer();
@ -385,6 +387,8 @@ namespace dawn_native {
size_t mLazyClearCountForTesting = 0;
ExtensionsSet mEnabledExtensions;
std::unique_ptr<InternalPipelineStore> mInternalPipelineStore;
};
} // namespace dawn_native

View File

@ -0,0 +1,34 @@
// 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.
#ifndef DAWNNATIVE_INTERNALPIPELINESTORE_H_
#define DAWNNATIVE_INTERNALPIPELINESTORE_H_
#include "dawn_native/ObjectBase.h"
#include "dawn_native/dawn_platform.h"
#include <unordered_map>
namespace dawn_native {
class RenderPipelineBase;
class ShaderModuleBase;
struct InternalPipelineStore {
Ref<RenderPipelineBase> copyTextureForBrowserPipeline;
Ref<ShaderModuleBase> copyTextureForBrowserVS;
Ref<ShaderModuleBase> copyTextureForBrowserFS;
};
} // namespace dawn_native
#endif // DAWNNATIVE_INTERNALPIPELINESTORE_H_

View File

@ -40,8 +40,8 @@ namespace dawn_native {
void SetBindGroup(uint32_t groupIndex,
BindGroupBase* group,
uint32_t dynamicOffsetCount,
const uint32_t* dynamicOffsets);
uint32_t dynamicOffsetCount = 0,
const uint32_t* dynamicOffsets = nullptr);
protected:
// Construct an "error" programmable pass encoder.

View File

@ -17,14 +17,18 @@
#include "common/Constants.h"
#include "dawn_native/Buffer.h"
#include "dawn_native/CommandBuffer.h"
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/CommandValidation.h"
#include "dawn_native/Commands.h"
#include "dawn_native/CopyTextureForBrowserHelper.h"
#include "dawn_native/Device.h"
#include "dawn_native/DynamicUploader.h"
#include "dawn_native/ErrorScope.h"
#include "dawn_native/ErrorScopeTracker.h"
#include "dawn_native/Fence.h"
#include "dawn_native/QuerySet.h"
#include "dawn_native/RenderPassEncoder.h"
#include "dawn_native/RenderPipeline.h"
#include "dawn_native/Texture.h"
#include "dawn_platform/DawnPlatform.h"
#include "dawn_platform/tracing/TraceEvent.h"
@ -131,7 +135,6 @@ namespace dawn_native {
UNREACHABLE();
}
};
} // namespace
// QueueBase
@ -306,6 +309,23 @@ namespace dawn_native {
return device->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout,
&textureCopy, writeSizePixel);
}
void QueueBase::CopyTextureForBrowser(const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize) {
GetDevice()->ConsumedError(CopyTextureForBrowserInternal(source, destination, copySize));
}
MaybeError QueueBase::CopyTextureForBrowserInternal(const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize) {
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(ValidateCopyTextureForBrowser(GetDevice(), source, destination, copySize));
}
return DoCopyTextureForBrowser(GetDevice(), source, destination, copySize);
}
MaybeError QueueBase::ValidateSubmit(uint32_t commandCount,
CommandBufferBase* const* commands) const {
TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit");

View File

@ -45,6 +45,9 @@ namespace dawn_native {
size_t dataSize,
const TextureDataLayout* dataLayout,
const Extent3D* writeSize);
void CopyTextureForBrowser(const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize);
void TrackTask(std::unique_ptr<TaskInFlight> task, ExecutionSerial serial);
void Tick(ExecutionSerial finishedSerial);
@ -63,6 +66,9 @@ namespace dawn_native {
size_t dataSize,
const TextureDataLayout* dataLayout,
const Extent3D* writeSize);
MaybeError CopyTextureForBrowserInternal(const TextureCopyView* source,
const TextureCopyView* destination,
const Extent3D* copySize);
virtual MaybeError SubmitImpl(uint32_t commandCount,
CommandBufferBase* const* commands) = 0;

View File

@ -25,9 +25,9 @@ namespace dawn_native {
RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext);
void Draw(uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance);
uint32_t instanceCount = 1,
uint32_t firstVertex = 0,
uint32_t firstInstance = 0);
void DrawIndexed(uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstIndex,

View File

@ -275,6 +275,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/ComputeSharedMemoryTests.cpp",
"end2end/ComputeStorageBufferBarrierTests.cpp",
"end2end/CopyTests.cpp",
"end2end/CopyTextureForBrowserTests.cpp",
"end2end/CreateReadyPipelineTests.cpp",
"end2end/CullingTests.cpp",
"end2end/DebugMarkerTests.cpp",

View File

@ -0,0 +1,183 @@
// 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 "tests/DawnTest.h"
#include "common/Constants.h"
#include "common/Math.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.h"
class CopyTextureForBrowserTests : public DawnTest {
protected:
static constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
struct TextureSpec {
wgpu::Origin3D copyOrigin;
wgpu::Extent3D textureSize;
uint32_t level;
};
static std::vector<RGBA8> GetExpectedTextureData(const utils::TextureDataCopyLayout& layout) {
std::vector<RGBA8> textureData(layout.texelBlockCount);
for (uint32_t layer = 0; layer < layout.mipSize.depth; ++layer) {
const uint32_t sliceOffset = layout.texelBlocksPerImage * layer;
for (uint32_t y = 0; y < layout.mipSize.height; ++y) {
const uint32_t rowOffset = layout.texelBlocksPerRow * y;
for (uint32_t x = 0; x < layout.mipSize.width; ++x) {
textureData[sliceOffset + rowOffset + x] =
RGBA8(static_cast<uint8_t>((x + layer * x) % 256),
static_cast<uint8_t>((y + layer * y) % 256),
static_cast<uint8_t>(x / 256), static_cast<uint8_t>(y / 256));
}
}
}
return textureData;
}
static void PackTextureData(const RGBA8* srcData,
uint32_t width,
uint32_t height,
uint32_t srcTexelsPerRow,
RGBA8* dstData,
uint32_t dstTexelsPerRow) {
for (unsigned int y = 0; y < height; ++y) {
for (unsigned int x = 0; x < width; ++x) {
unsigned int src = x + y * srcTexelsPerRow;
unsigned int dst = x + y * dstTexelsPerRow;
dstData[dst] = srcData[src];
}
}
}
void DoTest(const TextureSpec& srcSpec,
const TextureSpec& dstSpec,
const wgpu::Extent3D& copySize) {
wgpu::TextureDescriptor srcDescriptor;
srcDescriptor.size = srcSpec.textureSize;
srcDescriptor.format = kTextureFormat;
srcDescriptor.mipLevelCount = srcSpec.level + 1;
srcDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment;
wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
wgpu::Texture dstTexture;
wgpu::TextureDescriptor dstDescriptor;
dstDescriptor.size = dstSpec.textureSize;
dstDescriptor.format = kTextureFormat;
dstDescriptor.mipLevelCount = dstSpec.level + 1;
dstDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
wgpu::TextureUsage::OutputAttachment;
dstTexture = device.CreateTexture(&dstDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
// Use writeTexture to populate the current slice of the texture in
// `level` mip level for all platforms except OpenGL.
// TODO(shaobo.yan@intel.com): OpenGL doesn't have 'WriteTexture' implementation.
const utils::TextureDataCopyLayout copyLayout =
utils::GetTextureDataCopyLayoutForTexture2DAtLevel(
kTextureFormat,
{srcSpec.textureSize.width, srcSpec.textureSize.height, copySize.depth},
srcSpec.level, 0);
const std::vector<RGBA8> textureArrayCopyData = GetExpectedTextureData(copyLayout);
wgpu::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z});
wgpu::TextureDataLayout textureDataLayout;
textureDataLayout.offset = 0;
textureDataLayout.bytesPerRow = copyLayout.bytesPerRow;
textureDataLayout.rowsPerImage = copyLayout.bytesPerImage / copyLayout.bytesPerRow;
device.GetDefaultQueue().WriteTexture(&textureCopyView, textureArrayCopyData.data(),
textureArrayCopyData.size() * sizeof(RGBA8),
&textureDataLayout, &copyLayout.mipSize);
const wgpu::Extent3D copySizePerSlice = {copySize.width, copySize.height, 1};
// Perform the texture to texture copy
wgpu::TextureCopyView srcTextureCopyView =
utils::CreateTextureCopyView(srcTexture, srcSpec.level, srcSpec.copyOrigin);
wgpu::TextureCopyView dstTextureCopyView =
utils::CreateTextureCopyView(dstTexture, dstSpec.level, dstSpec.copyOrigin);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
// Perform a copy here for testing.
device.GetDefaultQueue().CopyTextureForBrowser(&srcTextureCopyView, &dstTextureCopyView,
&copySize);
// Texels in single slice.
const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion(
copyLayout.bytesPerRow, copyLayout.bytesPerImage / copyLayout.bytesPerRow,
copySizePerSlice, kTextureFormat);
std::vector<RGBA8> expected(texelCountInCopyRegion);
for (uint32_t slice = 0; slice < copySize.depth; ++slice) {
std::fill(expected.begin(), expected.end(), RGBA8());
const uint32_t texelIndexOffset = copyLayout.texelBlocksPerImage * slice;
const uint32_t expectedTexelArrayDataStartIndex =
texelIndexOffset +
(srcSpec.copyOrigin.x + srcSpec.copyOrigin.y * copyLayout.texelBlocksPerRow);
PackTextureData(&textureArrayCopyData[expectedTexelArrayDataStartIndex], copySize.width,
copySize.height, copyLayout.texelBlocksPerRow, expected.data(),
copySize.width);
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.copyOrigin.x,
dstSpec.copyOrigin.y, copySize.width, copySize.height,
dstSpec.level, dstSpec.copyOrigin.z + slice)
<< "Texture to Texture copy failed copying region [(" << srcSpec.copyOrigin.x
<< ", " << srcSpec.copyOrigin.y << "), (" << srcSpec.copyOrigin.x + copySize.width
<< ", " << srcSpec.copyOrigin.y + copySize.height << ")) from "
<< srcSpec.textureSize.width << " x " << srcSpec.textureSize.height
<< " texture at mip level " << srcSpec.level << " layer "
<< srcSpec.copyOrigin.z + slice << " to [(" << dstSpec.copyOrigin.x << ", "
<< dstSpec.copyOrigin.y << "), (" << dstSpec.copyOrigin.x + copySize.width << ", "
<< dstSpec.copyOrigin.y + copySize.height << ")) region of "
<< dstSpec.textureSize.width << " x " << dstSpec.textureSize.height
<< " texture at mip level " << dstSpec.level << " layer "
<< dstSpec.copyOrigin.z + slice << std::endl;
}
}
};
// Verify CopyTextureForBrowserTests works with internal pipeline.
// The case do copy without any transform.
TEST_P(CopyTextureForBrowserTests, PassthroughCopy) {
// These tests fails due to crbug.com/tint/63.
DAWN_SKIP_TEST_IF(IsSwiftshader());
DAWN_SKIP_TEST_IF(IsVulkan());
DAWN_SKIP_TEST_IF(IsD3D12() && IsBackendValidationEnabled());
// OpenGL tests fails due to 'WriteTexture' unimplemented.
// Related bug : crbug.com/dawn/483
DAWN_SKIP_TEST_IF(IsOpenGL());
constexpr uint32_t kWidth = 10;
constexpr uint32_t kHeight = 1;
TextureSpec textureSpec;
textureSpec.copyOrigin = {0, 0, 0};
textureSpec.level = 0;
textureSpec.textureSize = {kWidth, kHeight, 1};
DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1});
}
DAWN_INSTANTIATE_TEST(CopyTextureForBrowserTests,
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
VulkanBackend());