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)
|
||||
: mSampleCount(descriptor->multisample.count) {
|
||||
ASSERT(descriptor->fragment->targetCount <= kMaxColorAttachments);
|
||||
for (ColorAttachmentIndex i(uint8_t(0));
|
||||
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->fragment->targetCount));
|
||||
++i) {
|
||||
mColorAttachmentsSet.set(i);
|
||||
mColorFormats[i] = descriptor->fragment->targets[static_cast<uint8_t>(i)].format;
|
||||
if (descriptor->fragment != nullptr) {
|
||||
ASSERT(descriptor->fragment->targetCount <= kMaxColorAttachments);
|
||||
for (ColorAttachmentIndex i(uint8_t(0));
|
||||
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->fragment->targetCount));
|
||||
++i) {
|
||||
mColorAttachmentsSet.set(i);
|
||||
mColorFormats[i] = descriptor->fragment->targets[static_cast<uint8_t>(i)].format;
|
||||
}
|
||||
}
|
||||
if (descriptor->depthStencil != nullptr) {
|
||||
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
|
||||
// the pipeline will take another reference.
|
||||
DAWN_TRY_ASSIGN(layoutRef,
|
||||
PipelineLayoutBase::CreateDefault(device, GetStages(&descriptor)));
|
||||
PipelineLayoutBase::CreateDefault(
|
||||
device, GetRenderStagesAndSetDummyShader(device, &descriptor)));
|
||||
outDescriptor->layout = layoutRef.Get();
|
||||
}
|
||||
|
||||
|
@ -237,6 +238,21 @@ namespace dawn_native {
|
|||
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace dawn_native {
|
|||
|
||||
Ref<ComputePipelineBase> timestampComputePipeline;
|
||||
Ref<ShaderModuleBase> timestampCS;
|
||||
|
||||
Ref<ShaderModuleBase> dummyFragmentShader;
|
||||
};
|
||||
} // namespace dawn_native
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ namespace dawn_native {
|
|||
return mStages;
|
||||
}
|
||||
|
||||
wgpu::ShaderStage PipelineBase::GetStageMask() const {
|
||||
return mStageMask;
|
||||
}
|
||||
|
||||
MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t groupIndex) {
|
||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace dawn_native {
|
|||
const RequiredBufferSizes& GetMinBufferSizes() const;
|
||||
const ProgrammableStage& GetStage(SingleShaderStage stage) const;
|
||||
const PerStage<ProgrammableStage>& GetAllStages() const;
|
||||
wgpu::ShaderStage GetStageMask() const;
|
||||
|
||||
ResultOrError<Ref<BindGroupLayoutBase>> GetBindGroupLayout(uint32_t groupIndex);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "dawn_native/ChainUtils_autogen.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/InternalPipelineStore.h"
|
||||
#include "dawn_native/ObjectContentHasher.h"
|
||||
#include "dawn_native/ValidationUtils_autogen.h"
|
||||
#include "dawn_native/VertexFormat.h"
|
||||
|
@ -456,11 +457,6 @@ namespace dawn_native {
|
|||
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(ValidatePrimitiveState(device, &descriptor->primitive));
|
||||
|
@ -471,25 +467,36 @@ namespace dawn_native {
|
|||
|
||||
DAWN_TRY(ValidateMultisampleState(&descriptor->multisample));
|
||||
|
||||
ASSERT(descriptor->fragment != nullptr);
|
||||
DAWN_TRY(ValidateFragmentState(device, descriptor->fragment, descriptor->layout));
|
||||
if (descriptor->fragment != nullptr) {
|
||||
DAWN_TRY(ValidateFragmentState(device, descriptor->fragment, descriptor->layout));
|
||||
|
||||
if (descriptor->fragment->targetCount == 0 && !descriptor->depthStencil) {
|
||||
return DAWN_VALIDATION_ERROR("Should have at least one color target or a depthStencil");
|
||||
if (descriptor->fragment->targetCount == 0 && !descriptor->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 {};
|
||||
}
|
||||
|
||||
std::vector<StageAndDescriptor> GetStages(const RenderPipelineDescriptor* descriptor) {
|
||||
std::vector<StageAndDescriptor> GetRenderStagesAndSetDummyShader(
|
||||
DeviceBase* device,
|
||||
const RenderPipelineDescriptor* descriptor) {
|
||||
std::vector<StageAndDescriptor> stages;
|
||||
stages.push_back(
|
||||
{SingleShaderStage::Vertex, descriptor->vertex.module, descriptor->vertex.entryPoint});
|
||||
if (descriptor->fragment != nullptr) {
|
||||
stages.push_back({SingleShaderStage::Fragment, descriptor->fragment->module,
|
||||
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;
|
||||
}
|
||||
|
@ -512,10 +519,7 @@ namespace dawn_native {
|
|||
: PipelineBase(device,
|
||||
descriptor->layout,
|
||||
descriptor->label,
|
||||
{{SingleShaderStage::Vertex, descriptor->vertex.module,
|
||||
descriptor->vertex.entryPoint},
|
||||
{SingleShaderStage::Fragment, descriptor->fragment->module,
|
||||
descriptor->fragment->entryPoint}}),
|
||||
GetRenderStagesAndSetDummyShader(device, descriptor)),
|
||||
mAttachmentState(device->GetOrCreateAttachmentState(descriptor)) {
|
||||
mVertexBufferCount = descriptor->vertex.bufferCount;
|
||||
const VertexBufferLayout* buffers = descriptor->vertex.buffers;
|
||||
|
@ -597,6 +601,9 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
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 =
|
||||
&descriptor->fragment->targets[static_cast<uint8_t>(i)];
|
||||
mTargets[i] = *target;
|
||||
|
@ -947,5 +954,4 @@ namespace dawn_native {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -57,7 +57,9 @@ namespace dawn_native {
|
|||
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
|
||||
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);
|
||||
|
||||
|
|
|
@ -222,6 +222,12 @@ namespace dawn_native {
|
|||
"Disables mipmaps for r8unorm and rg8unorm textures, which are known on some drivers "
|
||||
"to not clear correctly.",
|
||||
"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.
|
||||
}};
|
||||
} // anonymous namespace
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace dawn_native {
|
|||
DisableSymbolRenaming,
|
||||
UseUserDefinedLabelsInBackend,
|
||||
DisableR8RG8Mipmaps,
|
||||
UseDummyFragmentInVertexOnlyPipeline,
|
||||
|
||||
EnumCount,
|
||||
InvalidEnum = EnumCount,
|
||||
|
|
|
@ -340,27 +340,19 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC descriptorD3D12 = {};
|
||||
|
||||
const ProgrammableStage& vertexStage = GetStage(SingleShaderStage::Vertex);
|
||||
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<ProgrammableStage> pipelineStages = GetAllStages();
|
||||
|
||||
PerStage<D3D12_SHADER_BYTECODE*> shaders;
|
||||
shaders[SingleShaderStage::Vertex] = &descriptorD3D12.VS;
|
||||
shaders[SingleShaderStage::Fragment] = &descriptorD3D12.PS;
|
||||
|
||||
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],
|
||||
modules[stage]->Compile(entryPoints[stage], stage,
|
||||
ToBackend(GetLayout()), compileFlags));
|
||||
ToBackend(pipelineStages[stage].module)
|
||||
->Compile(pipelineStages[stage].entryPoint.c_str(), stage,
|
||||
ToBackend(GetLayout()), compileFlags));
|
||||
*shaders[stage] = compiledShader[stage].GetD3D12ShaderBytecode();
|
||||
}
|
||||
|
||||
|
|
|
@ -214,6 +214,16 @@ namespace dawn_native { namespace metal {
|
|||
if (gpu_info::IsIntel(pciInfo.vendorId)) {
|
||||
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(
|
||||
|
|
|
@ -338,8 +338,9 @@ namespace dawn_native { namespace metal {
|
|||
}
|
||||
descriptorMTL.vertexDescriptor = vertexDesc.Get();
|
||||
|
||||
const ProgrammableStage& vertexStage = GetStage(SingleShaderStage::Vertex);
|
||||
ShaderModule* vertexModule = ToBackend(vertexStage.module.Get());
|
||||
const PerStage<ProgrammableStage>& allStages = GetAllStages();
|
||||
const ProgrammableStage& vertexStage = allStages[wgpu::ShaderStage::Vertex];
|
||||
ShaderModule* vertexModule = ToBackend(vertexStage.module).Get();
|
||||
const char* vertexEntryPoint = vertexStage.entryPoint.c_str();
|
||||
ShaderModule::MetalFunctionData vertexData;
|
||||
DAWN_TRY(vertexModule->CreateFunction(vertexEntryPoint, SingleShaderStage::Vertex,
|
||||
|
@ -351,17 +352,28 @@ namespace dawn_native { namespace metal {
|
|||
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Vertex;
|
||||
}
|
||||
|
||||
const ProgrammableStage& fragmentStage = GetStage(SingleShaderStage::Fragment);
|
||||
ShaderModule* fragmentModule = ToBackend(fragmentStage.module.Get());
|
||||
const char* fragmentEntryPoint = fragmentStage.entryPoint.c_str();
|
||||
ShaderModule::MetalFunctionData fragmentData;
|
||||
DAWN_TRY(fragmentModule->CreateFunction(fragmentEntryPoint, SingleShaderStage::Fragment,
|
||||
ToBackend(GetLayout()), &fragmentData,
|
||||
GetSampleMask()));
|
||||
if (GetStageMask() & wgpu::ShaderStage::Fragment) {
|
||||
const ProgrammableStage& fragmentStage = allStages[wgpu::ShaderStage::Fragment];
|
||||
ShaderModule* fragmentModule = ToBackend(fragmentStage.module).Get();
|
||||
const char* fragmentEntryPoint = fragmentStage.entryPoint.c_str();
|
||||
ShaderModule::MetalFunctionData fragmentData;
|
||||
DAWN_TRY(fragmentModule->CreateFunction(fragmentEntryPoint, SingleShaderStage::Fragment,
|
||||
ToBackend(GetLayout()), &fragmentData,
|
||||
GetSampleMask()));
|
||||
|
||||
descriptorMTL.fragmentFunction = fragmentData.function.Get();
|
||||
if (fragmentData.needsStorageBufferLength) {
|
||||
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Fragment;
|
||||
descriptorMTL.fragmentFunction = fragmentData.function.Get();
|
||||
if (fragmentData.needsStorageBufferLength) {
|
||||
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()) {
|
||||
|
@ -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.sampleCount = GetSampleCount();
|
||||
descriptorMTL.alphaToCoverageEnabled = IsAlphaToCoverageEnabled();
|
||||
|
|
|
@ -99,6 +99,8 @@ namespace dawn_native { namespace opengl {
|
|||
SetToggle(Toggle::DisableDepthStencilRead, !supportsDepthStencilRead);
|
||||
SetToggle(Toggle::DisableSampleVariables, !supportsSampleVariables);
|
||||
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) {
|
||||
|
|
|
@ -330,33 +330,43 @@ namespace dawn_native { namespace vulkan {
|
|||
MaybeError RenderPipeline::Initialize() {
|
||||
Device* device = ToBackend(GetDevice());
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[2];
|
||||
{
|
||||
// Generate a new VkShaderModule with BindingRemapper tint transform for each
|
||||
// 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();
|
||||
// There are at most 2 shader stages in render pipeline, i.e. vertex and fragment
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
||||
uint32_t stageCount = 0;
|
||||
|
||||
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shaderStages[1].pNext = nullptr;
|
||||
shaderStages[1].flags = 0;
|
||||
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderStages[1].pSpecializationInfo = nullptr;
|
||||
shaderStages[1].pName = fragmentStage.entryPoint.c_str();
|
||||
for (auto stage : IterateStages(this->GetStageMask())) {
|
||||
VkPipelineShaderStageCreateInfo shaderStage;
|
||||
|
||||
DAWN_TRY_ASSIGN(shaderStage.module,
|
||||
ToBackend(GetStage(stage).module)
|
||||
->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;
|
||||
|
@ -427,30 +437,35 @@ namespace dawn_native { namespace vulkan {
|
|||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
||||
ComputeDepthStencilDesc(GetDepthStencilState());
|
||||
|
||||
// Initialize the "blend state info" that will be chained in the "create info" from the data
|
||||
// pre-computed in the ColorState
|
||||
VkPipelineColorBlendStateCreateInfo colorBlend;
|
||||
// colorBlend may hold pointers to elements in colorBlendAttachments, so it must have a
|
||||
// definition scope as same as colorBlend
|
||||
ityp::array<ColorAttachmentIndex, VkPipelineColorBlendAttachmentState, kMaxColorAttachments>
|
||||
colorBlendAttachments;
|
||||
const auto& fragmentOutputsWritten =
|
||||
GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputsWritten;
|
||||
for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
|
||||
const ColorTargetState* target = GetColorTargetState(i);
|
||||
colorBlendAttachments[i] = ComputeColorDesc(target, fragmentOutputsWritten[i]);
|
||||
if (GetStageMask() & wgpu::ShaderStage::Fragment) {
|
||||
// Initialize the "blend state info" that will be chained in the "create info" from the
|
||||
// data pre-computed in the ColorState
|
||||
const auto& fragmentOutputsWritten =
|
||||
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.
|
||||
VkDynamicState dynamicStates[] = {
|
||||
|
@ -491,8 +506,8 @@ namespace dawn_native { namespace vulkan {
|
|||
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
createInfo.pNext = nullptr;
|
||||
createInfo.flags = 0;
|
||||
createInfo.stageCount = 2;
|
||||
createInfo.pStages = shaderStages;
|
||||
createInfo.stageCount = stageCount;
|
||||
createInfo.pStages = shaderStages.data();
|
||||
createInfo.pVertexInputState = &vertexInputCreateInfo;
|
||||
createInfo.pInputAssemblyState = &inputAssembly;
|
||||
createInfo.pTessellationState = nullptr;
|
||||
|
@ -500,7 +515,8 @@ namespace dawn_native { namespace vulkan {
|
|||
createInfo.pRasterizationState = &rasterization;
|
||||
createInfo.pMultisampleState = &multisample;
|
||||
createInfo.pDepthStencilState = &depthStencilState;
|
||||
createInfo.pColorBlendState = &colorBlend;
|
||||
createInfo.pColorBlendState =
|
||||
(GetStageMask() & wgpu::ShaderStage::Fragment) ? &colorBlend : nullptr;
|
||||
createInfo.pDynamicState = &dynamic;
|
||||
createInfo.layout = ToBackend(GetLayout())->GetHandle();
|
||||
createInfo.renderPass = renderPass;
|
||||
|
|
|
@ -358,6 +358,7 @@ source_set("dawn_end2end_tests_sources") {
|
|||
"end2end/TextureViewTests.cpp",
|
||||
"end2end/TextureZeroInitTests.cpp",
|
||||
"end2end/VertexFormatTests.cpp",
|
||||
"end2end/VertexOnlyRenderPipelineTests.cpp",
|
||||
"end2end/VertexStateTests.cpp",
|
||||
"end2end/ViewportOrientationTests.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;
|
||||
textureDescriptor.sampleCount = 1;
|
||||
textureDescriptor.format = kDepthStencilFormat;
|
||||
wgpu::Texture multisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor);
|
||||
wgpu::Texture nonMultisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor);
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor(
|
||||
{}, multisampledDepthStencilTexture.CreateView());
|
||||
{}, nonMultisampledDepthStencilTexture.CreateView());
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
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
|
||||
// when the alphaToCoverage mode is enabled.
|
||||
TEST_F(RenderPipelineValidationTest, AlphaToCoverageAndSampleCount) {
|
||||
|
|
Loading…
Reference in New Issue