aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorasuonpaa <34128694+asuonpaa@users.noreply.github.com>2021-11-12 12:25:00 +0200
committerGitHub <noreply@github.com>2021-11-12 10:25:00 +0000
commit5bd7d35a38e410892fb73c7a54fd2a20534480cd (patch)
tree6922c41a2aa5ad8d37c70818ae887f1567f0e299
parent02dc821ced6d786af712fcacd13462210bb34009 (diff)
downloadamber-5bd7d35a38e410892fb73c7a54fd2a20534480cd.tar.gz
Add support for multisample resolve (#970)
Multisample images were already supported, but graphics pipelines were not able to resolve these into single sample images.
-rw-r--r--docs/amber_script.md5
-rw-r--r--src/amberscript/parser.cc4
-rw-r--r--src/amberscript/parser_bind_test.cc127
-rw-r--r--src/buffer.h4
-rw-r--r--src/pipeline.cc12
-rw-r--r--src/pipeline.h9
-rw-r--r--src/vulkan/device.cc1
-rw-r--r--src/vulkan/engine_vulkan.cc4
-rw-r--r--src/vulkan/frame_buffer.cc47
-rw-r--r--src/vulkan/frame_buffer.h3
-rw-r--r--src/vulkan/graphics_pipeline.cc38
-rw-r--r--src/vulkan/graphics_pipeline.h4
-rw-r--r--tests/cases/multisample_resolve.amber50
13 files changed, 301 insertions, 7 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md
index 4161101..a0e4e54 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -555,6 +555,11 @@ contain image attachment content, depth/stencil content, uniform buffers, etc.
# pipelines.
BIND BUFFER {buffer_name} AS depth_stencil
+ # Attach |buffer_name| as a multisample resolve target. The order of resolve
+ # target images match with the order of color attachments that have more than
+ # one sample.
+ BIND BUFFER {buffer_name} AS resolve
+
# Attach |buffer_name| as the push_constant buffer. There can be only one
# push constant buffer attached to a pipeline.
BIND BUFFER {buffer_name} AS push_constant
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index dfc2cfb..c0f9475 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -1062,6 +1062,8 @@ Result Parser::ToBufferType(const std::string& name, BufferType* type) {
*type = BufferType::kUniformTexelBuffer;
else if (name == "storage_texel_buffer")
*type = BufferType::kStorageTexelBuffer;
+ else if (name == "resolve")
+ *type = BufferType::kResolve;
else
return Result("unknown buffer_type: " + name);
@@ -1176,6 +1178,8 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
for (auto& buf : buffers)
buf->SetSampler(sampler);
+ } else if (buffer_type == BufferType::kResolve) {
+ r = pipeline->AddResolveTarget(buffer);
}
}
diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc
index a39b69e..8aac5e5 100644
--- a/src/amberscript/parser_bind_test.cc
+++ b/src/amberscript/parser_bind_test.cc
@@ -3508,5 +3508,132 @@ END
r.Error());
}
+TEST_F(AmberScriptParserTest, BindResolveTarget) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+IMAGE my_fb_ms DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT SAMPLES 4
+IMAGE my_fb DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ FRAMEBUFFER_SIZE 64 64
+ BIND BUFFER my_fb_ms AS color LOCATION 0
+ BIND BUFFER my_fb AS resolve
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& resolve_targets = pipeline->GetResolveTargets();
+ ASSERT_EQ(1U, resolve_targets.size());
+
+ const auto& buf_info = resolve_targets[0];
+ ASSERT_TRUE(buf_info.buffer != nullptr);
+ EXPECT_EQ(64u * 64u, buf_info.buffer->ElementCount());
+ EXPECT_EQ(64u * 64u * 4u, buf_info.buffer->ValueCount());
+ EXPECT_EQ(64u * 64u * 4u * sizeof(float), buf_info.buffer->GetSizeInBytes());
+}
+
+TEST_F(AmberScriptParserTest, BindResolveTargetMissingBuffer) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER AS resolve
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: unknown buffer: AS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindResolveTargetNonDeclaredBuffer) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+IMAGE my_fb_ms DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT SAMPLES 4
+
+PIPELINE graphics my_pipeline
+ATTACH my_shader
+ATTACH my_fragment
+
+FRAMEBUFFER_SIZE 64 64
+BIND BUFFER my_fb_ms AS color LOCATION 0
+BIND BUFFER my_fb AS resolve
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: unknown buffer: my_fb", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindMultipleResolveTargets) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+IMAGE my_fb_ms0 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT SAMPLES 4
+IMAGE my_fb_ms1 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT SAMPLES 4
+IMAGE my_fb_ms2 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT SAMPLES 4
+IMAGE my_fb0 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT
+IMAGE my_fb1 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT
+IMAGE my_fb2 DIM_2D WIDTH 64 HEIGHT 64 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ FRAMEBUFFER_SIZE 64 64
+ BIND BUFFER my_fb_ms0 AS color LOCATION 0
+ BIND BUFFER my_fb_ms1 AS color LOCATION 1
+ BIND BUFFER my_fb_ms2 AS color LOCATION 2
+ BIND BUFFER my_fb0 AS resolve
+ BIND BUFFER my_fb1 AS resolve
+ BIND BUFFER my_fb2 AS resolve
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& resolve_targets = pipeline->GetResolveTargets();
+ ASSERT_EQ(3U, resolve_targets.size());
+
+ for (const auto& buf_info : resolve_targets) {
+ ASSERT_TRUE(buf_info.buffer != nullptr);
+ EXPECT_EQ(64u * 64u, buf_info.buffer->ElementCount());
+ EXPECT_EQ(64u * 64u * 4u, buf_info.buffer->ValueCount());
+ EXPECT_EQ(64u * 64u * 4u * sizeof(float),
+ buf_info.buffer->GetSizeInBytes());
+ }
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/buffer.h b/src/buffer.h
index 18f7fed..90f3b01 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -62,7 +62,9 @@ enum class BufferType : int8_t {
/// A uniform texel buffer.
kUniformTexelBuffer,
/// A storage texel buffer.
- kStorageTexelBuffer
+ kStorageTexelBuffer,
+ /// A resolve target.
+ kResolve
};
enum class InputRate : int8_t {
diff --git a/src/pipeline.cc b/src/pipeline.cc
index 9b73041..ae3bf0a 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -389,6 +389,18 @@ Result Pipeline::AddColorAttachment(Buffer* buf,
return {};
}
+Result Pipeline::AddResolveTarget(Buffer* buf) {
+ resolve_targets_.push_back(BufferInfo{buf});
+
+ auto& info = resolve_targets_.back();
+ info.type = BufferType::kResolve;
+ buf->SetWidth(fb_width_);
+ buf->SetHeight(fb_height_);
+ buf->SetElementCount(fb_width_ * fb_height_);
+
+ return {};
+}
+
Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
uint32_t* loc) const {
for (const auto& info : color_attachments_) {
diff --git a/src/pipeline.h b/src/pipeline.h
index 7a80ad4..9010c59 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -303,6 +303,14 @@ class Pipeline {
/// something goes wrong.
Result GetLocationForColorAttachment(Buffer* buf, uint32_t* loc) const;
+ /// Returns a list of all resolve targets in this pipeline.
+ const std::vector<BufferInfo>& GetResolveTargets() const {
+ return resolve_targets_;
+ }
+
+ /// Adds |buf| as a multisample resolve target in the pipeline.
+ Result AddResolveTarget(Buffer* buf);
+
/// Sets |buf| as the depth/stencil buffer for this pipeline.
Result SetDepthStencilBuffer(Buffer* buf);
/// Returns information on the depth/stencil buffer bound to the pipeline. If
@@ -436,6 +444,7 @@ class Pipeline {
std::string name_;
std::vector<ShaderInfo> shaders_;
std::vector<BufferInfo> color_attachments_;
+ std::vector<BufferInfo> resolve_targets_;
std::vector<BufferInfo> vertex_buffers_;
std::vector<BufferInfo> buffers_;
std::vector<std::unique_ptr<type::Type>> types_;
diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc
index 72ad468..5e18277 100644
--- a/src/vulkan/device.cc
+++ b/src/vulkan/device.cc
@@ -840,6 +840,7 @@ bool Device::IsFormatSupportedByPhysicalDevice(const Format& format,
bool is_buffer_type_image = false;
switch (type) {
case BufferType::kColor:
+ case BufferType::kResolve:
case BufferType::kStorageImage:
flag = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
is_buffer_type_image = true;
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
index 8d16545..1ed60f4 100644
--- a/src/vulkan/engine_vulkan.cc
+++ b/src/vulkan/engine_vulkan.cc
@@ -177,8 +177,8 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
} else {
vk_pipeline = MakeUnique<GraphicsPipeline>(
device_.get(), pipeline->GetColorAttachments(),
- pipeline->GetDepthStencilBuffer(), engine_data.fence_timeout_ms,
- stage_create_info);
+ pipeline->GetDepthStencilBuffer(), pipeline->GetResolveTargets(),
+ engine_data.fence_timeout_ms, stage_create_info);
vk_pipeline->AsGraphics()->SetPatchControlPoints(
pipeline->GetPipelineData()->GetPatchControlPoints());
diff --git a/src/vulkan/frame_buffer.cc b/src/vulkan/frame_buffer.cc
index 70b8328..b6ad13a 100644
--- a/src/vulkan/frame_buffer.cc
+++ b/src/vulkan/frame_buffer.cc
@@ -30,10 +30,12 @@ FrameBuffer::FrameBuffer(
Device* device,
const std::vector<const amber::Pipeline::BufferInfo*>& color_attachments,
amber::Pipeline::BufferInfo depth_stencil_attachment,
+ const std::vector<const amber::Pipeline::BufferInfo*>& resolve_targets,
uint32_t width,
uint32_t height)
: device_(device),
color_attachments_(color_attachments),
+ resolve_targets_(resolve_targets),
depth_stencil_attachment_(depth_stencil_attachment),
width_(width),
height_(height) {}
@@ -69,7 +71,7 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass) {
device_, *info->buffer->GetFormat(), VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_TYPE_2D, usage_flags, width_ << info->base_mip_level,
height_ << info->base_mip_level, depth_, info->buffer->GetMipLevels(),
- info->base_mip_level, 1u, 1u));
+ info->base_mip_level, 1u, info->buffer->GetSamples()));
Result r = color_images_.back()->Initialize();
if (!r.IsSuccess())
@@ -103,6 +105,22 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass) {
attachments.push_back(depth_stencil_image_->GetVkImageView());
}
+ for (auto* info : resolve_targets_) {
+ const VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ resolve_images_.push_back(MakeUnique<TransferImage>(
+ device_, *info->buffer->GetFormat(), VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_TYPE_2D, usage_flags, width_, height_, depth_, 1u, 0u, 1u,
+ 1u));
+
+ Result r = resolve_images_.back()->Initialize();
+ if (!r.IsSuccess())
+ return r;
+
+ attachments.push_back(resolve_images_.back()->GetVkImageView());
+ }
+
VkFramebufferCreateInfo frame_buffer_info = VkFramebufferCreateInfo();
frame_buffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frame_buffer_info.renderPass = render_pass;
@@ -129,6 +147,9 @@ void FrameBuffer::ChangeFrameLayout(CommandBuffer* command,
for (auto& img : color_images_)
img->ImageBarrier(command, color_layout, color_stage);
+ for (auto& img : resolve_images_)
+ img->ImageBarrier(command, color_layout, color_stage);
+
if (depth_stencil_image_)
depth_stencil_image_->ImageBarrier(command, depth_layout, depth_stage);
}
@@ -165,6 +186,9 @@ void FrameBuffer::TransferImagesToHost(CommandBuffer* command) {
for (auto& img : color_images_)
img->CopyToHost(command);
+ for (auto& img : resolve_images_)
+ img->CopyToHost(command);
+
if (depth_stencil_image_)
depth_stencil_image_->CopyToHost(command);
}
@@ -179,6 +203,15 @@ void FrameBuffer::CopyImagesToBuffers() {
info->buffer->GetSizeInBytes());
}
+ for (size_t i = 0; i < resolve_images_.size(); ++i) {
+ auto& img = resolve_images_[i];
+ auto* info = resolve_targets_[i];
+ auto* values = info->buffer->ValuePtr();
+ values->resize(info->buffer->GetSizeInBytes());
+ std::memcpy(values->data(), img->HostAccessibleMemoryPtr(),
+ info->buffer->GetSizeInBytes());
+ }
+
if (depth_stencil_image_) {
auto* values = depth_stencil_attachment_.buffer->ValuePtr();
values->resize(depth_stencil_attachment_.buffer->GetSizeInBytes());
@@ -208,6 +241,18 @@ void FrameBuffer::CopyBuffersToImages() {
info->buffer->GetSizeInBytes());
}
+ for (size_t i = 0; i < resolve_images_.size(); ++i) {
+ auto& img = resolve_images_[i];
+ auto* info = resolve_targets_[i];
+ auto* values = info->buffer->ValuePtr();
+ // Nothing to do if our local buffer is empty
+ if (values->empty())
+ continue;
+
+ std::memcpy(img->HostAccessibleMemoryPtr(), values->data(),
+ info->buffer->GetSizeInBytes());
+ }
+
if (depth_stencil_image_) {
auto* values = depth_stencil_attachment_.buffer->ValuePtr();
// Nothing to do if our local buffer is empty
diff --git a/src/vulkan/frame_buffer.h b/src/vulkan/frame_buffer.h
index 064b6d3..5774289 100644
--- a/src/vulkan/frame_buffer.h
+++ b/src/vulkan/frame_buffer.h
@@ -34,6 +34,7 @@ class FrameBuffer {
Device* device,
const std::vector<const amber::Pipeline::BufferInfo*>& color_attachments,
amber::Pipeline::BufferInfo depth_stencil_attachment,
+ const std::vector<const amber::Pipeline::BufferInfo*>& resolve_targets,
uint32_t width,
uint32_t height);
~FrameBuffer();
@@ -70,9 +71,11 @@ class FrameBuffer {
Device* device_ = nullptr;
std::vector<const amber::Pipeline::BufferInfo*> color_attachments_;
+ std::vector<const amber::Pipeline::BufferInfo*> resolve_targets_;
amber::Pipeline::BufferInfo depth_stencil_attachment_;
VkFramebuffer frame_ = VK_NULL_HANDLE;
std::vector<std::unique_ptr<TransferImage>> color_images_;
+ std::vector<std::unique_ptr<TransferImage>> resolve_images_;
std::unique_ptr<TransferImage> depth_stencil_image_;
uint32_t width_ = 0;
uint32_t height_ = 0;
diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc
index 5536440..9e3c7ac 100644
--- a/src/vulkan/graphics_pipeline.cc
+++ b/src/vulkan/graphics_pipeline.cc
@@ -386,6 +386,7 @@ GraphicsPipeline::GraphicsPipeline(
Device* device,
const std::vector<amber::Pipeline::BufferInfo>& color_buffers,
amber::Pipeline::BufferInfo depth_stencil_buffer,
+ const std::vector<amber::Pipeline::BufferInfo>& resolve_targets,
uint32_t fence_timeout_ms,
const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info)
: Pipeline(PipelineType::kGraphics,
@@ -395,6 +396,8 @@ GraphicsPipeline::GraphicsPipeline(
depth_stencil_buffer_(depth_stencil_buffer) {
for (const auto& info : color_buffers)
color_buffers_.push_back(&info);
+ for (const auto& info : resolve_targets)
+ resolve_targets_.push_back(&info);
}
GraphicsPipeline::~GraphicsPipeline() {
@@ -412,6 +415,7 @@ Result GraphicsPipeline::CreateRenderPass() {
std::vector<VkAttachmentReference> color_refer;
VkAttachmentReference depth_refer = VkAttachmentReference();
+ std::vector<VkAttachmentReference> resolve_refer;
for (const auto* info : color_buffers_) {
attachment_desc.push_back(kDefaultAttachmentDesc);
@@ -421,6 +425,8 @@ Result GraphicsPipeline::CreateRenderPass() {
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment_desc.back().finalLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attachment_desc.back().samples =
+ static_cast<VkSampleCountFlagBits>(info->buffer->GetSamples());
VkAttachmentReference ref = VkAttachmentReference();
ref.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
@@ -446,6 +452,23 @@ Result GraphicsPipeline::CreateRenderPass() {
subpass_desc.pDepthStencilAttachment = &depth_refer;
}
+ for (const auto* info : resolve_targets_) {
+ attachment_desc.push_back(kDefaultAttachmentDesc);
+ attachment_desc.back().format =
+ device_->GetVkFormat(*info->buffer->GetFormat());
+ attachment_desc.back().initialLayout =
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attachment_desc.back().finalLayout =
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ VkAttachmentReference ref = VkAttachmentReference();
+ ref.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
+ ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ resolve_refer.push_back(ref);
+ }
+
+ subpass_desc.pResolveAttachments = resolve_refer.data();
+
VkRenderPassCreateInfo render_pass_info = VkRenderPassCreateInfo();
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount =
@@ -621,6 +644,16 @@ Result GraphicsPipeline::CreateVkGraphicsPipeline(
VK_FALSE, /* alphaToOneEnable */
};
+ // Search for multisampled color buffers and adjust the rasterization samples
+ // to match.
+ for (const auto& cb : color_buffers_) {
+ uint32_t samples = cb->buffer->GetSamples();
+ assert(static_cast<VkSampleCountFlagBits>(samples) >=
+ multisampleInfo.rasterizationSamples);
+ multisampleInfo.rasterizationSamples =
+ static_cast<VkSampleCountFlagBits>(samples);
+ }
+
VkGraphicsPipelineCreateInfo pipeline_info = VkGraphicsPipelineCreateInfo();
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = static_cast<uint32_t>(shader_stage_info.size());
@@ -704,8 +737,9 @@ Result GraphicsPipeline::Initialize(uint32_t width,
if (!r.IsSuccess())
return r;
- frame_ = MakeUnique<FrameBuffer>(device_, color_buffers_,
- depth_stencil_buffer_, width, height);
+ frame_ =
+ MakeUnique<FrameBuffer>(device_, color_buffers_, depth_stencil_buffer_,
+ resolve_targets_, width, height);
r = frame_->Initialize(render_pass_);
if (!r.IsSuccess())
return r;
diff --git a/src/vulkan/graphics_pipeline.h b/src/vulkan/graphics_pipeline.h
index 7076231..cd55aad 100644
--- a/src/vulkan/graphics_pipeline.h
+++ b/src/vulkan/graphics_pipeline.h
@@ -43,6 +43,7 @@ class GraphicsPipeline : public Pipeline {
Device* device,
const std::vector<amber::Pipeline::BufferInfo>& color_buffers,
amber::Pipeline::BufferInfo depth_stencil_buffer,
+ const std::vector<amber::Pipeline::BufferInfo>& resolve_targets,
uint32_t fence_timeout_ms,
const std::vector<VkPipelineShaderStageCreateInfo>&);
~GraphicsPipeline() override;
@@ -86,8 +87,9 @@ class GraphicsPipeline : public Pipeline {
VkRenderPass render_pass_ = VK_NULL_HANDLE;
std::unique_ptr<FrameBuffer> frame_;
- // color buffers are owned by the amber::Pipeline.
+ // color buffers and resolve targets are owned by the amber::Pipeline.
std::vector<const amber::Pipeline::BufferInfo*> color_buffers_;
+ std::vector<const amber::Pipeline::BufferInfo*> resolve_targets_;
amber::Pipeline::BufferInfo depth_stencil_buffer_;
std::unique_ptr<IndexBuffer> index_buffer_;
diff --git a/tests/cases/multisample_resolve.amber b/tests/cases/multisample_resolve.amber
new file mode 100644
index 0000000..594f9bb
--- /dev/null
+++ b/tests/cases/multisample_resolve.amber
@@ -0,0 +1,50 @@
+#!amber
+# Copyright 2021 The Amber 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
+#
+# https://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.
+
+DEVICE_FEATURE sampleRateShading
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader GLSL
+#version 440
+layout(location = 0) out vec4 color;
+
+void main (void)
+{
+ if (gl_SampleID == 0)
+ color = vec4(1, 0, 0, 1);
+ else if (gl_SampleID == 1)
+ color = vec4(0, 1, 0, 1);
+ else if (gl_SampleID == 2)
+ color = vec4(0, 0, 1, 1);
+ else
+ color = vec4(1, 1, 1, 1);
+}
+END
+
+IMAGE framebuffer_ms FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 64 HEIGHT 64 SAMPLES 4
+IMAGE framebuffer FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 64 HEIGHT 64
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ FRAMEBUFFER_SIZE 64 64
+ BIND BUFFER framebuffer_ms AS color LOCATION 0
+ BIND BUFFER framebuffer AS resolve
+END
+
+RUN pipeline DRAW_RECT POS 0 0 SIZE 64 64
+
+EXPECT framebuffer IDX 0 0 SIZE 64 64 EQ_RGBA 128 128 128 255 TOLERANCE 5%