Support multisampled rendering on OpenGL
This patch adds the support of multisampled rendering on OpenGL backends and the related end2end tests to test all new features implemented in this patch. BUG=dawn:56 TEST=dawn_end2end_tests Change-Id: I91e462178ee39041ef591503c33c70db511775e9 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/5880 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
9d99f90c7d
commit
0bc168ed58
1
BUILD.gn
1
BUILD.gn
|
@ -643,6 +643,7 @@ test("dawn_end2end_tests") {
|
||||||
"src/tests/end2end/FenceTests.cpp",
|
"src/tests/end2end/FenceTests.cpp",
|
||||||
"src/tests/end2end/IndexFormatTests.cpp",
|
"src/tests/end2end/IndexFormatTests.cpp",
|
||||||
"src/tests/end2end/InputStateTests.cpp",
|
"src/tests/end2end/InputStateTests.cpp",
|
||||||
|
"src/tests/end2end/MultisampledRenderingTests.cpp",
|
||||||
"src/tests/end2end/PrimitiveTopologyTests.cpp",
|
"src/tests/end2end/PrimitiveTopologyTests.cpp",
|
||||||
"src/tests/end2end/PushConstantTests.cpp",
|
"src/tests/end2end/PushConstantTests.cpp",
|
||||||
"src/tests/end2end/RenderPassLoadOpTests.cpp",
|
"src/tests/end2end/RenderPassLoadOpTests.cpp",
|
||||||
|
|
|
@ -467,6 +467,11 @@ namespace dawn_native {
|
||||||
return mDepthStencilState.format;
|
return mDepthStencilState.format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t RenderPipelineBase::GetSampleCount() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
return mSampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
bool RenderPipelineBase::IsCompatibleWith(const BeginRenderPassCmd* renderPass) const {
|
bool RenderPipelineBase::IsCompatibleWith(const BeginRenderPassCmd* renderPass) const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
// TODO(cwallez@chromium.org): This is called on every SetPipeline command. Optimize it for
|
// TODO(cwallez@chromium.org): This is called on every SetPipeline command. Optimize it for
|
||||||
|
|
|
@ -59,6 +59,7 @@ namespace dawn_native {
|
||||||
bool HasDepthStencilAttachment() const;
|
bool HasDepthStencilAttachment() const;
|
||||||
dawn::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
|
dawn::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
|
||||||
dawn::TextureFormat GetDepthStencilFormat() const;
|
dawn::TextureFormat GetDepthStencilFormat() const;
|
||||||
|
uint32_t GetSampleCount() const;
|
||||||
|
|
||||||
// A pipeline can be used in a render pass if its attachment info matches the actual
|
// A pipeline can be used in a render pass if its attachment info matches the actual
|
||||||
// attachments in the render pass. This returns whether it is the case.
|
// attachments in the render pass. This returns whether it is the case.
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace dawn_native { namespace opengl {
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
|
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
|
||||||
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
mPCIInfo.name = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
mPCIInfo.name = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,6 +322,59 @@ namespace dawn_native { namespace opengl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResolveMultisampledRenderTargets(const BeginRenderPassCmd* renderPass) {
|
||||||
|
ASSERT(renderPass != nullptr);
|
||||||
|
|
||||||
|
GLuint readFbo = 0;
|
||||||
|
GLuint writeFbo = 0;
|
||||||
|
|
||||||
|
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
|
||||||
|
if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) {
|
||||||
|
if (readFbo == 0) {
|
||||||
|
ASSERT(writeFbo == 0);
|
||||||
|
glGenFramebuffers(1, &readFbo);
|
||||||
|
glGenFramebuffers(1, &writeFbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextureBase* colorTexture =
|
||||||
|
renderPass->colorAttachments[i].view->GetTexture();
|
||||||
|
ASSERT(colorTexture->IsMultisampledTexture());
|
||||||
|
ASSERT(colorTexture->GetArrayLayers() == 1);
|
||||||
|
ASSERT(renderPass->colorAttachments[i].view->GetBaseMipLevel() == 0);
|
||||||
|
|
||||||
|
GLuint colorHandle = ToBackend(colorTexture)->GetHandle();
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
ToBackend(colorTexture)->GetGLTarget(), colorHandle, 0);
|
||||||
|
|
||||||
|
const TextureBase* resolveTexture =
|
||||||
|
renderPass->colorAttachments[i].resolveTarget->GetTexture();
|
||||||
|
GLuint resolveTextureHandle = ToBackend(resolveTexture)->GetHandle();
|
||||||
|
GLuint resolveTargetMipmapLevel =
|
||||||
|
renderPass->colorAttachments[i].resolveTarget->GetBaseMipLevel();
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, writeFbo);
|
||||||
|
if (resolveTexture->GetArrayLayers() == 1) {
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_TEXTURE_2D, resolveTextureHandle,
|
||||||
|
resolveTargetMipmapLevel);
|
||||||
|
} else {
|
||||||
|
GLuint resolveTargetArrayLayer =
|
||||||
|
renderPass->colorAttachments[i].resolveTarget->GetBaseArrayLayer();
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
resolveTextureHandle, resolveTargetMipmapLevel,
|
||||||
|
resolveTargetArrayLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBlitFramebuffer(0, 0, renderPass->width, renderPass->height, 0, 0,
|
||||||
|
renderPass->width, renderPass->height, GL_COLOR_BUFFER_BIT,
|
||||||
|
GL_NEAREST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteFramebuffers(1, &readFbo);
|
||||||
|
glDeleteFramebuffers(1, &writeFbo);
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
|
CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
|
||||||
|
@ -548,8 +601,9 @@ namespace dawn_native { namespace opengl {
|
||||||
|
|
||||||
// Attach color buffers.
|
// Attach color buffers.
|
||||||
if (textureView->GetTexture()->GetArrayLayers() == 1) {
|
if (textureView->GetTexture()->GetArrayLayers() == 1) {
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
|
GLenum target = ToBackend(textureView->GetTexture())->GetGLTarget();
|
||||||
GL_TEXTURE_2D, texture, textureView->GetBaseMipLevel());
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target,
|
||||||
|
texture, textureView->GetBaseMipLevel());
|
||||||
} else {
|
} else {
|
||||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
|
||||||
texture, textureView->GetBaseMipLevel(),
|
texture, textureView->GetBaseMipLevel(),
|
||||||
|
@ -587,8 +641,8 @@ namespace dawn_native { namespace opengl {
|
||||||
glAttachment = GL_STENCIL_ATTACHMENT;
|
glAttachment = GL_STENCIL_ATTACHMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, glAttachment, GL_TEXTURE_2D, texture,
|
GLenum target = ToBackend(textureView->GetTexture())->GetGLTarget();
|
||||||
0);
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, glAttachment, target, texture, 0);
|
||||||
|
|
||||||
// TODO(kainino@chromium.org): the depth/stencil clears (later in
|
// TODO(kainino@chromium.org): the depth/stencil clears (later in
|
||||||
// this function) may be undefined for other texture formats.
|
// this function) may be undefined for other texture formats.
|
||||||
|
@ -656,6 +710,11 @@ namespace dawn_native { namespace opengl {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Command::EndRenderPass: {
|
case Command::EndRenderPass: {
|
||||||
mCommands.NextCommand<EndRenderPassCmd>();
|
mCommands.NextCommand<EndRenderPassCmd>();
|
||||||
|
|
||||||
|
if (renderPass->sampleCount > 1) {
|
||||||
|
ResolveMultisampledRenderTargets(renderPass);
|
||||||
|
}
|
||||||
|
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
return;
|
return;
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -24,22 +24,33 @@ namespace dawn_native { namespace opengl {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
GLenum TargetForDimensionAndArrayLayers(dawn::TextureDimension dimension,
|
GLenum TargetForTexture(const TextureDescriptor* descriptor) {
|
||||||
uint32_t arrayLayerCount) {
|
switch (descriptor->dimension) {
|
||||||
switch (dimension) {
|
|
||||||
case dawn::TextureDimension::e2D:
|
case dawn::TextureDimension::e2D:
|
||||||
return (arrayLayerCount > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
if (descriptor->arrayLayerCount > 1) {
|
||||||
|
ASSERT(descriptor->sampleCount == 1);
|
||||||
|
return GL_TEXTURE_2D_ARRAY;
|
||||||
|
} else {
|
||||||
|
if (descriptor->sampleCount > 1) {
|
||||||
|
return GL_TEXTURE_2D_MULTISAMPLE;
|
||||||
|
} else {
|
||||||
|
return GL_TEXTURE_2D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return GL_TEXTURE_2D;
|
return GL_TEXTURE_2D;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum TargetForTextureViewDimension(dawn::TextureViewDimension dimension) {
|
GLenum TargetForTextureViewDimension(dawn::TextureViewDimension dimension,
|
||||||
|
uint32_t sampleCount) {
|
||||||
switch (dimension) {
|
switch (dimension) {
|
||||||
case dawn::TextureViewDimension::e2D:
|
case dawn::TextureViewDimension::e2D:
|
||||||
return GL_TEXTURE_2D;
|
return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
|
||||||
case dawn::TextureViewDimension::e2DArray:
|
case dawn::TextureViewDimension::e2DArray:
|
||||||
|
ASSERT(sampleCount == 1);
|
||||||
return GL_TEXTURE_2D_ARRAY;
|
return GL_TEXTURE_2D_ARRAY;
|
||||||
case dawn::TextureViewDimension::Cube:
|
case dawn::TextureViewDimension::Cube:
|
||||||
return GL_TEXTURE_CUBE_MAP;
|
return GL_TEXTURE_CUBE_MAP;
|
||||||
|
@ -124,6 +135,7 @@ namespace dawn_native { namespace opengl {
|
||||||
uint32_t height = GetSize().height;
|
uint32_t height = GetSize().height;
|
||||||
uint32_t levels = GetNumMipLevels();
|
uint32_t levels = GetNumMipLevels();
|
||||||
uint32_t arrayLayers = GetArrayLayers();
|
uint32_t arrayLayers = GetArrayLayers();
|
||||||
|
uint32_t sampleCount = GetSampleCount();
|
||||||
|
|
||||||
auto formatInfo = GetGLFormatInfo(GetFormat());
|
auto formatInfo = GetGLFormatInfo(GetFormat());
|
||||||
|
|
||||||
|
@ -135,11 +147,17 @@ namespace dawn_native { namespace opengl {
|
||||||
switch (GetDimension()) {
|
switch (GetDimension()) {
|
||||||
case dawn::TextureDimension::e2D:
|
case dawn::TextureDimension::e2D:
|
||||||
if (arrayLayers > 1) {
|
if (arrayLayers > 1) {
|
||||||
|
ASSERT(!IsMultisampledTexture());
|
||||||
glTexStorage3D(mTarget, levels, formatInfo.internalFormat, width, height,
|
glTexStorage3D(mTarget, levels, formatInfo.internalFormat, width, height,
|
||||||
arrayLayers);
|
arrayLayers);
|
||||||
|
} else {
|
||||||
|
if (IsMultisampledTexture()) {
|
||||||
|
glTexStorage2DMultisample(mTarget, sampleCount, formatInfo.internalFormat,
|
||||||
|
width, height, true);
|
||||||
} else {
|
} else {
|
||||||
glTexStorage2D(mTarget, levels, formatInfo.internalFormat, width, height);
|
glTexStorage2D(mTarget, levels, formatInfo.internalFormat, width, height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
@ -155,7 +173,7 @@ namespace dawn_native { namespace opengl {
|
||||||
GLuint handle,
|
GLuint handle,
|
||||||
TextureState state)
|
TextureState state)
|
||||||
: TextureBase(device, descriptor, state), mHandle(handle) {
|
: TextureBase(device, descriptor, state), mHandle(handle) {
|
||||||
mTarget = TargetForDimensionAndArrayLayers(GetDimension(), GetArrayLayers());
|
mTarget = TargetForTexture(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture() {
|
Texture::~Texture() {
|
||||||
|
@ -183,7 +201,7 @@ namespace dawn_native { namespace opengl {
|
||||||
|
|
||||||
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
||||||
: TextureViewBase(texture, descriptor), mOwnsHandle(false) {
|
: TextureViewBase(texture, descriptor), mOwnsHandle(false) {
|
||||||
mTarget = TargetForTextureViewDimension(descriptor->dimension);
|
mTarget = TargetForTextureViewDimension(descriptor->dimension, texture->GetSampleCount());
|
||||||
|
|
||||||
if (!UsageNeedsTextureView(texture->GetUsage())) {
|
if (!UsageNeedsTextureView(texture->GetUsage())) {
|
||||||
mHandle = 0;
|
mHandle = 0;
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
// Copyright 2019 The Dawn Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "tests/DawnTest.h"
|
||||||
|
|
||||||
|
#include "common/Assert.h"
|
||||||
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "utils/DawnHelpers.h"
|
||||||
|
|
||||||
|
class MultisampledRenderingTest : public DawnTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
DawnTest::SetUp();
|
||||||
|
|
||||||
|
InitTexturesForTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitTexturesForTest() {
|
||||||
|
mMultisampledColorView =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultTextureView();
|
||||||
|
mResolveTexture = CreateTextureForOutputAttachment(kColorFormat, 1);
|
||||||
|
mResolveView = mResolveTexture.CreateDefaultTextureView();
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::RenderPipeline CreateRenderPipelineWithOneOutputForTest(bool testDepth) {
|
||||||
|
const char* kFsOneOutputWithDepth =
|
||||||
|
R"(#version 450
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
layout (std140, set = 0, binding = 0) uniform uBuffer {
|
||||||
|
vec4 color;
|
||||||
|
float depth;
|
||||||
|
};
|
||||||
|
void main() {
|
||||||
|
fragColor = color;
|
||||||
|
gl_FragDepth = depth;
|
||||||
|
})";
|
||||||
|
|
||||||
|
const char* kFsOneOutputWithoutDepth =
|
||||||
|
R"(#version 450
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
layout (std140, set = 0, binding = 0) uniform uBuffer {
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
void main() {
|
||||||
|
fragColor = color;
|
||||||
|
})";
|
||||||
|
|
||||||
|
const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
|
||||||
|
|
||||||
|
|
||||||
|
return CreateRenderPipelineForTest(fs, 1, testDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest() {
|
||||||
|
const char* kFsTwoOutputs =
|
||||||
|
R"(#version 450
|
||||||
|
layout(location = 0) out vec4 fragColor1;
|
||||||
|
layout(location = 1) out vec4 fragColor2;
|
||||||
|
layout (std140, set = 0, binding = 0) uniform uBuffer {
|
||||||
|
vec4 color1;
|
||||||
|
vec4 color2;
|
||||||
|
};
|
||||||
|
void main() {
|
||||||
|
fragColor1 = color1;
|
||||||
|
fragColor2 = color2;
|
||||||
|
})";
|
||||||
|
|
||||||
|
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::Texture CreateTextureForOutputAttachment(dawn::TextureFormat format,
|
||||||
|
uint32_t sampleCount,
|
||||||
|
uint32_t mipLevelCount = 1,
|
||||||
|
uint32_t arrayLayerCount = 1) {
|
||||||
|
dawn::TextureDescriptor descriptor;
|
||||||
|
descriptor.dimension = dawn::TextureDimension::e2D;
|
||||||
|
descriptor.size.width = kWidth << (mipLevelCount - 1);
|
||||||
|
descriptor.size.height = kHeight << (mipLevelCount - 1);
|
||||||
|
descriptor.size.depth = 1;
|
||||||
|
descriptor.arrayLayerCount = arrayLayerCount;
|
||||||
|
descriptor.sampleCount = sampleCount;
|
||||||
|
descriptor.format = format;
|
||||||
|
descriptor.mipLevelCount = mipLevelCount;
|
||||||
|
descriptor.usage =
|
||||||
|
dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc;
|
||||||
|
return device.CreateTexture(&descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeRenderPassForTest(dawn::CommandEncoder commandEncoder,
|
||||||
|
const dawn::RenderPassDescriptor& renderPass,
|
||||||
|
const dawn::RenderPipeline& pipeline,
|
||||||
|
const float* uniformData,
|
||||||
|
uint32_t uniformDataSize) {
|
||||||
|
dawn::Buffer uniformBuffer =
|
||||||
|
utils::CreateBufferFromData(device, uniformData, uniformDataSize,
|
||||||
|
dawn::BufferUsageBit::Uniform);
|
||||||
|
dawn::BindGroup bindGroup =
|
||||||
|
utils::MakeBindGroup(device, mBindGroupLayout,
|
||||||
|
{{0, uniformBuffer, 0, uniformDataSize}});
|
||||||
|
|
||||||
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
||||||
|
renderPassEncoder.SetPipeline(pipeline);
|
||||||
|
renderPassEncoder.SetBindGroup(0, bindGroup, 0, nullptr);
|
||||||
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
||||||
|
renderPassEncoder.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor CreateComboRenderPassDescriptorForTest(
|
||||||
|
std::initializer_list<dawn::TextureView> colorViews,
|
||||||
|
std::initializer_list<dawn::TextureView> resolveTargetViews,
|
||||||
|
dawn::LoadOp colorLoadOp,
|
||||||
|
dawn::LoadOp depthStencilLoadOp,
|
||||||
|
bool hasDepthStencilAttachment) {
|
||||||
|
ASSERT(colorViews.size() == resolveTargetViews.size());
|
||||||
|
|
||||||
|
constexpr dawn::Color kClearColor = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
constexpr float kClearDepth = 1.0f;
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor renderPass(colorViews);
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (const dawn::TextureView& resolveTargetView : resolveTargetViews) {
|
||||||
|
renderPass.cColorAttachmentsInfoPtr[i]->loadOp = colorLoadOp;
|
||||||
|
renderPass.cColorAttachmentsInfoPtr[i]->clearColor = kClearColor;
|
||||||
|
renderPass.cColorAttachmentsInfoPtr[i]->resolveTarget = resolveTargetView;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPass.cDepthStencilAttachmentInfo.clearDepth = kClearDepth;
|
||||||
|
renderPass.cDepthStencilAttachmentInfo.depthLoadOp = depthStencilLoadOp;
|
||||||
|
|
||||||
|
if (hasDepthStencilAttachment) {
|
||||||
|
dawn::Texture depthStencilTexture =
|
||||||
|
CreateTextureForOutputAttachment(kDepthStencilFormat, kSampleCount);
|
||||||
|
renderPass.cDepthStencilAttachmentInfo.attachment =
|
||||||
|
depthStencilTexture.CreateDefaultTextureView();
|
||||||
|
renderPass.depthStencilAttachment = &renderPass.cDepthStencilAttachmentInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyResolveTarget(const dawn::Color& inputColor,
|
||||||
|
dawn::Texture resolveTexture,
|
||||||
|
uint32_t mipmapLevel = 0,
|
||||||
|
uint32_t arrayLayer = 0) {
|
||||||
|
constexpr float kMSAACoverage = 0.5f;
|
||||||
|
|
||||||
|
// In this test we only check the pixel in the middle of the texture.
|
||||||
|
constexpr uint32_t kMiddleX = (kWidth - 1) / 2;
|
||||||
|
constexpr uint32_t kMiddleY = (kHeight - 1) / 2;
|
||||||
|
|
||||||
|
RGBA8 expectedColor;
|
||||||
|
expectedColor.r = static_cast<uint8_t>(0xFF * inputColor.r * kMSAACoverage);
|
||||||
|
expectedColor.g = static_cast<uint8_t>(0xFF * inputColor.g * kMSAACoverage);
|
||||||
|
expectedColor.b = static_cast<uint8_t>(0xFF * inputColor.b * kMSAACoverage);
|
||||||
|
expectedColor.a = static_cast<uint8_t>(0xFF * inputColor.a * kMSAACoverage);
|
||||||
|
|
||||||
|
EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, resolveTexture, kMiddleX, kMiddleY, 1, 1,
|
||||||
|
mipmapLevel, arrayLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static uint32_t kWidth = 3;
|
||||||
|
constexpr static uint32_t kHeight = 3;
|
||||||
|
constexpr static uint32_t kSampleCount = 4;
|
||||||
|
constexpr static dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm;
|
||||||
|
constexpr static dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint;
|
||||||
|
|
||||||
|
dawn::TextureView mMultisampledColorView;
|
||||||
|
dawn::Texture mResolveTexture;
|
||||||
|
dawn::TextureView mResolveView;
|
||||||
|
dawn::BindGroupLayout mBindGroupLayout;
|
||||||
|
|
||||||
|
private:
|
||||||
|
dawn::RenderPipeline CreateRenderPipelineForTest(const char* fs,
|
||||||
|
uint32_t numColorAttachments,
|
||||||
|
bool hasDepthStencilAttachment) {
|
||||||
|
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
|
||||||
|
|
||||||
|
// Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
|
||||||
|
// only two of the samples will be touched.
|
||||||
|
const char* vs =
|
||||||
|
R"(#version 450
|
||||||
|
const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(1.f, -1.f));
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
|
||||||
|
})";
|
||||||
|
pipelineDescriptor.cVertexStage.module =
|
||||||
|
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs);
|
||||||
|
|
||||||
|
pipelineDescriptor.cFragmentStage.module =
|
||||||
|
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fs);
|
||||||
|
|
||||||
|
|
||||||
|
mBindGroupLayout = utils::MakeBindGroupLayout(
|
||||||
|
device, {
|
||||||
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer},
|
||||||
|
});
|
||||||
|
dawn::PipelineLayout pipelineLayout =
|
||||||
|
utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
|
||||||
|
pipelineDescriptor.layout = pipelineLayout;
|
||||||
|
|
||||||
|
if (hasDepthStencilAttachment) {
|
||||||
|
pipelineDescriptor.cDepthStencilState.format = kDepthStencilFormat;
|
||||||
|
pipelineDescriptor.cDepthStencilState.depthWriteEnabled = true;
|
||||||
|
pipelineDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less;
|
||||||
|
pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipelineDescriptor.sampleCount = kSampleCount;
|
||||||
|
|
||||||
|
pipelineDescriptor.colorStateCount = numColorAttachments;
|
||||||
|
for (uint32_t i = 0; i < numColorAttachments; ++i) {
|
||||||
|
pipelineDescriptor.cColorStates[i]->format = kColorFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.CreateRenderPipeline(&pipelineDescriptor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test using one multisampled color attachment with resolve target can render correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) {
|
||||||
|
constexpr bool kTestDepth = false;
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
|
||||||
|
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr uint32_t kSize = sizeof(kGreen);
|
||||||
|
|
||||||
|
// Draw a green triangle.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear,
|
||||||
|
kTestDepth);
|
||||||
|
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
VerifyResolveTarget(kGreen, mResolveTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test multisampled rendering with depth test works correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) {
|
||||||
|
constexpr bool kTestDepth = true;
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
|
||||||
|
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
|
||||||
|
|
||||||
|
// In first render pass we draw a green triangle with depth value == 0.2f.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear,
|
||||||
|
true);
|
||||||
|
std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color
|
||||||
|
0.2f}; // depth
|
||||||
|
constexpr uint32_t kSize = sizeof(kUniformData);
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In second render pass we draw a red triangle with depth value == 0.5f.
|
||||||
|
// This red triangle should not be displayed because it is behind the green one that is drawn in
|
||||||
|
// the last render pass.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Load, dawn::LoadOp::Load,
|
||||||
|
kTestDepth);
|
||||||
|
|
||||||
|
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color
|
||||||
|
0.5f}; // depth
|
||||||
|
constexpr uint32_t kSize = sizeof(kUniformData);
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
// The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs
|
||||||
|
// correctly with depth test.
|
||||||
|
VerifyResolveTarget(kGreen, mResolveTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test rendering into a multisampled color attachment and doing MSAA resolve in another render pass
|
||||||
|
// works correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) {
|
||||||
|
constexpr bool kTestDepth = false;
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
|
||||||
|
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr uint32_t kSize = sizeof(kGreen);
|
||||||
|
|
||||||
|
// In first render pass we draw a green triangle and do not set the resolve target.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {nullptr}, dawn::LoadOp::Clear, dawn::LoadOp::Clear,
|
||||||
|
kTestDepth);
|
||||||
|
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In second render pass we ony do MSAA resolve with no draw call.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Load, dawn::LoadOp::Load,
|
||||||
|
kTestDepth);
|
||||||
|
|
||||||
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
||||||
|
renderPassEncoder.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
VerifyResolveTarget(kGreen, mResolveTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test doing MSAA resolve into multiple resolve targets works correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) {
|
||||||
|
dawn::TextureView multisampledColorView2 =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultTextureView();
|
||||||
|
dawn::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1);
|
||||||
|
dawn::TextureView resolveView2 = resolveTexture2.CreateDefaultTextureView();
|
||||||
|
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest();
|
||||||
|
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
|
||||||
|
constexpr bool kTestDepth = false;
|
||||||
|
|
||||||
|
// Draw a red triangle to the first color attachment, and a blue triangle to the second color
|
||||||
|
// attachment, and do MSAA resolve on two render targets in one render pass.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
|
||||||
|
dawn::LoadOp::Clear, dawn::LoadOp::Clear, kTestDepth);
|
||||||
|
|
||||||
|
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1
|
||||||
|
kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2
|
||||||
|
constexpr uint32_t kSize = sizeof(kUniformData);
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
VerifyResolveTarget(kRed, mResolveTexture);
|
||||||
|
VerifyResolveTarget(kGreen, resolveTexture2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using a layer of a 2D texture as resolve target works correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) {
|
||||||
|
constexpr uint32_t kBaseMipLevel = 2;
|
||||||
|
|
||||||
|
dawn::TextureViewDescriptor textureViewDescriptor;
|
||||||
|
textureViewDescriptor.dimension = dawn::TextureViewDimension::e2D;
|
||||||
|
textureViewDescriptor.format = kColorFormat;
|
||||||
|
textureViewDescriptor.baseArrayLayer = 0;
|
||||||
|
textureViewDescriptor.arrayLayerCount = 1;
|
||||||
|
textureViewDescriptor.mipLevelCount = 1;
|
||||||
|
textureViewDescriptor.baseMipLevel = kBaseMipLevel;
|
||||||
|
|
||||||
|
dawn::Texture resolveTexture =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel + 1, 1);
|
||||||
|
dawn::TextureView resolveView = resolveTexture.CreateTextureView(&textureViewDescriptor);
|
||||||
|
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr uint32_t kSize = sizeof(kGreen);
|
||||||
|
constexpr bool kTestDepth = false;
|
||||||
|
|
||||||
|
// Draw a green triangle and do MSAA resolve.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView}, {resolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear,
|
||||||
|
kTestDepth);
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
|
||||||
|
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
VerifyResolveTarget(kGreen, resolveTexture, kBaseMipLevel, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using a level or a layer of a 2D array texture as resolve target works correctly.
|
||||||
|
TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
|
||||||
|
dawn::TextureView multisampledColorView2 =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultTextureView();
|
||||||
|
|
||||||
|
dawn::TextureViewDescriptor baseTextureViewDescriptor;
|
||||||
|
baseTextureViewDescriptor.dimension = dawn::TextureViewDimension::e2D;
|
||||||
|
baseTextureViewDescriptor.format = kColorFormat;
|
||||||
|
baseTextureViewDescriptor.arrayLayerCount = 1;
|
||||||
|
baseTextureViewDescriptor.mipLevelCount = 1;
|
||||||
|
|
||||||
|
// Create resolveTexture1 with only 1 mipmap level.
|
||||||
|
constexpr uint32_t kBaseArrayLayer1 = 2;
|
||||||
|
constexpr uint32_t kBaseMipLevel1 = 0;
|
||||||
|
dawn::Texture resolveTexture1 =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel1 + 1, kBaseArrayLayer1 + 1);
|
||||||
|
dawn::TextureViewDescriptor resolveViewDescriptor1 = baseTextureViewDescriptor;
|
||||||
|
resolveViewDescriptor1.baseArrayLayer = kBaseArrayLayer1;
|
||||||
|
resolveViewDescriptor1.baseMipLevel = kBaseMipLevel1;
|
||||||
|
dawn::TextureView resolveView1 = resolveTexture1.CreateTextureView(&resolveViewDescriptor1);
|
||||||
|
|
||||||
|
// Create resolveTexture2 with (kBaseMipLevel2 + 1) mipmap levels and resolve into its last
|
||||||
|
// mipmap level.
|
||||||
|
constexpr uint32_t kBaseArrayLayer2 = 5;
|
||||||
|
constexpr uint32_t kBaseMipLevel2 = 3;
|
||||||
|
dawn::Texture resolveTexture2 =
|
||||||
|
CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel2 + 1, kBaseArrayLayer2 + 1);
|
||||||
|
dawn::TextureViewDescriptor resolveViewDescriptor2 = baseTextureViewDescriptor;
|
||||||
|
resolveViewDescriptor2.baseArrayLayer = kBaseArrayLayer2;
|
||||||
|
resolveViewDescriptor2.baseMipLevel = kBaseMipLevel2;
|
||||||
|
dawn::TextureView resolveView2 = resolveTexture2.CreateTextureView(&resolveViewDescriptor2);
|
||||||
|
|
||||||
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||||
|
dawn::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest();
|
||||||
|
|
||||||
|
constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||||
|
constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
|
||||||
|
constexpr bool kTestDepth = false;
|
||||||
|
|
||||||
|
// Draw a red triangle to the first color attachment, and a green triangle to the second color
|
||||||
|
// attachment, and do MSAA resolve on two render targets in one render pass.
|
||||||
|
{
|
||||||
|
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||||
|
{mMultisampledColorView, multisampledColorView2}, {resolveView1, resolveView2},
|
||||||
|
dawn::LoadOp::Clear, dawn::LoadOp::Clear, kTestDepth);
|
||||||
|
|
||||||
|
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1
|
||||||
|
kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2
|
||||||
|
constexpr uint32_t kSize = sizeof(kUniformData);
|
||||||
|
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
VerifyResolveTarget(kRed, resolveTexture1, kBaseMipLevel1, kBaseArrayLayer1);
|
||||||
|
VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): enable multisampled rendering on all Dawn backends.
|
||||||
|
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, OpenGLBackend);
|
Loading…
Reference in New Issue