blob: 23bb966f102a463e5f4fb7c86ac126747e1f56de [file] [log] [blame]
/* Copyright (c) 2017 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Based on the following files from the Granite rendering engine:
// - vulkan/render_pass.cpp
#include "lib/escher/third_party/granite/vk/render_pass_info.h"
#include "lib/escher/util/bit_ops.h"
namespace escher {
std::pair<vk::AttachmentLoadOp, vk::AttachmentStoreOp>
RenderPassInfo::LoadStoreOpsForColorAttachment(uint32_t index) const {
const bool should_clear_before_use = (clear_attachments & (1u << index)) != 0;
const bool should_load_before_use = (load_attachments & (1u << index)) != 0;
FXL_DCHECK(!should_clear_before_use || !should_load_before_use);
auto load_op = vk::AttachmentLoadOp::eDontCare;
if (should_clear_before_use) {
load_op = vk::AttachmentLoadOp::eClear;
} else if (should_load_before_use) {
// It doesn't make sense to load a transient attachment; the whole point is
// to not load/store (and when possible, not even allocate backing memory).
FXL_DCHECK(!color_attachments[index]->image()->is_transient());
// It doesn't make sense to load a swapchain image, since the point is to
// render a new one every frame.
// NOTE: we might want to relax this someday, e.g. to support temporal AA.
// If so, RenderPass::FillColorAttachmentDescription() would need to be
// adjusted to choose an appropriate initial layout for the attachment.
FXL_DCHECK(!color_attachments[index]->image()->is_swapchain_image());
load_op = vk::AttachmentLoadOp::eLoad;
}
auto store_op = vk::AttachmentStoreOp::eDontCare;
if ((store_attachments & (1u << index)) != 0) {
// It doesn't make sense to store a transient attachment; the whole point is
// to not load/store (and when possible, not even allocate backing memory).
FXL_DCHECK(!color_attachments[index]->image()->is_transient());
store_op = vk::AttachmentStoreOp::eStore;
} else {
FXL_DCHECK(!color_attachments[index]->image()->is_swapchain_image())
<< "Swapchain attachment image " << index
<< " must be marked as eStore.";
}
return {load_op, store_op};
}
std::pair<vk::AttachmentLoadOp, vk::AttachmentStoreOp>
RenderPassInfo::LoadStoreOpsForDepthStencilAttachment() const {
const bool should_clear_before_use =
static_cast<bool>(op_flags & RenderPassInfo::kClearDepthStencilOp);
const bool should_load_before_use =
static_cast<bool>(op_flags & RenderPassInfo::kLoadDepthStencilOp);
const bool should_store_after_use =
static_cast<bool>(op_flags & RenderPassInfo::kStoreDepthStencilOp);
FXL_DCHECK(!should_clear_before_use || !should_load_before_use);
auto load_op = vk::AttachmentLoadOp::eDontCare;
if (should_clear_before_use) {
load_op = vk::AttachmentLoadOp::eClear;
} else if (should_load_before_use) {
// It doesn't make sense to load a transient attachment; the whole point is
// to not load/store (and when possible, not even allocate backing memory).
FXL_DCHECK(!depth_stencil_attachment->image()->is_transient());
load_op = vk::AttachmentLoadOp::eLoad;
}
auto store_op = vk::AttachmentStoreOp::eDontCare;
if (should_store_after_use) {
// It doesn't make sense to store a transient attachment; the whole point is
// to not load/store (and when possible, not even allocate backing memory).
FXL_DCHECK(!depth_stencil_attachment->image()->is_transient());
store_op = vk::AttachmentStoreOp::eStore;
}
return {load_op, store_op};
}
// TODO(ES-83): unit-tests for validation.
// TODO(ES-83): what other validation should be performed?
bool RenderPassInfo::Validate() const {
bool success = true;
// Cannot load and clear the same attachment.
if (auto load_clear_conflicts = clear_attachments & load_attachments) {
success = false;
ForEachBitIndex(load_clear_conflicts, [](uint32_t i) {
FXL_LOG(ERROR) << "RenderPass color attachment " << i
<< " load/clear conflict.";
});
}
// Any attachment marked as clear, load or store must be non-null.
ForEachBitIndex(clear_attachments | load_attachments | store_attachments,
[&](uint32_t i) {
if (i >= num_color_attachments) {
success = false;
FXL_LOG(ERROR) << "Color attachment bit " << i
<< " is > num_color_attachments ("
<< num_color_attachments << ").";
} else if (!color_attachments[i]) {
success = false;
FXL_LOG(ERROR) << "Color attachment bit " << i
<< " is set but attachment is null.";
}
});
// |num_color_attachments| must match the number of non-null attachments.
for (uint32_t i = 0; i < num_color_attachments; ++i) {
if (!color_attachments[i]) {
success = false;
FXL_LOG(ERROR) << "Color attachment " << i << " should not be null.";
}
}
for (uint32_t i = num_color_attachments;
i < VulkanLimits::kNumColorAttachments; ++i) {
if (color_attachments[i]) {
success = false;
FXL_LOG(ERROR) << "Color attachment " << i << " should be null.";
}
}
// There must be at least one attachment.
if (!num_color_attachments && !depth_stencil_attachment) {
success = false;
FXL_LOG(ERROR) << "RenderPass has no attachments.";
}
if (depth_stencil_attachment) {
constexpr auto kLoadAndClearDepthStencil =
kLoadDepthStencilOp | kClearDepthStencilOp;
// Cannot load and clear the same attachment.
if (kLoadAndClearDepthStencil == (op_flags & kLoadAndClearDepthStencil)) {
success = false;
FXL_LOG(ERROR)
<< "RenderPass depth-stencil attachment load/clear conflict.";
}
// Cannot load or store transient image attachments.
if (depth_stencil_attachment->image()->is_transient()) {
if (op_flags & kLoadDepthStencilOp) {
success = false;
FXL_LOG(ERROR)
<< "Load flag specified for transient depth/stencil attachment.";
}
if (op_flags & kStoreDepthStencilOp) {
success = false;
FXL_LOG(ERROR)
<< "Load flag specified for transient depth/stencil attachment.";
}
}
// Cannot specify two conflicting depth-stencil layouts.
constexpr auto kBothDepthStencilLayouts =
kOptimalDepthStencilLayoutOp | kDepthStencilReadOnlyLayoutOp;
if (kBothDepthStencilLayouts == (op_flags & kBothDepthStencilLayouts)) {
success = false;
FXL_LOG(ERROR) << "Depth attachment is specified as both optimal "
"read-only and read-write.";
}
} else if (op_flags & (kClearDepthStencilOp | kLoadDepthStencilOp |
kStoreDepthStencilOp | kOptimalDepthStencilLayoutOp |
kDepthStencilReadOnlyLayoutOp)) {
success = false;
FXL_LOG(ERROR)
<< "RenderPass has no depth-stencil attachment, but depth-stencil "
"ops are specified.";
}
return success;
}
} // namespace escher