blob: dd9a24f9d243045c73139a7d9666dd9968d26bf8 [file] [log] [blame]
// Copyright 2019 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
//
// 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 "src/vulkan/push_constant.h"
#include <algorithm>
#include <cassert>
#include <limits>
#include "src/make_unique.h"
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/device.h"
namespace amber {
namespace vulkan {
PushConstant::PushConstant(Device* device)
: device_(device), buffer_(MakeUnique<Buffer>()) {}
PushConstant::~PushConstant() = default;
VkPushConstantRange PushConstant::GetVkPushConstantRange() {
if (push_constant_data_.empty())
return VkPushConstantRange();
auto it =
std::min_element(push_constant_data_.begin(), push_constant_data_.end(),
[](const BufferInput& a, const BufferInput& b) {
return a.offset < b.offset;
});
assert(it != push_constant_data_.end());
uint32_t first_offset = it->offset;
it = std::max_element(
push_constant_data_.begin(), push_constant_data_.end(),
[](const BufferInput& a, const BufferInput& b) {
return a.offset + static_cast<uint32_t>(a.buffer->GetSizeInBytes()) <
b.offset + static_cast<uint32_t>(b.buffer->GetSizeInBytes());
});
assert(it != push_constant_data_.end());
uint32_t size_in_bytes = it->offset +
static_cast<uint32_t>(it->buffer->GetSizeInBytes()) -
first_offset;
VkPushConstantRange range = VkPushConstantRange();
range.stageFlags = VK_SHADER_STAGE_ALL;
// Based on Vulkan spec, range.offset must be multiple of 4.
range.offset = (first_offset / 4U) * 4U;
// Based on Vulkan spec, range.size must be multiple of 4.
assert(size_in_bytes + 3U <= std::numeric_limits<uint32_t>::max());
range.size = ((size_in_bytes + 3U) / 4U) * 4U;
return range;
}
Result PushConstant::RecordPushConstantVkCommand(
CommandBuffer* command,
VkPipelineLayout pipeline_layout) {
if (push_constant_data_.empty())
return {};
auto push_const_range = GetVkPushConstantRange();
if (push_const_range.offset + push_const_range.size >
device_->GetMaxPushConstants()) {
return Result(
"PushConstant::RecordPushConstantVkCommand push constant size in bytes "
"exceeds maxPushConstantsSize of VkPhysicalDeviceLimits");
}
for (const auto& data : push_constant_data_) {
Result r = UpdateMemoryWithInput(data);
if (!r.IsSuccess())
return r;
}
// Based on spec, offset and size in bytes of push constant must
// be multiple of 4.
if (push_const_range.offset % 4U != 0)
return Result("PushConstant:: Offset must be a multiple of 4");
if (push_const_range.size % 4U != 0)
return Result("PushConstant:: Size must be a multiple of 4");
device_->GetPtrs()->vkCmdPushConstants(
command->GetVkCommandBuffer(), pipeline_layout, VK_SHADER_STAGE_ALL,
push_const_range.offset, push_const_range.size,
buffer_->GetValues<uint8_t*>() + push_const_range.offset);
return {};
}
Result PushConstant::AddBuffer(const Buffer* buffer, uint32_t offset) {
push_constant_data_.emplace_back();
push_constant_data_.back().offset = offset;
push_constant_data_.back().buffer = buffer;
return {};
}
Result PushConstant::UpdateMemoryWithInput(const BufferInput& input) {
if (static_cast<size_t>(input.offset) >= device_->GetMaxPushConstants()) {
return Result(
"Vulkan: UpdateMemoryWithInput BufferInput offset exceeds memory size");
}
if (input.buffer->GetSizeInBytes() >
(device_->GetMaxPushConstants() - input.offset)) {
return Result(
"Vulkan: UpdateMemoryWithInput BufferInput offset + size_in_bytes "
" exceeds memory size");
}
if (!buffer_->GetFormat()) {
buffer_->SetFormat(input.buffer->GetFormat());
} else if (!buffer_->GetFormat()->Equal(input.buffer->GetFormat())) {
return Result("Vulkan: push constants must all have the same format");
}
buffer_->SetDataFromBuffer(input.buffer, input.offset);
return {};
}
} // namespace vulkan
} // namespace amber