Support vertex-only render pipeline
Support vertex-only render pipeline on D3D12, Vulkan, Metal, OpenGL and OpenGL ES backends. Related validation tests and end to end tests are also implemented. Bug: dawn:136 Change-Id: If266fd441c1d39ccd940ea26b74b405f8abb351a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63080 Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
43ce892284
commit
857d4e62e3
|
@ -35,12 +35,14 @@ namespace dawn_native {
|
||||||
|
|
||||||
AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor)
|
AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor)
|
||||||
: mSampleCount(descriptor->multisample.count) {
|
: mSampleCount(descriptor->multisample.count) {
|
||||||
ASSERT(descriptor->fragment->targetCount <= kMaxColorAttachments);
|
if (descriptor->fragment != nullptr) {
|
||||||
for (ColorAttachmentIndex i(uint8_t(0));
|
ASSERT(descriptor->fragment->targetCount <= kMaxColorAttachments);
|
||||||
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->fragment->targetCount));
|
for (ColorAttachmentIndex i(uint8_t(0));
|
||||||
++i) {
|
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->fragment->targetCount));
|
||||||
mColorAttachmentsSet.set(i);
|
++i) {
|
||||||
mColorFormats[i] = descriptor->fragment->targets[static_cast<uint8_t>(i)].format;
|
mColorAttachmentsSet.set(i);
|
||||||
|
mColorFormats[i] = descriptor->fragment->targets[static_cast<uint8_t>(i)].format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (descriptor->depthStencil != nullptr) {
|
if (descriptor->depthStencil != nullptr) {
|
||||||
mDepthStencilFormat = descriptor->depthStencil->format;
|
mDepthStencilFormat = descriptor->depthStencil->format;
|
||||||
|
|
|
@ -154,7 +154,8 @@ namespace dawn_native {
|
||||||
// Ref will keep the pipeline layout alive until the end of the function where
|
// Ref will keep the pipeline layout alive until the end of the function where
|
||||||
// the pipeline will take another reference.
|
// the pipeline will take another reference.
|
||||||
DAWN_TRY_ASSIGN(layoutRef,
|
DAWN_TRY_ASSIGN(layoutRef,
|
||||||
PipelineLayoutBase::CreateDefault(device, GetStages(&descriptor)));
|
PipelineLayoutBase::CreateDefault(
|
||||||
|
device, GetRenderStagesAndSetDummyShader(device, &descriptor)));
|
||||||
outDescriptor->layout = layoutRef.Get();
|
outDescriptor->layout = layoutRef.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +238,21 @@ namespace dawn_native {
|
||||||
|
|
||||||
DAWN_TRY_ASSIGN(mEmptyBindGroupLayout, CreateEmptyBindGroupLayout());
|
DAWN_TRY_ASSIGN(mEmptyBindGroupLayout, CreateEmptyBindGroupLayout());
|
||||||
|
|
||||||
|
// If dummy fragment shader module is needed, initialize it
|
||||||
|
if (IsToggleEnabled(Toggle::UseDummyFragmentInVertexOnlyPipeline)) {
|
||||||
|
// The empty fragment shader, used as a work around for vertex-only render pipeline
|
||||||
|
constexpr char kEmptyFragmentShader[] = R"(
|
||||||
|
[[stage(fragment)]] fn fs_empty_main() {}
|
||||||
|
)";
|
||||||
|
ShaderModuleDescriptor descriptor;
|
||||||
|
ShaderModuleWGSLDescriptor wgslDesc;
|
||||||
|
wgslDesc.source = kEmptyFragmentShader;
|
||||||
|
descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
|
||||||
|
|
||||||
|
DAWN_TRY_ASSIGN(mInternalPipelineStore->dummyFragmentShader,
|
||||||
|
CreateShaderModule(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
Ref<ComputePipelineBase> timestampComputePipeline;
|
Ref<ComputePipelineBase> timestampComputePipeline;
|
||||||
Ref<ShaderModuleBase> timestampCS;
|
Ref<ShaderModuleBase> timestampCS;
|
||||||
|
|
||||||
|
Ref<ShaderModuleBase> dummyFragmentShader;
|
||||||
};
|
};
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,10 @@ namespace dawn_native {
|
||||||
return mStages;
|
return mStages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wgpu::ShaderStage PipelineBase::GetStageMask() const {
|
||||||
|
return mStageMask;
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t groupIndex) {
|
MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t groupIndex) {
|
||||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
|
@ -49,6 +49,7 @@ namespace dawn_native {
|
||||||
const RequiredBufferSizes& GetMinBufferSizes() const;
|
const RequiredBufferSizes& GetMinBufferSizes() const;
|
||||||
const ProgrammableStage& GetStage(SingleShaderStage stage) const;
|
const ProgrammableStage& GetStage(SingleShaderStage stage) const;
|
||||||
const PerStage<ProgrammableStage>& GetAllStages() const;
|
const PerStage<ProgrammableStage>& GetAllStages() const;
|
||||||
|
wgpu::ShaderStage GetStageMask() const;
|
||||||
|
|
||||||
ResultOrError<Ref<BindGroupLayoutBase>> GetBindGroupLayout(uint32_t groupIndex);
|
ResultOrError<Ref<BindGroupLayoutBase>> GetBindGroupLayout(uint32_t groupIndex);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "dawn_native/ChainUtils_autogen.h"
|
#include "dawn_native/ChainUtils_autogen.h"
|
||||||
#include "dawn_native/Commands.h"
|
#include "dawn_native/Commands.h"
|
||||||
#include "dawn_native/Device.h"
|
#include "dawn_native/Device.h"
|
||||||
|
#include "dawn_native/InternalPipelineStore.h"
|
||||||
#include "dawn_native/ObjectContentHasher.h"
|
#include "dawn_native/ObjectContentHasher.h"
|
||||||
#include "dawn_native/ValidationUtils_autogen.h"
|
#include "dawn_native/ValidationUtils_autogen.h"
|
||||||
#include "dawn_native/VertexFormat.h"
|
#include "dawn_native/VertexFormat.h"
|
||||||
|
@ -456,11 +457,6 @@ namespace dawn_native {
|
||||||
DAWN_TRY(device->ValidateObject(descriptor->layout));
|
DAWN_TRY(device->ValidateObject(descriptor->layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/dawn/136): Support vertex-only pipelines.
|
|
||||||
if (descriptor->fragment == nullptr) {
|
|
||||||
return DAWN_VALIDATION_ERROR("Null fragment stage is not supported (yet)");
|
|
||||||
}
|
|
||||||
|
|
||||||
DAWN_TRY(ValidateVertexState(device, &descriptor->vertex, descriptor->layout));
|
DAWN_TRY(ValidateVertexState(device, &descriptor->vertex, descriptor->layout));
|
||||||
|
|
||||||
DAWN_TRY(ValidatePrimitiveState(device, &descriptor->primitive));
|
DAWN_TRY(ValidatePrimitiveState(device, &descriptor->primitive));
|
||||||
|
@ -471,25 +467,36 @@ namespace dawn_native {
|
||||||
|
|
||||||
DAWN_TRY(ValidateMultisampleState(&descriptor->multisample));
|
DAWN_TRY(ValidateMultisampleState(&descriptor->multisample));
|
||||||
|
|
||||||
ASSERT(descriptor->fragment != nullptr);
|
if (descriptor->fragment != nullptr) {
|
||||||
DAWN_TRY(ValidateFragmentState(device, descriptor->fragment, descriptor->layout));
|
DAWN_TRY(ValidateFragmentState(device, descriptor->fragment, descriptor->layout));
|
||||||
|
|
||||||
if (descriptor->fragment->targetCount == 0 && !descriptor->depthStencil) {
|
if (descriptor->fragment->targetCount == 0 && !descriptor->depthStencil) {
|
||||||
return DAWN_VALIDATION_ERROR("Should have at least one color target or a depthStencil");
|
return DAWN_VALIDATION_ERROR(
|
||||||
|
"Should have at least one color target or a depthStencil");
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_TRY(
|
||||||
|
ValidateInterStageMatching(device, descriptor->vertex, *(descriptor->fragment)));
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_TRY(ValidateInterStageMatching(device, descriptor->vertex, *(descriptor->fragment)));
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StageAndDescriptor> GetStages(const RenderPipelineDescriptor* descriptor) {
|
std::vector<StageAndDescriptor> GetRenderStagesAndSetDummyShader(
|
||||||
|
DeviceBase* device,
|
||||||
|
const RenderPipelineDescriptor* descriptor) {
|
||||||
std::vector<StageAndDescriptor> stages;
|
std::vector<StageAndDescriptor> stages;
|
||||||
stages.push_back(
|
stages.push_back(
|
||||||
{SingleShaderStage::Vertex, descriptor->vertex.module, descriptor->vertex.entryPoint});
|
{SingleShaderStage::Vertex, descriptor->vertex.module, descriptor->vertex.entryPoint});
|
||||||
if (descriptor->fragment != nullptr) {
|
if (descriptor->fragment != nullptr) {
|
||||||
stages.push_back({SingleShaderStage::Fragment, descriptor->fragment->module,
|
stages.push_back({SingleShaderStage::Fragment, descriptor->fragment->module,
|
||||||
descriptor->fragment->entryPoint});
|
descriptor->fragment->entryPoint});
|
||||||
|
} else if (device->IsToggleEnabled(Toggle::UseDummyFragmentInVertexOnlyPipeline)) {
|
||||||
|
InternalPipelineStore* store = device->GetInternalPipelineStore();
|
||||||
|
// The dummy fragment shader module should already be initialized
|
||||||
|
DAWN_ASSERT(store->dummyFragmentShader != nullptr);
|
||||||
|
ShaderModuleBase* dummyFragmentShader = store->dummyFragmentShader.Get();
|
||||||
|
stages.push_back({SingleShaderStage::Fragment, dummyFragmentShader, "fs_empty_main"});
|
||||||
}
|
}
|
||||||
return stages;
|
return stages;
|
||||||
}
|
}
|
||||||
|
@ -512,10 +519,7 @@ namespace dawn_native {
|
||||||
: PipelineBase(device,
|
: PipelineBase(device,
|
||||||
descriptor->layout,
|
descriptor->layout,
|
||||||
descriptor->label,
|
descriptor->label,
|
||||||
{{SingleShaderStage::Vertex, descriptor->vertex.module,
|
GetRenderStagesAndSetDummyShader(device, descriptor)),
|
||||||
descriptor->vertex.entryPoint},
|
|
||||||
{SingleShaderStage::Fragment, descriptor->fragment->module,
|
|
||||||
descriptor->fragment->entryPoint}}),
|
|
||||||
mAttachmentState(device->GetOrCreateAttachmentState(descriptor)) {
|
mAttachmentState(device->GetOrCreateAttachmentState(descriptor)) {
|
||||||
mVertexBufferCount = descriptor->vertex.bufferCount;
|
mVertexBufferCount = descriptor->vertex.bufferCount;
|
||||||
const VertexBufferLayout* buffers = descriptor->vertex.buffers;
|
const VertexBufferLayout* buffers = descriptor->vertex.buffers;
|
||||||
|
@ -597,6 +601,9 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ColorAttachmentIndex i : IterateBitSet(mAttachmentState->GetColorAttachmentsMask())) {
|
for (ColorAttachmentIndex i : IterateBitSet(mAttachmentState->GetColorAttachmentsMask())) {
|
||||||
|
// Vertex-only render pipeline have no color attachment. For a render pipeline with
|
||||||
|
// color attachments, there must be a valid FragmentState.
|
||||||
|
ASSERT(descriptor->fragment != nullptr);
|
||||||
const ColorTargetState* target =
|
const ColorTargetState* target =
|
||||||
&descriptor->fragment->targets[static_cast<uint8_t>(i)];
|
&descriptor->fragment->targets[static_cast<uint8_t>(i)];
|
||||||
mTargets[i] = *target;
|
mTargets[i] = *target;
|
||||||
|
@ -947,5 +954,4 @@ namespace dawn_native {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -57,7 +57,9 @@ namespace dawn_native {
|
||||||
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
|
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
|
||||||
const RenderPipelineDescriptor* descriptor);
|
const RenderPipelineDescriptor* descriptor);
|
||||||
|
|
||||||
std::vector<StageAndDescriptor> GetStages(const RenderPipelineDescriptor* descriptor);
|
std::vector<StageAndDescriptor> GetRenderStagesAndSetDummyShader(
|
||||||
|
DeviceBase* device,
|
||||||
|
const RenderPipelineDescriptor* descriptor);
|
||||||
|
|
||||||
size_t IndexFormatSize(wgpu::IndexFormat format);
|
size_t IndexFormatSize(wgpu::IndexFormat format);
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,12 @@ namespace dawn_native {
|
||||||
"Disables mipmaps for r8unorm and rg8unorm textures, which are known on some drivers "
|
"Disables mipmaps for r8unorm and rg8unorm textures, which are known on some drivers "
|
||||||
"to not clear correctly.",
|
"to not clear correctly.",
|
||||||
"https://crbug.com/dawn/1071"}},
|
"https://crbug.com/dawn/1071"}},
|
||||||
|
{Toggle::UseDummyFragmentInVertexOnlyPipeline,
|
||||||
|
{"use_dummy_fragment_in_vertex_only_pipeline",
|
||||||
|
"Use a dummy empty fragment shader in vertex only render pipeline. This toggle must "
|
||||||
|
"be enabled for OpenGL ES backend, and serves as a workaround by default enabled on "
|
||||||
|
"some Metal devices with Intel GPU to ensure the depth result is correct.",
|
||||||
|
"https://crbug.com/dawn/136"}},
|
||||||
// Dummy comment to separate the }} so it is clearer what to copy-paste to add a toggle.
|
// Dummy comment to separate the }} so it is clearer what to copy-paste to add a toggle.
|
||||||
}};
|
}};
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
|
@ -60,6 +60,7 @@ namespace dawn_native {
|
||||||
DisableSymbolRenaming,
|
DisableSymbolRenaming,
|
||||||
UseUserDefinedLabelsInBackend,
|
UseUserDefinedLabelsInBackend,
|
||||||
DisableR8RG8Mipmaps,
|
DisableR8RG8Mipmaps,
|
||||||
|
UseDummyFragmentInVertexOnlyPipeline,
|
||||||
|
|
||||||
EnumCount,
|
EnumCount,
|
||||||
InvalidEnum = EnumCount,
|
InvalidEnum = EnumCount,
|
||||||
|
|
|
@ -340,27 +340,19 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC descriptorD3D12 = {};
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC descriptorD3D12 = {};
|
||||||
|
|
||||||
const ProgrammableStage& vertexStage = GetStage(SingleShaderStage::Vertex);
|
PerStage<ProgrammableStage> pipelineStages = GetAllStages();
|
||||||
const ProgrammableStage& fragmentStage = GetStage(SingleShaderStage::Fragment);
|
|
||||||
|
|
||||||
PerStage<const char*> entryPoints;
|
|
||||||
entryPoints[SingleShaderStage::Vertex] = vertexStage.entryPoint.c_str();
|
|
||||||
entryPoints[SingleShaderStage::Fragment] = fragmentStage.entryPoint.c_str();
|
|
||||||
|
|
||||||
PerStage<ShaderModule*> modules;
|
|
||||||
modules[SingleShaderStage::Vertex] = ToBackend(vertexStage.module.Get());
|
|
||||||
modules[SingleShaderStage::Fragment] = ToBackend(fragmentStage.module.Get());
|
|
||||||
|
|
||||||
PerStage<D3D12_SHADER_BYTECODE*> shaders;
|
PerStage<D3D12_SHADER_BYTECODE*> shaders;
|
||||||
shaders[SingleShaderStage::Vertex] = &descriptorD3D12.VS;
|
shaders[SingleShaderStage::Vertex] = &descriptorD3D12.VS;
|
||||||
shaders[SingleShaderStage::Fragment] = &descriptorD3D12.PS;
|
shaders[SingleShaderStage::Fragment] = &descriptorD3D12.PS;
|
||||||
|
|
||||||
PerStage<CompiledShader> compiledShader;
|
PerStage<CompiledShader> compiledShader;
|
||||||
wgpu::ShaderStage renderStages = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
|
|
||||||
for (auto stage : IterateStages(renderStages)) {
|
for (auto stage : IterateStages(GetStageMask())) {
|
||||||
DAWN_TRY_ASSIGN(compiledShader[stage],
|
DAWN_TRY_ASSIGN(compiledShader[stage],
|
||||||
modules[stage]->Compile(entryPoints[stage], stage,
|
ToBackend(pipelineStages[stage].module)
|
||||||
ToBackend(GetLayout()), compileFlags));
|
->Compile(pipelineStages[stage].entryPoint.c_str(), stage,
|
||||||
|
ToBackend(GetLayout()), compileFlags));
|
||||||
*shaders[stage] = compiledShader[stage].GetD3D12ShaderBytecode();
|
*shaders[stage] = compiledShader[stage].GetD3D12ShaderBytecode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,16 @@ namespace dawn_native { namespace metal {
|
||||||
if (gpu_info::IsIntel(pciInfo.vendorId)) {
|
if (gpu_info::IsIntel(pciInfo.vendorId)) {
|
||||||
SetToggle(Toggle::DisableR8RG8Mipmaps, true);
|
SetToggle(Toggle::DisableR8RG8Mipmaps, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On some Intel GPU vertex only render pipeline get wrong depth result if no fragment
|
||||||
|
// shader provided. Create a dummy fragment shader module to work around this issue.
|
||||||
|
if (gpu_info::IsIntel(this->GetAdapter()->GetPCIInfo().vendorId)) {
|
||||||
|
bool useDummyFragmentShader = true;
|
||||||
|
if (gpu_info::IsSkylake(this->GetAdapter()->GetPCIInfo().deviceId)) {
|
||||||
|
useDummyFragmentShader = false;
|
||||||
|
}
|
||||||
|
SetToggle(Toggle::UseDummyFragmentInVertexOnlyPipeline, useDummyFragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl(
|
ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl(
|
||||||
|
|
|
@ -338,8 +338,9 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
descriptorMTL.vertexDescriptor = vertexDesc.Get();
|
descriptorMTL.vertexDescriptor = vertexDesc.Get();
|
||||||
|
|
||||||
const ProgrammableStage& vertexStage = GetStage(SingleShaderStage::Vertex);
|
const PerStage<ProgrammableStage>& allStages = GetAllStages();
|
||||||
ShaderModule* vertexModule = ToBackend(vertexStage.module.Get());
|
const ProgrammableStage& vertexStage = allStages[wgpu::ShaderStage::Vertex];
|
||||||
|
ShaderModule* vertexModule = ToBackend(vertexStage.module).Get();
|
||||||
const char* vertexEntryPoint = vertexStage.entryPoint.c_str();
|
const char* vertexEntryPoint = vertexStage.entryPoint.c_str();
|
||||||
ShaderModule::MetalFunctionData vertexData;
|
ShaderModule::MetalFunctionData vertexData;
|
||||||
DAWN_TRY(vertexModule->CreateFunction(vertexEntryPoint, SingleShaderStage::Vertex,
|
DAWN_TRY(vertexModule->CreateFunction(vertexEntryPoint, SingleShaderStage::Vertex,
|
||||||
|
@ -351,17 +352,28 @@ namespace dawn_native { namespace metal {
|
||||||
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Vertex;
|
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Vertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProgrammableStage& fragmentStage = GetStage(SingleShaderStage::Fragment);
|
if (GetStageMask() & wgpu::ShaderStage::Fragment) {
|
||||||
ShaderModule* fragmentModule = ToBackend(fragmentStage.module.Get());
|
const ProgrammableStage& fragmentStage = allStages[wgpu::ShaderStage::Fragment];
|
||||||
const char* fragmentEntryPoint = fragmentStage.entryPoint.c_str();
|
ShaderModule* fragmentModule = ToBackend(fragmentStage.module).Get();
|
||||||
ShaderModule::MetalFunctionData fragmentData;
|
const char* fragmentEntryPoint = fragmentStage.entryPoint.c_str();
|
||||||
DAWN_TRY(fragmentModule->CreateFunction(fragmentEntryPoint, SingleShaderStage::Fragment,
|
ShaderModule::MetalFunctionData fragmentData;
|
||||||
ToBackend(GetLayout()), &fragmentData,
|
DAWN_TRY(fragmentModule->CreateFunction(fragmentEntryPoint, SingleShaderStage::Fragment,
|
||||||
GetSampleMask()));
|
ToBackend(GetLayout()), &fragmentData,
|
||||||
|
GetSampleMask()));
|
||||||
|
|
||||||
descriptorMTL.fragmentFunction = fragmentData.function.Get();
|
descriptorMTL.fragmentFunction = fragmentData.function.Get();
|
||||||
if (fragmentData.needsStorageBufferLength) {
|
if (fragmentData.needsStorageBufferLength) {
|
||||||
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Fragment;
|
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& fragmentOutputsWritten = fragmentStage.metadata->fragmentOutputsWritten;
|
||||||
|
for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
|
||||||
|
descriptorMTL.colorAttachments[static_cast<uint8_t>(i)].pixelFormat =
|
||||||
|
MetalPixelFormat(GetColorAttachmentFormat(i));
|
||||||
|
const ColorTargetState* descriptor = GetColorTargetState(i);
|
||||||
|
ComputeBlendDesc(descriptorMTL.colorAttachments[static_cast<uint8_t>(i)],
|
||||||
|
descriptor, fragmentOutputsWritten[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasDepthStencilAttachment()) {
|
if (HasDepthStencilAttachment()) {
|
||||||
|
@ -377,16 +389,6 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& fragmentOutputsWritten =
|
|
||||||
GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputsWritten;
|
|
||||||
for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
|
|
||||||
descriptorMTL.colorAttachments[static_cast<uint8_t>(i)].pixelFormat =
|
|
||||||
MetalPixelFormat(GetColorAttachmentFormat(i));
|
|
||||||
const ColorTargetState* descriptor = GetColorTargetState(i);
|
|
||||||
ComputeBlendDesc(descriptorMTL.colorAttachments[static_cast<uint8_t>(i)], descriptor,
|
|
||||||
fragmentOutputsWritten[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptorMTL.inputPrimitiveTopology = MTLInputPrimitiveTopology(GetPrimitiveTopology());
|
descriptorMTL.inputPrimitiveTopology = MTLInputPrimitiveTopology(GetPrimitiveTopology());
|
||||||
descriptorMTL.sampleCount = GetSampleCount();
|
descriptorMTL.sampleCount = GetSampleCount();
|
||||||
descriptorMTL.alphaToCoverageEnabled = IsAlphaToCoverageEnabled();
|
descriptorMTL.alphaToCoverageEnabled = IsAlphaToCoverageEnabled();
|
||||||
|
|
|
@ -99,6 +99,8 @@ namespace dawn_native { namespace opengl {
|
||||||
SetToggle(Toggle::DisableDepthStencilRead, !supportsDepthStencilRead);
|
SetToggle(Toggle::DisableDepthStencilRead, !supportsDepthStencilRead);
|
||||||
SetToggle(Toggle::DisableSampleVariables, !supportsSampleVariables);
|
SetToggle(Toggle::DisableSampleVariables, !supportsSampleVariables);
|
||||||
SetToggle(Toggle::FlushBeforeClientWaitSync, gl.GetVersion().IsES());
|
SetToggle(Toggle::FlushBeforeClientWaitSync, gl.GetVersion().IsES());
|
||||||
|
// For OpenGL ES, we must use dummy fragment shader for vertex-only render pipeline.
|
||||||
|
SetToggle(Toggle::UseDummyFragmentInVertexOnlyPipeline, gl.GetVersion().IsES());
|
||||||
}
|
}
|
||||||
|
|
||||||
const GLFormat& Device::GetGLFormat(const Format& format) {
|
const GLFormat& Device::GetGLFormat(const Format& format) {
|
||||||
|
|
|
@ -330,33 +330,43 @@ namespace dawn_native { namespace vulkan {
|
||||||
MaybeError RenderPipeline::Initialize() {
|
MaybeError RenderPipeline::Initialize() {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
VkPipelineShaderStageCreateInfo shaderStages[2];
|
// There are at most 2 shader stages in render pipeline, i.e. vertex and fragment
|
||||||
{
|
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
||||||
// Generate a new VkShaderModule with BindingRemapper tint transform for each
|
uint32_t stageCount = 0;
|
||||||
// pipeline
|
|
||||||
const ProgrammableStage& vertexStage = GetStage(SingleShaderStage::Vertex);
|
|
||||||
DAWN_TRY_ASSIGN(shaderStages[0].module,
|
|
||||||
ToBackend(vertexStage.module.Get())
|
|
||||||
->GetTransformedModuleHandle(vertexStage.entryPoint.c_str(),
|
|
||||||
ToBackend(GetLayout())));
|
|
||||||
const ProgrammableStage& fragmentStage = GetStage(SingleShaderStage::Fragment);
|
|
||||||
DAWN_TRY_ASSIGN(shaderStages[1].module,
|
|
||||||
ToBackend(fragmentStage.module.Get())
|
|
||||||
->GetTransformedModuleHandle(fragmentStage.entryPoint.c_str(),
|
|
||||||
ToBackend(GetLayout())));
|
|
||||||
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
||||||
shaderStages[0].pNext = nullptr;
|
|
||||||
shaderStages[0].flags = 0;
|
|
||||||
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
||||||
shaderStages[0].pSpecializationInfo = nullptr;
|
|
||||||
shaderStages[0].pName = vertexStage.entryPoint.c_str();
|
|
||||||
|
|
||||||
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
for (auto stage : IterateStages(this->GetStageMask())) {
|
||||||
shaderStages[1].pNext = nullptr;
|
VkPipelineShaderStageCreateInfo shaderStage;
|
||||||
shaderStages[1].flags = 0;
|
|
||||||
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
DAWN_TRY_ASSIGN(shaderStage.module,
|
||||||
shaderStages[1].pSpecializationInfo = nullptr;
|
ToBackend(GetStage(stage).module)
|
||||||
shaderStages[1].pName = fragmentStage.entryPoint.c_str();
|
->GetTransformedModuleHandle(GetStage(stage).entryPoint.c_str(),
|
||||||
|
ToBackend(GetLayout())));
|
||||||
|
|
||||||
|
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
shaderStage.pNext = nullptr;
|
||||||
|
shaderStage.flags = 0;
|
||||||
|
shaderStage.pSpecializationInfo = nullptr;
|
||||||
|
shaderStage.pName = GetStage(stage).entryPoint.c_str();
|
||||||
|
|
||||||
|
switch (stage) {
|
||||||
|
case dawn_native::SingleShaderStage::Vertex: {
|
||||||
|
shaderStage.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dawn_native::SingleShaderStage::Fragment: {
|
||||||
|
shaderStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// For render pipeline only Vertex and Fragment stage is possible
|
||||||
|
DAWN_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_ASSERT(stageCount < 2);
|
||||||
|
shaderStages[stageCount] = shaderStage;
|
||||||
|
stageCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
||||||
|
@ -427,30 +437,35 @@ namespace dawn_native { namespace vulkan {
|
||||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
||||||
ComputeDepthStencilDesc(GetDepthStencilState());
|
ComputeDepthStencilDesc(GetDepthStencilState());
|
||||||
|
|
||||||
// Initialize the "blend state info" that will be chained in the "create info" from the data
|
VkPipelineColorBlendStateCreateInfo colorBlend;
|
||||||
// pre-computed in the ColorState
|
// colorBlend may hold pointers to elements in colorBlendAttachments, so it must have a
|
||||||
|
// definition scope as same as colorBlend
|
||||||
ityp::array<ColorAttachmentIndex, VkPipelineColorBlendAttachmentState, kMaxColorAttachments>
|
ityp::array<ColorAttachmentIndex, VkPipelineColorBlendAttachmentState, kMaxColorAttachments>
|
||||||
colorBlendAttachments;
|
colorBlendAttachments;
|
||||||
const auto& fragmentOutputsWritten =
|
if (GetStageMask() & wgpu::ShaderStage::Fragment) {
|
||||||
GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputsWritten;
|
// Initialize the "blend state info" that will be chained in the "create info" from the
|
||||||
for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
|
// data pre-computed in the ColorState
|
||||||
const ColorTargetState* target = GetColorTargetState(i);
|
const auto& fragmentOutputsWritten =
|
||||||
colorBlendAttachments[i] = ComputeColorDesc(target, fragmentOutputsWritten[i]);
|
GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputsWritten;
|
||||||
|
for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
|
||||||
|
const ColorTargetState* target = GetColorTargetState(i);
|
||||||
|
colorBlendAttachments[i] = ComputeColorDesc(target, fragmentOutputsWritten[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||||
|
colorBlend.pNext = nullptr;
|
||||||
|
colorBlend.flags = 0;
|
||||||
|
// LogicOp isn't supported so we disable it.
|
||||||
|
colorBlend.logicOpEnable = VK_FALSE;
|
||||||
|
colorBlend.logicOp = VK_LOGIC_OP_CLEAR;
|
||||||
|
colorBlend.attachmentCount = static_cast<uint32_t>(GetColorAttachmentsMask().count());
|
||||||
|
colorBlend.pAttachments = colorBlendAttachments.data();
|
||||||
|
// The blend constant is always dynamic so we fill in a dummy value
|
||||||
|
colorBlend.blendConstants[0] = 0.0f;
|
||||||
|
colorBlend.blendConstants[1] = 0.0f;
|
||||||
|
colorBlend.blendConstants[2] = 0.0f;
|
||||||
|
colorBlend.blendConstants[3] = 0.0f;
|
||||||
}
|
}
|
||||||
VkPipelineColorBlendStateCreateInfo colorBlend;
|
|
||||||
colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
||||||
colorBlend.pNext = nullptr;
|
|
||||||
colorBlend.flags = 0;
|
|
||||||
// LogicOp isn't supported so we disable it.
|
|
||||||
colorBlend.logicOpEnable = VK_FALSE;
|
|
||||||
colorBlend.logicOp = VK_LOGIC_OP_CLEAR;
|
|
||||||
colorBlend.attachmentCount = static_cast<uint32_t>(GetColorAttachmentsMask().count());
|
|
||||||
colorBlend.pAttachments = colorBlendAttachments.data();
|
|
||||||
// The blend constant is always dynamic so we fill in a dummy value
|
|
||||||
colorBlend.blendConstants[0] = 0.0f;
|
|
||||||
colorBlend.blendConstants[1] = 0.0f;
|
|
||||||
colorBlend.blendConstants[2] = 0.0f;
|
|
||||||
colorBlend.blendConstants[3] = 0.0f;
|
|
||||||
|
|
||||||
// Tag all state as dynamic but stencil masks and depth bias.
|
// Tag all state as dynamic but stencil masks and depth bias.
|
||||||
VkDynamicState dynamicStates[] = {
|
VkDynamicState dynamicStates[] = {
|
||||||
|
@ -491,8 +506,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||||
createInfo.pNext = nullptr;
|
createInfo.pNext = nullptr;
|
||||||
createInfo.flags = 0;
|
createInfo.flags = 0;
|
||||||
createInfo.stageCount = 2;
|
createInfo.stageCount = stageCount;
|
||||||
createInfo.pStages = shaderStages;
|
createInfo.pStages = shaderStages.data();
|
||||||
createInfo.pVertexInputState = &vertexInputCreateInfo;
|
createInfo.pVertexInputState = &vertexInputCreateInfo;
|
||||||
createInfo.pInputAssemblyState = &inputAssembly;
|
createInfo.pInputAssemblyState = &inputAssembly;
|
||||||
createInfo.pTessellationState = nullptr;
|
createInfo.pTessellationState = nullptr;
|
||||||
|
@ -500,7 +515,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
createInfo.pRasterizationState = &rasterization;
|
createInfo.pRasterizationState = &rasterization;
|
||||||
createInfo.pMultisampleState = &multisample;
|
createInfo.pMultisampleState = &multisample;
|
||||||
createInfo.pDepthStencilState = &depthStencilState;
|
createInfo.pDepthStencilState = &depthStencilState;
|
||||||
createInfo.pColorBlendState = &colorBlend;
|
createInfo.pColorBlendState =
|
||||||
|
(GetStageMask() & wgpu::ShaderStage::Fragment) ? &colorBlend : nullptr;
|
||||||
createInfo.pDynamicState = &dynamic;
|
createInfo.pDynamicState = &dynamic;
|
||||||
createInfo.layout = ToBackend(GetLayout())->GetHandle();
|
createInfo.layout = ToBackend(GetLayout())->GetHandle();
|
||||||
createInfo.renderPass = renderPass;
|
createInfo.renderPass = renderPass;
|
||||||
|
|
|
@ -358,6 +358,7 @@ source_set("dawn_end2end_tests_sources") {
|
||||||
"end2end/TextureViewTests.cpp",
|
"end2end/TextureViewTests.cpp",
|
||||||
"end2end/TextureZeroInitTests.cpp",
|
"end2end/TextureZeroInitTests.cpp",
|
||||||
"end2end/VertexFormatTests.cpp",
|
"end2end/VertexFormatTests.cpp",
|
||||||
|
"end2end/VertexOnlyRenderPipelineTests.cpp",
|
||||||
"end2end/VertexStateTests.cpp",
|
"end2end/VertexStateTests.cpp",
|
||||||
"end2end/ViewportOrientationTests.cpp",
|
"end2end/ViewportOrientationTests.cpp",
|
||||||
"end2end/ViewportTests.cpp",
|
"end2end/ViewportTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,319 @@
|
||||||
|
// Copyright 2021 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 "utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
|
||||||
|
constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
constexpr uint32_t kRTWidth = 4;
|
||||||
|
constexpr uint32_t kRTHeight = 1;
|
||||||
|
|
||||||
|
class VertexOnlyRenderPipelineTest : public DawnTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
DawnTest::SetUp();
|
||||||
|
|
||||||
|
vertexBuffer =
|
||||||
|
utils::CreateBufferFromData<float>(device, wgpu::BufferUsage::Vertex,
|
||||||
|
{// The middle back line
|
||||||
|
-0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 1.0f,
|
||||||
|
|
||||||
|
// The right front line
|
||||||
|
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
|
||||||
|
|
||||||
|
// The whole in-between line
|
||||||
|
-1.0f, 0.0f, 0.5f, 1.0f, 1.0f, 0.0f, 0.5f, 1.0f});
|
||||||
|
|
||||||
|
// Create a color texture as render target
|
||||||
|
{
|
||||||
|
wgpu::TextureDescriptor descriptor;
|
||||||
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
|
descriptor.size = {kRTWidth, kRTHeight};
|
||||||
|
descriptor.format = kColorFormat;
|
||||||
|
descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
renderTargetColor = device.CreateTexture(&descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a DepthStencilView for vertex-only pipeline to write and full pipeline to read
|
||||||
|
{
|
||||||
|
wgpu::TextureDescriptor descriptor;
|
||||||
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
|
descriptor.size = {kRTWidth, kRTHeight};
|
||||||
|
descriptor.format = kDepthStencilFormat;
|
||||||
|
descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
depthStencilTexture = device.CreateTexture(&descriptor);
|
||||||
|
depthStencilView = depthStencilTexture.CreateView();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The vertex-only render pass to modify the depth and stencil
|
||||||
|
renderPassDescNoColor = utils::ComboRenderPassDescriptor({}, depthStencilView);
|
||||||
|
renderPassDescNoColor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
|
||||||
|
renderPassDescNoColor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
|
||||||
|
|
||||||
|
// The complete render pass to read the depth and stencil and draw to color attachment
|
||||||
|
renderPassDescWithColor =
|
||||||
|
utils::ComboRenderPassDescriptor({renderTargetColor.CreateView()}, depthStencilView);
|
||||||
|
renderPassDescWithColor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
|
||||||
|
renderPassDescWithColor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
|
||||||
|
|
||||||
|
// Create a vertex-only render pipeline that only modify the depth in DepthStencilView, and
|
||||||
|
// ignore the stencil component
|
||||||
|
depthPipelineNoFragment =
|
||||||
|
CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Keep,
|
||||||
|
wgpu::CompareFunction::Always, true, false);
|
||||||
|
depthPipelineWithFragment =
|
||||||
|
CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Keep,
|
||||||
|
wgpu::CompareFunction::Always, true, true);
|
||||||
|
|
||||||
|
// Create a vertex-only render pipeline that only modify the stencil in DepthStencilView,
|
||||||
|
// and ignore the depth component
|
||||||
|
stencilPipelineNoFragment =
|
||||||
|
CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Replace,
|
||||||
|
wgpu::CompareFunction::Always, false, false);
|
||||||
|
stencilPipelineWithFragment =
|
||||||
|
CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Replace,
|
||||||
|
wgpu::CompareFunction::Always, false, true);
|
||||||
|
|
||||||
|
// Create a complete render pipeline that do both depth and stencil tests, and draw to color
|
||||||
|
// attachment
|
||||||
|
fullPipeline =
|
||||||
|
CreateRenderPipeline(wgpu::CompareFunction::Equal, wgpu::StencilOperation::Keep,
|
||||||
|
wgpu::CompareFunction::GreaterEqual, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::RenderPipeline CreateRenderPipeline(
|
||||||
|
wgpu::CompareFunction stencilCompare = wgpu::CompareFunction::Always,
|
||||||
|
wgpu::StencilOperation stencilPassOp = wgpu::StencilOperation::Keep,
|
||||||
|
wgpu::CompareFunction depthCompare = wgpu::CompareFunction::Always,
|
||||||
|
bool writeDepth = false,
|
||||||
|
bool useFragment = true) {
|
||||||
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
||||||
|
[[stage(vertex)]]
|
||||||
|
fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
|
||||||
|
return pos;
|
||||||
|
})");
|
||||||
|
|
||||||
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
||||||
|
[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 1.0, 0.0, 1.0);
|
||||||
|
})");
|
||||||
|
|
||||||
|
utils::ComboRenderPipelineDescriptor descriptor;
|
||||||
|
descriptor.primitive.topology = wgpu::PrimitiveTopology::LineList;
|
||||||
|
|
||||||
|
descriptor.vertex.module = vsModule;
|
||||||
|
descriptor.vertex.bufferCount = 1;
|
||||||
|
descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
|
||||||
|
descriptor.cBuffers[0].attributeCount = 1;
|
||||||
|
descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
|
||||||
|
|
||||||
|
descriptor.cFragment.module = fsModule;
|
||||||
|
descriptor.cTargets[0].format = kColorFormat;
|
||||||
|
|
||||||
|
wgpu::DepthStencilState* depthStencil = descriptor.EnableDepthStencil(kDepthStencilFormat);
|
||||||
|
|
||||||
|
depthStencil->stencilFront.compare = stencilCompare;
|
||||||
|
depthStencil->stencilBack.compare = stencilCompare;
|
||||||
|
depthStencil->stencilFront.passOp = stencilPassOp;
|
||||||
|
depthStencil->stencilBack.passOp = stencilPassOp;
|
||||||
|
depthStencil->depthWriteEnabled = writeDepth;
|
||||||
|
depthStencil->depthCompare = depthCompare;
|
||||||
|
|
||||||
|
if (!useFragment) {
|
||||||
|
descriptor.fragment = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.CreateRenderPipeline(&descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearAttachment(const wgpu::CommandEncoder& encoder) {
|
||||||
|
utils::ComboRenderPassDescriptor clearPass =
|
||||||
|
utils::ComboRenderPassDescriptor({renderTargetColor.CreateView()}, depthStencilView);
|
||||||
|
clearPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
|
||||||
|
clearPass.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
|
||||||
|
clearPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
|
||||||
|
clearPass.cDepthStencilAttachmentInfo.clearStencil = 0x0;
|
||||||
|
for (auto& t : clearPass.cColorAttachments) {
|
||||||
|
t.loadOp = wgpu::LoadOp::Clear;
|
||||||
|
t.clearColor = {0.0, 0.0, 0.0, 0.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pass = encoder.BeginRenderPass(&clearPass);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render resource
|
||||||
|
wgpu::Buffer vertexBuffer;
|
||||||
|
// Render target
|
||||||
|
wgpu::Texture depthStencilTexture;
|
||||||
|
wgpu::TextureView depthStencilView;
|
||||||
|
wgpu::Texture renderTargetColor;
|
||||||
|
// Render result
|
||||||
|
const RGBA8 filled = RGBA8(0, 255, 0, 255);
|
||||||
|
const RGBA8 notFilled = RGBA8(0, 0, 0, 0);
|
||||||
|
// Render pass
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescNoColor{};
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescWithColor{};
|
||||||
|
// Render pipeline
|
||||||
|
wgpu::RenderPipeline stencilPipelineNoFragment;
|
||||||
|
wgpu::RenderPipeline stencilPipelineWithFragment;
|
||||||
|
wgpu::RenderPipeline depthPipelineNoFragment;
|
||||||
|
wgpu::RenderPipeline depthPipelineWithFragment;
|
||||||
|
wgpu::RenderPipeline fullPipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that a vertex-only render pipeline modify the stencil attachment as same as a complete
|
||||||
|
// render pipeline do.
|
||||||
|
TEST_P(VertexOnlyRenderPipelineTest, Stencil) {
|
||||||
|
auto doStencilTest = [&](const wgpu::RenderPassDescriptor* renderPass,
|
||||||
|
const wgpu::RenderPipeline& pipeline,
|
||||||
|
const RGBA8& colorExpect) -> void {
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
ClearAttachment(encoder);
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass);
|
||||||
|
pass.SetPipeline(pipeline);
|
||||||
|
// Set the stencil reference to a arbitrary value
|
||||||
|
pass.SetStencilReference(0x42);
|
||||||
|
pass.SetVertexBuffer(0, vertexBuffer);
|
||||||
|
// Draw the whole line
|
||||||
|
pass.Draw(2, 1, 4, 0);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 0, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 1, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 2, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 3, 0);
|
||||||
|
|
||||||
|
// Test that the stencil is set to the chosen value
|
||||||
|
ExpectAttachmentStencilTestData(depthStencilTexture, kDepthStencilFormat, 4, 1, 0, 0, 0x42);
|
||||||
|
};
|
||||||
|
|
||||||
|
doStencilTest(&renderPassDescWithColor, stencilPipelineWithFragment, filled);
|
||||||
|
doStencilTest(&renderPassDescNoColor, stencilPipelineNoFragment, notFilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a vertex-only render pipeline modify the depth attachment as same as a complete render
|
||||||
|
// pipeline do.
|
||||||
|
TEST_P(VertexOnlyRenderPipelineTest, Depth) {
|
||||||
|
auto doStencilTest = [&](const wgpu::RenderPassDescriptor* renderPass,
|
||||||
|
const wgpu::RenderPipeline& pipeline,
|
||||||
|
const RGBA8& colorExpect) -> void {
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
ClearAttachment(encoder);
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass);
|
||||||
|
pass.SetPipeline(pipeline);
|
||||||
|
pass.SetStencilReference(0x0);
|
||||||
|
pass.SetVertexBuffer(0, vertexBuffer);
|
||||||
|
// Draw the whole line
|
||||||
|
pass.Draw(2, 1, 4, 0);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 0, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 1, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 2, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 3, 0);
|
||||||
|
|
||||||
|
// Test that the stencil is set to the chosen value
|
||||||
|
uint8_t expectedStencil = 0;
|
||||||
|
ExpectAttachmentDepthStencilTestData(depthStencilTexture, kDepthStencilFormat, 4, 1, 0, 0,
|
||||||
|
{0.5, 0.5, 0.5, 0.5}, &expectedStencil);
|
||||||
|
};
|
||||||
|
|
||||||
|
doStencilTest(&renderPassDescWithColor, depthPipelineWithFragment, filled);
|
||||||
|
doStencilTest(&renderPassDescNoColor, depthPipelineNoFragment, notFilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that vertex-only render pipelines and complete render pipelines cooperate correctly in a
|
||||||
|
// single encoder, each in a render pass
|
||||||
|
// In this test we first draw with a vertex-only pipeline to set up stencil in a region, than draw
|
||||||
|
// with another vertex-only pipeline to modify depth in another region, and finally draw with a
|
||||||
|
// complete pipeline with depth and stencil tests enabled. We check the color result of the final
|
||||||
|
// draw, and make sure that it correctly use the stencil and depth result set in previous
|
||||||
|
// vertex-only pipelines.
|
||||||
|
TEST_P(VertexOnlyRenderPipelineTest, MultiplePass) {
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
ClearAttachment(encoder);
|
||||||
|
|
||||||
|
// Use the stencil pipeline to set the stencil on the middle
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescNoColor);
|
||||||
|
pass.SetStencilReference(0x1);
|
||||||
|
pass.SetPipeline(stencilPipelineNoFragment);
|
||||||
|
pass.SetVertexBuffer(0, vertexBuffer);
|
||||||
|
// Draw the middle line
|
||||||
|
pass.Draw(2, 1, 0, 0);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the depth pipeline to set the depth on the right
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescNoColor);
|
||||||
|
pass.SetStencilReference(0x0);
|
||||||
|
pass.SetPipeline(depthPipelineNoFragment);
|
||||||
|
pass.SetVertexBuffer(0, vertexBuffer);
|
||||||
|
// Draw the right line
|
||||||
|
pass.Draw(2, 1, 2, 0);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the complete pipeline to draw with depth and stencil tests
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescWithColor);
|
||||||
|
pass.SetStencilReference(0x1);
|
||||||
|
pass.SetPipeline(fullPipeline);
|
||||||
|
pass.SetVertexBuffer(0, vertexBuffer);
|
||||||
|
// Draw the full line with depth and stencil tests
|
||||||
|
pass.Draw(2, 1, 4, 0);
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
// Only the middle left pixel should pass both stencil and depth tests
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 0, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(filled, renderTargetColor, 1, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 2, 0);
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(VertexOnlyRenderPipelineTest,
|
||||||
|
D3D12Backend(),
|
||||||
|
D3D12Backend({"use_dummy_fragment_in_vertex_only_pipeline"}),
|
||||||
|
MetalBackend(),
|
||||||
|
MetalBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
|
||||||
|
OpenGLBackend(),
|
||||||
|
OpenGLBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
|
||||||
|
OpenGLESBackend(),
|
||||||
|
OpenGLESBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
|
||||||
|
VulkanBackend(),
|
||||||
|
VulkanBackend({"use_dummy_fragment_in_vertex_only_pipeline"}));
|
|
@ -497,9 +497,9 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) {
|
||||||
wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor;
|
wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor;
|
||||||
textureDescriptor.sampleCount = 1;
|
textureDescriptor.sampleCount = 1;
|
||||||
textureDescriptor.format = kDepthStencilFormat;
|
textureDescriptor.format = kDepthStencilFormat;
|
||||||
wgpu::Texture multisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor);
|
wgpu::Texture nonMultisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor);
|
||||||
utils::ComboRenderPassDescriptor renderPassDescriptor(
|
utils::ComboRenderPassDescriptor renderPassDescriptor(
|
||||||
{}, multisampledDepthStencilTexture.CreateView());
|
{}, nonMultisampledDepthStencilTexture.CreateView());
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
@ -510,6 +510,90 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that the vertex only pipeline must be used with a depth-stencil attachment only render pass
|
||||||
|
TEST_F(RenderPipelineValidationTest, VertexOnlyPipelineRequireDepthStencilAttachment) {
|
||||||
|
constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor baseTextureDescriptor;
|
||||||
|
baseTextureDescriptor.size = {4, 4};
|
||||||
|
baseTextureDescriptor.mipLevelCount = 1;
|
||||||
|
baseTextureDescriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
|
baseTextureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor colorTextureDescriptor = baseTextureDescriptor;
|
||||||
|
colorTextureDescriptor.format = kColorFormat;
|
||||||
|
colorTextureDescriptor.sampleCount = 1;
|
||||||
|
wgpu::Texture colorTexture = device.CreateTexture(&colorTextureDescriptor);
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor depthStencilTextureDescriptor = baseTextureDescriptor;
|
||||||
|
depthStencilTextureDescriptor.sampleCount = 1;
|
||||||
|
depthStencilTextureDescriptor.format = kDepthStencilFormat;
|
||||||
|
wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilTextureDescriptor);
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView());
|
||||||
|
|
||||||
|
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
|
||||||
|
renderPipelineDescriptor.multisample.count = 1;
|
||||||
|
renderPipelineDescriptor.vertex.module = vsModule;
|
||||||
|
|
||||||
|
renderPipelineDescriptor.fragment = nullptr;
|
||||||
|
|
||||||
|
renderPipelineDescriptor.EnableDepthStencil(kDepthStencilFormat);
|
||||||
|
|
||||||
|
wgpu::RenderPipeline vertexOnlyPipeline =
|
||||||
|
device.CreateRenderPipeline(&renderPipelineDescriptor);
|
||||||
|
|
||||||
|
// Vertex-only render pipeline can work with depth stencil attachment and no color target
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView());
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
renderPass.SetPipeline(vertexOnlyPipeline);
|
||||||
|
renderPass.EndPass();
|
||||||
|
|
||||||
|
encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex-only render pipeline must have a depth stencil attachment
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({});
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
renderPass.SetPipeline(vertexOnlyPipeline);
|
||||||
|
renderPass.EndPass();
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex-only render pipeline can not work with color target
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()},
|
||||||
|
depthStencilTexture.CreateView());
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
renderPass.SetPipeline(vertexOnlyPipeline);
|
||||||
|
renderPass.EndPass();
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex-only render pipeline can not work with color target, and must have a depth stencil
|
||||||
|
// attachment
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()});
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
renderPass.SetPipeline(vertexOnlyPipeline);
|
||||||
|
renderPass.EndPass();
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that the sample count of the render pipeline must be valid
|
// Tests that the sample count of the render pipeline must be valid
|
||||||
// when the alphaToCoverage mode is enabled.
|
// when the alphaToCoverage mode is enabled.
|
||||||
TEST_F(RenderPipelineValidationTest, AlphaToCoverageAndSampleCount) {
|
TEST_F(RenderPipelineValidationTest, AlphaToCoverageAndSampleCount) {
|
||||||
|
|
Loading…
Reference in New Issue