aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlkka Saarelainen <ilkka.saarelainen@siru.fi>2021-06-18 17:25:16 +0300
committerGitHub <noreply@github.com>2021-06-18 15:25:16 +0100
commit8c3bfef40c2387944fdc81746e2e3249e4da5566 (patch)
tree800b82e17bc0f286dbb6a2db35445f020020cd9b
parentaaac58d5009e8f6b33e2723258dc1542854d09a4 (diff)
downloadamber-8c3bfef40c2387944fdc81746e2e3249e4da5566.tar.gz
Use shared transfer buffers (#951)
Descriptors were using separate transfer buffers when they were using same buffer. This prevented e.g. using the same buffer in multiple storage buffer bindings. This commit moves the transfer buffer/image ownership from descriptors to pipeline to allow descriptors to share the transfer buffers. Now the descriptors can have the same buffer binding even if the descriptors are of different types e.g. storage/uniform/storage_texel_buffer etc. Fixes #950
-rw-r--r--src/vulkan/buffer_backed_descriptor.cc92
-rw-r--r--src/vulkan/buffer_backed_descriptor.h21
-rw-r--r--src/vulkan/buffer_descriptor.cc106
-rw-r--r--src/vulkan/buffer_descriptor.h10
-rw-r--r--src/vulkan/descriptor.h7
-rw-r--r--src/vulkan/frame_buffer.cc19
-rw-r--r--src/vulkan/image_descriptor.cc99
-rw-r--r--src/vulkan/image_descriptor.h11
-rw-r--r--src/vulkan/index_buffer.cc8
-rw-r--r--src/vulkan/pipeline.cc99
-rw-r--r--src/vulkan/pipeline.h13
-rw-r--r--src/vulkan/resource.h12
-rw-r--r--src/vulkan/transfer_buffer.cc14
-rw-r--r--src/vulkan/transfer_buffer.h13
-rw-r--r--src/vulkan/transfer_image.cc8
-rw-r--r--src/vulkan/transfer_image.h4
-rw-r--r--src/vulkan/vertex_buffer.cc5
-rw-r--r--tests/cases/compute_one_buffer_in_multiple_bindings.amber94
18 files changed, 370 insertions, 265 deletions
diff --git a/src/vulkan/buffer_backed_descriptor.cc b/src/vulkan/buffer_backed_descriptor.cc
index d1b1aed..a2e874d 100644
--- a/src/vulkan/buffer_backed_descriptor.cc
+++ b/src/vulkan/buffer_backed_descriptor.cc
@@ -26,79 +26,65 @@ BufferBackedDescriptor::BufferBackedDescriptor(Buffer* buffer,
DescriptorType type,
Device* device,
uint32_t desc_set,
- uint32_t binding)
- : Descriptor(type, device, desc_set, binding) {
+ uint32_t binding,
+ Pipeline* pipeline)
+ : Descriptor(type, device, desc_set, binding), pipeline_(pipeline) {
AddAmberBuffer(buffer);
}
BufferBackedDescriptor::~BufferBackedDescriptor() = default;
-Result BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(
- CommandBuffer* command) {
- for (const auto& resource : GetResources()) {
- if (!resource.first->ValuePtr()->empty()) {
- resource.second->UpdateMemoryWithRawData(*resource.first->ValuePtr());
- // If the resource is read-only, keep the buffer data; Amber won't copy
- // read-only resources back into the host buffers, so it makes sense to
- // leave the buffer intact.
- if (!IsReadOnly())
- resource.first->ValuePtr()->clear();
- }
-
- resource.second->CopyToDevice(command);
- }
+Result BufferBackedDescriptor::RecordCopyBufferDataToTransferResourceIfNeeded(
+ CommandBuffer* command_buffer,
+ Buffer* buffer,
+ Resource* transfer_resource) {
+ transfer_resource->UpdateMemoryWithRawData(*buffer->ValuePtr());
+ // If the resource is read-only, keep the buffer data; Amber won't copy
+ // read-only resources back into the host buffers, so it makes sense to
+ // leave the buffer intact.
+ if (!transfer_resource->IsReadOnly())
+ buffer->ValuePtr()->clear();
+
+ transfer_resource->CopyToDevice(command_buffer);
return {};
}
-Result BufferBackedDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
- if (!IsReadOnly()) {
- if (GetResources().empty()) {
- return Result(
- "Vulkan: BufferBackedDescriptor::RecordCopyDataToHost() no transfer "
- "resources");
- }
-
- for (const auto& r : GetResources())
- r.second->CopyToHost(command);
+Result BufferBackedDescriptor::RecordCopyTransferResourceToHost(
+ CommandBuffer* command_buffer,
+ Resource* transfer_resource) {
+ if (!transfer_resource->IsReadOnly()) {
+ transfer_resource->CopyToHost(command_buffer);
}
return {};
}
-Result BufferBackedDescriptor::MoveResourceToBufferOutput() {
- // No need to copy results of read only resources.
- if (IsReadOnly())
+Result BufferBackedDescriptor::MoveTransferResourceToBufferOutput(
+ Resource* transfer_resource,
+ Buffer* buffer) {
+ // No need to move read only resources to an output buffer.
+ if (transfer_resource->IsReadOnly()) {
return {};
+ }
- auto resources = GetResources();
-
- if (resources.empty()) {
+ void* resource_memory_ptr = transfer_resource->HostAccessibleMemoryPtr();
+ if (!resource_memory_ptr) {
return Result(
- "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() no "
- "transfer resource");
+ "Vulkan: BufferBackedDescriptor::MoveTransferResourceToBufferOutput() "
+ "no host accessible memory pointer");
}
- for (const auto& resource : resources) {
- void* resource_memory_ptr = resource.second->HostAccessibleMemoryPtr();
- auto* buffer = resource.first;
- if (!resource_memory_ptr) {
- return Result(
- "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
- "no host accessible memory pointer");
- }
-
- if (!buffer->ValuePtr()->empty()) {
- return Result(
- "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
- "output buffer is not empty");
- }
-
- auto size_in_bytes = resource.second->GetSizeInBytes();
- buffer->SetElementCount(size_in_bytes / buffer->GetFormat()->SizeInBytes());
- buffer->ValuePtr()->resize(size_in_bytes);
- std::memcpy(buffer->ValuePtr()->data(), resource_memory_ptr, size_in_bytes);
+ if (!buffer->ValuePtr()->empty()) {
+ return Result(
+ "Vulkan: BufferBackedDescriptor::MoveTransferResourceToBufferOutput() "
+ "output buffer is not empty");
}
+ auto size_in_bytes = transfer_resource->GetSizeInBytes();
+ buffer->SetElementCount(size_in_bytes / buffer->GetFormat()->SizeInBytes());
+ buffer->ValuePtr()->resize(size_in_bytes);
+ std::memcpy(buffer->ValuePtr()->data(), resource_memory_ptr, size_in_bytes);
+
return {};
}
diff --git a/src/vulkan/buffer_backed_descriptor.h b/src/vulkan/buffer_backed_descriptor.h
index 8f9df41..8a2d0a5 100644
--- a/src/vulkan/buffer_backed_descriptor.h
+++ b/src/vulkan/buffer_backed_descriptor.h
@@ -25,6 +25,7 @@
#include "src/buffer.h"
#include "src/engine.h"
#include "src/vulkan/descriptor.h"
+#include "src/vulkan/pipeline.h"
#include "src/vulkan/resource.h"
namespace amber {
@@ -36,13 +37,19 @@ class BufferBackedDescriptor : public Descriptor {
DescriptorType type,
Device* device,
uint32_t desc_set,
- uint32_t binding);
+ uint32_t binding,
+ Pipeline* pipeline);
~BufferBackedDescriptor() override;
Result CreateResourceIfNeeded() override { return {}; }
- Result RecordCopyDataToResourceIfNeeded(CommandBuffer* command) override;
- Result RecordCopyDataToHost(CommandBuffer* command) override;
- Result MoveResourceToBufferOutput() override;
+ static Result RecordCopyBufferDataToTransferResourceIfNeeded(
+ CommandBuffer* command_buffer,
+ Buffer* buffer,
+ Resource* transfer_resource);
+ static Result RecordCopyTransferResourceToHost(CommandBuffer* command_buffer,
+ Resource* transfer_resource);
+ static Result MoveTransferResourceToBufferOutput(Resource* transfer_resource,
+ Buffer* buffer);
uint32_t GetDescriptorCount() override {
return static_cast<uint32_t>(amber_buffers_.size());
}
@@ -52,10 +59,8 @@ class BufferBackedDescriptor : public Descriptor {
bool IsReadOnly() const;
protected:
- /// Returns a list of unique transfer buffer resources. Note that this list
- /// may contain less items than the |amber_buffers| vector contains if two or
- /// more amber buffers use same Vulkan buffer.
- virtual std::vector<std::pair<Buffer*, Resource*>> GetResources() = 0;
+ // Pipeline where this descriptor is attached to.
+ Pipeline* pipeline_;
private:
std::vector<Buffer*> amber_buffers_;
diff --git a/src/vulkan/buffer_descriptor.cc b/src/vulkan/buffer_descriptor.cc
index aa8b2dd..d60ce08 100644
--- a/src/vulkan/buffer_descriptor.cc
+++ b/src/vulkan/buffer_descriptor.cc
@@ -29,65 +29,63 @@ BufferDescriptor::BufferDescriptor(Buffer* buffer,
DescriptorType type,
Device* device,
uint32_t desc_set,
- uint32_t binding)
- : BufferBackedDescriptor(buffer, type, device, desc_set, binding) {}
+ uint32_t binding,
+ Pipeline* pipeline)
+ : BufferBackedDescriptor(buffer,
+ type,
+ device,
+ desc_set,
+ binding,
+ pipeline) {}
BufferDescriptor::~BufferDescriptor() = default;
Result BufferDescriptor::CreateResourceIfNeeded() {
- if (!transfer_buffers_.empty()) {
- return Result(
- "Vulkan: BufferDescriptor::CreateResourceIfNeeded() must be called "
- "only when |transfer_buffers| is empty");
+ auto& transfer_resources = pipeline_->GetDescriptorTransferResources();
+
+ VkBufferUsageFlags flags =
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ if (IsUniformBuffer() || IsUniformBufferDynamic()) {
+ flags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ } else if (IsStorageBuffer() || IsStorageBufferDynamic()) {
+ flags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ } else if (IsUniformTexelBuffer()) {
+ flags |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
+ } else if (IsStorageTexelBuffer()) {
+ flags |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+ } else {
+ return Result("Unexpected buffer type when deciding usage flags");
}
- descriptor_offsets_.reserve(GetAmberBuffers().size());
- descriptor_ranges_.reserve(GetAmberBuffers().size());
-
for (const auto& amber_buffer : GetAmberBuffers()) {
- if (amber_buffer->ValuePtr()->empty())
- continue;
-
- // Check if the transfer buffer is already created.
- if (transfer_buffers_.count(amber_buffer) > 0)
- continue;
-
- auto size_in_bytes =
- static_cast<uint32_t>(amber_buffer->ValuePtr()->size());
-
- auto transfer_buffer = MakeUnique<TransferBuffer>(
- device_, size_in_bytes, amber_buffer->GetFormat());
-
- VkBufferUsageFlags flags =
- VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- if (IsUniformBuffer() || IsUniformBufferDynamic()) {
- flags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
- } else if (IsStorageBuffer() || IsStorageBufferDynamic()) {
- flags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
- } else if (IsUniformTexelBuffer()) {
- flags |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
- } else if (IsStorageTexelBuffer()) {
- flags |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+ // Create (but don't initialize) the transfer buffer if not already created.
+ if (transfer_resources.count(amber_buffer) == 0) {
+ auto size_in_bytes =
+ static_cast<uint32_t>(amber_buffer->ValuePtr()->size());
+ auto transfer_buffer = MakeUnique<TransferBuffer>(
+ device_, size_in_bytes, amber_buffer->GetFormat());
+ transfer_buffer->SetReadOnly(IsReadOnly());
+ transfer_resources[amber_buffer] = std::move(transfer_buffer);
} else {
- return Result("Unexpected buffer type when deciding usage flags");
+ // Unset transfer buffer's read only property if needed.
+ if (!IsReadOnly()) {
+ transfer_resources[amber_buffer]->SetReadOnly(false);
+ }
}
- Result r = transfer_buffer->Initialize(flags);
+ // Update the buffer create flags.
+ Result r =
+ transfer_resources[amber_buffer]->AsTransferBuffer()->AddUsageFlags(
+ flags);
if (!r.IsSuccess())
return r;
- transfer_buffers_[amber_buffer] = std::move(transfer_buffer);
}
-
is_descriptor_set_update_needed_ = true;
- return {};
-}
-
-Result BufferDescriptor::MoveResourceToBufferOutput() {
- Result r = BufferBackedDescriptor::MoveResourceToBufferOutput();
- transfer_buffers_.clear();
+ descriptor_offsets_.reserve(GetAmberBuffers().size());
+ descriptor_ranges_.reserve(GetAmberBuffers().size());
- return r;
+ return {};
}
void BufferDescriptor::UpdateDescriptorSetIfNeeded(
@@ -100,7 +98,9 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
// Create VkDescriptorBufferInfo for every descriptor buffer.
for (uint32_t i = 0; i < GetAmberBuffers().size(); i++) {
- const auto& buffer = transfer_buffers_[GetAmberBuffers()[i]];
+ const auto& buffer =
+ pipeline_->GetDescriptorTransferResources()[GetAmberBuffers()[i]]
+ ->AsTransferBuffer();
assert(buffer->GetVkBuffer() && "Unexpected descriptor type");
// Add buffer infos for uniform and storage buffers.
if (IsUniformBuffer() || IsUniformBufferDynamic() || IsStorageBuffer() ||
@@ -146,23 +146,5 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
is_descriptor_set_update_needed_ = false;
}
-std::vector<std::pair<Buffer*, Resource*>> BufferDescriptor::GetResources() {
- std::vector<std::pair<Buffer*, Resource*>> ret;
- // Add unique amber buffers and related transfer buffers to the vector.
- for (const auto& amber_buffer : GetAmberBuffers()) {
- // Skip duplicate values.
- const auto& image =
- std::find_if(ret.begin(), ret.end(),
- [&](const std::pair<Buffer*, Resource*>& buffer) {
- return buffer.first == amber_buffer;
- });
- if (image != ret.end())
- continue;
-
- ret.emplace_back(amber_buffer, transfer_buffers_[amber_buffer].get());
- }
- return ret;
-}
-
} // namespace vulkan
} // namespace amber
diff --git a/src/vulkan/buffer_descriptor.h b/src/vulkan/buffer_descriptor.h
index 2c3f390..439afa5 100644
--- a/src/vulkan/buffer_descriptor.h
+++ b/src/vulkan/buffer_descriptor.h
@@ -26,6 +26,7 @@
#include "src/buffer.h"
#include "src/engine.h"
#include "src/vulkan/buffer_backed_descriptor.h"
+#include "src/vulkan/pipeline.h"
#include "src/vulkan/transfer_buffer.h"
namespace amber {
@@ -42,12 +43,12 @@ class BufferDescriptor : public BufferBackedDescriptor {
DescriptorType type,
Device* device,
uint32_t desc_set,
- uint32_t binding);
+ uint32_t binding,
+ vulkan::Pipeline* pipeline);
~BufferDescriptor() override;
void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
Result CreateResourceIfNeeded() override;
- Result MoveResourceToBufferOutput() override;
std::vector<uint32_t> GetDynamicOffsets() override {
return dynamic_offsets_;
}
@@ -67,12 +68,7 @@ class BufferDescriptor : public BufferBackedDescriptor {
BufferDescriptor* AsBufferDescriptor() override { return this; }
- protected:
- std::vector<std::pair<Buffer*, Resource*>> GetResources() override;
-
private:
- std::unordered_map<Buffer*, std::unique_ptr<TransferBuffer>>
- transfer_buffers_;
std::vector<uint32_t> dynamic_offsets_;
std::vector<VkDeviceSize> descriptor_offsets_;
std::vector<VkDeviceSize> descriptor_ranges_;
diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h
index 03434a3..88f6813 100644
--- a/src/vulkan/descriptor.h
+++ b/src/vulkan/descriptor.h
@@ -31,6 +31,7 @@ namespace vulkan {
class CommandBuffer;
class Device;
class BufferDescriptor;
+class ImageDescriptor;
class BufferBackedDescriptor;
class SamplerDescriptor;
@@ -57,16 +58,12 @@ class Descriptor {
virtual void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) = 0;
virtual Result CreateResourceIfNeeded() = 0;
- virtual Result RecordCopyDataToResourceIfNeeded(CommandBuffer*) { return {}; }
- virtual Result RecordCopyDataToHost(CommandBuffer*) { return {}; }
- virtual Result MoveResourceToBufferOutput() { return {}; }
- virtual Result SetSizeInElements(uint32_t) { return {}; }
- virtual Result AddToBuffer(const std::vector<Value>&, uint32_t) { return {}; }
virtual uint32_t GetDescriptorCount() { return 1; }
virtual std::vector<uint32_t> GetDynamicOffsets() { return {}; }
virtual std::vector<VkDeviceSize> GetDescriptorOffsets() { return {}; }
virtual std::vector<VkDeviceSize> GetDescriptorRanges() { return {}; }
virtual BufferDescriptor* AsBufferDescriptor() { return nullptr; }
+ virtual ImageDescriptor* AsImageDescriptor() { return nullptr; }
virtual BufferBackedDescriptor* AsBufferBackedDescriptor() { return nullptr; }
virtual SamplerDescriptor* AsSamplerDescriptor() { return nullptr; }
uint32_t GetDescriptorSet() const { return descriptor_set_; }
diff --git a/src/vulkan/frame_buffer.cc b/src/vulkan/frame_buffer.cc
index 47eb82d..70b8328 100644
--- a/src/vulkan/frame_buffer.cc
+++ b/src/vulkan/frame_buffer.cc
@@ -62,15 +62,16 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass) {
attachments.resize(color_attachments_.size());
for (auto* info : color_attachments_) {
+ const VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
color_images_.push_back(MakeUnique<TransferImage>(
device_, *info->buffer->GetFormat(), VK_IMAGE_ASPECT_COLOR_BIT,
- VK_IMAGE_TYPE_2D, width_ << info->base_mip_level,
+ 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));
- Result r = color_images_.back()->Initialize(
- VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
+ Result r = color_images_.back()->Initialize();
if (!r.IsSuccess())
return r;
@@ -87,13 +88,15 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass) {
aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
assert(aspect != 0);
+ const VkImageUsageFlags usage_flags =
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+
depth_stencil_image_ = MakeUnique<TransferImage>(
device_, *depth_stencil_attachment_.buffer->GetFormat(), aspect,
- VK_IMAGE_TYPE_2D, width_, height_, depth_, 1u, 0u, 1u, 1u);
+ VK_IMAGE_TYPE_2D, usage_flags, width_, height_, depth_, 1u, 0u, 1u, 1u);
- Result r = depth_stencil_image_->Initialize(
- VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
- VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
+ Result r = depth_stencil_image_->Initialize();
if (!r.IsSuccess())
return r;
diff --git a/src/vulkan/image_descriptor.cc b/src/vulkan/image_descriptor.cc
index 94e67d0..1d20244 100644
--- a/src/vulkan/image_descriptor.cc
+++ b/src/vulkan/image_descriptor.cc
@@ -29,51 +29,25 @@ ImageDescriptor::ImageDescriptor(Buffer* buffer,
Device* device,
uint32_t base_mip_level,
uint32_t desc_set,
- uint32_t binding)
- : BufferBackedDescriptor(buffer, type, device, desc_set, binding),
+ uint32_t binding,
+ Pipeline* pipeline)
+ : BufferBackedDescriptor(buffer, type, device, desc_set, binding, pipeline),
base_mip_level_(base_mip_level),
vulkan_sampler_(device) {}
ImageDescriptor::~ImageDescriptor() = default;
-Result ImageDescriptor::RecordCopyDataToResourceIfNeeded(
- CommandBuffer* command) {
- const auto transfer_images = GetResources();
- for (const auto& image : transfer_images) {
- // Static cast is safe, because the type is known to be TransferImage*.
- static_cast<TransferImage*>(image.second)
- ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- VK_PIPELINE_STAGE_TRANSFER_BIT);
- }
-
- Result r = BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(command);
- if (!r.IsSuccess())
- return r;
-
- // Just do this as early as possible.
- for (const auto& image : transfer_images) {
- static_cast<TransferImage*>(image.second)
- ->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
- }
-
- return {};
-}
-
Result ImageDescriptor::CreateResourceIfNeeded() {
- if (!transfer_images_.empty()) {
- return Result(
- "Vulkan: ImageDescriptor::CreateResourceIfNeeded() must be called "
- "only when |transfer_images| is empty");
- }
+ auto& transfer_resources = pipeline_->GetDescriptorTransferResources();
for (const auto& amber_buffer : GetAmberBuffers()) {
if (amber_buffer->ValuePtr()->empty())
continue;
// Check if the transfer image is already created.
- if (transfer_images_.count(amber_buffer) > 0)
+ if (transfer_resources.count(amber_buffer) > 0) {
continue;
+ }
// Default to 2D image.
VkImageType image_type = VK_IMAGE_TYPE_2D;
@@ -103,14 +77,8 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
- auto transfer_image = MakeUnique<TransferImage>(
- device_, *fmt, aspect, image_type, amber_buffer->GetWidth(),
- amber_buffer->GetHeight(), amber_buffer->GetDepth(),
- amber_buffer->GetMipLevels(), base_mip_level_, VK_REMAINING_MIP_LEVELS,
- amber_buffer->GetSamples());
VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-
if (type_ == DescriptorType::kStorageImage) {
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
} else {
@@ -119,11 +87,14 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
- Result r = transfer_image->Initialize(usage);
- if (!r.IsSuccess())
- return r;
+ auto transfer_image = MakeUnique<TransferImage>(
+ device_, *fmt, aspect, image_type, usage, amber_buffer->GetWidth(),
+ amber_buffer->GetHeight(), amber_buffer->GetDepth(),
+ amber_buffer->GetMipLevels(), base_mip_level_, VK_REMAINING_MIP_LEVELS,
+ amber_buffer->GetSamples());
- transfer_images_[amber_buffer] = std::move(transfer_image);
+ // Store the transfer image to the pipeline's map of transfer images.
+ transfer_resources[amber_buffer] = std::move(transfer_image);
}
if (amber_sampler_) {
@@ -136,28 +107,6 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
return {};
}
-Result ImageDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
- if (!IsReadOnly()) {
- for (auto& image : GetResources()) {
- static_cast<TransferImage*>(image.second)
- ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- VK_PIPELINE_STAGE_TRANSFER_BIT);
- }
-
- BufferBackedDescriptor::RecordCopyDataToHost(command);
- }
-
- return {};
-}
-
-Result ImageDescriptor::MoveResourceToBufferOutput() {
- Result r = BufferBackedDescriptor::MoveResourceToBufferOutput();
-
- transfer_images_.clear();
-
- return r;
-}
-
void ImageDescriptor::UpdateDescriptorSetIfNeeded(
VkDescriptorSet descriptor_set) {
if (!is_descriptor_set_update_needed_)
@@ -170,7 +119,9 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded(
// Create VkDescriptorImageInfo for every descriptor image.
for (const auto& amber_buffer : GetAmberBuffers()) {
- const auto& image = transfer_images_[amber_buffer];
+ const auto& image =
+ pipeline_->GetDescriptorTransferResources()[amber_buffer]
+ ->AsTransferImage();
VkDescriptorImageInfo image_info = {vulkan_sampler_.GetVkSampler(),
image->GetVkImageView(), layout};
image_infos.push_back(image_info);
@@ -191,23 +142,5 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded(
is_descriptor_set_update_needed_ = false;
}
-std::vector<std::pair<Buffer*, Resource*>> ImageDescriptor::GetResources() {
- std::vector<std::pair<Buffer*, Resource*>> ret;
- // Add unique amber buffers and related transfer images to the vector.
- for (const auto& amber_buffer : GetAmberBuffers()) {
- // Skip duplicate values.
- const auto& image =
- std::find_if(ret.begin(), ret.end(),
- [&](const std::pair<Buffer*, Resource*>& buffer) {
- return buffer.first == amber_buffer;
- });
- if (image != ret.end())
- continue;
-
- ret.emplace_back(amber_buffer, transfer_images_[amber_buffer].get());
- }
- return ret;
-}
-
} // namespace vulkan
} // namespace amber
diff --git a/src/vulkan/image_descriptor.h b/src/vulkan/image_descriptor.h
index 908ed17..3046436 100644
--- a/src/vulkan/image_descriptor.h
+++ b/src/vulkan/image_descriptor.h
@@ -21,6 +21,7 @@
#include <vector>
#include "src/vulkan/buffer_backed_descriptor.h"
+#include "src/vulkan/pipeline.h"
#include "src/vulkan/sampler.h"
#include "src/vulkan/transfer_image.h"
@@ -34,22 +35,18 @@ class ImageDescriptor : public BufferBackedDescriptor {
Device* device,
uint32_t base_mip_level,
uint32_t desc_set,
- uint32_t binding);
+ uint32_t binding,
+ Pipeline* pipeline);
~ImageDescriptor() override;
void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
- Result RecordCopyDataToResourceIfNeeded(CommandBuffer* command) override;
Result CreateResourceIfNeeded() override;
- Result RecordCopyDataToHost(CommandBuffer* command) override;
- Result MoveResourceToBufferOutput() override;
void SetAmberSampler(amber::Sampler* sampler) { amber_sampler_ = sampler; }
- protected:
- std::vector<std::pair<Buffer*, Resource*>> GetResources() override;
+ ImageDescriptor* AsImageDescriptor() override { return this; }
private:
uint32_t base_mip_level_ = 0;
- std::unordered_map<Buffer*, std::unique_ptr<TransferImage>> transfer_images_;
amber::Sampler* amber_sampler_ = nullptr;
amber::vulkan::Sampler vulkan_sampler_;
};
diff --git a/src/vulkan/index_buffer.cc b/src/vulkan/index_buffer.cc
index 73a9083..b651427 100644
--- a/src/vulkan/index_buffer.cc
+++ b/src/vulkan/index_buffer.cc
@@ -38,8 +38,12 @@ Result IndexBuffer::SendIndexData(CommandBuffer* command, Buffer* buffer) {
transfer_buffer_ =
MakeUnique<TransferBuffer>(device_, buffer->GetSizeInBytes(), nullptr);
- Result r = transfer_buffer_->Initialize(VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
- VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ Result r = transfer_buffer_->AddUsageFlags(VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ if (!r.IsSuccess())
+ return r;
+
+ r = transfer_buffer_->Initialize();
if (!r.IsSuccess())
return r;
diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc
index 259f375..df301e4 100644
--- a/src/vulkan/pipeline.cc
+++ b/src/vulkan/pipeline.cc
@@ -273,6 +273,18 @@ Result Pipeline::GetDescriptorSlot(uint32_t desc_set,
return {};
}
+Result Pipeline::AddDescriptorBuffer(Buffer* amber_buffer) {
+ // Don't add the buffer if it's already added.
+ const auto& buffer = std::find_if(
+ descriptor_buffers_.begin(), descriptor_buffers_.end(),
+ [&](const Buffer* buf) { return buf == amber_buffer; });
+ if (buffer != descriptor_buffers_.end()) {
+ return {};
+ }
+ descriptor_buffers_.push_back(amber_buffer);
+ return {};
+}
+
Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
if (cmd == nullptr)
return Result("Pipeline::AddBufferDescriptor BufferCommand is nullptr");
@@ -319,7 +331,7 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
if (is_image) {
auto image_desc = MakeUnique<ImageDescriptor>(
cmd->GetBuffer(), desc_type, device_, cmd->GetBaseMipLevel(),
- cmd->GetDescriptorSet(), cmd->GetBinding());
+ cmd->GetDescriptorSet(), cmd->GetBinding(), this);
if (cmd->IsCombinedImageSampler())
image_desc->SetAmberSampler(cmd->GetSampler());
@@ -327,9 +339,10 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
} else {
auto buffer_desc = MakeUnique<BufferDescriptor>(
cmd->GetBuffer(), desc_type, device_, cmd->GetDescriptorSet(),
- cmd->GetBinding());
+ cmd->GetBinding(), this);
descriptors.push_back(std::move(buffer_desc));
}
+ AddDescriptorBuffer(cmd->GetBuffer());
desc = descriptors.back().get();
} else {
if (desc->GetDescriptorType() != desc_type) {
@@ -338,6 +351,7 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
"descriptor types");
}
desc->AsBufferBackedDescriptor()->AddAmberBuffer(cmd->GetBuffer());
+ AddDescriptorBuffer(cmd->GetBuffer());
}
if (cmd->IsUniformDynamic() || cmd->IsSSBODynamic())
@@ -409,6 +423,16 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
}
}
+ // Initialize transfer buffers / images.
+ for (auto buffer : descriptor_buffers_) {
+ if (descriptor_transfer_resources_.count(buffer) == 0) {
+ return Result(
+ "Vulkan: Pipeline::SendDescriptorDataToDeviceIfNeeded() "
+ "descriptor's transfer resource is not found");
+ }
+ descriptor_transfer_resources_[buffer]->Initialize();
+ }
+
// Note that if a buffer for a descriptor is host accessible and
// does not need to record a command to copy data to device, it
// directly writes data to the buffer. The direct write must be
@@ -424,11 +448,27 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
if (!guard.IsRecording())
return guard.GetResult();
- for (auto& info : descriptor_set_info_) {
- for (auto& desc : info.descriptors) {
- Result r = desc->RecordCopyDataToResourceIfNeeded(command_.get());
- if (!r.IsSuccess())
- return r;
+ // Copy descriptor data to transfer resources.
+ for (auto& buffer : descriptor_buffers_) {
+ if (auto transfer_buffer =
+ descriptor_transfer_resources_[buffer]->AsTransferBuffer()) {
+ BufferBackedDescriptor::RecordCopyBufferDataToTransferResourceIfNeeded(
+ GetCommandBuffer(), buffer, transfer_buffer);
+ } else if (auto transfer_image =
+ descriptor_transfer_resources_[buffer]->AsTransferImage()) {
+ transfer_image->ImageBarrier(GetCommandBuffer(),
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ BufferBackedDescriptor::RecordCopyBufferDataToTransferResourceIfNeeded(
+ GetCommandBuffer(), buffer, transfer_image);
+
+ transfer_image->ImageBarrier(GetCommandBuffer(), VK_IMAGE_LAYOUT_GENERAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
+ } else {
+ return Result(
+ "Vulkan: Pipeline::SendDescriptorDataToDeviceIfNeeded() "
+ "this should be unreachable");
}
}
return guard.Submit(GetFenceTimeout());
@@ -472,14 +512,38 @@ void Pipeline::BindVkDescriptorSets(const VkPipelineLayout& pipeline_layout) {
}
Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
+ // Record required commands to copy the data to a host visible buffer.
{
CommandBufferGuard guard(GetCommandBuffer());
if (!guard.IsRecording())
return guard.GetResult();
- for (auto& desc_set : descriptor_set_info_) {
- for (auto& desc : desc_set.descriptors)
- desc->RecordCopyDataToHost(command_.get());
+ for (auto& buffer : descriptor_buffers_) {
+ if (descriptor_transfer_resources_.count(buffer) == 0) {
+ return Result(
+ "Vulkan: Pipeline::ReadbackDescriptorsToHostDataQueue() "
+ "descriptor's transfer resource is not found");
+ }
+ if (auto transfer_buffer =
+ descriptor_transfer_resources_[buffer]->AsTransferBuffer()) {
+ Result r = BufferBackedDescriptor::RecordCopyTransferResourceToHost(
+ GetCommandBuffer(), transfer_buffer);
+ if (!r.IsSuccess())
+ return r;
+ } else if (auto transfer_image = descriptor_transfer_resources_[buffer]
+ ->AsTransferImage()) {
+ transfer_image->ImageBarrier(GetCommandBuffer(),
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
+ Result r = BufferBackedDescriptor::RecordCopyTransferResourceToHost(
+ GetCommandBuffer(), transfer_image);
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ return Result(
+ "Vulkan: Pipeline::ReadbackDescriptorsToHostDataQueue() "
+ "this should be unreachable");
+ }
}
Result r = guard.Submit(GetFenceTimeout());
@@ -487,14 +551,15 @@ Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
return r;
}
- for (auto& desc_set : descriptor_set_info_) {
- for (auto& desc : desc_set.descriptors) {
- Result r = desc->MoveResourceToBufferOutput();
- if (!r.IsSuccess())
- return r;
- }
+ // Move data from transfer buffers to output buffers.
+ for (auto& buffer : descriptor_buffers_) {
+ auto& transfer_resource = descriptor_transfer_resources_[buffer];
+ Result r = BufferBackedDescriptor::MoveTransferResourceToBufferOutput(
+ transfer_resource.get(), buffer);
+ if (!r.IsSuccess())
+ return r;
}
-
+ descriptor_transfer_resources_.clear();
return {};
}
diff --git a/src/vulkan/pipeline.h b/src/vulkan/pipeline.h
index 98b0c2a..1f85cb6 100644
--- a/src/vulkan/pipeline.h
+++ b/src/vulkan/pipeline.h
@@ -27,6 +27,7 @@
#include "src/vulkan/buffer_backed_descriptor.h"
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/push_constant.h"
+#include "src/vulkan/resource.h"
namespace amber {
@@ -59,6 +60,11 @@ class Pipeline {
/// buffer data object and put it into buffer data queue in host.
Result ReadbackDescriptorsToHostDataQueue();
+ std::unordered_map<Buffer*, std::unique_ptr<Resource>>&
+ GetDescriptorTransferResources() {
+ return descriptor_transfer_resources_;
+ }
+
void SetEntryPointName(VkShaderStageFlagBits stage,
const std::string& entry) {
entry_points_[stage] = entry;
@@ -115,10 +121,17 @@ class Pipeline {
Result CreateDescriptorSetLayouts();
Result CreateDescriptorPools();
Result CreateDescriptorSets();
+ /// Adds a buffer used by a descriptor. The added buffers are be stored in
+ /// |descriptor_buffers_| vector in the order they are added.
+ Result AddDescriptorBuffer(Buffer* amber_buffer);
PipelineType pipeline_type_;
std::vector<DescriptorSetInfo> descriptor_set_info_;
std::vector<VkPipelineShaderStageCreateInfo> shader_stage_info_;
+ std::unordered_map<Buffer*, std::unique_ptr<Resource>>
+ descriptor_transfer_resources_;
+ /// Buffers used by descriptors (buffer descriptors and image descriptors).
+ std::vector<Buffer*> descriptor_buffers_;
uint32_t fence_timeout_ms_ = 1000;
bool descriptor_related_objects_already_created_ = false;
diff --git a/src/vulkan/resource.h b/src/vulkan/resource.h
index 1ed447a..d3cc0de 100644
--- a/src/vulkan/resource.h
+++ b/src/vulkan/resource.h
@@ -27,9 +27,10 @@ namespace vulkan {
class CommandBuffer;
class Device;
+class TransferBuffer;
+class TransferImage;
-// Class for Vulkan resources. Its children are Vulkan Buffer, Vulkan Image,
-// and a class for push constant.
+// Class for Vulkan resources. Its children are Vulkan Buffer and Vulkan Image.
class Resource {
public:
virtual ~Resource();
@@ -46,6 +47,12 @@ class Resource {
uint32_t GetSizeInBytes() const { return size_in_bytes_; }
void UpdateMemoryWithRawData(const std::vector<uint8_t>& raw_data);
+ bool IsReadOnly() const { return is_read_only_; }
+ void SetReadOnly(bool read_only) { is_read_only_ = read_only; }
+ virtual Result Initialize() = 0;
+ virtual TransferBuffer* AsTransferBuffer() { return nullptr; }
+ virtual TransferImage* AsTransferImage() { return nullptr; }
+
protected:
Resource(Device* device, uint32_t size);
Result CreateVkBuffer(VkBuffer* buffer, VkBufferUsageFlags usage);
@@ -82,6 +89,7 @@ class Resource {
private:
uint32_t size_in_bytes_ = 0;
void* memory_ptr_ = nullptr;
+ bool is_read_only_ = false;
};
} // namespace vulkan
diff --git a/src/vulkan/transfer_buffer.cc b/src/vulkan/transfer_buffer.cc
index a13b22e..512fb7b 100644
--- a/src/vulkan/transfer_buffer.cc
+++ b/src/vulkan/transfer_buffer.cc
@@ -44,8 +44,14 @@ TransferBuffer::~TransferBuffer() {
}
}
-Result TransferBuffer::Initialize(const VkBufferUsageFlags usage) {
- Result r = CreateVkBuffer(&buffer_, usage);
+Result TransferBuffer::Initialize() {
+ if (buffer_) {
+ return Result(
+ "Vulkan: TransferBuffer::Initialize() transfer buffer already "
+ "initialized.");
+ }
+
+ Result r = CreateVkBuffer(&buffer_, usage_flags_);
if (!r.IsSuccess())
return r;
@@ -58,8 +64,8 @@ Result TransferBuffer::Initialize(const VkBufferUsageFlags usage) {
return r;
// Create buffer view
- if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
- VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) {
+ if (usage_flags_ & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) {
VkBufferViewCreateInfo buffer_view_info = VkBufferViewCreateInfo();
buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
buffer_view_info.buffer = buffer_;
diff --git a/src/vulkan/transfer_buffer.h b/src/vulkan/transfer_buffer.h
index 2c0a51d..7d96bec 100644
--- a/src/vulkan/transfer_buffer.h
+++ b/src/vulkan/transfer_buffer.h
@@ -34,7 +34,17 @@ class TransferBuffer : public Resource {
TransferBuffer(Device* device, uint32_t size_in_bytes, Format* format);
~TransferBuffer() override;
- Result Initialize(const VkBufferUsageFlags usage);
+ TransferBuffer* AsTransferBuffer() override { return this; }
+ Result AddUsageFlags(VkBufferUsageFlags flags) {
+ if (buffer_ != VK_NULL_HANDLE) {
+ return Result(
+ "Vulkan: TransferBuffer::AddUsageFlags Usage flags can't be changed "
+ "after initializing the buffer.");
+ }
+ usage_flags_ |= flags;
+ return {};
+ }
+ Result Initialize() override;
const VkBufferView* GetVkBufferView() const { return &view_; }
VkBuffer GetVkBuffer() const { return buffer_; }
@@ -47,6 +57,7 @@ class TransferBuffer : public Resource {
void CopyToHost(CommandBuffer* command_buffer) override;
private:
+ VkBufferUsageFlags usage_flags_ = 0;
VkBuffer buffer_ = VK_NULL_HANDLE;
VkDeviceMemory memory_ = VK_NULL_HANDLE;
VkBufferView view_ = VK_NULL_HANDLE;
diff --git a/src/vulkan/transfer_image.cc b/src/vulkan/transfer_image.cc
index 545549d..adfae62 100644
--- a/src/vulkan/transfer_image.cc
+++ b/src/vulkan/transfer_image.cc
@@ -70,6 +70,7 @@ TransferImage::TransferImage(Device* device,
const Format& format,
VkImageAspectFlags aspect,
VkImageType image_type,
+ VkImageUsageFlags image_usage_flags,
uint32_t x,
uint32_t y,
uint32_t z,
@@ -96,6 +97,7 @@ TransferImage::TransferImage(Device* device,
image_info_.extent = {x, y, z};
image_info_.mipLevels = mip_levels;
image_info_.samples = GetVkSampleCount(samples);
+ image_info_.usage = image_usage_flags;
}
TransferImage::~TransferImage() {
@@ -122,12 +124,10 @@ TransferImage::~TransferImage() {
}
}
-Result TransferImage::Initialize(VkImageUsageFlags usage) {
+Result TransferImage::Initialize() {
if (image_ != VK_NULL_HANDLE)
return Result("Vulkan::TransferImage was already initialized");
- image_info_.usage = usage;
-
if (device_->GetPtrs()->vkCreateImage(device_->GetVkDevice(), &image_info_,
nullptr, &image_) != VK_SUCCESS) {
return Result("Vulkan::Calling vkCreateImage Fail");
@@ -141,7 +141,7 @@ Result TransferImage::Initialize(VkImageUsageFlags usage) {
return r;
if (aspect_ & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) &&
- !(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
+ !(image_info_.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
// Combined depth/stencil image used as a descriptor. Only one aspect can be
// used for the image view.
r = CreateVkImageView(VK_IMAGE_ASPECT_DEPTH_BIT);
diff --git a/src/vulkan/transfer_image.h b/src/vulkan/transfer_image.h
index 741ee3c..3af71d2 100644
--- a/src/vulkan/transfer_image.h
+++ b/src/vulkan/transfer_image.h
@@ -33,6 +33,7 @@ class TransferImage : public Resource {
const Format& format,
VkImageAspectFlags aspect,
VkImageType image_type,
+ VkImageUsageFlags image_usage_flags,
uint32_t x,
uint32_t y,
uint32_t z,
@@ -42,7 +43,8 @@ class TransferImage : public Resource {
uint32_t samples);
~TransferImage() override;
- Result Initialize(VkImageUsageFlags usage);
+ TransferImage* AsTransferImage() override { return this; }
+ Result Initialize() override;
VkImageView GetVkImageView() const { return view_; }
void ImageBarrier(CommandBuffer* command_buffer,
diff --git a/src/vulkan/vertex_buffer.cc b/src/vulkan/vertex_buffer.cc
index 7503681..557b0a3 100644
--- a/src/vulkan/vertex_buffer.cc
+++ b/src/vulkan/vertex_buffer.cc
@@ -85,8 +85,11 @@ Result VertexBuffer::SendVertexData(CommandBuffer* command) {
uint32_t bytes = buf->GetSizeInBytes();
transfer_buffers_.push_back(
MakeUnique<TransferBuffer>(device_, bytes, nullptr));
- Result r = transfer_buffers_.back()->Initialize(
+ Result r = transfer_buffers_.back()->AddUsageFlags(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ if (!r.IsSuccess())
+ return r;
+ r = transfer_buffers_.back()->Initialize();
std::memcpy(transfer_buffers_.back()->HostAccessibleMemoryPtr(),
buf->GetValues<void>(), bytes);
diff --git a/tests/cases/compute_one_buffer_in_multiple_bindings.amber b/tests/cases/compute_one_buffer_in_multiple_bindings.amber
new file mode 100644
index 0000000..d16bda5
--- /dev/null
+++ b/tests/cases/compute_one_buffer_in_multiple_bindings.amber
@@ -0,0 +1,94 @@
+#!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.
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(set = 0, binding = 0) uniform uniform_0 {
+ vec4 uniform_data_0;
+};
+layout(set = 0, binding = 1) buffer block_0 {
+ vec4 storage_data_0;
+};
+
+layout(set = 1, binding = 0) uniform uniform_1 {
+ vec4 uniform_data_1;
+};
+layout(set = 1, binding = 1) buffer block_1 {
+ vec4 storage_data_1;
+};
+
+uniform layout(set = 2, binding = 0, rgba32f) imageBuffer texelBuffer;
+uniform layout(set = 2, binding = 1) samplerBuffer texelsIn;
+
+void main() {
+ // Verify that the uniform and storage buffers have the same contents and multiply by 2 if true)
+ if (uniform_data_0 == storage_data_0) {
+ storage_data_0 = storage_data_0 * 2;
+ }
+ if (uniform_data_1 == storage_data_1) {
+ storage_data_1 = storage_data_1 * 2;
+ }
+
+ // Load values from index 1 using storage texel buffer.
+ vec4 texel = imageLoad(texelBuffer, 1);
+
+ // Load values from index 2 using uniform texel buffer.
+ vec4 color = texelFetch(texelsIn, 2);
+
+ // Store values to index 3.
+ imageStore(texelBuffer, 3, texel + color);
+}
+END
+
+# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment
+# (i.e. the maximum possible alignment requirement) as 256 bytes.
+# Meaningful data is placed at this offset.
+BUFFER buf0 DATA_TYPE R32G32B32A32_SFLOAT DATA
+1.0 2.0 3.0 4.0
+0.1 0.2 0.3 0.4
+0.5 0.6 0.7 0.8
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+5.0 6.0 7.0 8.0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER buf0 AS uniform DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER buf0 AS storage DESCRIPTOR_SET 0 BINDING 1
+ BIND BUFFER buf0 AS uniform DESCRIPTOR_SET 1 BINDING 0 DESCRIPTOR_OFFSET 256
+ BIND BUFFER buf0 AS storage DESCRIPTOR_SET 1 BINDING 1 DESCRIPTOR_OFFSET 256
+ BIND BUFFER buf0 AS storage_texel_buffer DESCRIPTOR_SET 2 BINDING 0
+ BIND BUFFER buf0 AS uniform_texel_buffer DESCRIPTOR_SET 2 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 IDX 0 EQ 2.0 4.0 6.0 8.0 # Values written to the first storage buffer binding
+EXPECT buf0 IDX 48 EQ 0.6 0.8 1.0 1.2 # Values written to the storage texel buffer binding
+EXPECT buf0 IDX 256 EQ 10.0 12.0 14.0 16.0 # Values written to the second storage buffer binding