Skip to content

Commit

Permalink
Add support for multisample resolve (google#970)
Browse files Browse the repository at this point in the history
Multisample images were already supported, but graphics pipelines were
not able to resolve these into single sample images.
  • Loading branch information
asuonpaa authored Nov 12, 2021
1 parent 02dc821 commit 5bd7d35
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 7 deletions.
5 changes: 5 additions & 0 deletions docs/amber_script.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/amberscript/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
}

Expand Down
127 changes: 127 additions & 0 deletions src/amberscript/parser_bind_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 3 additions & 1 deletion src/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down
9 changes: 9 additions & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_;
Expand Down
1 change: 1 addition & 0 deletions src/vulkan/device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/vulkan/engine_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
47 changes: 46 additions & 1 deletion src/vulkan/frame_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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());
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/vulkan/frame_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 5bd7d35

Please sign in to comment.